Skip to content

fix(metal): blend window-space layers instead of overwriting (#1788) - #1789

Merged
dfattal merged 2 commits into
mainfrom
fix/metal-window-space-blend-1788
Oct 2, 2026
Merged

dfattal merged 2 commits into
mainfrom
fix/metal-window-space-blend-1788

Conversation

@dfattal

@dfattal dfattal commented Oct 2, 2026

Copy link
Copy Markdown
Collaborator

Fixes #1788

Problem

The Metal compositor's window-space pass (XrCompositionLayerWindowSpaceDXR) bound projection_pipeline, which has blending disabled. A window-space HUD therefore overwrote its whole rectangle in the atlas, colour and alpha both, instead of compositing over the 3D content. Every other backend blends here: D3D11/D3D12, and vk_native and GL as of #1787.

Fix

The window-space draw now picks a blended pipeline per layer, using the same rule as the other backends:

colour src colour dst alpha src alpha dst
SOURCE_ALPHA_BIT clear (premultiplied) ONE ONE_MINUS_SOURCE_ALPHA ONE ONE_MINUS_SOURCE_ALPHA
SOURCE_ALPHA_BIT set (straight) SOURCE_ALPHA ONE_MINUS_SOURCE_ALPHA ONE ONE_MINUS_SOURCE_ALPHA

The alpha "over" keeps atlas alpha intact for transparent sessions (#225/#1784). proj_premult_pipeline and proj_straight_pipeline already exist with exactly these factors, and they use the same projection shaders and Depth32Float attachment that the window-space pass already used. So this change only swaps which pipeline is bound and creates no new pipeline state.

Testing (macOS)

Test setup: cube_handle_metal_macos, sim_display anaglyph, /tmp/dxr_atlas_trigger atlas capture. The HUD was forced visible by a temporary local-only edit, which is not in this PR. A/B on the same build tree:

  • Before (main): the HUD is a solid grey rectangle that hides the grid lines behind it. With DXR_ATLAS_CAPTURE_RAW_ALPHA=1, the HUD region writes atlas alpha down to 128 over opaque content. That is the DP compose-under-bg likely applied per-layer instead of post-composite — desktop bleeds through semi-transparent window-space HUD over opaque content #225-class bleed, where the DP would show the desktop through the HUD.
  • After, straight path (the HUD's real flag, SOURCE_ALPHA_BIT): the panel is translucent, the grid shows through, and there is no hard rectangle. Raw atlas alpha in the HUD region is 255 everywhere, so destination alpha is preserved.
  • After, premultiplied path (flags temporarily flipped to 0 in the build-dir copy of displayxr-common): translucent, with crisp text, no halo and no darkening. Atlas alpha is 255.

Not tested

  • Real Leia or other vendor weaving on macOS. Only sim_display anaglyph was tested; the check is atlas pixels before the DP.
  • A transparent-background session (DISPLAYXR_TRANSPARENT_BG) where the atlas starts with alpha < 1 under the HUD.
  • The _texture Metal apps (cube_zones_texture_metal_macos) and the hosted/legacy Metal apps. They share the same draw site but were not run.
  • A window-space layer with a non-zero disparity, and multiple window-space layers.
  • Not run through clang-format: src/xrt/.clang-format has no Objective-C section, so the tool refuses .m files. Style was matched by hand.

🤖 Generated with Claude Code

@dfattal
dfattal requested a review from a team as a code owner October 2, 2026 07:21
dfattal and others added 2 commits October 2, 2026 00:59
The Metal window-space pass (XrCompositionLayerWindowSpaceDXR) drew with
projection_pipeline, which has blending disabled, so a HUD overwrote its
whole rectangle in the atlas, colour AND alpha, instead of compositing
over the 3D content.

Pick the blended projection pipeline per layer, matching the rule that
D3D11/D3D12, vk_native and GL use (#1786/#1787): SOURCE_ALPHA_BIT clear
means premultiplied (colour src ONE), set means straight (colour src
SOURCE_ALPHA); colour dst and the alpha "over" (ONE /
ONE_MINUS_SOURCE_ALPHA) are shared, so atlas alpha survives for
transparent sessions (#225/#1784). proj_premult_pipeline and
proj_straight_pipeline already carry exactly those factors with the
same shaders and depth attachment, so no new pipeline is needed.
projection_pipeline's REPLACE for the base blit is untouched.

Verified on macOS (cube_handle_metal_macos, sim_display anaglyph,
atlas capture): before, the HUD rect is an opaque grey panel hiding the
grid and writes atlas alpha 128 over opaque content; after, the grid
shows through and atlas alpha stays 255. Premultiplied (flags 0) case
checked with a temporary local flag flip: translucent, no halo.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Same allowance D3D11/D3D12/GL have for the Local2D / window-space
channel; the #1621 guard moves Metal from 0 to 1.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@dfattal
dfattal force-pushed the fix/metal-window-space-blend-1788 branch from aec0122 to f3d2d37 Compare October 2, 2026 07:59
@dfattal
dfattal merged commit 087c0de into main Oct 2, 2026
40 checks passed
@dfattal
dfattal deleted the fix/metal-window-space-blend-1788 branch October 2, 2026 08:12
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.

Metal: window-space layers are drawn with blending disabled (HUD overwrites its rect instead of compositing)

1 participant