Loading...
Searching...
No Matches
DmaBufImportCapture.hpp File Reference

Zero-copy capture strategy: import a producer's DMA-BUF ring straight into the renderer's sampled texture. More...

Detailed Description

Zero-copy capture strategy: import a producer's DMA-BUF ring straight into the renderer's sampled texture.

The capture-side twin of DRMPrime.hpp, for producers that hand out a fixed set of DMA-BUF file descriptors for the lifetime of a stream (V4L2 MMAP+EXPBUF or V4L2_MEMORY_DMABUF, and anything else shaped like it) rather than one fd per frame. Because the fds are fixed, every import happens once at init(); the per-frame cost is a single re-bind of the already-imported image, and no pixel is ever copied.

Backend dispatch, same two rungs as DRMPrimeDecoder:

  • QRhi::Vulkan -> DMABufPlaneImporter (VK_KHR_external_memory_fd + VK_EXT_image_drm_format_modifier), one VkImage per slot, re-pointed into the QRhiTexture per frame.
  • QRhi::OpenGLES2 -> EglDmaBufImporter (EGL_EXT_image_dma_buf_import_ modifiers + GL_OES_EGL_image), one EGLImage per slot, re-targeted onto one persistent GL texture per frame.
  • anything else, or an import the driver refuses -> init() returns false so the renderer degrades to the CPU-staging rung.

Geometry and format come from the caller's outputTexture: the wire decoder already sized it to the producer's byte layout (a UYVY 4:2:2 frame is an RGBA8 texture of half the width), so importing at exactly that size and format reinterprets the same bytes the CPU rung would have uploaded.

Planar layouts (NV12 and friends) import one texture per plane. Where each plane starts comes from one of two places:

  • the producer, when it states a layout in DmaBufSlotDesc::planes. Used verbatim. Real allocators do things a derivation cannot express – NvBufSurface aligns every plane offset to 64 KB even with row padding disabled – so a producer that knows should say.
  • otherwise derived from the decoder's own plane textures, exactly as CpuStagedCapture derives it: each plane's byte size is its texture geometry times its texel size, offsets accumulating in order. That keeps the two rungs agreeing about where chroma begins for a V4L2-style producer, and a new planar decoder needs no change here.

The derivation assumes tightly-packed rows, so a producer that neither states its layout nor packs tightly is refused rather than rendered wrong.

BORROWED-BUFFER LIFETIME (the contract the producer must honour)

The renderer samples producer memory directly, so a slot must not go back to the producer's device while the GPU may still read it. Ownership of a slot is held by exactly one party at a time:

producer thread ingestFrame(i) – slot i becomes render-owned. If a previously published slot had not been consumed yet, it is handed straight back (it was never bound). render thread acquireForRender – takes the published slot, binds it, and retires the slot it displaced. render thread – a retired slot only becomes returnable after FramesInFlight + 1 further acquisitions, which is the point at which QRhi guarantees the frame that last sampled it has completed on the GPU. (Acquisitions are counted, not QRhi frames, and there is at most one acquisition per QRhi frame, so the count is conservative.) producer thread takeReturnedSlots – drains the bitmask of slots that are safe to give back to the device.

A producer that never calls takeReturnedSlots() starves its own queue; one that returns a slot without it corrupts the frame being displayed.

The imported images are created with a DRM format modifier and EXCLUSIVE sharing, and no queue-family-foreign acquire barrier is issued – same as DRMPrimeDecoder. Adding one does not help where this goes wrong: on NVIDIA a buffer exported by a foreign device reads back partly as zeros with the acquire barrier present or absent alike (20/30 vs 21/30 corrupted over fresh processes), which is why DmaBufOrigin, not a barrier, is what gates the rung. A tiled vendor modifier would still need the barrier added.

Go to the source code of this file.