129 lines
4.1 KiB
Rust
129 lines
4.1 KiB
Rust
//! WebKitGTK workarounds for NVIDIA's proprietary driver on Linux.
|
|||
|
|
//!
|
||
|
|
//! On NVIDIA + Wayland (reported on CachyOS/KDE, driver 615, webkit2gtk 2.52)
|
||
|
|
//! the app never shows a window: GDK dies with "Error 71 (Protocol error)
|
||
|
|
//! dispatching to Wayland display", and under XWayland WebKit's DMA-BUF
|
||
|
|
//! renderer then fails with "Failed to create GBM buffer … Invalid argument".
|
||
|
|
//! `WEBKIT_DISABLE_DMABUF_RENDERER=1 GDK_BACKEND=x11` makes it run normally.
|
||
|
|
//!
|
||
|
|
//! So, before GTK/WebKit initialise, and only when the NVIDIA driver is loaded:
|
||
|
|
//! - `WEBKIT_DISABLE_DMABUF_RENDERER=1`;
|
||
|
|
//! - on a Wayland session with XWayland available, `GDK_BACKEND=x11`.
|
||
|
|
//!
|
||
|
|
//! Anything the user already set wins, and `LOTUS_NO_GPU_WORKAROUNDS=1`
|
||
|
|
//! disables all of it (e.g. once a newer driver/WebKit fixes this).
|
||
|
|
|
||
|
|
/// Environment variables to set: pure, for tests.
|
||
|
|
pub(crate) fn decide(
|
||
|
|
nvidia: bool,
|
||
|
|
get: impl Fn(&str) -> Option<String>,
|
||
|
|
) -> Vec<(&'static str, &'static str)> {
|
||
|
|
let set = |k: &str| get(k).is_some_and(|v| !v.is_empty());
|
||
|
|
if !nvidia || set("LOTUS_NO_GPU_WORKAROUNDS") {
|
||
|
|
return Vec::new();
|
||
|
|
}
|
||
|
|
let mut out = Vec::new();
|
||
|
|
if !set("WEBKIT_DISABLE_DMABUF_RENDERER") {
|
||
|
|
out.push(("WEBKIT_DISABLE_DMABUF_RENDERER", "1"));
|
||
|
|
}
|
||
|
|
let wayland = set("WAYLAND_DISPLAY")
|
||
|
|
|| get("XDG_SESSION_TYPE").is_some_and(|v| v.eq_ignore_ascii_case("wayland"));
|
||
|
|
// Only fall back to X11 when there is an X server (XWayland) to talk to.
|
||
|
|
if wayland && set("DISPLAY") && !set("GDK_BACKEND") {
|
||
|
|
out.push(("GDK_BACKEND", "x11"));
|
||
|
|
}
|
||
|
|
out
|
||
|
|
}
|
||
|
|
|
||
|
|
#[cfg(target_os = "linux")]
|
||
|
|
fn nvidia_driver_loaded() -> bool {
|
||
|
|
std::path::Path::new("/proc/driver/nvidia/version").exists()
|
||
|
|
|| std::path::Path::new("/sys/module/nvidia").exists()
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Call first thing in `main`, before anything starts GTK or spawns threads.
|
||
|
|
pub fn apply() {
|
||
|
|
#[cfg(target_os = "linux")]
|
||
|
|
for (key, value) in decide(nvidia_driver_loaded(), |k| std::env::var(k).ok()) {
|
||
|
|
eprintln!("gpu-workarounds: NVIDIA driver detected, setting {key}={value} (LOTUS_NO_GPU_WORKAROUNDS=1 to disable)");
|
||
|
|
std::env::set_var(key, value);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
#[cfg(test)]
|
||
|
|
mod tests {
|
||
|
|
use super::decide;
|
||
|
|
use std::collections::HashMap;
|
||
|
|
|
||
|
|
fn run(nvidia: bool, env: &[(&str, &str)]) -> Vec<(&'static str, &'static str)> {
|
||
|
|
let map: HashMap<String, String> = env
|
||
|
|
.iter()
|
||
|
|
.map(|(k, v)| (k.to_string(), v.to_string()))
|
||
|
|
.collect();
|
||
|
|
decide(nvidia, |k| map.get(k).cloned())
|
||
|
|
}
|
||
|
|
|
||
|
|
const WAYLAND: &[(&str, &str)] = &[("WAYLAND_DISPLAY", "wayland-0"), ("DISPLAY", ":0")];
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn nothing_without_nvidia() {
|
||
|
|
assert!(run(false, WAYLAND).is_empty());
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn nvidia_wayland_gets_both() {
|
||
|
|
assert_eq!(
|
||
|
|
run(true, WAYLAND),
|
||
|
|
vec![
|
||
|
|
("WEBKIT_DISABLE_DMABUF_RENDERER", "1"),
|
||
|
|
("GDK_BACKEND", "x11")
|
||
|
|
]
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn nvidia_x11_session_only_disables_dmabuf() {
|
||
|
|
assert_eq!(
|
||
|
|
run(true, &[("DISPLAY", ":0")]),
|
||
|
|
vec![("WEBKIT_DISABLE_DMABUF_RENDERER", "1")]
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn wayland_without_xwayland_keeps_wayland() {
|
||
|
|
assert_eq!(
|
||
|
|
run(true, &[("WAYLAND_DISPLAY", "wayland-0")]),
|
||
|
|
vec![("WEBKIT_DISABLE_DMABUF_RENDERER", "1")]
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn xdg_session_type_counts_as_wayland() {
|
||
|
|
assert_eq!(
|
||
|
|
run(true, &[("XDG_SESSION_TYPE", "wayland"), ("DISPLAY", ":1")]),
|
||
|
|
vec![
|
||
|
|
("WEBKIT_DISABLE_DMABUF_RENDERER", "1"),
|
||
|
|
("GDK_BACKEND", "x11")
|
||
|
|
]
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn user_settings_win() {
|
||
|
|
let env = [
|
||
|
|
("WAYLAND_DISPLAY", "wayland-0"),
|
||
|
|
("DISPLAY", ":0"),
|
||
|
|
("WEBKIT_DISABLE_DMABUF_RENDERER", "0"),
|
||
|
|
("GDK_BACKEND", "wayland"),
|
||
|
|
];
|
||
|
|
assert!(run(true, &env).is_empty());
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn opt_out_disables_everything() {
|
||
|
|
let mut env = WAYLAND.to_vec();
|
||
|
|
env.push(("LOTUS_NO_GPU_WORKAROUNDS", "1"));
|
||
|
|
assert!(run(true, &env).is_empty());
|
||
|
|
}
|
||
|
|
}
|