diff --git a/.env.example b/.env.example index 1e6b053d2..b8626e847 100644 --- a/.env.example +++ b/.env.example @@ -130,6 +130,12 @@ OPENHUMAN_REASONING_ENABLED= # shell tool spawns child processes (CREATE_NO_WINDOW). Default: false. # Equivalent TOML: [shell] hide_window = true OPENHUMAN_SHELL_HIDE_WINDOW= +# [optional] Desktop CEF startup escape hatch. Set to 1/true/yes/on only when +# CEF fails before the app can render, for example on a GPU/driver stack where +# startup logs show `cef::initialize returned 0`. Adds --disable-gpu and +# --disable-gpu-compositing to the embedded CEF runtime. Leave unset otherwise; +# this disables GPU acceleration and can break WebGL-heavy surfaces. +# OPENHUMAN_DISABLE_GPU= # --------------------------------------------------------------------------- # Web search diff --git a/app/src-tauri/src/lib.rs b/app/src-tauri/src/lib.rs index 46c15c870..490cd7705 100644 --- a/app/src-tauri/src/lib.rs +++ b/app/src-tauri/src/lib.rs @@ -2001,12 +2001,39 @@ fn cef_force_gpu_enabled(env_override: Option<&str>) -> bool { } } +/// Emergency CEF startup escape hatch for driver/runtime combinations where +/// CEF cannot initialize its GPU process before the app can render UI. +fn cef_disable_gpu_enabled(env_override: Option<&str>) -> bool { + match env_override { + Some(v) => { + let v = v.trim().to_ascii_lowercase(); + v == "1" || v == "true" || v == "yes" || v == "on" + } + None => false, + } +} + fn append_platform_cef_gpu_workarounds( args: &mut Vec, os: &str, arch: &str, force_gpu_override: Option<&str>, + disable_gpu_override: Option<&str>, ) { + let disable_gpu = cef_disable_gpu_enabled(disable_gpu_override); + + // Issue #4294: on some Windows CEF/GPU stacks, cef::initialize can fail + // before the app renders any UI, and user-supplied process argv switches + // do not reach the vendored CEF runtime. Provide a narrow, release-safe + // env escape hatch instead of forwarding arbitrary Chromium flags. + if disable_gpu { + args.push(("--disable-gpu", None)); + args.push(("--disable-gpu-compositing", None)); + log::info!( + "[cef-startup] OPENHUMAN_DISABLE_GPU set: adding --disable-gpu and --disable-gpu-compositing for CEF startup compatibility (issue #4294)" + ); + } + // Issue #1697: on Arch/Manjaro-family Linux systems, the AppImage can // abort during CEF GPU process startup when EGL context creation fails // before Chromium's own fallback path gets a usable renderer. @@ -2028,7 +2055,7 @@ fn append_platform_cef_gpu_workarounds( // SwiftShader-backed WebGL behind it (the "unsafe" label is about software // perf, not security). Users with working GPU stacks can still opt into // hardware acceleration via `OPENHUMAN_FORCE_GPU=1`. - if os == "linux" { + if os == "linux" && !disable_gpu { if cef_force_gpu_enabled(force_gpu_override) { log::info!( "[cef-startup] OPENHUMAN_FORCE_GPU set — skipping SwiftShader software-GL fallback (issue #1697). If the app fails to launch with a GPU process abort, unset the env var." @@ -2049,7 +2076,7 @@ fn append_platform_cef_gpu_workarounds( // Metal on Intel GPU hardware/drivers. Disable GPU compositing on // x86_64 macOS so the browser process falls back to software compositing // instead of aborting. - if os == "macos" && arch == "x86_64" { + if os == "macos" && arch == "x86_64" && !disable_gpu { args.push(("--disable-gpu-compositing", None)); log::info!( "[cef-startup] Intel macOS detected: adding --disable-gpu-compositing (issue #1012)" @@ -2790,11 +2817,13 @@ pub fn run() { args.push(("--remote-debugging-port", Some(leaked_port))); } let force_gpu_env = std::env::var("OPENHUMAN_FORCE_GPU").ok(); + let disable_gpu_env = std::env::var("OPENHUMAN_DISABLE_GPU").ok(); append_platform_cef_gpu_workarounds( &mut args, std::env::consts::OS, std::env::consts::ARCH, force_gpu_env.as_deref(), + disable_gpu_env.as_deref(), ); // #3554: never forward a `--time-ticks-at-unix-epoch` switch to CEF — // a corrupt/negative value drives the renderer's clock decades off and diff --git a/app/src-tauri/src/lib_tests.rs b/app/src-tauri/src/lib_tests.rs index 2447477e9..cd52d3adb 100644 --- a/app/src-tauri/src/lib_tests.rs +++ b/app/src-tauri/src/lib_tests.rs @@ -316,7 +316,7 @@ fn platform_os_is_macos_on_macos_build() { #[test] fn platform_cef_gpu_workarounds_force_swiftshader_on_linux() { let mut args = Vec::new(); - append_platform_cef_gpu_workarounds(&mut args, "linux", "x86_64", None); + append_platform_cef_gpu_workarounds(&mut args, "linux", "x86_64", None, None); // #4193: the GPU process must NOT be killed outright — `--disable-gpu` // takes every WebGL surface (the Tiny Place world renderer) down with it. @@ -336,7 +336,7 @@ fn platform_cef_gpu_workarounds_force_swiftshader_on_linux() { #[test] fn platform_cef_gpu_workarounds_disable_intel_macos_compositing_only() { let mut args = Vec::new(); - append_platform_cef_gpu_workarounds(&mut args, "macos", "x86_64", None); + append_platform_cef_gpu_workarounds(&mut args, "macos", "x86_64", None, None); assert_eq!(args, vec![("--disable-gpu-compositing", None)]); } @@ -345,7 +345,7 @@ fn platform_cef_gpu_workarounds_disable_intel_macos_compositing_only() { fn platform_cef_gpu_workarounds_leave_other_platforms_alone() { for (os, arch) in [("macos", "aarch64"), ("windows", "x86_64")] { let mut args = Vec::new(); - append_platform_cef_gpu_workarounds(&mut args, os, arch, None); + append_platform_cef_gpu_workarounds(&mut args, os, arch, None, None); assert!( args.is_empty(), @@ -392,7 +392,7 @@ fn platform_cef_gpu_workarounds_skip_linux_disable_when_force_gpu_set() { // OPENHUMAN-TAURI-K1 branch, which would make a strict `is_empty()` // check fail spuriously. We only care about the GPU branch here. let mut args = Vec::new(); - append_platform_cef_gpu_workarounds(&mut args, "linux", "x86_64", Some("1")); + append_platform_cef_gpu_workarounds(&mut args, "linux", "x86_64", Some("1"), None); assert!( !args.contains(&("--disable-gpu", None)), @@ -417,11 +417,64 @@ fn platform_cef_gpu_workarounds_force_gpu_does_not_affect_intel_macos_path() { // separate Intel-macOS #1012 disable must still apply, regardless of // the env var. let mut args = Vec::new(); - append_platform_cef_gpu_workarounds(&mut args, "macos", "x86_64", Some("1")); + append_platform_cef_gpu_workarounds(&mut args, "macos", "x86_64", Some("1"), None); assert_eq!(args, vec![("--disable-gpu-compositing", None)]); } +// ------------------------------------------------------------------------- +// OPENHUMAN_DISABLE_GPU override (emergency CEF startup escape hatch) +// ------------------------------------------------------------------------- + +#[test] +fn disable_gpu_default_off_when_env_unset() { + assert!(!cef_disable_gpu_enabled(None)); +} + +#[test] +fn disable_gpu_explicit_enable_values_match_force_gpu_pattern() { + for v in ["1", "true", "yes", "on", "TRUE", "Yes", "On"] { + assert!( + cef_disable_gpu_enabled(Some(v)), + "OPENHUMAN_DISABLE_GPU={v:?} should opt in" + ); + } +} + +#[test] +fn disable_gpu_anything_else_is_off() { + for v in ["", "0", "false", "no", "off", "FALSE", "Off", "maybe", " "] { + assert!( + !cef_disable_gpu_enabled(Some(v)), + "OPENHUMAN_DISABLE_GPU={v:?} must not silently opt in" + ); + } +} + +#[test] +fn platform_cef_gpu_workarounds_disable_windows_gpu_when_requested() { + let mut args = Vec::new(); + append_platform_cef_gpu_workarounds(&mut args, "windows", "x86_64", None, Some("1")); + + assert_eq!( + args, + vec![("--disable-gpu", None), ("--disable-gpu-compositing", None)] + ); +} + +#[test] +fn platform_cef_gpu_workarounds_disable_gpu_wins_over_linux_force_gpu() { + let mut args = Vec::new(); + append_platform_cef_gpu_workarounds(&mut args, "linux", "x86_64", Some("1"), Some("1")); + + assert!(args.contains(&("--disable-gpu", None))); + assert!(args.contains(&("--disable-gpu-compositing", None))); + assert!( + !args.contains(&("--use-angle", Some("swiftshader"))), + "OPENHUMAN_DISABLE_GPU=1 must not also force SwiftShader, got: {args:?}" + ); +} + // ------------------------------------------------------------------------- // #3554 — never forward --time-ticks-at-unix-epoch to CEF (wrong system time) // ------------------------------------------------------------------------- diff --git a/gitbooks/developing/cef.md b/gitbooks/developing/cef.md index 626b1cfbe..62bbca94d 100644 --- a/gitbooks/developing/cef.md +++ b/gitbooks/developing/cef.md @@ -117,6 +117,13 @@ for these details in the issue: For Windows Insider builds, also confirm whether the same installer launches on the current stable Windows release. That separates a profile/cache problem from an OS/runtime compatibility regression in CEF startup. + +If the logs point to a GPU-process startup failure rather than a stale CEF +profile lock, set `OPENHUMAN_DISABLE_GPU=1` before launching OpenHuman. This +passes `--disable-gpu` and `--disable-gpu-compositing` to the embedded CEF +runtime without forwarding arbitrary Chromium flags. Leave it unset for normal +use because disabling the GPU path can break WebGL-heavy surfaces. + ## Linux shell fallback for CEF startup crashes On some Linux desktops, especially NVIDIA proprietary driver setups under Wayland/XWayland, the Tauri/CEF shell can fail during native window configuration before the React app becomes usable. One known symptom is an X11 `BadWindow` error after CEF reports the main browser context.