Skip to content

Commit e54a35d

Browse files
committed
fix(linux/wlr): resilient GBM allocation for headless NVIDIA
GBM buffer allocation with GBM_BO_USE_RENDERING|GBM_BO_USE_LINEAR fails on headless NVIDIA render nodes. Try progressively relaxed flag combinations before falling back to SHM. Also prefer DRM render nodes over primary nodes in CUDA device lookup — primary nodes require DRM master which is unavailable on headless setups. Tested on RTX 4060 Ti headless (TrueNAS, no monitor): - GBM succeeds with GBM_BO_USE_RENDERING flag alone - VRAM capture path works (zero-copy DMA-BUF → EGL → CUDA → NVENC) - SHM fallback still catches cases where all GBM combos fail
1 parent 79cc8e9 commit e54a35d

2 files changed

Lines changed: 48 additions & 4 deletions

File tree

src/platform/linux/cuda.cpp

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -248,19 +248,36 @@ namespace cuda {
248248
return std::tolower(c);
249249
});
250250

251-
// Look for the name of the primary node in sysfs
251+
// Look for DRM nodes in sysfs — prefer render node (doesn't require DRM master)
252252
try {
253253
char sysfs_path[PATH_MAX];
254254
std::snprintf(sysfs_path, sizeof(sysfs_path), "/sys/bus/pci/devices/%s/drm", pci_bus_id.data());
255255
fs::path sysfs_dir {sysfs_path};
256+
257+
// First pass: look for render node (renderD*)
258+
for (auto &entry : fs::directory_iterator {sysfs_dir}) {
259+
auto file = entry.path().filename();
260+
auto filestring = file.generic_string();
261+
if (std::string_view {filestring}.substr(0, 7) != "renderD"sv) {
262+
continue;
263+
}
264+
265+
BOOST_LOG(debug) << "Found DRM render node: "sv << filestring;
266+
267+
fs::path dri_path {"/dev/dri"sv};
268+
auto device_path = dri_path / file;
269+
return open(device_path.c_str(), O_RDWR);
270+
}
271+
272+
// Second pass: fallback to primary node (card*)
256273
for (auto &entry : fs::directory_iterator {sysfs_dir}) {
257274
auto file = entry.path().filename();
258275
auto filestring = file.generic_string();
259276
if (std::string_view {filestring}.substr(0, 4) != "card"sv) {
260277
continue;
261278
}
262279

263-
BOOST_LOG(debug) << "Found DRM primary node: "sv << filestring;
280+
BOOST_LOG(debug) << "Found DRM primary node (fallback): "sv << filestring;
264281

265282
fs::path dri_path {"/dev/dri"sv};
266283
auto device_path = dri_path / file;

src/platform/linux/wayland.cpp

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -485,9 +485,36 @@ namespace wl {
485485
return false;
486486
}
487487

488-
current_bo = gbm_bo_create(gbm_device, dmabuf_info.width, dmabuf_info.height, dmabuf_info.format, GBM_BO_USE_RENDERING | GBM_BO_USE_LINEAR);
488+
// Try progressively relaxed GBM usage flags.
489+
// NVIDIA GBM on headless render nodes may not support all combinations.
490+
static const struct {
491+
uint32_t flags;
492+
const char *desc;
493+
} flag_combos[] = {
494+
{ GBM_BO_USE_RENDERING | GBM_BO_USE_LINEAR, "RENDERING|LINEAR" },
495+
{ GBM_BO_USE_RENDERING, "RENDERING" },
496+
{ GBM_BO_USE_LINEAR, "LINEAR" },
497+
{ 0, "none" },
498+
};
499+
500+
for (const auto &combo : flag_combos) {
501+
current_bo = gbm_bo_create(gbm_device, dmabuf_info.width, dmabuf_info.height, dmabuf_info.format, combo.flags);
502+
if (current_bo) {
503+
static bool flags_logged = false;
504+
if (!flags_logged) {
505+
BOOST_LOG(info) << "[wayland] GBM buffer created with flags: "sv << combo.desc
506+
<< " format=0x"sv << std::hex << dmabuf_info.format << std::dec
507+
<< " "sv << dmabuf_info.width << "x"sv << dmabuf_info.height;
508+
flags_logged = true;
509+
}
510+
break;
511+
}
512+
}
513+
489514
if (!current_bo) {
490-
BOOST_LOG(error) << "Failed to create GBM buffer"sv;
515+
BOOST_LOG(error) << "Failed to create GBM buffer with any flag combination"sv
516+
<< " format=0x"sv << std::hex << dmabuf_info.format << std::dec
517+
<< " "sv << dmabuf_info.width << "x"sv << dmabuf_info.height;
491518
return false;
492519
}
493520

0 commit comments

Comments
 (0)