fix(desktop): disable WebKitGTK DMA-BUF renderer on NVIDIA Linux (#14687)

On Linux with the proprietary NVIDIA driver the desktop app window opens
but stays blank. The sidecar/hub is healthy; only rendering fails, with
stderr showing:

  KMS: DRM_IOCTL_MODE_CREATE_DUMB failed: Permission denied
  Failed to create GBM buffer of size 1500x980: Permission denied

This is the known WebKitGTK DMA-BUF/NVIDIA incompatibility documented by
Tauri (https://v2.tauri.app/develop/debug/linux-graphics/), whose fix is
to set WEBKIT_DISABLE_DMABUF_RENDERER before the webview is created.

When /sys/module/nvidia is loaded and the variable is not already set, set
it to "1" as the first statement in main(), before tauri::Builder and any
threads. Existing values (including "0" and empty) are left alone so users
on hybrid-GPU systems can opt back into the DMA-BUF path with
WEBKIT_DISABLE_DMABUF_RENDERER=0. Done in the executable rather than the
.desktop launcher so terminal launches and every package format benefit.

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
This commit is contained in:
Saoud Rizwan
2026-09-29 23:15:11 -07:00
committed by GitHub
co-authored by Saoud Rizwan
parent f58bc118bd
commit 816513fded
3 changed files with 86 additions and 0 deletions
+11
View File
@@ -365,6 +365,17 @@ credentials, request headers, recorded audio, or transcript contents.
- Tauri restarts the desktop backend if the sidecar process exits and kills it on app teardown.
- Chat sends now preflight provider credentials. If a provider that requires API-key auth is selected without a key, the UI blocks the turn with a clear error message instead of starting a hanging session.
- If a turn completes with `finishReason=error` before any assistant content is produced, the UI now adds an explicit error chat message so failed turns are visible in the transcript.
- Linux with the proprietary NVIDIA driver: WebKitGTK's DMA-BUF renderer
fails there (`KMS: DRM_IOCTL_MODE_CREATE_DUMB failed: Permission denied`,
`Failed to create GBM buffer ...: Permission denied`) and the window stays
blank, so when `/sys/module/nvidia` is loaded the app sets
`WEBKIT_DISABLE_DMABUF_RENDERER=1` before creating the webview (see
[Tauri's Linux graphics guide](https://v2.tauri.app/develop/debug/linux-graphics/)).
This also matches hybrid-GPU systems where another GPU renders, which then
lose the faster DMA-BUF path (not hardware acceleration as a whole). To opt
out, fully Quit from the tray (closing the window only hides it) and launch
with `WEBKIT_DISABLE_DMABUF_RENDERER=0 cline-app`; any value you set is
left untouched.
- If package changes are not reflected, rebuild SDK packages (`bun run build:sdk`).
The next desktop or CLI Hub connection will reuse a compatible running Hub or
replace an incompatible one through the shared discovery path.
@@ -0,0 +1,68 @@
//! WebKitGTK's DMA-BUF renderer does not work on the proprietary NVIDIA
//! driver: the window opens but stays blank while stderr reports
//! `KMS: DRM_IOCTL_MODE_CREATE_DUMB failed: Permission denied` and
//! `Failed to create GBM buffer of size WxH: Permission denied`. The sidecar
//! is healthy; only rendering fails. Tauri's guidance is to set
//! `WEBKIT_DISABLE_DMABUF_RENDERER` before the webview is created:
//! https://v2.tauri.app/develop/debug/linux-graphics/
use std::ffi::OsStr;
use std::path::Path;
const DMABUF_RENDERER_VAR: &str = "WEBKIT_DISABLE_DMABUF_RENDERER";
const NVIDIA_MODULE_PATH: &str = "/sys/module/nvidia";
/// Must run before `tauri::Builder` and before any thread is spawned: WebKit
/// reads the variable during initialization, and mutating the environment is
/// only sound while the process is still single-threaded.
pub fn configure_environment() {
if should_disable_dmabuf_renderer(
Path::new(NVIDIA_MODULE_PATH).is_dir(),
std::env::var_os(DMABUF_RENDERER_VAR).as_deref(),
) {
std::env::set_var(DMABUF_RENDERER_VAR, "1");
}
}
/// Gated on the loaded NVIDIA kernel module rather than arch or session type
/// (the failure reproduces on ARM64/X11 too). Any value the user already set
/// is left alone, including "0" and the empty string, so the documented
/// opt-out (`WEBKIT_DISABLE_DMABUF_RENDERER=0`) keeps working on hybrid-GPU
/// systems where another GPU does the rendering.
fn should_disable_dmabuf_renderer(nvidia_module_loaded: bool, current: Option<&OsStr>) -> bool {
nvidia_module_loaded && current.is_none()
}
#[cfg(test)]
mod tests {
use super::*;
use std::os::unix::ffi::OsStrExt;
#[test]
fn disables_the_renderer_when_nvidia_is_loaded_and_the_var_is_unset() {
assert!(should_disable_dmabuf_renderer(true, None));
}
#[test]
fn leaves_the_renderer_alone_without_the_nvidia_module() {
assert!(!should_disable_dmabuf_renderer(false, None));
assert!(!should_disable_dmabuf_renderer(
false,
Some(OsStr::new("1"))
));
}
#[test]
fn preserves_an_explicit_opt_out() {
assert!(!should_disable_dmabuf_renderer(true, Some(OsStr::new("0"))));
assert!(!should_disable_dmabuf_renderer(true, Some(OsStr::new(""))));
}
#[test]
fn preserves_a_non_utf8_value() {
assert!(!should_disable_dmabuf_renderer(
true,
Some(OsStr::from_bytes(b"\xff"))
));
}
}
@@ -1,5 +1,7 @@
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
#[cfg(target_os = "linux")]
mod linux_webview;
#[cfg(target_os = "macos")]
mod macos_notification;
@@ -1465,6 +1467,11 @@ fn set_tray_status(
}
fn main() {
// Must stay first: WebKitGTK reads the environment while initializing,
// and the variable has to be in place before any thread exists.
#[cfg(target_os = "linux")]
linux_webview::configure_environment();
let desktop_backend = Arc::new(DesktopBackendState::default());
let launch_cwd = std::env::current_dir()
.map(|p| p.to_string_lossy().to_string())