Skip to content

fix(test-apps): D3D12 cubes decode authored colour for an _SRGB RTV; GL macOS cubes prefer GL_SRGB8_ALPHA8 (ADR-044, #1882) - #1885

Merged
dfattal merged 1 commit into
mainfrom
fix/1882-d3d12-cube-colour
Oct 9, 2026
Merged

dfattal merged 1 commit into
mainfrom
fix/1882-d3d12-cube-colour

Conversation

@dfattal

@dfattal dfattal commented Oct 9, 2026

Copy link
Copy Markdown
Collaborator

Closes #1882.

What

cube_handle_d3d12_win was washed out on the rig while cube_handle_d3d11_win looked right on the same runtime, plug-in and panels. The app asks for an _SRGB swapchain and creates _SRGB-typed RTVs/PSOs, but its pixel shaders wrote the authored display-referred colours raw, so the render target encoded them a second time. The D3D11 cube has had the DXR_LINEARIZE pixel-shader variant since #1589; the D3D12 copy of the renderer only ever received the display-referred clear (#1679).

Changes

  • d3d12_renderer.cpp (handle; verbatim copies in cube_zones_d3d12_win and cube_zones_texture_d3d12_win; shared by cube_hosted_legacy_d3d12_win): both pixel shaders gain the same DXR_LINEARIZE decode as the D3D11 reference renderer. The variant is chosen in CreateSwapchainRTVs, the first point where the RTV format is known, and is keyed on that format rather than the process-wide dxr::RenderSceneLinear() flag (the test_apps: D3D12 (and macOS/Linux) apps need a display-referred clear before they get honest _SRGB swapchains; stale per-app displayxr-common pins #1647 lesson: the legacy D3D12 app resolves TYPELESS to the UNORM sibling and writes raw bytes, so it must keep the raw variant while the handle app's _SRGB RTV gets the decoding one; both store the same bytes). DXR_TRUE_LINEAR still overrides. One [color] rtvFormat=.. sceneLinear=.. WARN at init, the same discriminator the VK cubes log.
  • cube_hosted_legacy_d3d12_win: the comment on the UNORM RTV described the pre-CTS composition: UNORM (linear) swapchains are passed through un-encoded on every backend — GradientFormatsLinearVsNonLinear FAIL; ADR-021 Model A vs OpenXR format semantics #1589 runtime; it now states the ADR-044 row it sits on.
  • cube_handle_gl_win: the stale "may need glEnable(GL_FRAMEBUFFER_SRGB)" TODO is replaced by the actual contract (it stays off: raw display-referred bytes into an honestly-declared GL_SRGB8_ALPHA8 swapchain). The DXR_TEST_QUAD swapchain declares its raw bytes GL_SRGB8_ALPHA8 like the Local2D panels already do.
  • cube_handle_gl_macos, cube_hosted_legacy_gl_macos: preferred GL_RGBA8 and wrote display-referred bytes, the exact washed-out shape ADR-044 §2 describes on the format-honest GL compositor (≥ v2.21.2). They now prefer GL_SRGB8_ALPHA8, GL_RGBA8 as the fallback. cube_zones_texture_gl_macos already preferred sRGB; its hex literals become the named codes so the per-leg INV-4.6 lint judges it on tokens.
  • cube_zones_texture_vk_win: the strip/zone fallback format is the _SRGB sibling, not UNORM.

Fleet audit (ADR-044 / INV-4.6)

Apps Status
every D3D11 cube (shared displayxr-common renderer, DXR_LINEARIZE + display-referred clears) already on the contract
every VK cube, Windows / Linux / macOS / Android (uLinearize specialisation constant, _SRGB first) already on the contract
GL Windows cubes (_SRGB swapchain, GL_FRAMEBUFFER_SRGB off, raw bytes) already on the contract; stale TODO fixed
D3D12 cubes ×4 fixed here
GL macOS handle + legacy fixed here (format preference)
Metal cubes ×5 unchanged on purpose: Metal is the one backend ADR-044 §7 lists as not format-honest (UNORM passthrough), so BGRA8Unorm + raw bytes is correct there today. Migrating them (sRGB pipeline format + shader decode + linearised clears) is a macOS-verified change of its own.

scripts/check_displayxr_app.py: 0 INV-4.6 findings on every cube app (the remaining INV-5.9 errors on the VK enable1 apps are pre-existing and out of scope).

Verification

  • scripts\build_windows.bat test-apps: green, every touched executable rebuilt.
  • Rig: pending. The panel is held by a browser check right now; the numerical A/B (in-process displayxr_atlas_trigger.projection capture of the D3D12 cube vs the D3D11 cube, same authored scene → same atlas bytes) and David's eyeball follow before merge.

🤖 Generated with Claude Code

…ADR-044, #1882)

cube_handle_d3d12_win asks for an _SRGB swapchain (displayxr-common default
since v2.15.0) and creates its RTVs + PSOs typed _SRGB, but its pixel shaders
wrote the authored display-referred colours raw, so the render target encoded
them a second time: washed out on the rig while the D3D11 cube (which has had
the DXR_LINEARIZE PS variant since #1589) looked right. The D3D12 copy of the
renderer only ever got the display-referred clear (#1679).

- d3d12_renderer.cpp (handle; copied verbatim into cube_zones_d3d12_win and
  cube_zones_texture_d3d12_win; shared by cube_hosted_legacy_d3d12_win): both
  pixel shaders gain the same DXR_LINEARIZE decode as the D3D11 reference
  renderer; CompileShader takes a `linearize` flag; the variant is chosen in
  CreateSwapchainRTVs, the first point where the RTV format is known, keyed on
  THAT format (not the process-wide dxr::RenderSceneLinear(), for the #1647
  reason: the legacy D3D12 app resolves TYPELESS to the UNORM sibling and
  writes raw bytes, which must keep the raw variant). DXR_TRUE_LINEAR still
  overrides. One WARN line `[color] rtvFormat=.. sceneLinear=..` at init, the
  same discriminator the VK cubes log.
- cube_hosted_legacy_d3d12_win: the comment describing the UNORM RTV still
  described the pre-#1589 runtime; it now states the ADR-044 row it sits on.
- cube_handle_gl_win: the stale "may need glEnable(GL_FRAMEBUFFER_SRGB)" TODO
  is replaced by the actual contract (it stays off: raw display-referred bytes
  into an honestly-declared GL_SRGB8_ALPHA8 swapchain); the DXR_TEST_QUAD
  swapchain declares its raw bytes GL_SRGB8_ALPHA8 like the Local2D panels.
- cube_handle_gl_macos, cube_hosted_legacy_gl_macos: preferred GL_RGBA8 and
  wrote display-referred bytes, i.e. the exact washed-out shape ADR-044 §2
  describes on the format-honest GL compositor (>= v2.21.2); they now prefer
  GL_SRGB8_ALPHA8 with GL_RGBA8 as the fallback. cube_zones_texture_gl_macos
  already preferred sRGB; its hex literals become the named GL codes so the
  per-leg INV-4.6 lint judges it on tokens.
- cube_zones_texture_vk_win: the strip/zone fallback format is the _SRGB
  sibling, not UNORM.

Audit of the rest of the fleet: every D3D11 cube (shared displayxr-common
renderer), every VK cube (uLinearize specialisation constant) and the GL
Windows cubes were already on the contract. The Metal cubes keep BGRA8Unorm +
raw bytes on purpose: Metal is the one backend ADR-044 §7 lists as not
format-honest (UNORM passthrough), so they are correct there today and their
migration (sRGB pipeline format + shader decode) is a macOS-verified change of
its own.

scripts/check_displayxr_app.py: 0 INV-4.6 findings on every cube app.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@dfattal
dfattal requested a review from a team as a code owner October 9, 2026 17:24
@dfattal

dfattal commented Oct 9, 2026

Copy link
Copy Markdown
Collaborator Author

Rig verification (2026-10-09, win box, installed runtime main after #1879 + Leia plug-in #323 build):

  • David eyeballed cube_handle_d3d12_win next to cube_handle_d3d11_win: "looks good now".
  • Numerical oracle, in-process displayxr_atlas_trigger.<exe>.projection captures of both apps, dominant (background) atlas pixel:
app atlas dominant pixel
cube_handle_d3d11_win 1280×360 (13, 13, 64)
cube_handle_d3d12_win 1280×360 (13, 13, 64)

(13,13,64) is the authored 0.05/0.05/0.25 clear in display-referred bytes, i.e. the atlas holds exactly the authored colour once (ADR-044 §5). Before the fix the D3D12 cube stored one encode too many (the #1647 measurement of the same clear was 63,63,137).

@dfattal
dfattal merged commit beea071 into main Oct 9, 2026
34 checks passed
@dfattal
dfattal deleted the fix/1882-d3d12-cube-colour branch October 9, 2026 17:54
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.

test_apps: cube_handle_d3d12_win is washed out on the rig (double sRGB encode in the app's colour path)

1 participant