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 ProfileBalanced:
89 return resolveBalancedProfile()
90 case ProfileMax:
91 return resolveMaxProfile(hw)
92 case ProfileCustom:
93 return resolveCustomProfile(cfg)
94 default: // ProfileDefault or empty
95 return resolveDefaultProfile()
96 }
97}
98
99// resolveDefaultProfile returns WebKit defaults (no tuning).
100func resolveDefaultProfile() ResolvedPerformanceSettings {
101 return ResolvedPerformanceSettings{
102 // All zeros/negatives mean "unset, use WebKit defaults"
103 SkiaCPUPaintingThreads: 0,
104 SkiaGPUPaintingThreads: -1,
105 SkiaEnableCPURendering: false,
106
107 WebProcessMemoryLimitMB: 0,
108 WebProcessMemoryPollIntervalSec: 0,
109 WebProcessMemoryConservativeThreshold: 0,
110 WebProcessMemoryStrictThreshold: 0,
111
112 NetworkProcessMemoryLimitMB: 0,
113 NetworkProcessMemoryPollIntervalSec: 0,
114 NetworkProcessMemoryConservativeThreshold: 0,
115 NetworkProcessMemoryStrictThreshold: 0,
116
117 WebViewPoolPrewarmCount: 4,
118 }
119}
120
121// resolveBalancedProfile preserves the legacy balanced profile as WebKit's
122// default tuning with the normal WebView prewarm count.
123func resolveBalancedProfile() ResolvedPerformanceSettings {
124 return resolveDefaultProfile()
125}
126
127// resolveLiteProfile returns settings optimized for low-RAM systems.
128// Uses hardware info to scale CPU threads if available.
129func resolveLiteProfile(hw *port.HardwareInfo) ResolvedPerformanceSettings {
130 // Use 2 CPU threads, or scale down on very low-core systems
131 cpuThreads := 2
132 if hw != nil && hw.CPUCores > 0 && hw.CPUCores < 4 {
133 cpuThreads = 1
134 }
135
136 return ResolvedPerformanceSettings{
137 SkiaCPUPaintingThreads: cpuThreads,
138 SkiaGPUPaintingThreads: -1, // unset
139 SkiaEnableCPURendering: false,
140
141 WebProcessMemoryLimitMB: 768,
142 WebProcessMemoryPollIntervalSec: 0, // use WebKit default (30s)
143 WebProcessMemoryConservativeThreshold: 0.25,
144 WebProcessMemoryStrictThreshold: 0.4,
145
146 NetworkProcessMemoryLimitMB: 384,
147 NetworkProcessMemoryPollIntervalSec: 0,
148 NetworkProcessMemoryConservativeThreshold: 0.25,
149 NetworkProcessMemoryStrictThreshold: 0.4,
150
151 WebViewPoolPrewarmCount: 2,
152 }
153}
154
155// resolveMaxProfile returns settings optimized for maximum responsiveness.
156// Scales values based on detected hardware capabilities.
157func resolveMaxProfile(hw *port.HardwareInfo) ResolvedPerformanceSettings {
158 // CPU threads: use physical cores (not threads), capped at WebKitGTK limit
159 cpuThreads := computeMaxCPUThreads(hw)
160
161 // GPU threads: scale based on VRAM
162 gpuThreads := computeMaxGPUThreads(hw)
163
164 // WebView pool: scale based on RAM
165 poolPrewarm := computeMaxPoolPrewarm(hw)
166
167 return ResolvedPerformanceSettings{
168 SkiaCPUPaintingThreads: cpuThreads,
169 SkiaGPUPaintingThreads: gpuThreads,
170 SkiaEnableCPURendering: false,
171
172 // Max profile: no memory limits, let WebKit use defaults
173 WebProcessMemoryLimitMB: 0,
174 WebProcessMemoryPollIntervalSec: 0,
175 WebProcessMemoryConservativeThreshold: 0,
176 WebProcessMemoryStrictThreshold: 0,
177
178 NetworkProcessMemoryLimitMB: 0,
179 NetworkProcessMemoryPollIntervalSec: 0,
180 NetworkProcessMemoryConservativeThreshold: 0,
181 NetworkProcessMemoryStrictThreshold: 0,
182
183 WebViewPoolPrewarmCount: poolPrewarm,
184 }
185}
186
187// computeMaxCPUThreads calculates Skia CPU threads for max profile.
188// Uses physical cores (not hyperthreaded) capped at WebKitGTK's limit.
189func computeMaxCPUThreads(hw *port.HardwareInfo) int {
190 var cpuThreads int
191
192 if hw != nil && hw.CPUCores > 0 {
193 // Use physical cores directly - they're better for parallel painting
194 cpuThreads = hw.CPUCores
195 } else {
196 // Fallback: assume hyperthreading, use half of logical CPUs
197 cpuThreads = runtime.NumCPU() / 2
198 }
199
200 // Minimum 4 for meaningful parallelism
201 if cpuThreads < 4 {
202 cpuThreads = 4
203 }
204 // Cap at WebKitGTK Skia limit
205 if cpuThreads > maxSkiaCPUThreads {
206 cpuThreads = maxSkiaCPUThreads
207 }
208
209 return cpuThreads
210}
211
212// computeMaxGPUThreads calculates Skia GPU threads based on VRAM.
213func computeMaxGPUThreads(hw *port.HardwareInfo) int {
214 if hw == nil || hw.VRAM == 0 {
215 // No VRAM info - use conservative default
216 return gpuThreadsMin
217 }
218
219 // Scale GPU threads based on VRAM tiers
220 switch {
221 case hw.VRAM >= vramTierHigh: // 16GB+ VRAM (high-end: RX 7900, RTX 4080+)
222 return gpuThreadsHigh
223 case hw.VRAM >= vramTierMid: // 8-16GB VRAM (mid-range: RX 7800, RTX 4070)
224 return gpuThreadsMid
225 case hw.VRAM >= vramTierLow: // 4-8GB VRAM (entry discrete)
226 return gpuThreadsLow
227 default: // <4GB VRAM (integrated / old discrete)
228 return gpuThreadsMin
229 }
230}
231
232// computeMaxMemoryLimits returns web process and network process memory limits.
233func computeMaxMemoryLimits(hw *port.HardwareInfo) (webMB, netMB int) {
234 if hw == nil || hw.TotalRAM == 0 {
235 // Fallback: assume 16GB system
236 return webMemFallback, netMemFallback
237 }
238
239 // Scale memory limits based on system RAM
240 switch {
241 case hw.TotalRAM >= ramTierHigh: // 32GB+ RAM
242 return webMemHigh, netMemHigh
243 case hw.TotalRAM >= ramTierMedium: // 16-32GB RAM
244 return webMemMedium, netMemMedium
245 case hw.TotalRAM >= ramTierLow: // 8-16GB RAM
246 return webMemLow, netMemLow
247 default: // <8GB RAM
248 return webMemMin, netMemMin
249 }
250}
251
252// computeMaxPoolPrewarm returns WebView pool prewarm count based on RAM.
253func computeMaxPoolPrewarm(hw *port.HardwareInfo) int {
254 if hw == nil || hw.TotalRAM == 0 {
255 return poolPrewarmFallback
256 }
257
258 switch {
259 case hw.TotalRAM >= ramTierHigh: // 32GB+
260 return poolPrewarmHigh
261 case hw.TotalRAM >= ramTierMedium: // 16-32GB
262 return poolPrewarmMedium
263 case hw.TotalRAM >= ramTierLow: // 8-16GB
264 return poolPrewarmLow
265 default: // <8GB
266 return poolPrewarmMin
267 }
268}
269
270// resolveCustomProfile returns the user's configured values directly.
271func resolveCustomProfile(cfg *PerformanceConfig) ResolvedPerformanceSettings {
272 return ResolvedPerformanceSettings{
273 SkiaCPUPaintingThreads: cfg.SkiaCPUPaintingThreads,
274 SkiaGPUPaintingThreads: cfg.SkiaGPUPaintingThreads,
275 SkiaEnableCPURendering: cfg.SkiaEnableCPURendering,
276
277 WebProcessMemoryLimitMB: cfg.WebProcessMemoryLimitMB,
278 WebProcessMemoryPollIntervalSec: cfg.WebProcessMemoryPollIntervalSec,
279 WebProcessMemoryConservativeThreshold: cfg.WebProcessMemoryConservativeThreshold,
280 WebProcessMemoryStrictThreshold: cfg.WebProcessMemoryStrictThreshold,
281
282 NetworkProcessMemoryLimitMB: cfg.NetworkProcessMemoryLimitMB,
283 NetworkProcessMemoryPollIntervalSec: cfg.NetworkProcessMemoryPollIntervalSec,
284 NetworkProcessMemoryConservativeThreshold: cfg.NetworkProcessMemoryConservativeThreshold,
285 NetworkProcessMemoryStrictThreshold: cfg.NetworkProcessMemoryStrictThreshold,
286
287 WebViewPoolPrewarmCount: cfg.WebViewPoolPrewarmCount,
288 }
289}
290
291// HasCustomPerformanceFields returns true if any individual performance tuning
292// fields are set to non-default values. Used for validation.
293func HasCustomPerformanceFields(cfg *PerformanceConfig) bool {
294 return cfg.SkiaCPUPaintingThreads != 0 ||
295 (cfg.SkiaGPUPaintingThreads != -1 && cfg.SkiaGPUPaintingThreads != 0) ||
296 cfg.SkiaEnableCPURendering ||
297 cfg.WebProcessMemoryLimitMB != 0 ||
298 cfg.WebProcessMemoryPollIntervalSec != 0 ||
299 cfg.WebProcessMemoryConservativeThreshold != 0 ||
300 cfg.WebProcessMemoryStrictThreshold != 0 ||
301 cfg.NetworkProcessMemoryLimitMB != 0 ||
302 cfg.NetworkProcessMemoryPollIntervalSec != 0 ||
303 cfg.NetworkProcessMemoryConservativeThreshold != 0 ||
304 cfg.NetworkProcessMemoryStrictThreshold != 0
305}
306
307// IsValidPerformanceProfile returns true if the profile name is recognized.
308func IsValidPerformanceProfile(profile PerformanceProfile) bool {
309 switch profile {
310 case ProfileDefault, ProfileLite, ProfileBalanced, ProfileMax, ProfileCustom, "":
311 return true
312 default:
313 return false
314 }
315}