Skip to main content

Module dri3

Module dri3 

Source
Expand description

DRI3 zero-copy X11 capture: the X server blits the screen into GPU buffers this process allocated, and the hardware encoder reads them where they lie.

It serves a server whose screen lives on the GPU (XLibre’s Xvfb with glamor, an Xorg on a DRM driver), where the XShm path in super would read the screen back into host memory for the hardware session to upload again. A small pool of dmabufs is allocated through GBM on the render node the server draws with, each is wrapped in an X pixmap (PixmapFromBuffers), and every frame is one CopyArea from the root window into the next pixmap, which glamor runs as a GPU-to-GPU blit. The encoder then imports the dmabuf in place, through the same FrameEncoder::encode_dmabuf the Wayland zero-copy path uses, so a frame is never touched by the CPU between the screen and the bitstream.

Completion is waited for with a one-pixel GetImage of the destination pixmap: the server answers only after the blit it ordered before has landed, so the encoder never reads a half-copied frame, and no fence object or implicit-sync assumption is involved.

Where the server offers SELKIES-SEMAPHORE (the images’ Xvfb) and the encoder can wait on a GPU semaphore (NVENC), the server signals one the encoder made after each blit instead, and the encode is queued at once behind a wait on it. Under a GPU-bound client each GPU job that has to wait for the CPU to submit it waits a draw of that client for its turn, so the GetImage cost two draws a frame where the semaphore costs one: an uncapped 72 fps client streamed at 36 fps with the GetImage. A checked Signal request is the round trip the wait follows, which answers once the server has queued the signal, not once the GPU ran it: a wait queued before the server’s signal lost its turn as well.

Nothing here reads pixels, so what stands in for the XShm path’s per-stripe content hashing is the Damage extension on the root window: the server’s report is both what says a frame is worth encoding and what ends the wait for one, within the pacing crate::pace keeps, so a change is published as it lands rather than on the next tick. Its region says how much changed, so a blinking caret stays a small change and a still screen is cleaned up around it. The XFixes cursor is composited by the server through Render, on the GPU, so the capture_cursor overlay costs no readback either; the server keeps its own software cursor out of a copy from the screen, so the cursor is never drawn twice.

Everything that can be checked is checked before a frame is delivered, and the path is declined otherwise: a codec no hardware engine serves, software encoding, a server without DRI3 1.2, Damage, or Render, a server drawing on a GPU other than the encode node’s, a buffer the server will not import, or a first frame the encoder cannot read. A watermark is not among them: it is composited by the server through Render, as the cursor is. The capture then runs the XShm path with nothing half-built.

Functions§

run_capture
Run the zero-copy X11 capture until controls.stop is set, or report that it cannot start.