v0.7.2 71ebe61

0003-vulkan-dmabuf-presentation.md RawHistoryFolder

ADR 0003 — Vulkan presentation through DMA-BUF

Status: accepted

Decision

NeferGUI renders with Vulkan into exportable images and hands them to the application as DMA-BUF buffers (Renderer, see ADR 0007). The application presents them through zwp_linux_dmabuf_v1. NeferGUI does not use VK_KHR_swapchain.

  • Synchronization uses DRM syncobj timelines (wp_linux_drm_syncobj_v1 on the Wayland side) with Vulkan timeline semaphores. There is no CPU-fence fallback.
  • The Vulkan physical device must match the main_device of linux-dmabuf feedback, passed in RendererConfig.MainDevice. There is no cross-GPU fallback.
  • Formats: one-plane XRGB8888 by default and ARGB8888 when RendererConfig.Transparent is set, both mapped to VK_FORMAT_B8G8R8A8_UNORM, with a modifier supported by both the compositor (RendererConfig.Formats) and the GPU.
  • Three exportable images per Renderer, re-created on resize.
  • Shaders are GLSL compiled by glslc through go generate. SPIR-V is committed and embedded; consumers do not need glslc.

Buffer lifecycle

Available → Rendering → acquire point exported → Output returned by Render
→ compositor reads → release point of its latest commit signaled → Released → Available

Each buffer has its own release timeline, because points may signal out of order. After each Render the Renderer arms an eventfd on the release point of that commit (DRM_IOCTL_SYNCOBJ_EVENTFD, one-shot). Output.ReleaseFD is that eventfd. When it becomes readable the application calls Released(buffer), which reads the eventfd and returns the buffer to the pool. A buffer is reused only after the release point set for its most recent commit has signaled; wl_buffer.release never gates reuse, because its delivery is undefined while a syncobj surface exists. Frame callbacks schedule drawing; they never authorize reuse.

The Renderer starts no goroutine and never blocks on the GPU or the compositor. Render returns false when no buffer is free; Pending() reports a built frame that waits for the GPU rather than the compositor, and the application retries after a short timer. Resize creates a new generation: buffers of the old one are reported in Output.Retired once their release point has signaled, and their descriptors stay open until the next Render or Close.

All descriptors in Output (DMA-BUF planes, timelines, eventfds) stay owned by the Renderer. The application duplicates them before a transport consumes them.

Shutdown

Release points protect reuse of contents, not the lifetime of exported DMA-BUF memory: kernel descriptor references and compositor imports are independent of the client's Vulkan allocation. Close waits for client GPU completion, then frees all images, memory, semaphores, syncobj handles and eventfds, and destroys the device. It never abandons Vulkan allocations because a compositor release point has not arrived. The application detaches and destroys its own Wayland buffers and syncobj surface before or after Close; the Renderer does not know about them. Close also returns any Vulkan validation errors recorded while NEFERGUI_VULKAN_VALIDATION=1 was set.

Opaque or transparent regions, background blur and cursor mechanics belong to the application: Output.InputRects and Output.Cursor carry the requests, and RendererConfig.Transparent selects premultiplied alpha output.

source
1# ADR 0003 — Vulkan presentation through DMA-BUF
2
3Status: accepted
4
5## Decision
6
7NeferGUI renders with Vulkan into exportable images and hands them to the application as DMA-BUF buffers (`Renderer`, see ADR 0007). The application presents them through `zwp_linux_dmabuf_v1`. NeferGUI does not use `VK_KHR_swapchain`.
8
9- Synchronization uses DRM syncobj timelines (`wp_linux_drm_syncobj_v1` on the Wayland side) with Vulkan timeline semaphores. There is no CPU-fence fallback.
10- The Vulkan physical device must match the `main_device` of linux-dmabuf feedback, passed in `RendererConfig.MainDevice`. There is no cross-GPU fallback.
11- Formats: one-plane `XRGB8888` by default and `ARGB8888` when `RendererConfig.Transparent` is set, both mapped to `VK_FORMAT_B8G8R8A8_UNORM`, with a modifier supported by both the compositor (`RendererConfig.Formats`) and the GPU.
12- Three exportable images per Renderer, re-created on resize.
13- Shaders are GLSL compiled by `glslc` through `go generate`. SPIR-V is committed and embedded; consumers do not need `glslc`.
14
15## Buffer lifecycle
16
17```text
18Available → Rendering → acquire point exported → Output returned by Render
19→ compositor reads → release point of its latest commit signaled → Released → Available
20```
21
22Each buffer has its own release timeline, because points may signal out of order. After each `Render` the Renderer arms an eventfd on the release point of that commit (`DRM_IOCTL_SYNCOBJ_EVENTFD`, one-shot). `Output.ReleaseFD` is that eventfd. When it becomes readable the application calls `Released(buffer)`, which reads the eventfd and returns the buffer to the pool. A buffer is reused only after the release point set for its most recent commit has signaled; `wl_buffer.release` never gates reuse, because its delivery is undefined while a syncobj surface exists. Frame callbacks schedule drawing; they never authorize reuse.
23
24The Renderer starts no goroutine and never blocks on the GPU or the compositor. `Render` returns false when no buffer is free; `Pending()` reports a built frame that waits for the GPU rather than the compositor, and the application retries after a short timer. Resize creates a new generation: buffers of the old one are reported in `Output.Retired` once their release point has signaled, and their descriptors stay open until the next `Render` or `Close`.
25
26All descriptors in `Output` (DMA-BUF planes, timelines, eventfds) stay owned by the Renderer. The application duplicates them before a transport consumes them.
27
28## Shutdown
29
30Release points protect reuse of contents, **not** the lifetime of exported DMA-BUF memory: kernel descriptor references and compositor imports are independent of the client's Vulkan allocation. `Close` waits for client GPU completion, then frees **all** images, memory, semaphores, syncobj handles and eventfds, and destroys the device. It never abandons Vulkan allocations because a compositor release point has not arrived. The application detaches and destroys its own Wayland buffers and syncobj surface before or after `Close`; the Renderer does not know about them. `Close` also returns any Vulkan validation errors recorded while `NEFERGUI_VULKAN_VALIDATION=1` was set.
31
32Opaque or transparent regions, background blur and cursor mechanics belong to the application: `Output.InputRects` and `Output.Cursor` carry the requests, and `RendererConfig.Transparent` selects premultiplied alpha output.

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
esc
close a dialog, leave a field

Branches 1

Tags 12

↑↓ moveenter openesc close