Repository navigation
vk_native: composite window-space alpha into the atlas (#1780) - #1784
Merged
Merged
Conversation
vk_hud_blend stamped the HUD with RGB writes only (src alpha ZERO, dst alpha ONE). In a transparent session the atlas alpha is the window's transparency, so a window-space layer was see-through everywhere no 3D content sat under it: on Linux the HUD showed only inside the avatar's silhouette. vk_hud_blend_init_ex(..., write_alpha) adds the option to composite alpha "over" as well (src ONE, dst ONE_MINUS_SRC_ALPHA, all channels written), the blend D3D11 already uses for window-space layers (DisplayXR#225). vk_native's window-space blend turns it on. Alpha only grows under "over", so an opaque atlas (alpha 1) is unchanged. It is not keyed on the session being transparent: that can switch on mid-session and the pipeline is built once. vk_hud_blend_init() keeps the RGB-only blend, so comp_multi_system's hud/chrome blends are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
dfattal
approved these changes
Oct 2, 2026
dfattal
left a comment
Collaborator
There was a problem hiding this comment.
LGTM — small, correct, well-scoped.
- D3D11 parity confirmed: the new blend (colour
SRC_ALPHA/INV_SRC_ALPHA, alphaONE/INV_SRC_ALPHA, all channels written) matchescomp_d3d11_renderer.cppblend_alpha(#225) exactly. - Unconditional enable is safe: alpha only grows under "over". The lazy-transparency alpha probe runs after the window-space stamp (
comp_vk_native_compositor.cstamp →vk_lazy_transparency_frame), so a HUD can never create false alpha<1, and a HUD over a transparent region is now correctly counted as content. Opaque sessions present opaque.window_space_blendhas one call site;comp_multi_system.ckeeps the RGB-only blend. - Output convention matches the present: a straight-alpha HUD source through this blend yields a valid premultiplied pixel, which is what the transparent target (
COMPOSITE_ALPHA_PRE_MULTIPLIED) expects.
Non-blocking follow-up: vk_hud_blend always treats the source as straight alpha and ignores the layer's XR_COMPOSITION_LAYER_UNPREMULTIPLIED_ALPHA_BIT (OpenXR default is premultiplied), so a premultiplied HUD gets rgb·a² — darker translucent pixels. D3D11 has the same behaviour and RGB already did this, but it's now more visible over the desktop. Could pick src factor from the layer flags like comp_vk_native_renderer.c does for projection layers.
Not tested by me: macOS (MoltenVK) / Windows Vulkan transparent sessions — the monotone-alpha argument covers opaque ones.
This was referenced Oct 2, 2026
dfattal
added a commit
that referenced
this pull request
Oct 2, 2026
vk_hud_blend stamped every window-space layer with a straight-alpha colour blend (SRC_ALPHA / ONE_MINUS_SRC_ALPHA) and ignored the layer flags, so a layer submitted with premultiplied bytes (SOURCE_ALPHA_BIT clear) came out as rgb*a^2: translucent pixels and antialiased edges too dark. Since #1784 composites the HUD's alpha too, those panels now show over the desktop in a transparent session, darkened. vk_hud_blend now builds a second pipeline in the same instance, identical except for colour src ONE, and vk_hud_blend_draw_no_layout_ex() picks one per draw. Both share the render pass, layouts and the per-image cache, so cache eviction keeps working on the one instance. vk_native picks it with the D3D11/D3D12 window-space rule: premultiplied when SOURCE_ALPHA_BIT is clear, straight when set. Deliberately not comp_layer_blend_mode() (#1599). The alpha blend from #1784 is unchanged. The GL compositor's window-space pass had the same fixed straight blend; it gets the same one-line rule. vk_hud_blend_init(), vk_hud_blend_draw() and vk_hud_blend_draw_no_layout() keep the straight pipeline, so the comp_multi_system.c callers are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
dfattal
added a commit
that referenced
this pull request
Oct 2, 2026
… (#1787) * vk_native: honour the premultiplied flag on window-space layers (#1786) vk_hud_blend stamped every window-space layer with a straight-alpha colour blend (SRC_ALPHA / ONE_MINUS_SRC_ALPHA) and ignored the layer flags, so a layer submitted with premultiplied bytes (SOURCE_ALPHA_BIT clear) came out as rgb*a^2: translucent pixels and antialiased edges too dark. Since #1784 composites the HUD's alpha too, those panels now show over the desktop in a transparent session, darkened. vk_hud_blend now builds a second pipeline in the same instance, identical except for colour src ONE, and vk_hud_blend_draw_no_layout_ex() picks one per draw. Both share the render pass, layouts and the per-image cache, so cache eviction keeps working on the one instance. vk_native picks it with the D3D11/D3D12 window-space rule: premultiplied when SOURCE_ALPHA_BIT is clear, straight when set. Deliberately not comp_layer_blend_mode() (#1599). The alpha blend from #1784 is unchanged. The GL compositor's window-space pass had the same fixed straight blend; it gets the same one-line rule. vk_hud_blend_init(), vk_hud_blend_draw() and vk_hud_blend_draw_no_layout() keep the straight pipeline, so the comp_multi_system.c callers are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * tests: allow GL's window-space SOURCE_ALPHA_BIT read (#1786) The #1621 guard pins each backend's direct reads of the source-alpha bit. #1786 gives GL's window-space pass the same premul/straight decision D3D11 and D3D12 already have an allowance for (#1581), so GL moves from 0 to 1. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
dfattal
added a commit
that referenced
this pull request
Oct 2, 2026
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>
dfattal
added a commit
that referenced
this pull request
Oct 2, 2026
…1789) * 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 <noreply@anthropic.com> * 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 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
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.
Fixes #1780.
Problem
In a transparent session on the Vulkan native compositor, a window-space layer (HUD) was visible only where 3D content already sat under it. On Linux the HUD showed inside the avatar's silhouette and was see-through everywhere else.
vk_hud_blendstamps the HUD into the atlas with RGB writes only (srcAlpha ZERO,dstAlpha ONE, noAincolorWriteMask). In a transparent session the atlas alpha decides where the window is see-through. Outside the silhouette it is 0, so the HUD's colour was written but stayed fully transparent.Fix
vk_hud_blend_init_ex(blend, vk, fmt, write_alpha). Withwrite_alphathe HUD is also composited "over" in alpha:srcAlpha ONE,dstAlpha ONE_MINUS_SRC_ALPHA, all channels written. This is the blend D3D11 already uses for window-space layers (comp_d3d11_renderer.cpp, DP compose-under-bg likely applied per-layer instead of post-composite — desktop bleeds through semi-transparent window-space HUD over opaque content #225).vk_hud_blend_init()keeps the RGB-only blend, socomp_multi_system.c(hud_blend,chrome_blend,shared_chrome_blend) is unchanged.Behaviour change: this applies to every vk_native session, not only transparent ones. Under "over" the alpha only grows (
a = a_src + a_dst·(1 − a_src)), so an opaque atlas (alpha 1) is unchanged. I didn't key it on the session's transparency because that can switch on mid-session (lazy transparency) and this pipeline is built once. That affects Linux, and macOS through MoltenVK. Windows Vulkan apps use vk_native too. I only tested Linux.Testing
Ubuntu 26.04, RTX 4090 Laptop, Leia DS1, Leia SR plug-in 2.7.6. App: the lenovo-avatar Unity player (transparent) with displayxr-unity window-space UI on Vulkan. Two HUD panels, opened with Shift+Tab.
The hardware test used v2.21.11 with this patch plus #1783 (the patches apply cleanly). The branch is on current
mainand builds there (build_linux.sh, Release, no new warnings).🤖 Generated with Claude Code