fix(overlay): render the HUD on SteamVR/OpenXR (sRGB swapchains)#77
Merged
Conversation
The overlay refused to initialise on SteamVR/OpenXR (seen via OpenComposite) because pickSwapchainFormat() required DXGI_FORMAT_B8G8R8A8_UNORM exactly and bailed when the runtime didn't advertise it. SteamVR advertises RGBA8/sRGB variants instead. Accept a prioritised list (BGRA8_UNORM -> RGBA8_UNORM -> their sRGB siblings) and create the per-image RTV in the chosen format on both the D3D11 and D3D11On12 paths. The GPU shader path writes a logical float4(r,g,b,a) and the output-merger swizzles to the RTV layout, so an RGBA8 target renders identical colours. On failure we now log the full list of formats the runtime advertised instead of guessing. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…out) SteamVR/OpenXR advertises only the _SRGB swapchain variants (no linear UNORM) and returns a TYPELESS resource. The previous change picked B8G8R8A8_UNORM_SRGB and created the RTV in that same sRGB format, so the GPU applied linear->sRGB encoding when writing our already-sRGB UI colours; SteamVR then decoded again, leaving the overlay washed out / light grey. Pimax/WMR/Oculus were unaffected because they advertise a UNORM format. Add rtvFormatForSwapchain(): map an sRGB swapchain pick to its UNORM sibling (91->87, 29->28) for the render-target view, so the shader output is stored verbatim and the runtime does the correct sRGB decode on composite. A UNORM view over the TYPELESS resource is valid. UNORM picks pass through unchanged, so the working runtimes keep their exact path. Applied to both the D3D11 and D3D11On12 backends. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…path Addresses code-review findings on the swapchain-format fallback: - Single source of truth: fold the accept list and the sRGB->UNORM RTV map into one kOverlayFormats table, so adding a format is one row and the two can't drift out of sync (a missing map entry previously fell through to an sRGB RTV, silently reintroducing the washout). - RTV fallback: createOverlayImageRtv retries with the sRGB view if the UNORM view is rejected, for runtimes that hand back a fully-typed (non-typeless) sRGB resource where CreateRenderTargetView(UNORM) would fail with E_INVALIDARG. Shared by the D3D11 and D3D11On12 paths. - Diagnostics: the 'renderer ready' logs now record the picked swapchain format (and tag it (sRGB)), not just the de-sRGB'd RTV format, so a color/text report shows whether the sRGB composite path engaged. - Fix the failure-path format list: trust the count from the second xrEnumerateSwapchainFormats (resize) so the diagnostic never lists stale trailing zeros or a count that disagrees with the list. - Refresh stale comments that still claimed the RTV is always BGRA8_UNORM. No behavioural change on the working runtimes (UNORM picks map to themselves); SteamVR's sRGB pick keeps the UNORM RTV from c95bb97. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… space The glyph PS pre-corrects DirectWrite coverage with pow(c, 1/TEXT_GAMMA), tuned for runtimes that composite the quad in linear space (UNORM swapchain). Now that the overlay also runs on sRGB-decoding runtimes (SteamVR), that direction is wrong there — the shader's own DIRECTION CAVEAT says to invert it — so HUD text renders at the wrong stem weight. Thread an `srgbComposite` flag from the picked swapchain format through initOverlayRenderers -> glyph Renderer::init -> the TextConstants cbuffer (reusing a pad slot, layout unchanged, static_assert intact). The PS picks pow(c, 1/TEXT_GAMMA) for linear composites and pow(c, TEXT_GAMMA) for sRGB. Gated on supersample > 1, so the snapshot/golden (1x) path is byte-identical; the flag defaults to false so every other init() caller keeps the linear direction. Direction + weight are still worth an on-headset A/B (SteamVR vs Pimax); tunable via TEXT_GAMMA. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Overlay snapshot test now passingThe overlay snapshot test passes on this PR. |
f236b2c to
a4d2aea
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The telemetry HUD overlay didn't work on SteamVR/OpenXR (seen via OpenComposite). This branch makes it render there while leaving the passthrough runtimes (Pimax / WMR / Oculus) byte-for-byte unchanged.
What was broken
pickSwapchainFormat()requiredDXGI_FORMAT_B8G8R8A8_UNORMexactly and bailed when the runtime didn't advertise it — SteamVR advertises the RGBA8 / sRGB variants instead, so the overlay silently disabled itself._SRGBswapchains and returns a TYPELESS resource; painting through an sRGB RTV double-encoded our already-sRGB UI colours.Changes
BGRA8_UNORM → RGBA8_UNORM → their sRGB siblings. The GPU shader path writes a logicalfloat4(r,g,b,a)and the output-merger swizzles to the RTV layout, so BGRA8 and RGBA8 render identical colours. On failure we now log the full advertised list instead of guessing.srgbCompositecbuffer flag threaded to the glyph renderer (seeoverlay_text_ps.hlsl's DIRECTION CAVEAT): linear composite lifts with1/TEXT_GAMMA, sRGB composite uses the inverse.formats.resize(count)on the diagnostic path, and refreshed comments. Addresses the code-review findings raised on the branch.Testing
Notes / deliberately out of scope
A fully colour-managed path (shaders emit linear on sRGB runtimes + sRGB RTV) was prototyped but reverted: it also changed the look on the passthrough runtimes, which we want left alone. The residual "slightly more open" look of the intentionally-translucent elements (the 0.94 frame fill, the faint grid/axis lines) on SteamVR is inherent to correct linear compositing vs the passthrough runtimes' non-linear compositing — no swapchain-format choice changes it, only per-element alpha. Parked as possible future work; the
srgbCompositeflag is already plumbed to the text path if we revisit.🤖 Generated with Claude Code