Skip to content

Commit

Permalink
x11: Workaround nvidia driver lacking DRI
Browse files Browse the repository at this point in the history
  • Loading branch information
Drakulix authored and mmstick committed Jul 16, 2024
1 parent e6a8a07 commit cb315f6
Show file tree
Hide file tree
Showing 2 changed files with 111 additions and 17 deletions.
2 changes: 1 addition & 1 deletion wgpu/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,4 @@ wayland-client = { version = "0.31.2" }
wayland-sys = { version = "0.31.1", features = ["dlopen"] }
as-raw-xcb-connection = "1.0.1"
tiny-xlib = "0.2.3"
x11rb = { version = "0.13.1", features = ["allow-unsafe-code", "dl-libxcb", "dri3"] }
x11rb = { version = "0.13.1", features = ["allow-unsafe-code", "dl-libxcb", "dri3", "randr"] }
126 changes: 110 additions & 16 deletions wgpu/src/window/x11.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,26 @@
use std::{
fs,
io::{BufRead, BufReader},
path::Path,
};

use crate::graphics::compositor::Window;

use as_raw_xcb_connection::AsRawXcbConnection;
use raw_window_handle::{
RawDisplayHandle, XcbDisplayHandle, XlibDisplayHandle,
};
use rustix::fs::fstat;
use rustix::fs::{fstat, stat};
use tiny_xlib::Display;
use x11rb::{
connection::{Connection, RequestConnection},
protocol::dri3::{ConnectionExt as _, X11_EXTENSION_NAME as DRI3_NAME},
protocol::{
dri3::{ConnectionExt as _, X11_EXTENSION_NAME as DRI3_NAME},
randr::{
ConnectionExt as _, ProviderCapability,
X11_EXTENSION_NAME as RANDR_NAME,
},
},
xcb_ffi::XCBConnection,
};

Expand Down Expand Up @@ -57,21 +69,103 @@ pub fn get_x11_device_ids<W: Window>(window: &W) -> Option<(u16, u16)> {
return None;
}
};
let root = conn.setup().roots[screen as usize].root;

// check for DRI3
let _ = conn.extension_information(DRI3_NAME).ok()??;
// we have dri3, dri3_open exists on any version, so lets skip version checks.
// The nvidia xorg driver advertises DRI2 and DRI3,
// but doesn't really return any useful data for either of them.
// We also can't query EGL, as a display created from an X11 display
// running on the properietary driver won't return an EGLDevice.
//
// So we have to resort to hacks.

// check for randr
let _ = conn.extension_information(RANDR_NAME).ok()??;
// check version, because we need providers to exist
let version = conn.randr_query_version(1, 4).ok()?.reply().ok()?;
if version.major_version < 1
|| (version.major_version == 1 && version.minor_version < 4)
{
return None;
}

// get the name of the first Source Output provider, that will be our main device
let randr = conn.randr_get_providers(root).ok()?.reply().ok()?;
let mut name = None;
for provider in randr.providers {
let info = conn
.randr_get_provider_info(provider, randr.timestamp)
.ok()?
.reply()
.ok()?;
if info
.capabilities
.contains(ProviderCapability::SOURCE_OUTPUT)
|| name.is_none()
{
name = std::str::from_utf8(&info.name)
.ok()
.map(ToString::to_string);
}
}

// if that name is formatted `NVIDIA-x`, then x represents the /dev/nvidiaX number, which we can relate to /dev/dri
if let Some(number) = name.and_then(|name| {
name.trim().strip_prefix("NVIDIA-")?.parse::<u32>().ok()
}) {
// let it be known, that I hate this "interface"...
for busid in fs::read_dir("/proc/driver/nvidia/gpus")
.ok()?
.map(Result::ok)
.flatten()
{
for line in BufReader::new(
fs::File::open(busid.path().join("information")).ok()?,
)
.lines()
{
if let Ok(line) = line {
if line.starts_with("Device Minor") {
if let Some((_, num)) = line.split_once(":") {
let minor = num.trim().parse::<u32>().ok()?;
if minor == number {
// we found the device
for device in fs::read_dir(
Path::new("/sys/module/nvidia/drivers/pci:nvidia/")
.join(busid.file_name())
.join("drm"),
)
.ok()?
.map(Result::ok)
.flatten()
{
let device = device.file_name();
if device.to_string_lossy().starts_with("card")
|| device.to_string_lossy().starts_with("render")
{
let stat =
stat(Path::new("/dev/dri").join(device)).ok()?;
let dev = stat.st_rdev;
return super::ids_from_dev(dev);
}
}
}
}
}
}
}
}

// provider being NONE tells the X server to use the RandR provider.
let screen = &conn.setup().roots[screen as usize];
let dri3 = conn
.dri3_open(screen.root, x11rb::NONE)
.ok()?
.reply()
.ok()?;
let device_fd = dri3.device_fd;
let stat = fstat(device_fd).ok()?;
let dev = stat.st_rdev;
None
} else {
// check via DRI3
let _ = conn.extension_information(DRI3_NAME).ok()??;
// we have dri3, dri3_open exists on any version, so skip version checks.

super::ids_from_dev(dev)
// provider being NONE tells the X server to use the RandR provider.
let dri3 = conn.dri3_open(root, x11rb::NONE).ok()?.reply().ok()?;
let device_fd = dri3.device_fd;
let stat = fstat(device_fd).ok()?;
let dev = stat.st_rdev;
super::ids_from_dev(dev)
}
}

0 comments on commit cb315f6

Please sign in to comment.