Skip to content

Productize true-headless HDR through Gamescope Portal/PipeWire capture #152

Description

@papi-ux

Status

Target: Polaris v1.3.3 planning
v1.3.2: explicitly out of scope; this issue does not block or add claims to that release.

The original DRM-lease/forged-EDID idea in this issue has been superseded by a working true-headless architecture demonstrated on an NVIDIA host:

Moonlight client
  → Polaris
  → XDG Desktop Portal ScreenCast
  → PipeWire from headless Gamescope
  → LINEAR DMA-BUF
  → Vulkan copy
  → CUDA/NVENC

The downstream stack has produced stable 4K60 HDR/10-bit results on tested NVIDIA hardware and useful SDR/fallback controls. That proves the direction is viable; it does not make the downstream patch stack upstream-ready as one large merge.

Maintainer handoff

The external patches are reference implementations and test inputs, not an automatic merge source. Each phase must be reconciled against current Polaris master, reviewed independently, and verified with Polaris-owned tests and hardware evidence.

Polaris v1.3.3 productization plan

Phase 0 — current-master source map and architecture contract

  • Compare current Polaris master, the stale perf/issue-152-pipewire-capture branch, and the downstream phase patches.
  • Identify what is already upstream, what is obsolete, and what still needs an upstream-shaped implementation.
  • Freeze interfaces for transport selection, buffer lifetime, conversion-path reporting, session HDR intent, Gamescope ownership, and fallback behavior.
  • Keep Phase 1 independent from Gamescope-specific lifecycle, private D-Bus, WSI policy, and HDR.

Phase 1 — Portal/PipeWire foundation with reliable SHM fallback

  • Implement/refresh real XDG Portal ScreenCast → PipeWire capture.
  • Choose transport from actual PipeWire allocation, not requested descriptors.
  • Handle add_buffer / remove_buffer lifetime correctly.
  • Offer DMA-BUF only when same-GPU and modifier/import requirements are proven.
  • Preserve MemFd/MemPtr as a first-class working path.
  • Keep an explicit forced-SHM diagnostic escape hatch such as POLARIS_PORTAL_DMABUF=0.
  • Report truthful diagnostics:
    • capture_transport=shm|dmabuf
    • frame_residency=cpu|gpu
  • A refused or failed DMA-BUF path must continue through SHM rather than crash or black-screen.

Phase 2 — NVIDIA DMA-BUF → Vulkan → CUDA/NVENC fast path

  • Gate the fast path on runtime capability detection and exact same-GPU ownership.
  • Support the proven LINEAR one-plane BGRx/BGRA path first; add 10-bit formats only with Phase 4.
  • Invalidate imports when PipeWire removes/recycles buffers.
  • Document synchronization and ownership assumptions explicitly.
  • Report conversion honestly:
    • convert_path=vulkan_cuda
    • convert_path=mmap_cuda for loud sticky fallback
  • Do not label mmap/CPU fallback as GPU-native.
  • Do not revive desktop cuImportExternalMemory(DMABUF_FD) or cuGraphicsEGLRegisterImage as the generic NVIDIA path without new evidence.

Phase 3 — first-class experimental Gamescope Stream mode

Polaris should own the happy path rather than requiring users to assemble shell scripts and user services:

  • Start, attach to, health-check, and stop headless Gamescope.
  • Support attach and nested presentation modes with explicit policy.
  • Pin Gamescope, PipeWire capture, Vulkan conversion, CUDA, and NVENC to the same GPU.
  • Own Steam/game start → wait → stop and cleanup behavior.
  • Keep session markers, environment files, restore tokens, HDR intent, and process ownership scoped to the active stream.
  • Support a private ScreenCast D-Bus only when desktop coexistence requires it.
  • Keep Polaris itself on the normal session bus so Avahi, tray, and desktop services continue working.
  • Do not globally replace KDE/GNOME ScreenCast routing or break KRDP/desktop capture.

Phase 4 — HDR contract and encoded-signal truth

Keep these states separate:

  • hdr_capable
  • hdr_requested
  • explicit session HDR policy
  • negotiated capture format/colorimetry
  • encoder pixel format and metadata

Enable HDR only when all layers agree:

  1. the active client/session explicitly requests HDR;
  2. Gamescope is launched/configured for that session's HDR policy;
  3. Portal/PipeWire negotiates the expected 10-bit format and colorimetry;
  4. conversion produces P010;
  5. the encoder emits matching Rec.2020/PQ/VUI/SEI/mastering metadata.

Hard invariants:

  • Device-database HDR capability alone must never force session HDR.
  • Detect P010 through hardware-frame sw_format, not frame->format when frames are CUDA-backed.
  • SDR remains an independent 8-bit NV12 path.
  • Hybrid PQ-shaped capture with SDR NV12 encode is forbidden.
  • Force state must be scoped to the owning session and cleaned up deterministically.

Verification gates

Each accepted phase needs its own focused tests, independent review, and local checkpoint before the next phase begins.

Minimum release-readiness evidence for this feature:

  • CUDA and no-CUDA builds/tests pass on current master.
  • Forced SHM streams continuously and reports CPU residency truthfully.
  • DMA-BUF refusal/import failure falls back without crash, black screen, or false telemetry.
  • NVIDIA fast path and sticky fallback are exercised on a real RTX-class host.
  • Gamescope start/wait/stop leaves no Steam, Gamescope, portal, session-marker, or audio-routing residue.
  • Host KDE/GNOME portal behavior remains intact when Gamescope Stream is idle or disabled.
  • HDR and SDR color-pattern tests match the encoded bitstream metadata.
  • Supported-client matrix records codec, resolution, refresh, HDR state, transport, conversion path, and cleanup result.
  • Zero new Polaris coredumps; privacy-safe logs only.

Compatibility boundary

Initial fast-path confidence is NVIDIA-first because the working Vulkan→CUDA→NVENC evidence is NVIDIA-specific. AMD/VAAPI support requires its own design and hardware matrix; it must not be implied by this issue's NVIDIA evidence.

The reported iPhone/VoidLink HDR color problem remains a compatibility caveat until encoded-sample testing separates host bitstream correctness from client decode/render behavior. macOS/iPad/tvOS success does not automatically clear every iOS client path.

Definition of done for this issue

This issue is complete when Polaris has an opt-in, accurately named Gamescope Stream mode with:

  • reliable Portal/PipeWire SHM capture;
  • capability-gated, truthful GPU fast paths;
  • Polaris-owned Gamescope/portal/session lifecycle;
  • deterministic cleanup and desktop coexistence;
  • HDR enabled only through an end-to-end session/format/encode contract;
  • documented support and fallback boundaries backed by real hardware/client evidence.

Historical context

The disconnected-connector, forged-EDID, and DRM-lease approach remains useful historical research, but it is no longer the primary design target. The current product direction is true-headless Gamescope Portal capture with incremental upstreaming and honest fallback behavior.

Metadata

Metadata

Assignees

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions