On the NVIDIA proprietary driver, the screen shows heavy corruption: horizontal bands, repeated fragments and large black or grey areas. KMS accepts the output framebuffers (modifier 0x300000000606014, block-linear) and nothing crashes.
NeferWL allocated its two output images with Vulkan and exported them to KMS. NVIDIA does not scan these images out correctly. gamescope hits the same bug with the same modifier (ValveSoftware/gamescope#2309). wlroots, Hyprland/Aquamarine, niri/Smithay and gamescope's fix all allocate scanout buffers with GBM instead.
Fix
GBM allocation: output images are allocated with GBM (SCANOUT | RENDERING) on the KMS card. The modifier list is the plane's IN_FORMATS intersected with the modifiers the renderer can import. libgbm is loaded with purego, so the build stays CGO_ENABLED=0.
Vulkan import: the images are imported into Vulkan with explicit modifier layouts. Rendering is still zero-copy.
Fallback (deprecated, neferwl#23): for each image kind (driver, single-plane, linear), the renderer's Vulkan export is used when GBM is missing or fails. The zero-copy output line is then a warning with "alloc":"vulkan".
Old nvidia-drm: before 560.35.3, IN_FENCE_FD makes commits fail (NVIDIA/open-gpu-kernel-modules#622). On these versions the render fence is waited on the CPU, and a client buffer whose fence is still pending is composed rather than scanned out.
Verified
make check, make race, make mocks-check and staticcheck ./... pass.
The GPU test (GBM allocation, Vulkan import, render, pixel readback) passes on AMD RX 9070 (GBM picks 0x200000000082305) and on NVIDIA RTX 3070 Ti, driver 615.78.08, render node only (GBM picks 0x300000000606013).
AMD, real KMS: two outputs (5120×2160@165, 3840×2160@120) run on "alloc":"gbm" with the same modifier as before, a clean display, and workspace and VT switching.
NVIDIA, real KMS: a user confirmed the display works on nvidia-drm (DP-1 3440×1440, "alloc":"gbm", modifier 0x300000000606014).
Not verified
The IN_FENCE_FD gate on an nvidia-drm older than 560.35.3.
HDR (XRGB2101010) with GBM images.
Follow-ups
neferwl#22: the hardware cursor plane is refused on nvidia-drm.
neferwl#23: remove the deprecated Vulkan-export fallback.
// SDR targets are composed and read back; HDR targets are color
171
258
// attachments, and transfer sources only with hdrReadback.
172
-
result, planes := filterModifiers(mods[:list.DrmFormatModifierCount], need, func(m uint64) bool { return r.exportable(physical, format, m) })
259
+
result, planes := filterModifiers(mods[:list.DrmFormatModifierCount], need, func(m uint64) bool { return r.externalTarget(physical, format, m, feature) })
173
260
if r.modPlanes == nil {
174
261
r.modPlanes = make(map[uint64]uint32)
175
262
}
@@ -216,7 +303,10 @@ func filterModifiers(mods []vk.DrmFormatModifierPropertiesEXT, need vk.FormatFea
// SDR targets are composed and read back; HDR targets are color
257
// SDR targets are composed and read back; HDR targets are color
171
// attachments, and transfer sources only with hdrReadback.
258
// attachments, and transfer sources only with hdrReadback.
172
-
result, planes := filterModifiers(mods[:list.DrmFormatModifierCount], need, func(m uint64) bool { return r.exportable(physical, format, m) })
259
+
result, planes := filterModifiers(mods[:list.DrmFormatModifierCount], need, func(m uint64) bool { return r.externalTarget(physical, format, m, feature) })
173
if r.modPlanes == nil {
260
if r.modPlanes == nil {
174
r.modPlanes = make(map[uint64]uint32)
261
r.modPlanes = make(map[uint64]uint32)
175
}
262
}
@@ -216,7 +303,10 @@ func filterModifiers(mods []vk.DrmFormatModifierPropertiesEXT, need vk.FormatFea
'gsettings set org.xfce.mousepad.preferences.view use-default-monospace-font false; gsettings set org.xfce.mousepad.preferences.view font-name "JetBrainsMono Nerd Font 15"; gsettings set org.xfce.mousepad.preferences.window menubar-visible false; mousepad /home/demo/config'
# GSettings and no session bus (it needs the root-owned bus otherwise).
82
+
'gsettings set org.xfce.mousepad.preferences.view use-default-monospace-font false; gsettings set org.xfce.mousepad.preferences.view font-name "JetBrainsMono Nerd Font 15"; gsettings set org.xfce.mousepad.preferences.window menubar-visible false; mousepad /home/demo/config'
<main><header>◈ NEFERWL</header><h1>Your desktop.<br>A little more focused.</h1><p>A Wayland compositor with room for your work.<br>Tile naturally, set things aside, see the whole picture.</p><div class="cards"><section class="card"><b>01 / Performance</b><p>Small, responsive.<br>Built in Go. Made to stay out of your way.</p></section><section class="card"><b>02 / Stash</b><p>Keep tools close.<br>Set aside a window. Bring it back in a keystroke.</p></section><section class="card"><b>03 / Overview</b><p>Find your focus.<br>Every workspace and stashed window, at a glance.</p></section></div><pre># A desktop that flows with you
10
+
layout.max-columns = 2
11
+
layout.overflow = cascade
12
+
animations = on</pre><footer>SUPER + ↑ / ↓ bands · SUPER + S stash · SUPER + O overview</footer></main>
<main><header>◈ NEFERWL</header><h1>Your desktop.<br>A little more focused.</h1><p>A Wayland compositor with room for your work.<br>Tile naturally, set things aside, see the whole picture.</p><div class="cards"><section class="card"><b>01 / Performance</b><p>Small, responsive.<br>Built in Go. Made to stay out of your way.</p></section><section class="card"><b>02 / Stash</b><p>Keep tools close.<br>Set aside a window. Bring it back in a keystroke.</p></section><section class="card"><b>03 / Overview</b><p>Find your focus.<br>Every workspace and stashed window, at a glance.</p></section></div><pre># A desktop that flows with you
10
+
layout.max-columns = 2
11
+
layout.overflow = cascade
12
+
animations = on</pre><footer>SUPER + ↑ / ↓ bands · SUPER + S stash · SUPER + O overview</footer></main>