Skip to content

pipewire: Add support for SPA_VIDEO_FORMAT_xBGR_210LE video streams - #2270

Draft
flibitijibibo wants to merge 1 commit into
ValveSoftware:masterfrom
flibitijibibo:pipewire-hdr
Draft

pipewire: Add support for SPA_VIDEO_FORMAT_xBGR_210LE video streams#2270
flibitijibibo wants to merge 1 commit into
ValveSoftware:masterfrom
flibitijibibo:pipewire-hdr

Conversation

@flibitijibibo

Copy link
Copy Markdown

This is pulled from @IceDBorn's local work to support headless HDR streaming; the full patchset for that can be found here:

https://github.com/IceDOS/apps/blob/main/modules/steam/modules/sunshine-headless-session/lib/

While this appears to work with the above patches in headless mode, this is marked as a draft since I haven't been able to test this with the DRM backend (see #2126), and we didn't want to break Steam Machines by accident.

Reviewed-by: Ethan Lee <flibitijibibo@gmail.com>
@IceDBorn

IceDBorn commented Jul 20, 2026

Copy link
Copy Markdown

Precisely, this has only been tested by me and some of my friends, on our TVs - headless only. I'm not even sure if the HDR signal is correct, it just looks ok to me.

@luxus luxus left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good for pipewire.cpp. A few notes on pairing with paint_pipewire so remote HDR color stays correct.

Comment thread src/pipewire.cpp
build_format_params(builder, SPA_VIDEO_FORMAT_BGRx, params);
build_format_params(builder, SPA_VIDEO_FORMAT_NV12, params);
if (g_bOutputHDREnabled)
build_format_params(builder, SPA_VIDEO_FORMAT_xBGR_210LE, params);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Offering xBGR_210LE when HDR is on is correct.

Still needed in paint_pipewire (not in this PR):

frameInfo.outputEncodingEOTF = g_bOutputHDREnabled ? EOTF_PQ : EOTF_Gamma22;

Today it stays EOTF_Gamma22 while we advertise BT.2020/PQ here → washed hybrid streams on remote clients.

Comment thread src/pipewire.cpp
Comment thread src/pipewire.cpp
Comment thread src/pipewire.cpp
@flibitijibibo

Copy link
Copy Markdown
Author

Looking at paint_pipewire it looks like we may also need to apply similar logic to the color management as well, does this make sense?

diff --git a/src/steamcompmgr.cpp b/src/steamcompmgr.cpp
index 9b32e2b..74240bd 100644
--- a/src/steamcompmgr.cpp
+++ b/src/steamcompmgr.cpp
@@ -2337,15 +2337,16 @@ static void paint_pipewire()
 
        struct FrameInfo_t frameInfo = {};
        frameInfo.applyOutputColorMgmt = true;
-       frameInfo.outputEncodingEOTF   = EOTF_Gamma22;
+       frameInfo.outputEncodingEOTF   = g_bOutputHDREnabled ? EOTF_PQ : EOTF_Gamma22;
        frameInfo.allowVRR             = false;
        frameInfo.bFadingOut           = false;
 
        // Apply screenshot-style color management.
+       auto& luts = g_bOutputHDREnabled ? g_ScreenshotColorMgmtLutsHDR : g_ScreenshotColorMgmtLuts;
        for ( uint32_t nInputEOTF = 0; nInputEOTF < EOTF_Count; nInputEOTF++ )
        {
-               frameInfo.lut3D[nInputEOTF]     = g_ScreenshotColorMgmtLuts[nInputEOTF].vk_lut3d;
-               frameInfo.shaperLut[nInputEOTF] = g_ScreenshotColorMgmtLuts[nInputEOTF].vk_lut1d;
+               frameInfo.lut3D[nInputEOTF]     = luts[nInputEOTF].vk_lut3d;
+               frameInfo.shaperLut[nInputEOTF] = luts[nInputEOTF].vk_lut1d;
        }
 
        const uint64_t ulFocusAppId = s_pPipewireBuffer->gamescope_info.focus_appid;

@luxus

luxus commented Jul 29, 2026

Copy link
Copy Markdown

Yes — that paint_path change makes sense, and it belongs with this PR (or as a tight companion).

Why: this PR offers xBGR_210LE with BT.2020 + SMPTE2084 when HDR is on, but paint_pipewire still hardcodes EOTF_Gamma22 and the SDR screenshot LUTs. That mismatches metadata vs what is actually written into the PW buffers (hybrid: PQ-tagged stream, gamma/SDR paint) and washes out or skews colors on remote capture clients.

What needs to stay locked together:

  1. Format offer + colorimetry for xBGR_210LE (this PR)
  2. frameInfo.outputEncodingEOTF = g_bOutputHDREnabled ? EOTF_PQ : EOTF_Gamma22
  3. HDR vs SDR screenshot LUT sets: g_ScreenshotColorMgmtLutsHDR when HDR is on, g_ScreenshotColorMgmtLuts when not

Your diff covers (2) and (3) cleanly. Without them, mid-session HDR renegotiation to 10-bit is incomplete for PipeWire consumers.

(We've been shipping that paint companion next to a cherry-pick of this PR on a gamescope fork used for headless streaming; drop it when this lands upstream.)

@flibitijibibo

Copy link
Copy Markdown
Author

Well, that covers what the robots think... anyone on payroll have any thoughts?

@luxus

luxus commented Jul 29, 2026

Copy link
Copy Markdown

the user of the robot can confirm that it works :D
the reason i let my clanker wrote the post.. i want to confirm that this pr is working on 3-5 devices

VolanDeVovan added a commit to VolanDeVovan/punktfunk that referenced this pull request Aug 4, 2026
…eliver

HDR sessions arrived uniformly blue-shifted on an LG panel, through every colour
label tried: BT.709, BT.601 and BT.2020 NCL, in both the bitstream VUI and the
handshake ColorInfo, each confirmed applied in the log. A tint that survives
every label is not a signalling bug — no colour description moves a channel.

Red and blue were transposed. The HDR path offered xRGB_210LE then xBGR_210LE,
PipeWire intersects in offer order, and the gamescope patch here offered both;
so the pair agreed on xRGB while the composite came back BGR-ordered. Only
XBGR2101010/ABGR2101010 exist for these formats on NVIDIA
(ValveSoftware/gamescope#1029), so that texture could never have been RGB-ordered
— the negotiation succeeded and meant nothing.

Two halves, matching:

- the gamescope patch stops offering the RGB-ordered twin. Upstream's own version
  of this work (ValveSoftware/gamescope#2270, still a draft) offers xBGR alone,
  for the same reason; ours added xRGB on top, which is where this came from.
- the capture offers xBGR first. xRGB stays available for producers that really
  do deliver it — Mutter's HDR screencast advertises it — just no longer ahead of
  the order that works.

Replaces the PUNKTFUNK_HDR_RGB_ORDER knob from the previous commit, which existed
to tell a transposition from a mislabel and has now answered that.

Verified on the glass, 3840x2160 HDR10 into an LG panel: negotiates xBGR_210LE,
zero-copy dmabuf straight to CUDA, colours correct.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants