1package config
2
3import (
4 "runtime"
5
6 "github.com/bnema/dumber/internal/application/port"
7)
8
9const (
10 // maxSkiaCPUThreads is the maximum number of Skia CPU painting threads
11 // supported by WebKitGTK (0-8 range).
12 maxSkiaCPUThreads = 8
13
14 // maxSkiaGPUThreads is the maximum number of Skia GPU painting threads
15 // supported by WebKitGTK (-1 to 8 range, where -1 means "let WebKit decide").
16 maxSkiaGPUThreads = 8
17
18 // Memory tier thresholds (in bytes)
19 // Explicitly typed as uint64 to prevent overflow on 32-bit systems.
20 ramTierLow uint64 = 8 * 1024 * 1024 * 1024 // 8 GB
21 ramTierMedium uint64 = 16 * 1024 * 1024 * 1024 // 16 GB
22 ramTierHigh uint64 = 32 * 1024 * 1024 * 1024 // 32 GB
23
24 // VRAM tier thresholds (in bytes)
25 vramTierLow uint64 = 4 * 1024 * 1024 * 1024 // 4 GB (low-end discrete / integrated)
26 vramTierMid uint64 = 8 * 1024 * 1024 * 1024 // 8 GB (mid-range discrete)
27 vramTierHigh uint64 = 16 * 1024 * 1024 * 1024 // 16 GB (high-end discrete)
28
29 // GPU thread scaling values
30 gpuThreadsHigh = 8 // For 16GB+ VRAM
31 gpuThreadsMid = 6 // For 8-16GB VRAM
32 gpuThreadsLow = 4 // For 4-8GB VRAM
33 gpuThreadsMin = 2 // For <4GB VRAM or unknown
34
35 // Memory limit values (in MB)
36 webMemHigh = 4096 // For 32GB+ RAM
37 webMemMedium = 3072 // For 16-32GB RAM
38 webMemLow = 2048 // For 8-16GB RAM
39 webMemMin = 1024 // For <8GB RAM
40 netMemHigh = 1024 // For 32GB+ RAM
41 netMemMedium = 768 // For 16-32GB RAM
42 netMemLow = 512 // For 8-16GB RAM
43 netMemMin = 256 // For <8GB RAM
44 webMemFallback = 2048 // When RAM unknown
45 netMemFallback = 512 // When RAM unknown
46
47 // WebView pool prewarm counts
48 poolPrewarmHigh = 12 // For 32GB+ RAM
49 poolPrewarmMedium = 8 // For 16-32GB RAM
50 poolPrewarmLow = 6 // For 8-16GB RAM
51 poolPrewarmMin = 4 // For <8GB RAM
52 poolPrewarmFallback = 8 // When RAM unknown
53)
54
55// ResolvedPerformanceSettings contains the computed performance settings
56// after profile resolution. These are the actual values to apply.
57type ResolvedPerformanceSettings struct {
58 // Skia threading
59 SkiaCPUPaintingThreads int
60 SkiaGPUPaintingThreads int
61 SkiaEnableCPURendering bool
62
63 // Web process memory pressure
64 WebProcessMemoryLimitMB int
65 WebProcessMemoryPollIntervalSec float64
66 WebProcessMemoryConservativeThreshold float64
67 WebProcessMemoryStrictThreshold float64
68
69 // Network process memory pressure
70 NetworkProcessMemoryLimitMB int
71 NetworkProcessMemoryPollIntervalSec float64
72 NetworkProcessMemoryConservativeThreshold float64
73 NetworkProcessMemoryStrictThreshold float64
74
75 // WebView pool
76 WebViewPoolPrewarmCount int
77}
78
79// ResolvePerformanceProfile computes the actual performance settings based on
80// the selected profile. For ProfileCustom, it returns the user's configured values.
81// For other profiles, it computes appropriate values based on detected hardware.
82//
83// The hw parameter is optional - if nil, fallback values are used.
84func ResolvePerformanceProfile(cfg *PerformanceConfig, hw *port.HardwareInfo) ResolvedPerformanceSettings {
85 switch cfg.Profile {
86 case ProfileLite:
87 return resolveLiteProfile(hw)
88 case ProfileMax:
89 return resolveMaxProfile(hw)
90 case ProfileCustom:
91 return resolveCustomProfile(cfg)
92 default: // ProfileDefault or empty
93 return resolveDefaultProfile()
94 }
95}
96
97// resolveDefaultProfile returns WebKit defaults (no tuning).
98func resolveDefaultProfile() ResolvedPerformanceSettings {
99 return ResolvedPerformanceSettings{
100 // All zeros/negatives mean "unset, use WebKit defaults"
101 SkiaCPUPaintingThreads: 0,
102 SkiaGPUPaintingThreads: -1,
103 SkiaEnableCPURendering: false,
104
105 WebProcessMemoryLimitMB: 0,
106 WebProcessMemoryPollIntervalSec: 0,
107 WebProcessMemoryConservativeThreshold: 0,
108 WebProcessMemoryStrictThreshold: 0,
109
110 NetworkProcessMemoryLimitMB: 0,
111 NetworkProcessMemoryPollIntervalSec: 0,
112 NetworkProcessMemoryConservativeThreshold: 0,
113 NetworkProcessMemoryStrictThreshold: 0,
114
115 WebViewPoolPrewarmCount: 4,
116 }
117}
118
119// resolveLiteProfile returns settings optimized for low-RAM systems.
120// Uses hardware info to scale CPU threads if available.
121func resolveLiteProfile(hw *port.HardwareInfo) ResolvedPerformanceSettings {
122 // Use 2 CPU threads, or scale down on very low-core systems
123 cpuThreads := 2
124 if hw != nil && hw.CPUCores > 0 && hw.CPUCores < 4 {
125 cpuThreads = 1
126 }
127
128 return ResolvedPerformanceSettings{
129 SkiaCPUPaintingThreads: cpuThreads,
130 SkiaGPUPaintingThreads: -1, // unset
131 SkiaEnableCPURendering: false,
132
133 WebProcessMemoryLimitMB: 768,
134 WebProcessMemoryPollIntervalSec: 0, // use WebKit default (30s)
135 WebProcessMemoryConservativeThreshold: 0.25,
136 WebProcessMemoryStrictThreshold: 0.4,
137
138 NetworkProcessMemoryLimitMB: 384,
139 NetworkProcessMemoryPollIntervalSec: 0,
140 NetworkProcessMemoryConservativeThreshold: 0.25,
141 NetworkProcessMemoryStrictThreshold: 0.4,
142
143 WebViewPoolPrewarmCount: 2,
144 }
145}
146
147// resolveMaxProfile returns settings optimized for maximum responsiveness.
148// Scales values based on detected hardware capabilities.
149func resolveMaxProfile(hw *port.HardwareInfo) ResolvedPerformanceSettings {
150 // CPU threads: use physical cores (not threads), capped at WebKitGTK limit
151 cpuThreads := computeMaxCPUThreads(hw)
152
153 // GPU threads: scale based on VRAM
154 gpuThreads := computeMaxGPUThreads(hw)
155
156 // WebView pool: scale based on RAM
157 poolPrewarm := computeMaxPoolPrewarm(hw)
158
159 return ResolvedPerformanceSettings{
160 SkiaCPUPaintingThreads: cpuThreads,
161 SkiaGPUPaintingThreads: gpuThreads,
162 SkiaEnableCPURendering: false,
163
164 // Max profile: no memory limits, let WebKit use defaults
165 WebProcessMemoryLimitMB: 0,
166 WebProcessMemoryPollIntervalSec: 0,
167 WebProcessMemoryConservativeThreshold: 0,
168 WebProcessMemoryStrictThreshold: 0,
169
170 NetworkProcessMemoryLimitMB: 0,
171 NetworkProcessMemoryPollIntervalSec: 0,
172 NetworkProcessMemoryConservativeThreshold: 0,
173 NetworkProcessMemoryStrictThreshold: 0,
174
175 WebViewPoolPrewarmCount: poolPrewarm,
176 }
177}
178
179// computeMaxCPUThreads calculates Skia CPU threads for max profile.
180// Uses physical cores (not hyperthreaded) capped at WebKitGTK's limit.
181func computeMaxCPUThreads(hw *port.HardwareInfo) int {
182 var cpuThreads int
183
184 if hw != nil && hw.CPUCores > 0 {
185 // Use physical cores directly - they're better for parallel painting
186 cpuThreads = hw.CPUCores
187 } else {
188 // Fallback: assume hyperthreading, use half of logical CPUs
189 cpuThreads = runtime.NumCPU() / 2
190 }
191
192 // Minimum 4 for meaningful parallelism
193 if cpuThreads < 4 {
194 cpuThreads = 4
195 }
196 // Cap at WebKitGTK Skia limit
197 if cpuThreads > maxSkiaCPUThreads {
198 cpuThreads = maxSkiaCPUThreads
199 }
200
201 return cpuThreads
202}
203
204// computeMaxGPUThreads calculates Skia GPU threads based on VRAM.
205func computeMaxGPUThreads(hw *port.HardwareInfo) int {
206 if hw == nil || hw.VRAM == 0 {
207 // No VRAM info - use conservative default
208 return gpuThreadsMin
209 }
210
211 // Scale GPU threads based on VRAM tiers
212 switch {
213 case hw.VRAM >= vramTierHigh: // 16GB+ VRAM (high-end: RX 7900, RTX 4080+)
214 return gpuThreadsHigh
215 case hw.VRAM >= vramTierMid: // 8-16GB VRAM (mid-range: RX 7800, RTX 4070)
216 return gpuThreadsMid
217 case hw.VRAM >= vramTierLow: // 4-8GB VRAM (entry discrete)
218 return gpuThreadsLow
219 default: // <4GB VRAM (integrated / old discrete)
220 return gpuThreadsMin
221 }
222}
223
224// computeMaxMemoryLimits returns web process and network process memory limits.
225func computeMaxMemoryLimits(hw *port.HardwareInfo) (webMB, netMB int) {
226 if hw == nil || hw.TotalRAM == 0 {
227 // Fallback: assume 16GB system
228 return webMemFallback, netMemFallback
229 }
230
231 // Scale memory limits based on system RAM
232 switch {
233 case hw.TotalRAM >= ramTierHigh: // 32GB+ RAM
234 return webMemHigh, netMemHigh
235 case hw.TotalRAM >= ramTierMedium: // 16-32GB RAM
236 return webMemMedium, netMemMedium
237 case hw.TotalRAM >= ramTierLow: // 8-16GB RAM
238 return webMemLow, netMemLow
239 default: // <8GB RAM
240 return webMemMin, netMemMin
241 }
242}
243
244// computeMaxPoolPrewarm returns WebView pool prewarm count based on RAM.
245func computeMaxPoolPrewarm(hw *port.HardwareInfo) int {
246 if hw == nil || hw.TotalRAM == 0 {
247 return poolPrewarmFallback
248 }
249
250 switch {
251 case hw.TotalRAM >= ramTierHigh: // 32GB+
252 return poolPrewarmHigh
253 case hw.TotalRAM >= ramTierMedium: // 16-32GB
254 return poolPrewarmMedium
255 case hw.TotalRAM >= ramTierLow: // 8-16GB
256 return poolPrewarmLow
257 default: // <8GB
258 return poolPrewarmMin
259 }
260}
261
262// resolveCustomProfile returns the user's configured values directly.
263func resolveCustomProfile(cfg *PerformanceConfig) ResolvedPerformanceSettings {
264 return ResolvedPerformanceSettings{
265 SkiaCPUPaintingThreads: cfg.SkiaCPUPaintingThreads,
266 SkiaGPUPaintingThreads: cfg.SkiaGPUPaintingThreads,
267 SkiaEnableCPURendering: cfg.SkiaEnableCPURendering,
268
269 WebProcessMemoryLimitMB: cfg.WebProcessMemoryLimitMB,
270 WebProcessMemoryPollIntervalSec: cfg.WebProcessMemoryPollIntervalSec,
271 WebProcessMemoryConservativeThreshold: cfg.WebProcessMemoryConservativeThreshold,
272 WebProcessMemoryStrictThreshold: cfg.WebProcessMemoryStrictThreshold,
273
274 NetworkProcessMemoryLimitMB: cfg.NetworkProcessMemoryLimitMB,
275 NetworkProcessMemoryPollIntervalSec: cfg.NetworkProcessMemoryPollIntervalSec,
276 NetworkProcessMemoryConservativeThreshold: cfg.NetworkProcessMemoryConservativeThreshold,
277 NetworkProcessMemoryStrictThreshold: cfg.NetworkProcessMemoryStrictThreshold,
278
279 WebViewPoolPrewarmCount: cfg.WebViewPoolPrewarmCount,
280 }
281}
282
283// HasCustomPerformanceFields returns true if any individual performance tuning
284// fields are set to non-default values. Used for validation.
285func HasCustomPerformanceFields(cfg *PerformanceConfig) bool {
286 return cfg.SkiaCPUPaintingThreads != 0 ||
287 cfg.SkiaGPUPaintingThreads != -1 ||
288 cfg.SkiaEnableCPURendering ||
289 cfg.WebProcessMemoryLimitMB != 0 ||
290 cfg.WebProcessMemoryPollIntervalSec != 0 ||
291 cfg.WebProcessMemoryConservativeThreshold != 0 ||
292 cfg.WebProcessMemoryStrictThreshold != 0 ||
293 cfg.NetworkProcessMemoryLimitMB != 0 ||
294 cfg.NetworkProcessMemoryPollIntervalSec != 0 ||
295 cfg.NetworkProcessMemoryConservativeThreshold != 0 ||
296 cfg.NetworkProcessMemoryStrictThreshold != 0
297}
298
299// IsValidPerformanceProfile returns true if the profile name is recognized.
300func IsValidPerformanceProfile(profile PerformanceProfile) bool {
301 switch profile {
302 case ProfileDefault, ProfileLite, ProfileMax, ProfileCustom, "":
303 return true
304 default:
305 return false
306 }
307}