Skip to content

HDR and Native Rendering

Symphoenix edited this page Aug 6, 2026 · 1 revision

HDR and native rendering

HDR is not a brightness checkbox.
It is a chain of numerical promises, and breaking any one of them produces a very expensive shade of grey.

SyLC's current renderer is a native C++ Direct3D 11 engine. Decoded YUV planes move directly into D3D11 textures; a shader performs colour conversion and stereo assembly; the final surface is presented through DXGI. The retired Qt/RHI path is not waiting backstage with a fake moustache — there is one production renderer.


The signal path

compressed video
      │
      ▼
decoded Y / U / V planes ──► D3D11 textures
      │
      ▼
stream-aware shader
  • range expansion
  • YUV matrix
  • transfer-function handling
  • gamut conversion
  • stereo packing / scaling
      │
      ▼
RGBA16F scRGB swapchain ──► DXGI ──► Windows HDR desktop ──► display

The important absence is an 8-bit SDR intermediate. Once PQ highlights have been decoded into the renderer's linear-light space, they remain high precision until presentation.


Reading the stream, not guessing from the resolution

Resolution does not identify HDR. A 4K stream can be SDR; a lower-resolution stream can carry HDR metadata. SyLC selects conversion from the decoded stream's actual properties:

Signal property Typical value Renderer consequence
Transfer SMPTE ST 2084 / PQ Convert code values through the PQ EOTF to absolute luminance
Primaries BT.2020 Convert wide-gamut linear RGB into the scRGB output basis
Matrix BT.2020 non-constant luminance Use the matching YUV→RGB transform
Bit depth 10-bit, often P010 Upload through the 16-bit plane path with the correct scale
SDR metadata BT.709 and conventional transfer Use the SDR branch without pretending it is PQ

Dolby Vision profiles with an HDR10-compatible base layer can play through that base layer. Dynamic RPU metadata is not applied; the fallback is explicit HDR10 compatibility, not full Dolby Vision rendering.


Why scRGB?

Windows HDR composition expects a linear, floating-point representation for mixed desktop content. SyLC uses an RGBA16F scRGB swapchain when HDR output is active.

scRGB provides:

  • enough precision for smooth gradients and HDR highlights;
  • linear-light values suitable for predictable composition;
  • headroom above conventional SDR white;
  • one output representation for the final stereo shader pass.

PQ itself is not stored in the swapchain as “still-PQ pixels.” The shader decodes PQ to linear absolute-light values, maps colour into the output basis, and writes scRGB. That distinction is why copying PQ code values into a float texture would be technically high precision and visually nonsense.


SDR displays and tone mapping

When the target output is not HDR-capable, the renderer uses its SDR branch. HDR sources must be mapped into the SDR display range rather than sent as unprocessed PQ, which would appear dim and washed out.

This gives two intentionally different outcomes:

Environment Intended result
Windows HDR enabled on an HDR display PQ/BT.2020 source preserved through the HDR path
SDR display or HDR output disabled Controlled SDR rendering of the source

If both paths look identical on genuinely HDR material, something upstream or downstream is probably flattening the signal. If the HDR path looks uniformly grey, suspect a transfer-function mismatch before blaming the film's colourist.


Stereo assembly happens in the same engine

The renderer does more than colour conversion. It also owns the final geometry of the two views:

  • MVC and MV-HEVC arrive as separate decoded eye planes.
  • SBS/TAB sources are split into per-eye regions.
  • Full-SBS eyes are fitted without stretching.
  • MultiView output is assembled for the detached stereo surface.
  • Dual Projector routes one eye to each window.
  • PGS subtitles are composited with their stereo depth.
  • 2D→3D can bind synthesized eyes where authored views normally appear.

Keeping those operations together avoids a procession of full-frame copies between UI, CPU and GPU APIs. The picture travels as planes and textures, not as a series of screenshots of itself.


Frame packing and aspect ratio

Frame-packed displays often expect a fixed output geometry while sources arrive in several aspect ratios. SyLC treats each eye's intended picture area as authoritative:

  1. derive the eye aspect from source geometry and stereo layout;
  2. fit that eye inside its output slot;
  3. add letterboxing where required;
  4. never stretch one axis merely to occupy every pixel;
  5. apply the same geometry to both eyes.

Stretching both eyes equally does not preserve stereo correctness; it preserves stereo wrongness with admirable consistency.


Timing, presentation and the audio clock

Colour-perfect frames are not useful if they arrive late or in the wrong order. Audio runs through libmpv and provides the master timeline. Native video sources expose real presentation timestamps; the presenter compares those timestamps to the audio clock and uses bounded buffering to absorb decode spikes.

The renderer therefore receives a selected eye pair, not “whatever two frames finished most recently.” Back-pressure limits queued work, and expensive key frames do not force the UI thread to stop presenting already decoded frames.

For the full thread and buffer model, see Architecture: from bits to two eyes.


HDR troubleshooting

The image is washed out

  • Confirm Windows HDR is enabled for the display hosting the output window.
  • Confirm the display itself entered its HDR mode.
  • Test a file with known PQ/BT.2020 metadata.
  • Make sure no capture, remote-desktop or mirroring layer has converted the desktop to SDR.
  • Compare the automatic path with the renderer override SYLC_NATIVE_HDR=1 or SYLC_NATIVE_HDR=0 for diagnosis only.

The image clips or looks over-bright

  • Check the display's HDR calibration and desktop SDR-white setting.
  • Disable display-side “dynamic contrast” experiments while diagnosing.
  • Verify that the source is not tagged PQ while containing SDR values.

Only one output is wrong

That usually points to display routing or swapchain state, not stream decoding. Move the same output window between displays and compare. Two windows can share source frames while presenting through different adapters, colour capabilities or Windows HDR states.


A useful correctness test

Use one short clip containing:

  • a dark gradient;
  • saturated BT.2020 colour;
  • a small bright highlight;
  • neutral skin tones;
  • fine stereo edges near the frame boundary.

Check it in SDR and HDR modes, paused and moving, on both eyes. A spectacular demo reel can hide errors behind spectacle; a dull grey ramp is an exceptionally vindictive witness.

Clone this wiki locally