feat(host): HDR Vulkan layer so Vulkan games get HDR on the virtual display
windows-host / package (push) Failing after 4m16s
ci / rust (push) Failing after 4m56s
ci / web (push) Failing after 22s
ci / docs-site (push) Successful in 1m7s
android / android (push) Successful in 9m19s
ci / bench (push) Successful in 4m47s
decky / build-publish (push) Successful in 11s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 5s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Failing after 3s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 4s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 4s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 3s
docker / deploy-docs (push) Has been skipped
deb / build-publish (push) Failing after 6m29s
rpm / build-publish (bazzite, punktfunk-fedora-rpm) (push) Failing after 7m4s
rpm / build-publish (fedora-44, punktfunk-fedora44-rpm) (push) Failing after 7m17s
apple / swift (push) Successful in 1m13s
apple / screenshots (push) Successful in 5m27s
windows-host / package (push) Failing after 4m16s
ci / rust (push) Failing after 4m56s
ci / web (push) Failing after 22s
ci / docs-site (push) Successful in 1m7s
android / android (push) Successful in 9m19s
ci / bench (push) Successful in 4m47s
decky / build-publish (push) Successful in 11s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 5s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Failing after 3s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 4s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 4s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 3s
docker / deploy-docs (push) Has been skipped
deb / build-publish (push) Failing after 6m29s
rpm / build-publish (bazzite, punktfunk-fedora-rpm) (push) Failing after 7m4s
rpm / build-publish (fedora-44, punktfunk-fedora44-rpm) (push) Failing after 7m17s
apple / swift (push) Successful in 1m13s
apple / screenshots (push) Successful in 5m27s
NVIDIA/AMD Vulkan ICDs refuse to *advertise* an HDR color space for a surface on an
IddCx indirect/virtual display, so Vulkan games (Doom: The Dark Ages, id Tech, Indiana
Jones, …) report "device does not support HDR" — even though Windows HDR, DWM compose,
and the client PQ stream all work, and the ICD happily *accepts + presents* a forced HDR
swapchain there. The whole gap is enumeration; the community (Apollo/Sunshine/VDD) wrote
this off as kernel-side / unfixable.
Add VK_LAYER_PUNKTFUNK_hdr_inject (packaging/windows/pf-vkhdr-layer/): a standalone
cdylib Vulkan implicit layer that appends {A2B10G10R10, HDR10_ST2084} + {RGBA16F, scRGB}
to vkGetPhysicalDeviceSurfaceFormats[2]KHR (no need to hook vkCreateSwapchainKHR — the
ICD doesn't validate the color space there). Self-gated on the surface monitor's actual
advanced-color state (DisplayConfig GET_ADVANCED_COLOR_INFO), so it is a complete no-op
on SDR sessions and real monitors (dedup). Always-on (registry-discovered) so it works
regardless of how a game is launched — env-scoping silently fails for already-running
Steam. Escape hatches: DISABLE_PF_VKHDR, PF_VKHDR_EXCLUDE, and a built-in kernel-anti-
cheat denylist.
The installer builds/signs/stages it and registers it under
HKLM64\SOFTWARE\Khronos\Vulkan\ImplicitLayers (opt-out "Install the HDR Vulkan layer"
task); windows-host CI fmt+clippy-gates it (msvc-only FFI).
Live-validated on the RTX box: Doom: The Dark Ages enables HDR over the pf-vdisplay
virtual display.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,70 @@
|
||||
# Session-aware host — known limitations & follow-ups
|
||||
|
||||
Status: 2026-06-14. The host auto-detects the live session (Gaming / KDE / GNOME / wlroots) **per
|
||||
connect** and routes both video and input at it — managed gamescope at the client's resolution in
|
||||
Steam Gaming Mode, a KWin/Mutter virtual output at the client's resolution on a Desktop. A watcher
|
||||
(opt-in: `PUNKTFUNK_SESSION_WATCH=1`) follows a Gaming↔Desktop switch **mid-stream** and rebuilds the
|
||||
backend in place without a reconnect.
|
||||
|
||||
Live-validated on the Bazzite F44 box (`bazzite-deck-nvidia:testing`, RTX 4090): Desktop KDE at
|
||||
5120×1440 + input; Gaming managed at 5120×1440; warm-session reuse on quick reconnect; Feature B
|
||||
video-switch both directions.
|
||||
|
||||
## Resolved (2026-06-15, `3363576`)
|
||||
|
||||
- **#2 — mid-stream-switch input** ✅ `vdisplay::settle_desktop_portal()` pushes the live session env
|
||||
into the systemd/D-Bus activation environment and restarts the KWin portal on a switch, so input
|
||||
lands without a reconnect. Validated live: `settled desktop portal env … compositor=kwin` →
|
||||
`libei: portal granted devices` → `device RESUMED` on a Gaming→Desktop mid-stream switch.
|
||||
- **#3 — KWin/Mutter virtual output primary** ✅ `apply_session_env` defaults
|
||||
`PUNKTFUNK_KWIN_VIRTUAL_PRIMARY` / `PUNKTFUNK_MUTTER_VIRTUAL_PRIMARY` on for the auto desktop path.
|
||||
Validated live: `KWin: streamed output set as the sole desktop also_disabled=["HDMI-A-1"]` — panels
|
||||
now render on the streamed screen.
|
||||
|
||||
## Still parked
|
||||
|
||||
### 1. F44 gamescope teardown corrupts the GPU context
|
||||
Every gamescope teardown on this box (stop the autologin on connect; stop the managed session on
|
||||
restore) risks leaking the NVIDIA GPU context — surfaces as `CUDA_ERROR_ILLEGAL_STATE` (401) in
|
||||
`cuCtxCreate` / `vkCreateDevice` `VK_ERROR_INITIALIZATION_FAILED` (-3), then a black screen that
|
||||
**needs a reboot**. The 5 s debounced restore + the desktop restore-guard cut the teardown *count*
|
||||
but don't eliminate it. Options, in order of preference:
|
||||
- **SIGKILL the gamescope on teardown** instead of `systemctl stop` (SIGTERM). Hypothesis: skipping
|
||||
gamescope's buggy SIGTERM teardown handler (the part that SIGSEGVs, exit 139) lets the process die
|
||||
hard and the driver reclaim its GPU resources cleanly via normal process exit — no half-torn-down
|
||||
context. Change `stop_autologin_sessions` + `stop_session` (`vdisplay/gamescope.rs`) to
|
||||
`systemctl --user kill --signal=SIGKILL <unit>` (+ a follow-up `stop`/`reset-failed` to clear unit
|
||||
state). **Untested** — this is the first thing to try; it would preserve "managed client-res
|
||||
gaming AND TV-shows-gaming-when-idle".
|
||||
- **Keep the managed session warm** (no per-disconnect restore): spawn once, reuse forever, never
|
||||
tear down → ~1 teardown per host lifetime. Tradeoff: the TV is blank/idle when no client is
|
||||
connected (the autologin is never restored; return to gaming manually).
|
||||
- Upstream gamescope/driver fix.
|
||||
|
||||
(#2 mid-stream-switch input and #3 virtual-output-primary are **resolved** — see the Resolved section above.)
|
||||
|
||||
## Lower priority / polish
|
||||
|
||||
### 4. Mid-stream-switch input loss window (~6 s)
|
||||
During the libei portal setup on a switch, buffered input drops (`libei: DROP — no resumed device`,
|
||||
hundreds of events). Polish: pre-warm the portal, or hold events instead of dropping during the
|
||||
device-resume window.
|
||||
|
||||
### 5. NVENC `InitializeEncoder failed: invalid param` (recovered)
|
||||
At 5120×1440@240 the first NVENC open fails with `invalid param (8)` and **recovers** via the 2-way
|
||||
split-encode path (the stream is live). Cosmetic but noisy — investigate the first-attempt failure /
|
||||
silence the log.
|
||||
|
||||
### 6. NVENC HEVC bitrate cap (~800 Mbps on the RTX 4090)
|
||||
HEVC opens at the GPU's max (~800 Mbps) when a higher rate is requested (e.g. 1600). Not a bug;
|
||||
consider preferring AV1 when the client requests >~800 Mbps HEVC, and surface the cap in the
|
||||
speed-test / bitrate UI.
|
||||
|
||||
### 7. Restore-guard / keep-warm model interaction
|
||||
`do_restore_tv_session`, when a desktop is active, still stops the idle managed gamescope (a teardown
|
||||
— leak risk per #1) and consumes `STOPPED_AUTOLOGIN` (so a later return-to-gaming won't auto-restore
|
||||
the TV session). Resolve together with the keep-warm decision in #1.
|
||||
|
||||
### 8. Feature B is opt-in
|
||||
The mid-stream watcher is gated behind `PUNKTFUNK_SESSION_WATCH=1` pending broader validation. Promote
|
||||
to default-on once #2 (mid-stream input) lands and it's exercised on more boxes.
|
||||
Reference in New Issue
Block a user