Repository navigation
vk_native window-space + all-backend Local2D are not format-honest (ADR-044 §7) #1795
Description
Two composition paths ignore the swapchain's declared colour format. They show up as open rows in ADR-044 §7, added in #1794.
1. vk_native window-space layers are passthrough
vk_hud_blend (src/xrt/auxiliary/vk/vk_hud_blend.c:366) samples every window-space source through a hard-coded VK_FORMAT_R8G8B8A8_UNORM view and blends into the atlas in encoded space.
Measured with the windowspace_handle_*_win probes (opaque layers authored at byte 38):
| backend | _SRGB swapchain |
UNORM swapchain |
|---|---|---|
| D3D11 / D3D12 / GL | 38 | 108 (one encode), honest |
| vk_native | 38 | 38, format ignored |
On the model viewer's common button bar, the atlas is byte-identical with swapchain 37 vs 43, and a 55 % black fill lands at exactly 0.45 × the background's encoded value. Linear-light blending would give a visibly lighter value.
Side bug: because the view is hard-coded R8G8B8A8, a B8G8R8A8_* window-space swapchain would be sampled with R and B swapped.
2. Local2D flatten is passthrough on every in-process backend
Each backend samples the Local2D layer through a non-decoding view into a UNORM scratch:
- D3D11:
comp_d3d11_swapchain_get_srv, notget_compose_srv - D3D12:
comp_d3d12_swapchain_sample_format - GL:
gl_bind_layer_source(…, compose=false) - vk_native:
get_image_view, notget_true_image_view
UNORM and _SRGB Local2D therefore render identically. A true-linear UNORM Local2D renders too dark. Local2D is flattened after the atlas, so the atlas capture (ADR-044 §5) cannot verify it; a post-weave or plane capture is needed.
Ordering: apps first, then the runtime
Making either path honest washes out every app that still declares UNORM with encoded bytes. Known state:
- Already
_SRGB:- displayxr-common v2.27.0 window-space (displayxr-common#72), with demo repins in modelviewer#162, gaussiansplat#139, earthview#78, avatar#114
- displayxr-unity v2.21.0 (window-space UI + Local2D)
- runtime
test_appsLocal2D panels (ADR-044 §7: Local2D/vk window-space gaps + test_apps on common v2.27.0 #1794) - the avatar and model-viewer Android HUDs
- Still UNORM: displayxr-shell-pvt's macOS surfaces (hard-coded 37). Any third-party app is unknown.
Proposed fix
- vk_native window-space: sample through the swapchain's format-honest view (
get_true_image_view, or the UNORM↔_SRGBsibling matching the declared format) and blend in the private_SRGBtarget, as the projection path does since compositor blends layers in sRGB-encoded space; OpenXR expects linear-space blending (CTS SourceAlphaBlending) #1610. That also removes the R/B swap. - Local2D on all four backends: switch the flatten to the format-honest view, with an
_SRGBscratch or a matched encode. - Add a
windowspace_handle_vk_win/ Local2D row to the colour regression checks so the passthrough can't silently come back.
Verification
Re-run the probe A/B with and without DXR_SWAPCHAIN_ENCODING=unorm. After the fix vk_native should match D3D11 (38 / 108). Then confirm that displayxr-unity (already _SRGB) is a no-op.
Activity
dfattal commentedon Oct 2, 2026
Downstream dependency (shell, macOS). The macOS service composites the shell's chrome, cursor, focus ring, launcher, splash and taskbar through the same
vk_hud_blend(comp_multi_system.cchrome_blend / shared_chrome_blend). All of those are UNORM 37 holding CoreGraphics (display-referred) bytes. They look right today only becausevk_hud_blendignores the format.So the release that fixes this issue washes out the macOS shell unless displayxr-shell-pvt#121 ships alongside it, with an
_SRGBflip gated on this release's version, the same pattern as #119 and runtime v2.21.13 on Windows.Plan the fix as:
Also, a UNORM view of an
_SRGBimage needsMUTABLE_FORMAT. Keep that in mind if the fix keeps any non-decoding view.fix(vk_native): composite window-space layers in linear light, sample…
fix(multi): macOS shared-surface decorations blend in linear light, s…
docs(adr-044): the macOS service decorations are format-honest too (#…
dfattal commentedon Oct 2, 2026
Window-space part landed in #1799 (vk_native in-process + macOS service decorations), shipping in v2.23.0 with displayxr-shell-pvt#122. Still open here: the Local2D flatten honesty (all four backends). It is deferred until the Unity samples and any other UNORM-Local2D apps are re-released on _SRGB. The macOS content-pass darkening is tracked separately in #1800.
fix(gl): Local2D flatten blends in linear light, sampled as declared (#…
fix(d3d12): Local2D flatten blends in linear light, sampled as declar…
fix(vk_native): Local2D flatten blends in linear light, sampled as de…
fix(d3d11_service): pure-zones commits compose when a source is not _…
fix(vk_native): honest Local2D flatten under the output-device split (#…
fix(multi): macOS shared-surface content draws through the _SRGB atla…
fix(d3d11): Local2D flatten blends in linear light, sampled as declar…
test(test_apps): DXR_SWAPCHAIN_ENCODING=unorm forces the Local2D pane…
fix(gl): Local2D flatten blends in linear light, sampled as declared (#…
fix(d3d12): Local2D flatten blends in linear light, sampled as declar…
fix(vk_native): Local2D flatten blends in linear light, sampled as de…
fix(d3d11_service): pure-zones commits compose when a source is not _…
dfattal commentedon Oct 3, 2026
Local2D half landed in #1809 (D3D11, D3D12, GL and vk_native in-process, all HW-measured; the D3D11 service pure-zones path was verified by eye). Apps-first was cleared beforehand: Unity samples v2.3.0 and both Lenovo Unity builds are on displayxr-unity v2.21.0. Ships with the next runtime release. The remaining ADR-044 §7 items are tracked in #1800 (macOS content pass) and #1810 (IPC capture).
fix(vk_native): honest Local2D flatten under the output-device split (#…
dfattal commentedon Oct 3, 2026
Both halves have landed: window-space in #1799 (v2.23.0) and Local2D in #1809 (next release). Follow-ups: #1800 (macOS content pass) and #1810 (IPC capture).
dfattal commentedon Oct 6, 2026
Unity no-op confirmation for #1809: PASSED on runtime v2.26.0 (Windows Leia panel, desktop-avatar from displayxr-unity-samples v2.3.0, D3D12, standalone; judged by eye by David):
_SRGBprimary: swapchain 29 with Unity rendering into 28 and raw-copying; Local2D_SRGBvia the D3D12 bridge): the speech bubble and the whole frame look correct.So Unity apps on plugin ≥ v2.21.0 are unaffected by honest Local2D, and pre-v2.21 Unity builds now wash out everywhere: rebuild on ≥ v2.21.0. Released at that level: displayxr-unity v2.21.0, displayxr-unity-samples v2.3.0, and lenovo-avatar main (dfattal + LeiaInc,
fbe2d04). Not covered: the under-shell (service) leg with a Unity app, because desktop-avatar isn't installed or registered on this box.feat(metal): format-honest colour per ADR-044 — sources as declared, …
Metal compositor: format-honest colour (ADR-044) (#1901)