detached bd83a2d

#26 fix(drm): allocate hardware cursor images as KMS dumb buffers

state
merged · 6d4359d · merge commit
branch
fix/nvidia-cursor-plane → main
opened
bnema ·
updated

Fixes neferwl#22.

Problem

On nvidia-drm, KMS refuses the hardware cursor plane with EINVAL (cursor plane refused; hardware cursor off). NeferWL then uses the software cursor, so every pointer motion forces a full frame. The cursor images were linear images that Vulkan allocated and exported, the same allocate-in-Vulkan, scan-out-in-KMS path that corrupted the primary plane on NVIDIA.

Fix

  • Allocation: cursor images are KMS dumb buffers (CREATE_DUMB, MAP_DUMB and mmap, AddFB2 by GEM handle), written by the CPU when the cursor image changes. Moves are unchanged: they only update the plane position.
  • Fallback: dumb buffers, then Vulkan cursor images, then the software cursor. Vulkan images are tried both when dumb allocation fails and when the plane refuses the dumb images in the TEST_ONLY probe; the current image is rewritten into them.
  • Log: hardware cursor logs alloc (dumb or vulkan, a warning for vulkan) and the size.
  • HDR cursor conversion: moved to internal/adapters/hdrcursor, so the drm adapter and the renderer write the same pixels. A recorded vector checks byte-identical output.

Verified

  • make check, make race, make mocks-check and staticcheck ./... pass.
  • Real-device dumb-buffer test (allocate, map, write, AddFB, read back; no modeset or commit) passes on amdgpu and on nvidia-drm 615.78.08.
  • Unit tests with generated mocks cover the allocator order, the TEST_ONLY refusal and swap (in-flight state, queued and hidden images), writes, crop and pitch, and cleanup.

Not verified

  • NVIDIA cursor plane acceptance with a display attached. This needs a test on an NVIDIA machine: the log should show "message":"hardware cursor" with "alloc":"dumb" and no cursor plane refused, and the cursor should show at startup.
CommitSubjectAuthorDateSignatureStatus
bd83a2dtest(drm): cover cursor fallback state, crop rows and file ownershipbriceGPG Verified
a5f1806fix(drm): allocate hardware cursor images as KMS dumb buffersbriceGPG Verified

Checks 1

