GPU RGBA -> NV12 / I420 / YV12, one texture holding the framestore. More...
Detailed Description
GPU RGBA -> NV12 / I420 / YV12, one texture holding the framestore.
The plane encoders give two or three plane textures; a consumer that hands a device one pointer then has to concatenate them, and pays a GPU->CPU round trip per plane to get there. This produces the framestore directly, so one readback is the whole thing. At 2160p that is 2.4 ms a frame against 10.3.
Plane geometry, which is what chroma_byte() has to navigate:
NV12: Y is w bytes over h rows; interleaved CbCr is also w bytes (w/2 sites x 2 components) over h/2 rows. Uniform rows. I420: Y is w bytes over h rows; Cb then Cr are w/2 bytes over h/2 rows. YV12: I420 with Cr before Cb.
The target is RGBA8 of (w/4) x (3h/2), four framestore bytes per texel, and R8 of w x (3h/2) when the width is not a multiple of four – 722 and 1922 are even, so 4:2:0 can express them, but an RGBA8 target cannot be w/4 texels wide. One byte per texel runs a fragment, a fetch and a colour conversion per byte, which measured slower than the plane encoders it replaces, so it is the fallback rather than the rule.
Chroma is a 2x2 box, centre-sited. Both plausible improvements were tried and measured worse against a real NDI receiver; they remain in chroma_at() behind SCORE_GFX_CHROMA_SITING so the measurement can be repeated.
Siting. MPEG-2/H.264 put the sample on the even luma column. Sending colour bars through the SDK and locating each chroma edge on the way back: centre displaced it by -0.001 luma columns, left by +0.165. The receiver assumes centre.
Pre-filtering. [1 3 3 1]/8 each way band-limits before decimating. On red/blue stripes round-tripped through the SDK, mean |RGB error| by stripe period in luma columns:
period 4 6 8 12 16 32 box 5.48 33.06 8.46 5.23 3.15 2.67 [1 3 3 1] 49.63 47.04 27.02 15.74 12.95 8.02
Worse everywhere, including at Nyquist: the receiver upsamples with something box-like and does not reconstruct a band-limited signal, so the blur costs more than the aliasing it removes.
Requires even width and height.
Public Types | |
| enum class | Siting { Centre = 0 , Left = 1 , CentreWide = 2 } |
| Where a chroma sample sits horizontally relative to its luma pair. More... | |
| enum class | Layout { NV12 , I420 , YV12 } |
Public Member Functions | |
| Yuv420PackedEncoder (Layout layout, Siting siting=Siting::Centre) noexcept | |
| int | framestoreRows () const noexcept |
| Rows in the whole framestore: the picture, plus half of it again. | |
| void | init (QRhi &rhi, const RenderState &state, QRhiTexture *inputRGBA, int width, int height, const QString &colorConversion) override |
| void | exec (QRhi &rhi, QRhiCommandBuffer &cb) override |
| int | planeCount () const override |
| Number of readback planes (1 for UYVY, 2 for NV12, 3 for I420). | |
| const QRhiReadbackResult & | readback (int) const override |
| Get the readback result for a given plane. Valid after endOffscreenFrame. | |
| QRhiTexture * | outputTexture () const noexcept override |
| void | setReadbackEnabled (bool e) noexcept override |
| void | release () override |
| Release all GPU resources. | |
Static Public Member Functions | |
| static std::unique_ptr< Yuv420PackedEncoder > | nv12 () |
| static std::unique_ptr< Yuv420PackedEncoder > | i420 () |
| static std::unique_ptr< Yuv420PackedEncoder > | yv12 () |
Public Attributes | |
| Layout | m_layout {Layout::NV12} |
| Siting | m_siting {Siting::Centre} |
| QRhiTexture * | m_outTexture {} |
| QRhiTextureRenderTarget * | m_renderTarget {} |
| QRhiRenderPassDescriptor * | m_rpDesc {} |
| QRhiSampler * | m_sampler {} |
| QRhiShaderResourceBindings * | m_srb {} |
| QRhiGraphicsPipeline * | m_pipeline {} |
| QRhiReadbackResult | m_readback {} |
| int | m_width {} |
| int | m_height {} |
| int | m_bytesPerTexel {1} |
| bool | m_readbackEnabled {true} |
Static Public Attributes | |
| static constexpr const char * | chroma_nv12 |
| static constexpr const char * | chroma_planar |
| static constexpr const char * | frag |
Static Public Attributes inherited from score::gfx::GPUVideoEncoder | |
| static constexpr const char * | vertex_shader |
| static constexpr const char * | rgb_to_yuv_glsl |
| static constexpr const char * | y_flip_glsl |
| Fragment shader Y-flip: on GL, flip v_texcoord.y. On Metal/HLSL, no flip. | |
Member Enumeration Documentation
◆ Layout
|
strong |
◆ Siting
|
strong |
Member Function Documentation
◆ exec()
|
inlineoverridevirtual |
Execute the conversion pass. Call inside beginOffscreenFrame/endOffscreenFrame. Renders the conversion shader and schedules GPU->CPU readback(s).
Implements score::gfx::GPUVideoEncoder.
◆ init()
|
inlineoverridevirtual |
Create render targets, textures, samplers, and the graphics pipeline.
- Parameters
-
rhi The QRhi instance. state The render state (for shader compilation). inputRGBA The scene RGBA texture to read from. width Source width in pixels. height Source height in pixels. colorConversion GLSL code from colorMatrixOut() defining convert_from_rgb(). If empty, defaults to BT.709 full range.
Implements score::gfx::GPUVideoEncoder.
◆ outputTexture()
|
inlineoverridevirtualnoexcept |
The single output texture (RGBA8, format-dependent dimensions) for single-plane encoders, so vendor-neutral code (CpuStagedVideoOutput's DVP download) can copy it straight to sysmem instead of using readback(). Default nullptr (multi-plane encoders) => callers must use readback().
Reimplemented from score::gfx::GPUVideoEncoder.
◆ planeCount()
|
inlineoverridevirtual |
Number of readback planes (1 for UYVY, 2 for NV12, 3 for I420).
Implements score::gfx::GPUVideoEncoder.
◆ readback()
|
inlineoverridevirtual |
Get the readback result for a given plane. Valid after endOffscreenFrame.
Implements score::gfx::GPUVideoEncoder.
◆ release()
|
inlineoverridevirtual |
Release all GPU resources.
Implements score::gfx::GPUVideoEncoder.
◆ setReadbackEnabled()
|
inlineoverridevirtualnoexcept |
Enable/disable the GPU->CPU readback scheduled in exec(). Default enabled (readback runs). Disable when a GPU-direct download reads outputTexture() directly, so exec() doesn't also pay the readback (no double transfer).
Reimplemented from score::gfx::GPUVideoEncoder.
Member Data Documentation
◆ chroma_nv12
|
staticconstexpr |
One byte of the chroma region, for a semi-planar layout: site b/2 of row cr, Cb on even bytes and Cr on odd ones.
◆ chroma_planar
|
staticconstexpr |
And for the fully planar ones. %1 / %2 are the components of the first and second plane: (x, y) is Cb-then-Cr (I420), (y, x) is Cr-then-Cb (YV12).
The documentation for this struct was generated from the following file:
Static Public Attributes inherited from