From 8977653e364511a20e322286d0b7d6604be33a87 Mon Sep 17 00:00:00 2001 From: David Date: Fri, 2 Oct 2026 00:21:17 -0700 Subject: [PATCH 1/2] fix(metal): blend window-space layers instead of overwriting (#1788) 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 --- .../compositor/metal/comp_metal_compositor.m | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/xrt/compositor/metal/comp_metal_compositor.m b/src/xrt/compositor/metal/comp_metal_compositor.m index d261b96ecf..c20da9b87f 100644 --- a/src/xrt/compositor/metal/comp_metal_compositor.m +++ b/src/xrt/compositor/metal/comp_metal_compositor.m @@ -4018,6 +4018,21 @@ - (BOOL)wantsUpdateLayer } } + // #1788: window-space layers BLEND over the projection content, + // same rule as D3D11/D3D12, vk_native and GL (#1786/#1787): + // SOURCE_ALPHA_BIT clear means premultiplied bytes (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). + // The blended projection pipelines carry exactly those factors. + // Deliberately NOT comp_layer_blend_mode() (#1599): see the + // comment in comp_d3d11_renderer.cpp's window-space draw. And + // never projection_pipeline — its REPLACE is for the base blit. + bool ws_premultiplied = + (layer->data.flags & XRT_LAYER_COMPOSITION_BLEND_TEXTURE_SOURCE_ALPHA_BIT) == 0; + id ws_pipeline = + ws_premultiplied ? c->proj_premult_pipeline : c->proj_straight_pipeline; + // Source UV sub-rect (default to full texture if not specified) struct xrt_normalized_rect nr = ws->sub.norm_rect; if (nr.w <= 0.0f || nr.h <= 0.0f) { @@ -4060,7 +4075,7 @@ - (BOOL)wantsUpdateLayer } [encoder setViewport:vp]; - [encoder setRenderPipelineState:c->projection_pipeline]; + [encoder setRenderPipelineState:ws_pipeline]; [encoder setDepthStencilState:c->depth_stencil_state]; [encoder setFragmentTexture:src_tex atIndex:0]; [encoder setFragmentSamplerState:c->sampler_linear atIndex:0]; From f3d2d375d2a86c8f39c04fc140e3af9c7d91c45c Mon Sep 17 00:00:00 2001 From: David Date: Fri, 2 Oct 2026 00:59:20 -0700 Subject: [PATCH 2/2] tests: allow Metal's window-space SOURCE_ALPHA_BIT read (#1788) 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 --- tests/tests_comp_layer_view_camera.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/tests_comp_layer_view_camera.cpp b/tests/tests_comp_layer_view_camera.cpp index 88c4f004f3..1b1c4332ff 100644 --- a/tests/tests_comp_layer_view_camera.cpp +++ b/tests/tests_comp_layer_view_camera.cpp @@ -1138,7 +1138,9 @@ TEST_CASE("comp_layer_blend_mode: no backend reimplements the blend rule (#1621) // channel D3D11's is (its own premul/straight decision, deliberately // not comp_layer_blend_mode() -- see comp_d3d11_renderer.cpp). {"gl/comp_gl_compositor.cpp", 1}, - {"metal/comp_metal_compositor.m", 0}, + // #1788: Metal's one allowance is the same Local2D / window-space + // channel as D3D11/D3D12/GL (#1581, #1786). + {"metal/comp_metal_compositor.m", 1}, // The Local2D / window-space channel, above. {"d3d11/comp_d3d11_renderer.cpp", 1}, /*