28 files · +1824 -238
1/1
M .goreleaser.yaml +2 -2
M .mockery.yml +5 -0
M LICENSE +80 -67
M Makefile +1 -1
M README.md +2 -2
M docs/dev/architecture.md +7 -0
M internal/adapters/drm/atomic.go +9 -0
M internal/adapters/drm/commit_test.go +8 -6
M internal/adapters/drm/cursor.go +73 -29
A internal/adapters/drm/cursor_images.go +164 -0
A internal/adapters/drm/cursor_images_mock_test.go +204 -0
A internal/adapters/drm/cursor_images_test.go +491 -0
A internal/adapters/drm/dumb.go +77 -0
A internal/adapters/drm/dumb_device_test.go +111 -0
M internal/adapters/drm/kms_mock_test.go +308 -0
M internal/adapters/drm/kms_test.go +3 -0
M internal/adapters/drm/output.go +51 -20
A internal/adapters/hdrcursor/convert_test.go +29 -0
A internal/adapters/hdrcursor/hdrcursor.go +100 -0
A internal/adapters/hdrcursor/hdrcursor_test.go +76 -0
M internal/adapters/vulkan/cursor.go +5 -42
M internal/adapters/vulkan/cursor_test.go +6 -25
M internal/adapters/vulkan/hdr.go +2 -2
M internal/adapters/vulkan/pq.go +6 -34
M internal/adapters/vulkan/pq_test.go +0 -5
M internal/adapters/vulkan/renderer.go +2 -1
@@ -9,6 +9,7 @@ import (
99 "strconv"
1010 "unsafe"
1111
12+ "github.com/bnema/neferwl/internal/adapters/hdrcursor"
1213 "github.com/bnema/neferwl/internal/adapters/hintlabel"
1314 "github.com/bnema/neferwl/internal/ports"
1415 "github.com/bnema/zerowrap"
@@ -55,7 +56,7 @@ type Renderer struct {
5556 virtual bool
5657 physical vk.PhysicalDevice
5758 // One converted cursor image is kept; changes replace it in bounded memory.
58- cursorCache cursorConversion
59+ cursorCache hdrcursor.Cache
5960 // lastHDRTarget is the index in targets of the exported PQ target the
6061 // last HDR frame rendered into; -1 when the current targets hold none.
6162 lastHDRTarget int
@@ -9,6 +9,7 @@ import (
9 "strconv"9 "strconv"
10 "unsafe"10 "unsafe"
11 11
12+ "github.com/bnema/neferwl/internal/adapters/hdrcursor"
12 "github.com/bnema/neferwl/internal/adapters/hintlabel"13 "github.com/bnema/neferwl/internal/adapters/hintlabel"
13 "github.com/bnema/neferwl/internal/ports"14 "github.com/bnema/neferwl/internal/ports"
14 "github.com/bnema/zerowrap"15 "github.com/bnema/zerowrap"
@@ -55,7 +56,7 @@ type Renderer struct {
55 virtual bool56 virtual bool
56 physical vk.PhysicalDevice57 physical vk.PhysicalDevice
57 // One converted cursor image is kept; changes replace it in bounded memory.58 // One converted cursor image is kept; changes replace it in bounded memory.
58- cursorCache cursorConversion59+ cursorCache hdrcursor.Cache
59 // lastHDRTarget is the index in targets of the exported PQ target the60 // lastHDRTarget is the index in targets of the exported PQ target the
60 // last HDR frame rendered into; -1 when the current targets hold none.61 // last HDR frame rendered into; -1 when the current targets hold none.
61 lastHDRTarget int62 lastHDRTarget int
M packaging/arch/PKGBUILD +1 -1
@@ -5,7 +5,7 @@ pkgrel=1
55 pkgdesc='A Wayland compositor that spends its frames on your apps, not on itself'
66 arch=('x86_64')
77 url='https://github.com/bnema/neferwl'
8-license=('AGPL-3.0-only')
8+license=('GPL-3.0-only')
99 # Loaded at runtime (purego, no cgo): not seen by makepkg's library scan.
1010 depends=('glibc' 'libinput' 'libxkbcommon' 'seatd' 'systemd-libs' 'vulkan-icd-loader'
1111 'vulkan-driver' 'wayland' 'dbus' 'libcap')
@@ -5,7 +5,7 @@ pkgrel=1
5 pkgdesc='A Wayland compositor that spends its frames on your apps, not on itself'5 pkgdesc='A Wayland compositor that spends its frames on your apps, not on itself'
6 arch=('x86_64')6 arch=('x86_64')
7 url='https://github.com/bnema/neferwl'7 url='https://github.com/bnema/neferwl'
8-license=('AGPL-3.0-only')8+license=('GPL-3.0-only')
9 # Loaded at runtime (purego, no cgo): not seen by makepkg's library scan.9 # Loaded at runtime (purego, no cgo): not seen by makepkg's library scan.
10 depends=('glibc' 'libinput' 'libxkbcommon' 'seatd' 'systemd-libs' 'vulkan-icd-loader'10 depends=('glibc' 'libinput' 'libxkbcommon' 'seatd' 'systemd-libs' 'vulkan-icd-loader'
11 'vulkan-driver' 'wayland' 'dbus' 'libcap')11 'vulkan-driver' 'wayland' 'dbus' 'libcap')
M packaging/aur/neferwl-git/PKGBUILD +1 -1
@@ -7,7 +7,7 @@ pkgrel=1
77 pkgdesc='A Wayland compositor that spends its frames on your apps, not on itself (git version)'
88 arch=('x86_64' 'aarch64')
99 url='https://git.bnema.dev/neferwl'
10-license=('AGPL-3.0-only')
10+license=('GPL-3.0-only')
1111 # Loaded at runtime (purego, no cgo): not seen by makepkg's library scan.
1212 depends=('glibc' 'libinput' 'libxkbcommon' 'seatd' 'systemd-libs' 'vulkan-icd-loader'
1313 'vulkan-driver' 'wayland' 'dbus' 'libcap')
@@ -7,7 +7,7 @@ pkgrel=1
7 pkgdesc='A Wayland compositor that spends its frames on your apps, not on itself (git version)'7 pkgdesc='A Wayland compositor that spends its frames on your apps, not on itself (git version)'
8 arch=('x86_64' 'aarch64')8 arch=('x86_64' 'aarch64')
9 url='https://git.bnema.dev/neferwl'9 url='https://git.bnema.dev/neferwl'
10-license=('AGPL-3.0-only')10+license=('GPL-3.0-only')
11 # Loaded at runtime (purego, no cgo): not seen by makepkg's library scan.11 # Loaded at runtime (purego, no cgo): not seen by makepkg's library scan.
12 depends=('glibc' 'libinput' 'libxkbcommon' 'seatd' 'systemd-libs' 'vulkan-icd-loader'12 depends=('glibc' 'libinput' 'libxkbcommon' 'seatd' 'systemd-libs' 'vulkan-icd-loader'
13 'vulkan-driver' 'wayland' 'dbus' 'libcap')13 'vulkan-driver' 'wayland' 'dbus' 'libcap')

Comments0 open / 0

no comments yet.

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
l
merge request: the list of files (j k move, enter opens, esc back)
M
merge request: the merge menu; there 1–3 pick the mode, D deletes the branch, enter merges
R
merge request: mark the head read
esc
close a dialog, leave a field

Branches 25

Tags 10

↑↓ moveenter openesc close