Skip to content

fix: overlay canvases bleed into each other; wsui blank under the URP foreground clip - #342

Merged
dfattal merged 2 commits into
DisplayXR:mainfrom
byungjul:fix/overlay-canvas-isolation
Oct 1, 2026
Merged

dfattal merged 2 commits into
DisplayXR:mainfrom
byungjul:fix/overlay-canvas-isolation

Conversation

@byungjul

@byungjul byungjul commented Oct 1, 2026

Copy link
Copy Markdown
Contributor

Summary

Two fixes for apps that use more than one 2D overlay, or the URP transparent-overlay foreground clip. Both showed up in lenovo-avatar, which has a Local2D speech bubble and two window-space HUDs.

1. Each overlay canvas gets its own stage

DisplayXRLocal2D and DisplayXRWindowSpaceUI all parked their offscreen canvas at one shared world position ((0, 100000, 0)), on the shared private layer 30. Each overlay camera culls to that layer, so every camera also rendered the other overlays' canvases. Reading the bubble's Local2D RT back showed the radio and tuning HUDs drawn on top of the bubble text. Sharing the layer means it can't keep them apart.

The new DisplayXROverlayStage gives each live overlay its own stage, 1000 world units apart. That spacing is well beyond any canvas (a 4096-unit canvas at scale 0.01 is about 41 units) and the cameras' 10-unit far plane. Each component acquires a stage in OnEnable and releases it in OnDisable.

2. The window-space UI camera no longer gets the foreground clip

The URP foreground clip (DisplayXR/ForegroundClipURP) is a full-screen pass on the renderer. It ran for DisplayXRWindowSpaceUI's overlay camera too, and with _DXRForegroundFar armed it discarded the whole canvas. The HUD RT came out fully transparent (read back as all zeros), so no HUD ever appeared. DisplayXRLocal2D already zeroes the global around its own camera's render. DisplayXRWindowSpaceUI now does the same.

⚠️ Behaviour changes on every platform

  • With two or more overlays: each overlay texture now contains only its own canvas. Before, overlapping canvases bled into each other.
  • For apps that wire the URP foreground clip: window-space UI now renders. Before, it was blank.
  • Apps with a single overlay and no foreground clip are unaffected.

Testing

Linux, RTX 4090 + DS1, lenovo-avatar (one Local2D, two wsui):

Related: #336

🤖 Generated with Claude Code

@byungjul
byungjul requested a review from dfattal as a code owner October 1, 2026 08:51
byungjul and others added 2 commits October 1, 2026 08:00
DisplayXRLocal2D and DisplayXRWindowSpaceUI park their offscreen canvas at one
shared world position on the shared private layer 30, and each overlay camera
culls to that layer. With two or more overlays every camera also rendered the
other canvases: in lenovo-avatar the speech bubble's Local2D texture held the
radio and tuning HUDs on top of the bubble text (verified by reading the RT
back). The layer can't separate them because they share it.

DisplayXROverlayStage hands each live overlay its own stage, 1000 world units
apart (a 4096-unit canvas at scale 0.01 is ~41 units; the cameras' far plane is
10), acquired in OnEnable and released in OnDisable.

Behaviour change on every platform: an overlay's texture now contains only its
own canvas. Scenes with a single overlay are unaffected.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The transparent-overlay foreground clip (DisplayXR/ForegroundClipURP) is a
full-screen pass on the URP renderer, so it also ran for DisplayXRWindowSpaceUI's
offscreen overlay camera and discarded the whole canvas: the HUD RT came out
fully transparent and nothing ever showed (verified by reading the RT back: all
zero; with this change both lenovo-avatar HUDs render). DisplayXRLocal2D already
zeroes _DXRForegroundFar around its own camera's render; do the same here.

Behaviour change on every platform for apps that wire the URP foreground clip:
their window-space UI now renders. Apps without it are unaffected.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@dfattal
dfattal force-pushed the fix/overlay-canvas-isolation branch from 106f1a8 to 9f980d5 Compare October 1, 2026 15:00
@dfattal
dfattal merged commit 863465b into DisplayXR:main Oct 1, 2026
8 checks passed
dfattal pushed a commit that referenced this pull request Oct 2, 2026
* chore(#332): put UpdateBakedHitColliders' doc comment back above it

The Linux UpdatePointerFromInputSystem() method (#343) was inserted
between UpdateBakedHitColliders' doc comment and the method, so the
comment read as if it described the new method. Move the Linux method
above the comment. No code change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(#332): round Linux PointerPosition/PointerDelta like macOS

The macOS path rounds the cursor to whole window-client pixels
(Mathf.RoundToInt on x and on the flipped y) before computing
PointerPosition and PointerDelta; the Linux path (#343) did not, so
Linux reported fractional pixels. Round the same way. Linux only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(#336): each overlay canvas has its own stage, not (0,100000,0)

Since #342 every Local2D / window-space UI canvas gets its own stage
from DisplayXROverlayStage (first at (0,100000,0), then 1000 units
apart along x). The WindowSpaceUI sample README and router comment,
window-space-ui.md and a leftover comment in DisplayXRWindowSpaceUI
still said every canvas sits at (0,100000,0). Also note in
DisplayXROverlayStage that the spacing assumes an overlay camera
ortho half-width under 1000 world units. Comment/doc only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(#336): say how several window-space UIs stack

Several DisplayXRWindowSpaceUI components are composited in slot order
(lowest slot at the bottom), and each takes the lowest free slot on
enable, so the draw order of overlapping HUDs follows enable/disable
history, not anything the app sets. Document that (class doc, the
slot-acquire comment, window-space-ui.md), advise keeping HUDs from
overlapping, and note that under the DisplayXR shell only the first
window-space layer is composited. Doc only, no behaviour change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
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.

2 participants