feat(video): add comprehensive VA-API/VDPAU hardware video acceleration with auto GPU detection
- commit
- 3d1723e59fb424699bea57f35ef514b6ac230d5e
- author
- bamen · · 2025-09-12 05:23 UTC
- parent
- 80fc97a
- signature
- unsigned
6 files · +354 -2
M internal/config/config.go +50 -1
| @@ -11,6 +11,7 @@ import ( | |||
| 11 | 11 | "sync" | |
| 12 | 12 | "time" | |
| 13 | 13 | ||
| 14 | + | "github.com/bnema/dumber/pkg/gpu" | |
| 14 | 15 | "github.com/fsnotify/fsnotify" | |
| 15 | 16 | "github.com/spf13/viper" | |
| 16 | 17 | ) | |
| @@ -28,7 +29,8 @@ type Config struct { | |||
| 28 | 29 | SearchShortcuts map[string]SearchShortcut `mapstructure:"search_shortcuts" yaml:"search_shortcuts"` | |
| 29 | 30 | Dmenu DmenuConfig `mapstructure:"dmenu" yaml:"dmenu"` | |
| 30 | 31 | Logging LoggingConfig `mapstructure:"logging" yaml:"logging"` | |
| 31 | - | Appearance AppearanceConfig `mapstructure:"appearance" yaml:"appearance"` | |
| 32 | + | Appearance AppearanceConfig `mapstructure:"appearance" yaml:"appearance"` | |
| 33 | + | VideoAcceleration VideoAccelerationConfig `mapstructure:"video_acceleration" yaml:"video_acceleration"` | |
| 32 | 34 | // RenderingMode controls GPU/CPU rendering selection for WebKit | |
| 33 | 35 | RenderingMode RenderingMode `mapstructure:"rendering_mode" yaml:"rendering_mode"` | |
| 34 | 36 | } | |
| @@ -109,6 +111,15 @@ type AppearanceConfig struct { | |||
| 109 | 111 | DefaultFontSize int `mapstructure:"default_font_size" yaml:"default_font_size"` | |
| 110 | 112 | } | |
| 111 | 113 | ||
| 114 | + | // VideoAccelerationConfig holds video hardware acceleration preferences. | |
| 115 | + | type VideoAccelerationConfig struct { | |
| 116 | + | EnableVAAPI bool `mapstructure:"enable_vaapi" yaml:"enable_vaapi"` | |
| 117 | + | AutoDetectGPU bool `mapstructure:"auto_detect_gpu" yaml:"auto_detect_gpu"` | |
| 118 | + | VAAPIDriverName string `mapstructure:"vaapi_driver_name" yaml:"vaapi_driver_name"` | |
| 119 | + | EnableAllDrivers bool `mapstructure:"enable_all_drivers" yaml:"enable_all_drivers"` | |
| 120 | + | LegacyVAAPI bool `mapstructure:"legacy_vaapi" yaml:"legacy_vaapi"` | |
| 121 | + | } | |
| 122 | + | ||
| 112 | 123 | // Manager handles configuration loading, watching, and reloading. | |
| 113 | 124 | type Manager struct { | |
| 114 | 125 | config *Config | |
| @@ -178,6 +189,21 @@ func NewManager() (*Manager, error) { | |||
| 178 | 189 | return nil, fmt.Errorf("failed to bind DUMBER_RENDERING_MODE: %w", err) | |
| 179 | 190 | } | |
| 180 | 191 | ||
| 192 | + | // Video acceleration environment variable bindings | |
| 193 | + | videoAccelEnvBindings := map[string]string{ | |
| 194 | + | "video_acceleration.enable_vaapi": "DUMBER_VIDEO_ACCELERATION_ENABLE", | |
| 195 | + | "video_acceleration.auto_detect_gpu": "DUMBER_VIDEO_AUTO_DETECT", | |
| 196 | + | "video_acceleration.vaapi_driver_name": "LIBVA_DRIVER_NAME", | |
| 197 | + | "video_acceleration.enable_all_drivers": "GST_VAAPI_ALL_DRIVERS", | |
| 198 | + | "video_acceleration.legacy_vaapi": "WEBKIT_GST_ENABLE_LEGACY_VAAPI", | |
| 199 | + | } | |
| 200 | + | ||
| 201 | + | for key, env := range videoAccelEnvBindings { | |
| 202 | + | if err := v.BindEnv(key, env); err != nil { | |
| 203 | + | return nil, fmt.Errorf("failed to bind environment variable %s: %w", env, err) | |
| 204 | + | } | |
| 205 | + | } | |
| 206 | + | ||
| 181 | 207 | return &Manager{ | |
| 182 | 208 | viper: v, | |
| 183 | 209 | callbacks: make([]func(*Config), 0), | |
| @@ -237,6 +263,14 @@ func (m *Manager) Load() error { | |||
| 237 | 263 | config.RenderingMode = RenderingModeAuto | |
| 238 | 264 | } | |
| 239 | 265 | ||
| 266 | + | // Auto-detect GPU if enabled and driver name is not set | |
| 267 | + | if config.VideoAcceleration.AutoDetectGPU && config.VideoAcceleration.VAAPIDriverName == "" { | |
| 268 | + | gpuInfo := gpu.DetectGPU() | |
| 269 | + | if gpuInfo.SupportsVAAPI() { | |
| 270 | + | config.VideoAcceleration.VAAPIDriverName = gpuInfo.GetVAAPIDriverName() | |
| 271 | + | } | |
| 272 | + | } | |
| 273 | + | ||
| 240 | 274 | m.config = config | |
| 241 | 275 | return nil | |
| 242 | 276 | } | |
| @@ -324,6 +358,14 @@ func (m *Manager) reload() error { | |||
| 324 | 358 | config.RenderingMode = RenderingModeAuto | |
| 325 | 359 | } | |
| 326 | 360 | ||
| 361 | + | // Auto-detect GPU if enabled and driver name is not set | |
| 362 | + | if config.VideoAcceleration.AutoDetectGPU && config.VideoAcceleration.VAAPIDriverName == "" { | |
| 363 | + | gpuInfo := gpu.DetectGPU() | |
| 364 | + | if gpuInfo.SupportsVAAPI() { | |
| 365 | + | config.VideoAcceleration.VAAPIDriverName = gpuInfo.GetVAAPIDriverName() | |
| 366 | + | } | |
| 367 | + | } | |
| 368 | + | ||
| 327 | 369 | m.config = config | |
| 328 | 370 | return nil | |
| 329 | 371 | } | |
| @@ -379,6 +421,13 @@ func (m *Manager) setDefaults() { | |||
| 379 | 421 | m.viper.SetDefault("appearance.monospace_font", defaults.Appearance.MonospaceFont) | |
| 380 | 422 | m.viper.SetDefault("appearance.default_font_size", defaults.Appearance.DefaultFontSize) | |
| 381 | 423 | ||
| 424 | + | // Video acceleration defaults | |
| 425 | + | m.viper.SetDefault("video_acceleration.enable_vaapi", defaults.VideoAcceleration.EnableVAAPI) | |
| 426 | + | m.viper.SetDefault("video_acceleration.auto_detect_gpu", defaults.VideoAcceleration.AutoDetectGPU) | |
| 427 | + | m.viper.SetDefault("video_acceleration.vaapi_driver_name", defaults.VideoAcceleration.VAAPIDriverName) | |
| 428 | + | m.viper.SetDefault("video_acceleration.enable_all_drivers", defaults.VideoAcceleration.EnableAllDrivers) | |
| 429 | + | m.viper.SetDefault("video_acceleration.legacy_vaapi", defaults.VideoAcceleration.LegacyVAAPI) | |
| 430 | + | ||
| 382 | 431 | // Rendering defaults | |
| 383 | 432 | m.viper.SetDefault("rendering_mode", string(RenderingModeAuto)) | |
| 384 | 433 | } | |
| @@ -11,6 +11,7 @@ import ( | |||||
| 11 | "sync" | 11 | "sync" | ||
| 12 | "time" | 12 | "time" | ||
| 13 | 13 | ||||
| 14 | + | "github.com/bnema/dumber/pkg/gpu" | |||
| 14 | "github.com/fsnotify/fsnotify" | 15 | "github.com/fsnotify/fsnotify" | ||
| 15 | "github.com/spf13/viper" | 16 | "github.com/spf13/viper" | ||
| 16 | ) | 17 | ) | ||
| @@ -28,7 +29,8 @@ type Config struct { | |||||
| 28 | SearchShortcuts map[string]SearchShortcut `mapstructure:"search_shortcuts" yaml:"search_shortcuts"` | 29 | SearchShortcuts map[string]SearchShortcut `mapstructure:"search_shortcuts" yaml:"search_shortcuts"` | ||
| 29 | Dmenu DmenuConfig `mapstructure:"dmenu" yaml:"dmenu"` | 30 | Dmenu DmenuConfig `mapstructure:"dmenu" yaml:"dmenu"` | ||
| 30 | Logging LoggingConfig `mapstructure:"logging" yaml:"logging"` | 31 | Logging LoggingConfig `mapstructure:"logging" yaml:"logging"` | ||
| 31 | - | Appearance AppearanceConfig `mapstructure:"appearance" yaml:"appearance"` | 32 | + | Appearance AppearanceConfig `mapstructure:"appearance" yaml:"appearance"` |
| 33 | + | VideoAcceleration VideoAccelerationConfig `mapstructure:"video_acceleration" yaml:"video_acceleration"` | |||
| 32 | // RenderingMode controls GPU/CPU rendering selection for WebKit | 34 | // RenderingMode controls GPU/CPU rendering selection for WebKit | ||
| 33 | RenderingMode RenderingMode `mapstructure:"rendering_mode" yaml:"rendering_mode"` | 35 | RenderingMode RenderingMode `mapstructure:"rendering_mode" yaml:"rendering_mode"` | ||
| 34 | } | 36 | } | ||
| @@ -109,6 +111,15 @@ type AppearanceConfig struct { | |||||
| 109 | DefaultFontSize int `mapstructure:"default_font_size" yaml:"default_font_size"` | 111 | DefaultFontSize int `mapstructure:"default_font_size" yaml:"default_font_size"` | ||
| 110 | } | 112 | } | ||
| 111 | 113 | ||||
| 114 | + | // VideoAccelerationConfig holds video hardware acceleration preferences. | |||
| 115 | + | type VideoAccelerationConfig struct { | |||
| 116 | + | EnableVAAPI bool `mapstructure:"enable_vaapi" yaml:"enable_vaapi"` | |||
| 117 | + | AutoDetectGPU bool `mapstructure:"auto_detect_gpu" yaml:"auto_detect_gpu"` | |||
| 118 | + | VAAPIDriverName string `mapstructure:"vaapi_driver_name" yaml:"vaapi_driver_name"` | |||
| 119 | + | EnableAllDrivers bool `mapstructure:"enable_all_drivers" yaml:"enable_all_drivers"` | |||
| 120 | + | LegacyVAAPI bool `mapstructure:"legacy_vaapi" yaml:"legacy_vaapi"` | |||
| 121 | + | } | |||
| 122 | + | ||||
| 112 | // Manager handles configuration loading, watching, and reloading. | 123 | // Manager handles configuration loading, watching, and reloading. | ||
| 113 | type Manager struct { | 124 | type Manager struct { | ||
| 114 | config *Config | 125 | config *Config | ||
| @@ -178,6 +189,21 @@ func NewManager() (*Manager, error) { | |||||
| 178 | return nil, fmt.Errorf("failed to bind DUMBER_RENDERING_MODE: %w", err) | 189 | return nil, fmt.Errorf("failed to bind DUMBER_RENDERING_MODE: %w", err) | ||
| 179 | } | 190 | } | ||
| 180 | 191 | ||||
| 192 | + | // Video acceleration environment variable bindings | |||
| 193 | + | videoAccelEnvBindings := map[string]string{ | |||
| 194 | + | "video_acceleration.enable_vaapi": "DUMBER_VIDEO_ACCELERATION_ENABLE", | |||
| 195 | + | "video_acceleration.auto_detect_gpu": "DUMBER_VIDEO_AUTO_DETECT", | |||
| 196 | + | "video_acceleration.vaapi_driver_name": "LIBVA_DRIVER_NAME", | |||
| 197 | + | "video_acceleration.enable_all_drivers": "GST_VAAPI_ALL_DRIVERS", | |||
| 198 | + | "video_acceleration.legacy_vaapi": "WEBKIT_GST_ENABLE_LEGACY_VAAPI", | |||
| 199 | + | } | |||
| 200 | + | ||||
| 201 | + | for key, env := range videoAccelEnvBindings { | |||
| 202 | + | if err := v.BindEnv(key, env); err != nil { | |||
| 203 | + | return nil, fmt.Errorf("failed to bind environment variable %s: %w", env, err) | |||
| 204 | + | } | |||
| 205 | + | } | |||
| 206 | + | ||||
| 181 | return &Manager{ | 207 | return &Manager{ | ||
| 182 | viper: v, | 208 | viper: v, | ||
| 183 | callbacks: make([]func(*Config), 0), | 209 | callbacks: make([]func(*Config), 0), | ||
| @@ -237,6 +263,14 @@ func (m *Manager) Load() error { | |||||
| 237 | config.RenderingMode = RenderingModeAuto | 263 | config.RenderingMode = RenderingModeAuto | ||
| 238 | } | 264 | } | ||
| 239 | 265 | ||||
| 266 | + | // Auto-detect GPU if enabled and driver name is not set | |||
| 267 | + | if config.VideoAcceleration.AutoDetectGPU && config.VideoAcceleration.VAAPIDriverName == "" { | |||
| 268 | + | gpuInfo := gpu.DetectGPU() | |||
| 269 | + | if gpuInfo.SupportsVAAPI() { | |||
| 270 | + | config.VideoAcceleration.VAAPIDriverName = gpuInfo.GetVAAPIDriverName() | |||
| 271 | + | } | |||
| 272 | + | } | |||
| 273 | + | ||||
| 240 | m.config = config | 274 | m.config = config | ||
| 241 | return nil | 275 | return nil | ||
| 242 | } | 276 | } | ||
| @@ -324,6 +358,14 @@ func (m *Manager) reload() error { | |||||
| 324 | config.RenderingMode = RenderingModeAuto | 358 | config.RenderingMode = RenderingModeAuto | ||
| 325 | } | 359 | } | ||
| 326 | 360 | ||||
| 361 | + | // Auto-detect GPU if enabled and driver name is not set | |||
| 362 | + | if config.VideoAcceleration.AutoDetectGPU && config.VideoAcceleration.VAAPIDriverName == "" { | |||
| 363 | + | gpuInfo := gpu.DetectGPU() | |||
| 364 | + | if gpuInfo.SupportsVAAPI() { | |||
| 365 | + | config.VideoAcceleration.VAAPIDriverName = gpuInfo.GetVAAPIDriverName() | |||
| 366 | + | } | |||
| 367 | + | } | |||
| 368 | + | ||||
| 327 | m.config = config | 369 | m.config = config | ||
| 328 | return nil | 370 | return nil | ||
| 329 | } | 371 | } | ||
| @@ -379,6 +421,13 @@ func (m *Manager) setDefaults() { | |||||
| 379 | m.viper.SetDefault("appearance.monospace_font", defaults.Appearance.MonospaceFont) | 421 | m.viper.SetDefault("appearance.monospace_font", defaults.Appearance.MonospaceFont) | ||
| 380 | m.viper.SetDefault("appearance.default_font_size", defaults.Appearance.DefaultFontSize) | 422 | m.viper.SetDefault("appearance.default_font_size", defaults.Appearance.DefaultFontSize) | ||
| 381 | 423 | ||||
| 424 | + | // Video acceleration defaults | |||
| 425 | + | m.viper.SetDefault("video_acceleration.enable_vaapi", defaults.VideoAcceleration.EnableVAAPI) | |||
| 426 | + | m.viper.SetDefault("video_acceleration.auto_detect_gpu", defaults.VideoAcceleration.AutoDetectGPU) | |||
| 427 | + | m.viper.SetDefault("video_acceleration.vaapi_driver_name", defaults.VideoAcceleration.VAAPIDriverName) | |||
| 428 | + | m.viper.SetDefault("video_acceleration.enable_all_drivers", defaults.VideoAcceleration.EnableAllDrivers) | |||
| 429 | + | m.viper.SetDefault("video_acceleration.legacy_vaapi", defaults.VideoAcceleration.LegacyVAAPI) | |||
| 430 | + | ||||
| 382 | // Rendering defaults | 431 | // Rendering defaults | ||
| 383 | m.viper.SetDefault("rendering_mode", string(RenderingModeAuto)) | 432 | m.viper.SetDefault("rendering_mode", string(RenderingModeAuto)) | ||
| 384 | } | 433 | } | ||
M internal/config/defaults.go +7 -0
| @@ -99,6 +99,13 @@ func DefaultConfig() *Config { | |||
| 99 | 99 | MonospaceFont: "Fira Code", | |
| 100 | 100 | DefaultFontSize: defaultFontSize, | |
| 101 | 101 | }, | |
| 102 | + | VideoAcceleration: VideoAccelerationConfig{ | |
| 103 | + | EnableVAAPI: true, | |
| 104 | + | AutoDetectGPU: true, | |
| 105 | + | VAAPIDriverName: "", // Will be auto-detected | |
| 106 | + | EnableAllDrivers: true, | |
| 107 | + | LegacyVAAPI: false, | |
| 108 | + | }, | |
| 102 | 109 | RenderingMode: RenderingModeAuto, | |
| 103 | 110 | } | |
| 104 | 111 | } | |
| @@ -99,6 +99,13 @@ func DefaultConfig() *Config { | |||||
| 99 | MonospaceFont: "Fira Code", | 99 | MonospaceFont: "Fira Code", | ||
| 100 | DefaultFontSize: defaultFontSize, | 100 | DefaultFontSize: defaultFontSize, | ||
| 101 | }, | 101 | }, | ||
| 102 | + | VideoAcceleration: VideoAccelerationConfig{ | |||
| 103 | + | EnableVAAPI: true, | |||
| 104 | + | AutoDetectGPU: true, | |||
| 105 | + | VAAPIDriverName: "", // Will be auto-detected | |||
| 106 | + | EnableAllDrivers: true, | |||
| 107 | + | LegacyVAAPI: false, | |||
| 108 | + | }, | |||
| 102 | RenderingMode: RenderingModeAuto, | 109 | RenderingMode: RenderingModeAuto, | ||
| 103 | } | 110 | } | ||
| 104 | } | 111 | } | ||
M main.go +8 -1
| @@ -437,7 +437,14 @@ func runBrowser() { | |||
| 437 | 437 | DefaultMonospaceFont: cfg.Appearance.MonospaceFont, | |
| 438 | 438 | DefaultFontSize: cfg.Appearance.DefaultFontSize, | |
| 439 | 439 | Rendering: webkit.RenderingConfig{Mode: string(cfg.RenderingMode)}, | |
| 440 | - | Memory: readMemoryConfigFromEnv(), | |
| 440 | + | VideoAcceleration: webkit.VideoAccelerationConfig{ | |
| 441 | + | EnableVAAPI: cfg.VideoAcceleration.EnableVAAPI, | |
| 442 | + | AutoDetectGPU: cfg.VideoAcceleration.AutoDetectGPU, | |
| 443 | + | VAAPIDriverName: cfg.VideoAcceleration.VAAPIDriverName, | |
| 444 | + | EnableAllDrivers: cfg.VideoAcceleration.EnableAllDrivers, | |
| 445 | + | LegacyVAAPI: cfg.VideoAcceleration.LegacyVAAPI, | |
| 446 | + | }, | |
| 447 | + | Memory: readMemoryConfigFromEnv(), | |
| 441 | 448 | }) | |
| 442 | 449 | if err != nil { | |
| 443 | 450 | log.Printf("Warning: failed to create WebView: %v", err) | |
| @@ -437,7 +437,14 @@ func runBrowser() { | |||||
| 437 | DefaultMonospaceFont: cfg.Appearance.MonospaceFont, | 437 | DefaultMonospaceFont: cfg.Appearance.MonospaceFont, | ||
| 438 | DefaultFontSize: cfg.Appearance.DefaultFontSize, | 438 | DefaultFontSize: cfg.Appearance.DefaultFontSize, | ||
| 439 | Rendering: webkit.RenderingConfig{Mode: string(cfg.RenderingMode)}, | 439 | Rendering: webkit.RenderingConfig{Mode: string(cfg.RenderingMode)}, | ||
| 440 | - | Memory: readMemoryConfigFromEnv(), | 440 | + | VideoAcceleration: webkit.VideoAccelerationConfig{ |
| 441 | + | EnableVAAPI: cfg.VideoAcceleration.EnableVAAPI, | |||
| 442 | + | AutoDetectGPU: cfg.VideoAcceleration.AutoDetectGPU, | |||
| 443 | + | VAAPIDriverName: cfg.VideoAcceleration.VAAPIDriverName, | |||
| 444 | + | EnableAllDrivers: cfg.VideoAcceleration.EnableAllDrivers, | |||
| 445 | + | LegacyVAAPI: cfg.VideoAcceleration.LegacyVAAPI, | |||
| 446 | + | }, | |||
| 447 | + | Memory: readMemoryConfigFromEnv(), | |||
| 441 | }) | 448 | }) | ||
| 442 | if err != nil { | 449 | if err != nil { | ||
| 443 | log.Printf("Warning: failed to create WebView: %v", err) | 450 | log.Printf("Warning: failed to create WebView: %v", err) | ||
A pkg/gpu/detect.go +233 -0
| @@ -0,0 +1,233 @@ | |||
| 1 | + | package gpu | |
| 2 | + | ||
| 3 | + | import ( | |
| 4 | + | "os" | |
| 5 | + | "os/exec" | |
| 6 | + | "path/filepath" | |
| 7 | + | "regexp" | |
| 8 | + | "strings" | |
| 9 | + | ||
| 10 | + | "github.com/bnema/dumber/internal/logging" | |
| 11 | + | ) | |
| 12 | + | ||
| 13 | + | // GPUVendor represents GPU vendor types | |
| 14 | + | type GPUVendor string | |
| 15 | + | ||
| 16 | + | const ( | |
| 17 | + | GPUVendorAMD GPUVendor = "amd" | |
| 18 | + | GPUVendorNVIDIA GPUVendor = "nvidia" | |
| 19 | + | GPUVendorIntel GPUVendor = "intel" | |
| 20 | + | GPUVendorUnknown GPUVendor = "unknown" | |
| 21 | + | ) | |
| 22 | + | ||
| 23 | + | // GPUInfo contains information about the detected GPU | |
| 24 | + | type GPUInfo struct { | |
| 25 | + | Vendor GPUVendor | |
| 26 | + | Name string | |
| 27 | + | Driver string // radeonsi, nouveau, i965, etc. | |
| 28 | + | } | |
| 29 | + | ||
| 30 | + | // String returns a string representation of the GPU info | |
| 31 | + | func (g GPUInfo) String() string { | |
| 32 | + | if g.Name != "" { | |
| 33 | + | return string(g.Vendor) + " (" + g.Name + ")" | |
| 34 | + | } | |
| 35 | + | return string(g.Vendor) | |
| 36 | + | } | |
| 37 | + | ||
| 38 | + | // DetectGPU attempts to detect the GPU vendor and driver using multiple methods | |
| 39 | + | func DetectGPU() GPUInfo { | |
| 40 | + | logging.Debug("Starting GPU detection") | |
| 41 | + | ||
| 42 | + | // Try methods in order of reliability | |
| 43 | + | methods := []func() GPUInfo{ | |
| 44 | + | detectViaGLXInfo, | |
| 45 | + | detectViaLSPCI, | |
| 46 | + | detectViaSysFS, | |
| 47 | + | detectViaNVIDIAProc, | |
| 48 | + | } | |
| 49 | + | ||
| 50 | + | for _, method := range methods { | |
| 51 | + | if info := method(); info.Vendor != GPUVendorUnknown { | |
| 52 | + | logging.Info("GPU detected: " + info.String() + " (driver: " + info.GetVAAPIDriverName() + ")") | |
| 53 | + | return info | |
| 54 | + | } | |
| 55 | + | } | |
| 56 | + | ||
| 57 | + | logging.Warn("No GPU detected using any method") | |
| 58 | + | return GPUInfo{Vendor: GPUVendorUnknown} | |
| 59 | + | } | |
| 60 | + | ||
| 61 | + | // detectViaGLXInfo uses glxinfo to detect GPU vendor and driver | |
| 62 | + | func detectViaGLXInfo() GPUInfo { | |
| 63 | + | cmd := exec.Command("glxinfo") | |
| 64 | + | output, err := cmd.Output() | |
| 65 | + | if err != nil { | |
| 66 | + | return GPUInfo{Vendor: GPUVendorUnknown} | |
| 67 | + | } | |
| 68 | + | ||
| 69 | + | lines := strings.Split(string(output), "\n") | |
| 70 | + | var vendor, renderer string | |
| 71 | + | ||
| 72 | + | for _, line := range lines { | |
| 73 | + | line = strings.TrimSpace(line) | |
| 74 | + | if strings.HasPrefix(line, "OpenGL vendor string:") { | |
| 75 | + | vendor = strings.TrimSpace(strings.TrimPrefix(line, "OpenGL vendor string:")) | |
| 76 | + | } else if strings.HasPrefix(line, "OpenGL renderer string:") { | |
| 77 | + | renderer = strings.TrimSpace(strings.TrimPrefix(line, "OpenGL renderer string:")) | |
| 78 | + | } | |
| 79 | + | } | |
| 80 | + | ||
| 81 | + | info := GPUInfo{Name: renderer} | |
| 82 | + | ||
| 83 | + | // Parse vendor | |
| 84 | + | vendor = strings.ToLower(vendor) | |
| 85 | + | if strings.Contains(vendor, "amd") || strings.Contains(vendor, "ati") { | |
| 86 | + | info.Vendor = GPUVendorAMD | |
| 87 | + | // Extract driver from renderer (e.g., "radeonsi" from "AMD Radeon RX 9070 XT (radeonsi, ...)") | |
| 88 | + | if strings.Contains(renderer, "radeonsi") { | |
| 89 | + | info.Driver = "radeonsi" | |
| 90 | + | } | |
| 91 | + | } else if strings.Contains(vendor, "nvidia") { | |
| 92 | + | info.Vendor = GPUVendorNVIDIA | |
| 93 | + | info.Driver = "nvidia" | |
| 94 | + | } else if strings.Contains(vendor, "intel") { | |
| 95 | + | info.Vendor = GPUVendorIntel | |
| 96 | + | // Intel can use iHD or i965 drivers | |
| 97 | + | if strings.Contains(renderer, "iHD") { | |
| 98 | + | info.Driver = "iHD" | |
| 99 | + | } else { | |
| 100 | + | info.Driver = "i965" | |
| 101 | + | } | |
| 102 | + | } | |
| 103 | + | ||
| 104 | + | return info | |
| 105 | + | } | |
| 106 | + | ||
| 107 | + | // detectViaLSPCI uses lspci to detect GPU vendor | |
| 108 | + | func detectViaLSPCI() GPUInfo { | |
| 109 | + | cmd := exec.Command("lspci") | |
| 110 | + | output, err := cmd.Output() | |
| 111 | + | if err != nil { | |
| 112 | + | return GPUInfo{Vendor: GPUVendorUnknown} | |
| 113 | + | } | |
| 114 | + | ||
| 115 | + | lines := strings.Split(string(output), "\n") | |
| 116 | + | vgaPattern := regexp.MustCompile(`(?i)VGA|3D|Display`) | |
| 117 | + | ||
| 118 | + | for _, line := range lines { | |
| 119 | + | if vgaPattern.MatchString(line) { | |
| 120 | + | line = strings.ToLower(line) | |
| 121 | + | if strings.Contains(line, "amd") || strings.Contains(line, "ati") { | |
| 122 | + | return GPUInfo{ | |
| 123 | + | Vendor: GPUVendorAMD, | |
| 124 | + | Name: extractDeviceName(line), | |
| 125 | + | Driver: "radeonsi", // Default for modern AMD GPUs | |
| 126 | + | } | |
| 127 | + | } else if strings.Contains(line, "nvidia") { | |
| 128 | + | return GPUInfo{ | |
| 129 | + | Vendor: GPUVendorNVIDIA, | |
| 130 | + | Name: extractDeviceName(line), | |
| 131 | + | Driver: "nvidia", | |
| 132 | + | } | |
| 133 | + | } else if strings.Contains(line, "intel") { | |
| 134 | + | return GPUInfo{ | |
| 135 | + | Vendor: GPUVendorIntel, | |
| 136 | + | Name: extractDeviceName(line), | |
| 137 | + | Driver: "iHD", // Default to newer Intel driver | |
| 138 | + | } | |
| 139 | + | } | |
| 140 | + | } | |
| 141 | + | } | |
| 142 | + | ||
| 143 | + | return GPUInfo{Vendor: GPUVendorUnknown} | |
| 144 | + | } | |
| 145 | + | ||
| 146 | + | // detectViaSysFS checks sysfs for GPU vendor IDs | |
| 147 | + | func detectViaSysFS() GPUInfo { | |
| 148 | + | pattern := "/sys/class/drm/card*/device/vendor" | |
| 149 | + | matches, err := filepath.Glob(pattern) | |
| 150 | + | if err != nil { | |
| 151 | + | return GPUInfo{Vendor: GPUVendorUnknown} | |
| 152 | + | } | |
| 153 | + | ||
| 154 | + | for _, vendorFile := range matches { | |
| 155 | + | data, err := os.ReadFile(vendorFile) | |
| 156 | + | if err != nil { | |
| 157 | + | continue | |
| 158 | + | } | |
| 159 | + | ||
| 160 | + | vendorID := strings.TrimSpace(string(data)) | |
| 161 | + | switch vendorID { | |
| 162 | + | case "0x1002": // AMD | |
| 163 | + | return GPUInfo{ | |
| 164 | + | Vendor: GPUVendorAMD, | |
| 165 | + | Driver: "radeonsi", | |
| 166 | + | } | |
| 167 | + | case "0x10de": // NVIDIA | |
| 168 | + | return GPUInfo{ | |
| 169 | + | Vendor: GPUVendorNVIDIA, | |
| 170 | + | Driver: "nvidia", | |
| 171 | + | } | |
| 172 | + | case "0x8086": // Intel | |
| 173 | + | return GPUInfo{ | |
| 174 | + | Vendor: GPUVendorIntel, | |
| 175 | + | Driver: "iHD", | |
| 176 | + | } | |
| 177 | + | } | |
| 178 | + | } | |
| 179 | + | ||
| 180 | + | return GPUInfo{Vendor: GPUVendorUnknown} | |
| 181 | + | } | |
| 182 | + | ||
| 183 | + | // detectViaNVIDIAProc checks for NVIDIA driver via /proc | |
| 184 | + | func detectViaNVIDIAProc() GPUInfo { | |
| 185 | + | if _, err := os.Stat("/proc/driver/nvidia/version"); err == nil { | |
| 186 | + | return GPUInfo{ | |
| 187 | + | Vendor: GPUVendorNVIDIA, | |
| 188 | + | Driver: "nvidia", | |
| 189 | + | } | |
| 190 | + | } | |
| 191 | + | return GPUInfo{Vendor: GPUVendorUnknown} | |
| 192 | + | } | |
| 193 | + | ||
| 194 | + | // extractDeviceName extracts a readable device name from lspci output | |
| 195 | + | func extractDeviceName(line string) string { | |
| 196 | + | // Extract text after the colon but before any brackets | |
| 197 | + | parts := strings.Split(line, ":") | |
| 198 | + | if len(parts) < 2 { | |
| 199 | + | return "" | |
| 200 | + | } | |
| 201 | + | ||
| 202 | + | name := strings.TrimSpace(parts[len(parts)-1]) | |
| 203 | + | ||
| 204 | + | // Remove revision info in parentheses | |
| 205 | + | if idx := strings.Index(name, " (rev "); idx != -1 { | |
| 206 | + | name = name[:idx] | |
| 207 | + | } | |
| 208 | + | ||
| 209 | + | return name | |
| 210 | + | } | |
| 211 | + | ||
| 212 | + | // GetVAAPIDriverName returns the appropriate VA-API driver name for the GPU vendor | |
| 213 | + | func (g GPUInfo) GetVAAPIDriverName() string { | |
| 214 | + | if g.Driver != "" { | |
| 215 | + | return g.Driver | |
| 216 | + | } | |
| 217 | + | ||
| 218 | + | switch g.Vendor { | |
| 219 | + | case GPUVendorAMD: | |
| 220 | + | return "radeonsi" | |
| 221 | + | case GPUVendorNVIDIA: | |
| 222 | + | return "vdpau" // NVIDIA uses VDPAU backend for VA-API | |
| 223 | + | case GPUVendorIntel: | |
| 224 | + | return "iHD" // Modern Intel GPUs | |
| 225 | + | default: | |
| 226 | + | return "" | |
| 227 | + | } | |
| 228 | + | } | |
| 229 | + | ||
| 230 | + | // SupportsVAAPI returns true if the GPU vendor supports VA-API | |
| 231 | + | func (g GPUInfo) SupportsVAAPI() bool { | |
| 232 | + | return g.Vendor == GPUVendorAMD || g.Vendor == GPUVendorIntel || g.Vendor == GPUVendorNVIDIA | |
| 233 | + | }⏎̸ | |
| @@ -0,0 +1,233 @@ | |||||
| 1 | + | package gpu | |||
| 2 | + | ||||
| 3 | + | import ( | |||
| 4 | + | "os" | |||
| 5 | + | "os/exec" | |||
| 6 | + | "path/filepath" | |||
| 7 | + | "regexp" | |||
| 8 | + | "strings" | |||
| 9 | + | ||||
| 10 | + | "github.com/bnema/dumber/internal/logging" | |||
| 11 | + | ) | |||
| 12 | + | ||||
| 13 | + | // GPUVendor represents GPU vendor types | |||
| 14 | + | type GPUVendor string | |||
| 15 | + | ||||
| 16 | + | const ( | |||
| 17 | + | GPUVendorAMD GPUVendor = "amd" | |||
| 18 | + | GPUVendorNVIDIA GPUVendor = "nvidia" | |||
| 19 | + | GPUVendorIntel GPUVendor = "intel" | |||
| 20 | + | GPUVendorUnknown GPUVendor = "unknown" | |||
| 21 | + | ) | |||
| 22 | + | ||||
| 23 | + | // GPUInfo contains information about the detected GPU | |||
| 24 | + | type GPUInfo struct { | |||
| 25 | + | Vendor GPUVendor | |||
| 26 | + | Name string | |||
| 27 | + | Driver string // radeonsi, nouveau, i965, etc. | |||
| 28 | + | } | |||
| 29 | + | ||||
| 30 | + | // String returns a string representation of the GPU info | |||
| 31 | + | func (g GPUInfo) String() string { | |||
| 32 | + | if g.Name != "" { | |||
| 33 | + | return string(g.Vendor) + " (" + g.Name + ")" | |||
| 34 | + | } | |||
| 35 | + | return string(g.Vendor) | |||
| 36 | + | } | |||
| 37 | + | ||||
| 38 | + | // DetectGPU attempts to detect the GPU vendor and driver using multiple methods | |||
| 39 | + | func DetectGPU() GPUInfo { | |||
| 40 | + | logging.Debug("Starting GPU detection") | |||
| 41 | + | ||||
| 42 | + | // Try methods in order of reliability | |||
| 43 | + | methods := []func() GPUInfo{ | |||
| 44 | + | detectViaGLXInfo, | |||
| 45 | + | detectViaLSPCI, | |||
| 46 | + | detectViaSysFS, | |||
| 47 | + | detectViaNVIDIAProc, | |||
| 48 | + | } | |||
| 49 | + | ||||
| 50 | + | for _, method := range methods { | |||
| 51 | + | if info := method(); info.Vendor != GPUVendorUnknown { | |||
| 52 | + | logging.Info("GPU detected: " + info.String() + " (driver: " + info.GetVAAPIDriverName() + ")") | |||
| 53 | + | return info | |||
| 54 | + | } | |||
| 55 | + | } | |||
| 56 | + | ||||
| 57 | + | logging.Warn("No GPU detected using any method") | |||
| 58 | + | return GPUInfo{Vendor: GPUVendorUnknown} | |||
| 59 | + | } | |||
| 60 | + | ||||
| 61 | + | // detectViaGLXInfo uses glxinfo to detect GPU vendor and driver | |||
| 62 | + | func detectViaGLXInfo() GPUInfo { | |||
| 63 | + | cmd := exec.Command("glxinfo") | |||
| 64 | + | output, err := cmd.Output() | |||
| 65 | + | if err != nil { | |||
| 66 | + | return GPUInfo{Vendor: GPUVendorUnknown} | |||
| 67 | + | } | |||
| 68 | + | ||||
| 69 | + | lines := strings.Split(string(output), "\n") | |||
| 70 | + | var vendor, renderer string | |||
| 71 | + | ||||
| 72 | + | for _, line := range lines { | |||
| 73 | + | line = strings.TrimSpace(line) | |||
| 74 | + | if strings.HasPrefix(line, "OpenGL vendor string:") { | |||
| 75 | + | vendor = strings.TrimSpace(strings.TrimPrefix(line, "OpenGL vendor string:")) | |||
| 76 | + | } else if strings.HasPrefix(line, "OpenGL renderer string:") { | |||
| 77 | + | renderer = strings.TrimSpace(strings.TrimPrefix(line, "OpenGL renderer string:")) | |||
| 78 | + | } | |||
| 79 | + | } | |||
| 80 | + | ||||
| 81 | + | info := GPUInfo{Name: renderer} | |||
| 82 | + | ||||
| 83 | + | // Parse vendor | |||
| 84 | + | vendor = strings.ToLower(vendor) | |||
| 85 | + | if strings.Contains(vendor, "amd") || strings.Contains(vendor, "ati") { | |||
| 86 | + | info.Vendor = GPUVendorAMD | |||
| 87 | + | // Extract driver from renderer (e.g., "radeonsi" from "AMD Radeon RX 9070 XT (radeonsi, ...)") | |||
| 88 | + | if strings.Contains(renderer, "radeonsi") { | |||
| 89 | + | info.Driver = "radeonsi" | |||
| 90 | + | } | |||
| 91 | + | } else if strings.Contains(vendor, "nvidia") { | |||
| 92 | + | info.Vendor = GPUVendorNVIDIA | |||
| 93 | + | info.Driver = "nvidia" | |||
| 94 | + | } else if strings.Contains(vendor, "intel") { | |||
| 95 | + | info.Vendor = GPUVendorIntel | |||
| 96 | + | // Intel can use iHD or i965 drivers | |||
| 97 | + | if strings.Contains(renderer, "iHD") { | |||
| 98 | + | info.Driver = "iHD" | |||
| 99 | + | } else { | |||
| 100 | + | info.Driver = "i965" | |||
| 101 | + | } | |||
| 102 | + | } | |||
| 103 | + | ||||
| 104 | + | return info | |||
| 105 | + | } | |||
| 106 | + | ||||
| 107 | + | // detectViaLSPCI uses lspci to detect GPU vendor | |||
| 108 | + | func detectViaLSPCI() GPUInfo { | |||
| 109 | + | cmd := exec.Command("lspci") | |||
| 110 | + | output, err := cmd.Output() | |||
| 111 | + | if err != nil { | |||
| 112 | + | return GPUInfo{Vendor: GPUVendorUnknown} | |||
| 113 | + | } | |||
| 114 | + | ||||
| 115 | + | lines := strings.Split(string(output), "\n") | |||
| 116 | + | vgaPattern := regexp.MustCompile(`(?i)VGA|3D|Display`) | |||
| 117 | + | ||||
| 118 | + | for _, line := range lines { | |||
| 119 | + | if vgaPattern.MatchString(line) { | |||
| 120 | + | line = strings.ToLower(line) | |||
| 121 | + | if strings.Contains(line, "amd") || strings.Contains(line, "ati") { | |||
| 122 | + | return GPUInfo{ | |||
| 123 | + | Vendor: GPUVendorAMD, | |||
| 124 | + | Name: extractDeviceName(line), | |||
| 125 | + | Driver: "radeonsi", // Default for modern AMD GPUs | |||
| 126 | + | } | |||
| 127 | + | } else if strings.Contains(line, "nvidia") { | |||
| 128 | + | return GPUInfo{ | |||
| 129 | + | Vendor: GPUVendorNVIDIA, | |||
| 130 | + | Name: extractDeviceName(line), | |||
| 131 | + | Driver: "nvidia", | |||
| 132 | + | } | |||
| 133 | + | } else if strings.Contains(line, "intel") { | |||
| 134 | + | return GPUInfo{ | |||
| 135 | + | Vendor: GPUVendorIntel, | |||
| 136 | + | Name: extractDeviceName(line), | |||
| 137 | + | Driver: "iHD", // Default to newer Intel driver | |||
| 138 | + | } | |||
| 139 | + | } | |||
| 140 | + | } | |||
| 141 | + | } | |||
| 142 | + | ||||
| 143 | + | return GPUInfo{Vendor: GPUVendorUnknown} | |||
| 144 | + | } | |||
| 145 | + | ||||
| 146 | + | // detectViaSysFS checks sysfs for GPU vendor IDs | |||
| 147 | + | func detectViaSysFS() GPUInfo { | |||
| 148 | + | pattern := "/sys/class/drm/card*/device/vendor" | |||
| 149 | + | matches, err := filepath.Glob(pattern) | |||
| 150 | + | if err != nil { | |||
| 151 | + | return GPUInfo{Vendor: GPUVendorUnknown} | |||
| 152 | + | } | |||
| 153 | + | ||||
| 154 | + | for _, vendorFile := range matches { | |||
| 155 | + | data, err := os.ReadFile(vendorFile) | |||
| 156 | + | if err != nil { | |||
| 157 | + | continue | |||
| 158 | + | } | |||
| 159 | + | ||||
| 160 | + | vendorID := strings.TrimSpace(string(data)) | |||
| 161 | + | switch vendorID { | |||
| 162 | + | case "0x1002": // AMD | |||
| 163 | + | return GPUInfo{ | |||
| 164 | + | Vendor: GPUVendorAMD, | |||
| 165 | + | Driver: "radeonsi", | |||
| 166 | + | } | |||
| 167 | + | case "0x10de": // NVIDIA | |||
| 168 | + | return GPUInfo{ | |||
| 169 | + | Vendor: GPUVendorNVIDIA, | |||
| 170 | + | Driver: "nvidia", | |||
| 171 | + | } | |||
| 172 | + | case "0x8086": // Intel | |||
| 173 | + | return GPUInfo{ | |||
| 174 | + | Vendor: GPUVendorIntel, | |||
| 175 | + | Driver: "iHD", | |||
| 176 | + | } | |||
| 177 | + | } | |||
| 178 | + | } | |||
| 179 | + | ||||
| 180 | + | return GPUInfo{Vendor: GPUVendorUnknown} | |||
| 181 | + | } | |||
| 182 | + | ||||
| 183 | + | // detectViaNVIDIAProc checks for NVIDIA driver via /proc | |||
| 184 | + | func detectViaNVIDIAProc() GPUInfo { | |||
| 185 | + | if _, err := os.Stat("/proc/driver/nvidia/version"); err == nil { | |||
| 186 | + | return GPUInfo{ | |||
| 187 | + | Vendor: GPUVendorNVIDIA, | |||
| 188 | + | Driver: "nvidia", | |||
| 189 | + | } | |||
| 190 | + | } | |||
| 191 | + | return GPUInfo{Vendor: GPUVendorUnknown} | |||
| 192 | + | } | |||
| 193 | + | ||||
| 194 | + | // extractDeviceName extracts a readable device name from lspci output | |||
| 195 | + | func extractDeviceName(line string) string { | |||
| 196 | + | // Extract text after the colon but before any brackets | |||
| 197 | + | parts := strings.Split(line, ":") | |||
| 198 | + | if len(parts) < 2 { | |||
| 199 | + | return "" | |||
| 200 | + | } | |||
| 201 | + | ||||
| 202 | + | name := strings.TrimSpace(parts[len(parts)-1]) | |||
| 203 | + | ||||
| 204 | + | // Remove revision info in parentheses | |||
| 205 | + | if idx := strings.Index(name, " (rev "); idx != -1 { | |||
| 206 | + | name = name[:idx] | |||
| 207 | + | } | |||
| 208 | + | ||||
| 209 | + | return name | |||
| 210 | + | } | |||
| 211 | + | ||||
| 212 | + | // GetVAAPIDriverName returns the appropriate VA-API driver name for the GPU vendor | |||
| 213 | + | func (g GPUInfo) GetVAAPIDriverName() string { | |||
| 214 | + | if g.Driver != "" { | |||
| 215 | + | return g.Driver | |||
| 216 | + | } | |||
| 217 | + | ||||
| 218 | + | switch g.Vendor { | |||
| 219 | + | case GPUVendorAMD: | |||
| 220 | + | return "radeonsi" | |||
| 221 | + | case GPUVendorNVIDIA: | |||
| 222 | + | return "vdpau" // NVIDIA uses VDPAU backend for VA-API | |||
| 223 | + | case GPUVendorIntel: | |||
| 224 | + | return "iHD" // Modern Intel GPUs | |||
| 225 | + | default: | |||
| 226 | + | return "" | |||
| 227 | + | } | |||
| 228 | + | } | |||
| 229 | + | ||||
| 230 | + | // SupportsVAAPI returns true if the GPU vendor supports VA-API | |||
| 231 | + | func (g GPUInfo) SupportsVAAPI() bool { | |||
| 232 | + | return g.Vendor == GPUVendorAMD || g.Vendor == GPUVendorIntel || g.Vendor == GPUVendorNVIDIA | |||
| 233 | + | }⏎̸ | |||
M pkg/webkit/settings.go +12 -0
| @@ -29,6 +29,9 @@ type Config struct { | |||
| 29 | 29 | // Rendering controls GPU/CPU selection and debug options | |
| 30 | 30 | Rendering RenderingConfig | |
| 31 | 31 | ||
| 32 | + | // VideoAcceleration controls video hardware acceleration settings | |
| 33 | + | VideoAcceleration VideoAccelerationConfig | |
| 34 | + | ||
| 32 | 35 | // Memory controls memory optimization settings | |
| 33 | 36 | Memory MemoryConfig | |
| 34 | 37 | } | |
| @@ -41,6 +44,15 @@ type RenderingConfig struct { | |||
| 41 | 44 | DebugGPU bool | |
| 42 | 45 | } | |
| 43 | 46 | ||
| 47 | + | // VideoAccelerationConfig controls video hardware acceleration settings. | |
| 48 | + | type VideoAccelerationConfig struct { | |
| 49 | + | EnableVAAPI bool | |
| 50 | + | AutoDetectGPU bool | |
| 51 | + | VAAPIDriverName string | |
| 52 | + | EnableAllDrivers bool | |
| 53 | + | LegacyVAAPI bool | |
| 54 | + | } | |
| 55 | + | ||
| 44 | 56 | // CacheModel represents WebKit cache model options for memory optimization. | |
| 45 | 57 | type CacheModel string | |
| 46 | 58 | ||
| @@ -29,6 +29,9 @@ type Config struct { | |||||
| 29 | // Rendering controls GPU/CPU selection and debug options | 29 | // Rendering controls GPU/CPU selection and debug options | ||
| 30 | Rendering RenderingConfig | 30 | Rendering RenderingConfig | ||
| 31 | 31 | ||||
| 32 | + | // VideoAcceleration controls video hardware acceleration settings | |||
| 33 | + | VideoAcceleration VideoAccelerationConfig | |||
| 34 | + | ||||
| 32 | // Memory controls memory optimization settings | 35 | // Memory controls memory optimization settings | ||
| 33 | Memory MemoryConfig | 36 | Memory MemoryConfig | ||
| 34 | } | 37 | } | ||
| @@ -41,6 +44,15 @@ type RenderingConfig struct { | |||||
| 41 | DebugGPU bool | 44 | DebugGPU bool | ||
| 42 | } | 45 | } | ||
| 43 | 46 | ||||
| 47 | + | // VideoAccelerationConfig controls video hardware acceleration settings. | |||
| 48 | + | type VideoAccelerationConfig struct { | |||
| 49 | + | EnableVAAPI bool | |||
| 50 | + | AutoDetectGPU bool | |||
| 51 | + | VAAPIDriverName string | |||
| 52 | + | EnableAllDrivers bool | |||
| 53 | + | LegacyVAAPI bool | |||
| 54 | + | } | |||
| 55 | + | ||||
| 44 | // CacheModel represents WebKit cache model options for memory optimization. | 56 | // CacheModel represents WebKit cache model options for memory optimization. | ||
| 45 | type CacheModel string | 57 | type CacheModel string | ||
| 46 | 58 | ||||
M pkg/webkit/webview_cgo.go +44 -0
| @@ -50,6 +50,35 @@ static void on_tls_dialog_response(GtkDialog *dialog, gint response_id, gpointer | |||
| 50 | 50 | static gboolean show_tls_warning_dialog_sync(GtkWindow *parent, const char* hostname, const char* error_msg); | |
| 51 | 51 | static char* extract_certificate_info(GTlsCertificate* certificate); | |
| 52 | 52 | ||
| 53 | + | // Setup video acceleration environment variables | |
| 54 | + | static void setup_video_acceleration(const char* driver_name, int enable_all, int legacy) { | |
| 55 | + | if (legacy) { | |
| 56 | + | // Legacy mode: force old VA-API plugins, disable modern ones | |
| 57 | + | setenv("WEBKIT_GST_ENABLE_LEGACY_VAAPI", "1", 1); | |
| 58 | + | // Don't set other env vars in legacy mode to avoid conflicts | |
| 59 | + | return; | |
| 60 | + | } | |
| 61 | + | ||
| 62 | + | // Modern mode (default): use improved VA-API handling | |
| 63 | + | if (driver_name && strlen(driver_name) > 0) { | |
| 64 | + | if (strcmp(driver_name, "nvidia") == 0) { | |
| 65 | + | // NVIDIA uses different env vars for hardware video acceleration | |
| 66 | + | setenv("VDPAU_DRIVER", "nvidia", 1); | |
| 67 | + | setenv("LIBVA_DRIVER_NAME", "vdpau", 1); | |
| 68 | + | } else if (strcmp(driver_name, "vdpau") == 0) { | |
| 69 | + | // Explicit VDPAU backend (for NVIDIA via VA-API) | |
| 70 | + | setenv("LIBVA_DRIVER_NAME", "vdpau", 1); | |
| 71 | + | } else { | |
| 72 | + | // AMD/Intel use VA-API directly | |
| 73 | + | setenv("LIBVA_DRIVER_NAME", driver_name, 1); | |
| 74 | + | } | |
| 75 | + | } | |
| 76 | + | ||
| 77 | + | if (enable_all) { | |
| 78 | + | setenv("GST_VAAPI_ALL_DRIVERS", "1", 1); | |
| 79 | + | } | |
| 80 | + | } | |
| 81 | + | ||
| 53 | 82 | // Construct a WebKitWebView via g_object_new with a fresh UserContentManager. | |
| 54 | 83 | static GtkWidget* new_webview_with_ucm_and_session(const char* data_dir, const char* cache_dir, const char* cookie_path, WebKitUserContentManager** out_ucm, WebKitMemoryPressureSettings* pressure_settings) { | |
| 55 | 84 | WebKitUserContentManager* u = webkit_user_content_manager_new(); | |
| @@ -601,6 +630,21 @@ func NewWebView(cfg *Config) (*WebView, error) { | |||
| 601 | 630 | cfg = &Config{} | |
| 602 | 631 | } | |
| 603 | 632 | ||
| 633 | + | // Setup video acceleration environment variables before creating WebView | |
| 634 | + | if cfg.VideoAcceleration.EnableVAAPI { | |
| 635 | + | driverName := C.CString(cfg.VideoAcceleration.VAAPIDriverName) | |
| 636 | + | enableAll := 0 | |
| 637 | + | if cfg.VideoAcceleration.EnableAllDrivers { | |
| 638 | + | enableAll = 1 | |
| 639 | + | } | |
| 640 | + | legacy := 0 | |
| 641 | + | if cfg.VideoAcceleration.LegacyVAAPI { | |
| 642 | + | legacy = 1 | |
| 643 | + | } | |
| 644 | + | C.setup_video_acceleration(driverName, C.int(enableAll), C.int(legacy)) | |
| 645 | + | C.free(unsafe.Pointer(driverName)) | |
| 646 | + | } | |
| 647 | + | ||
| 604 | 648 | // Prepare persistent website data/caches | |
| 605 | 649 | dataDir := cfg.DataDir | |
| 606 | 650 | cacheDir := cfg.CacheDir | |
| @@ -50,6 +50,35 @@ static void on_tls_dialog_response(GtkDialog *dialog, gint response_id, gpointer | |||||
| 50 | static gboolean show_tls_warning_dialog_sync(GtkWindow *parent, const char* hostname, const char* error_msg); | 50 | static gboolean show_tls_warning_dialog_sync(GtkWindow *parent, const char* hostname, const char* error_msg); | ||
| 51 | static char* extract_certificate_info(GTlsCertificate* certificate); | 51 | static char* extract_certificate_info(GTlsCertificate* certificate); | ||
| 52 | 52 | ||||
| 53 | + | // Setup video acceleration environment variables | |||
| 54 | + | static void setup_video_acceleration(const char* driver_name, int enable_all, int legacy) { | |||
| 55 | + | if (legacy) { | |||
| 56 | + | // Legacy mode: force old VA-API plugins, disable modern ones | |||
| 57 | + | setenv("WEBKIT_GST_ENABLE_LEGACY_VAAPI", "1", 1); | |||
| 58 | + | // Don't set other env vars in legacy mode to avoid conflicts | |||
| 59 | + | return; | |||
| 60 | + | } | |||
| 61 | + | ||||
| 62 | + | // Modern mode (default): use improved VA-API handling | |||
| 63 | + | if (driver_name && strlen(driver_name) > 0) { | |||
| 64 | + | if (strcmp(driver_name, "nvidia") == 0) { | |||
| 65 | + | // NVIDIA uses different env vars for hardware video acceleration | |||
| 66 | + | setenv("VDPAU_DRIVER", "nvidia", 1); | |||
| 67 | + | setenv("LIBVA_DRIVER_NAME", "vdpau", 1); | |||
| 68 | + | } else if (strcmp(driver_name, "vdpau") == 0) { | |||
| 69 | + | // Explicit VDPAU backend (for NVIDIA via VA-API) | |||
| 70 | + | setenv("LIBVA_DRIVER_NAME", "vdpau", 1); | |||
| 71 | + | } else { | |||
| 72 | + | // AMD/Intel use VA-API directly | |||
| 73 | + | setenv("LIBVA_DRIVER_NAME", driver_name, 1); | |||
| 74 | + | } | |||
| 75 | + | } | |||
| 76 | + | ||||
| 77 | + | if (enable_all) { | |||
| 78 | + | setenv("GST_VAAPI_ALL_DRIVERS", "1", 1); | |||
| 79 | + | } | |||
| 80 | + | } | |||
| 81 | + | ||||
| 53 | // Construct a WebKitWebView via g_object_new with a fresh UserContentManager. | 82 | // Construct a WebKitWebView via g_object_new with a fresh UserContentManager. | ||
| 54 | static GtkWidget* new_webview_with_ucm_and_session(const char* data_dir, const char* cache_dir, const char* cookie_path, WebKitUserContentManager** out_ucm, WebKitMemoryPressureSettings* pressure_settings) { | 83 | static GtkWidget* new_webview_with_ucm_and_session(const char* data_dir, const char* cache_dir, const char* cookie_path, WebKitUserContentManager** out_ucm, WebKitMemoryPressureSettings* pressure_settings) { | ||
| 55 | WebKitUserContentManager* u = webkit_user_content_manager_new(); | 84 | WebKitUserContentManager* u = webkit_user_content_manager_new(); | ||
| @@ -601,6 +630,21 @@ func NewWebView(cfg *Config) (*WebView, error) { | |||||
| 601 | cfg = &Config{} | 630 | cfg = &Config{} | ||
| 602 | } | 631 | } | ||
| 603 | 632 | ||||
| 633 | + | // Setup video acceleration environment variables before creating WebView | |||
| 634 | + | if cfg.VideoAcceleration.EnableVAAPI { | |||
| 635 | + | driverName := C.CString(cfg.VideoAcceleration.VAAPIDriverName) | |||
| 636 | + | enableAll := 0 | |||
| 637 | + | if cfg.VideoAcceleration.EnableAllDrivers { | |||
| 638 | + | enableAll = 1 | |||
| 639 | + | } | |||
| 640 | + | legacy := 0 | |||
| 641 | + | if cfg.VideoAcceleration.LegacyVAAPI { | |||
| 642 | + | legacy = 1 | |||
| 643 | + | } | |||
| 644 | + | C.setup_video_acceleration(driverName, C.int(enableAll), C.int(legacy)) | |||
| 645 | + | C.free(unsafe.Pointer(driverName)) | |||
| 646 | + | } | |||
| 647 | + | ||||
| 604 | // Prepare persistent website data/caches | 648 | // Prepare persistent website data/caches | ||
| 605 | dataDir := cfg.DataDir | 649 | dataDir := cfg.DataDir | ||
| 606 | cacheDir := cfg.CacheDir | 650 | cacheDir := cfg.CacheDir | ||