Re: Software rendering with XCB: (MIT-SHM + Present) or (dma-buf + DRI3 + Present) or other?
Michel Dänzer <michel.daenzer-cl+VPiYnx/[email protected]> Wed, 4 Mar 2026 18:44:41 +0100
| Newsgroups | gmane.comp.freedesktop.xcb |
|---|---|
| Message-ID | <[email protected]> |
There's a lot to unpack here... On 3/4/26 00:49, Tristan Cadet wrote: > > I could not find a guide on writing XCB software renderers, The joys of being a trailblazer. :) > but based on my investigation, the options are: > > Option A) MIT-SHM extension + Present > > 1. get a pixel buffer: `xcb_shm_create_segment` + `mmap` > 2. get a pixmap associated with the buffer: `xcb_shm_create_pixmap` > 3. event loop: > - software render in buffer > - present with `xcb_present_pixmap` > - block until `XCB_PRESENT_COMPLETE_NOTIFY` (VSYNC) > - continue > > Questions: > a) Is it recommended to create the pixel buffer on the client side (`memfd_create` + `xcb_shm_attach_fd`) or on the server side (`xcb_shm_create_segment`)? Don't know of any reason why it would matter either way offhand. I'm not really an expert in this area though. Note that SHM pixmaps are optional in the MIT-SHM extension (e.g. the xserver EXA acceleration architecture doesn't support them). > b) Is the event loop correctly organized? I heard that I should double-buffer to avoid tearing, but it does not seem necessary. If I synchronize rendering to VSYNC (blocking on `XCB_PRESENT_COMPLETE_NOTIFY` after presenting), I know that when the next frame begins the buffer of the previous frame has been displayed and is free to render into, so single buffering should be enough. That's not correct. You need to wait for the XCB_PRESENT_IDLE_NOTIFY event before you can reuse the pixmap for a new frame, XCB_PRESENT_COMPLETE_NOTIFY doesn't suffice for that. The XCB_PRESENT_IDLE_NOTIFY event may not be sent before the server processes another xcb_present_pixmap request, which means you need at least two pixmaps per window to guarantee forward progress. In order to be able to start working on the next frame ASAP, you might want to use even more pixmaps per window. > The only issue I see is if a frame takes too long to render, in that case the loop blocks until the next VSYNC and latency doubles to 33ms. To avoid that, it would be helpful to receive a stream of VSYNC events I can drain (or alternatively I could use a client-side clock to detect when a frame runs late). You can trigger a XCB_PRESENT_COMPLETE_KIND_NOTIFY_MSC event via xcb_present_notify_msc. > c) What is the performance of `xcb_present_pixmap`? I suppose what matters is the number of frame copies it does? Does it basically do 1 CPU memcpy of the frame to VRAM? 1 copy from the source pixmap storage to the window storage, which may or may not be in VRAM. (In theory the copy could be performed by the GPU, AFAIK that's not commonly the case ATM though) > Option B) dma-buf + DRI3 + Present > > 1. get a pixel buffer: `memfd_create` + `mmap` + `ioctl` with `UDMABUF_CREATE` > 2. get a pixmap associated with the buffer: `xcb_dri3_pixmap_from_buffer` > 3. event loop: same as for MIT-SHM > > Questions: > a) As far as I understand, DRI3 is intended for GPU rendering, so I am surprised that this works with memfd buffers. Is it just luck or is this a supported path? It's supported. > b) Performance doesn't seem very different from the MIT-SHM approach, except maybe slightly less CPU usage. dma-buf is supposed to allow passing buffers directly to devices, but is there any benefit here given that the backing memory is a memfd rather than VRAM? My current understanding is that in the best case this turns the CPU copy of the MIT-SHM path into a slightly cheaper GPU copy? With fullscreen windows (or even just undecorated windows with rootless Xwayland), in principle it's even possible to hit the zero-copy page flip path. I'd say that option B) is generally the preferred one. > Option C) DRM dumb buffer > > 1. get a pointer to a pixel buffer: `xcb_dri3_open` + `ioctl` `DRM_IOCTL_MODE_CREATE_DUMB`, `DRM_IOCTL_MODE_MAP_DUMB`, `DRM_IOCTL_PRIME_HANDLE_TO_FD` + `mmap` > 2. get a pixmap: same as B) > 3. event loop: same as B) > > Questions: > a) So far I get a black window with this approach, is it supposed to work? Even if it might work in theory, dumb BOs are intended for different use cases, I'd recommend against using them for this. > c) Read performance on the dumb buffer is terrible (6 times worse than RAM based off quick tests) which I believe is due to write-combined memory. It can be even worse if the dumb BO is located in VRAM (which is the intention for discrete GPUs), think tens of MB/s. > I also read something about fences, are these only for the GPU path, or are these (or some other means of synchronization besides Present) required/recommended for a software renderer? Unless the software rendering to the pixmap has already finished when you call xcb_present_pixmap, you need to pass a wait_fence and signal it only once the rendering has finished, or the server might start reading from the pixmap earlier, potentially getting incorrect contents. > In addition to the above questions, are there any other trade-offs or points I should pay attention to for an XCB software renderer? Nothing comes to mind offhand, you've covered it pretty comprehensively. :) -- Earthling Michel Dänzer \ GNOME / Xwayland / Mesa developer https://redhat.com \ Libre software enthusiast