Skip to content

fix(weave/linux): an overlay that cannot be imported refuses the submit (XR_DXR_weave v16) - #1825

Merged
dfattal merged 1 commit into
mainfrom
fix/linux-weave-refuse-unreadable-overlay
Oct 5, 2026
Merged

dfattal merged 1 commit into
mainfrom
fix/linux-weave-refuse-unreadable-overlay

Conversation

@dfattal

@dfattal dfattal commented Oct 5, 2026

Copy link
Copy Markdown
Collaborator

What

The desktop-Linux weave engine (comp_multi_weave_linux.c) dropped a chained v4 overlay that it could not import, wove the frame anyway, and returned success. The D3D11 service does the opposite: it refuses a submit whose overlay it cannot open or acquire, before it clears the woven output.

From this change, both Linux submit paths do what D3D11 does:

  • the stage-A OPAQUE_FD path;
  • the stage-B dma-buf path.

When the overlay import fails, the submit is refused before the acquire fence is taken and before anything is recorded. The woven output is left exactly as the previous accepted submit left it.

XR_DXR_weave SPEC_VERSION 16. There is no new structure. The version is a gate for one kind of desktop-Linux caller: one whose overlay is its 2D. The macOS and Android engines are unchanged and still drop an unreadable overlay. Their only overlay callers decorate content that they present anyway.

Why

The browser's whole-page overlay (browser-pvt#226, now being ported to Linux) hands the runtime the whole flat page as the overlay. It reads "accepted with the overlay chained" as "the page was composited" and presents the woven output as the complete frame. With the old Linux behaviour, one non-importable overlay would put a frame on screen with the page missing. A DRM modifier that does not import on the service's device is enough to cause it.

With v16, the browser holds its last composed frame on a refused submit. After a bounded number of refused frames it paints the page itself. The browser enables Linux page mode only when the runtime reports spec 16 or later.

Tests

Headless, sim_display only. Both the service and the client ran with DXR_PLUGIN_EXCLUSIVE=sim-display, and the log confirmed active plug-in: id=sim-display. Each run used a private XDG_RUNTIME_DIR and XRT_FORCE_MODE=ipc, against a --hybrid --apps build.

weave_probe_vk_linux --dmabuf gains one case after its pixel run:

  • it submits with an overlay whose fd is a memfd (a valid fd that is not a dma-buf), so the import fails in the service and nowhere earlier;
  • it expects the submit to be refused;
  • it reads the output back and expects the last accepted frame's parity there.
build result
this branch unreadable overlay: submit refused (-2); woven output UNTOUCHED, PASS (also with --linear; the stage-A probe without --dmabuf passes; fd counts stay flat)
main engine (A/B) FAIL: a submit whose overlay cannot be imported was ACCEPTED (the overlay was dropped and the frame woven anyway)

Interplay

🤖 Generated with Claude Code

@dfattal
dfattal requested a review from a team as a code owner October 5, 2026 07:33
…it (XR_DXR_weave v16)

The desktop-Linux weave engine dropped a chained v4 overlay it could not
import (weave_cache_acquire -> NULL) and wove the frame anyway, returning
success. The D3D11 service refuses such a submit before it clears the
woven output. A caller that hands the whole of its 2D over as the overlay
(the browser's whole-page overlay, browser-pvt#226) reads "accepted with
the overlay chained" as "composited" and presents the woven output as the
complete frame, so the Linux behaviour would show a frame with the page
missing - for example whenever the overlay's DRM modifier does not import
on the service's device.

Both Linux submit paths (stage-A OPAQUE_FD and stage-B dma-buf) now refuse
the submit when the overlay import fails, before the acquire fence is
taken and before anything is recorded, so the woven output is left exactly
as the previous accepted submit left it. One WARN on the first refusal,
then 1 in 300 (the import helper already names the cause once per slot).

XR_DXR_weave SPEC_VERSION 16: no new structure, a gate for the one kind of
desktop-Linux caller that needs it (overlay == its 2D). The macOS and
Android engines still drop an unreadable overlay; their only overlay
callers decorate content they present anyway.

weave_probe_vk_linux --dmabuf gains the case: after the pixel run, a
submit whose overlay fd is a memfd (a valid fd that is no dma-buf) must be
refused, and the woven output read back afterwards must still hold the
last accepted frame's parity. Headless, sim_display only
(DXR_PLUGIN_EXCLUSIVE=sim-display, active plug-in confirmed in the log):
  fixed:  "unreadable overlay: submit refused (-2); woven output UNTOUCHED"
          -> PASS (also with --linear; stage-A probe PASS; fd counts flat)
  main:   "FAIL: a submit whose overlay cannot be imported was ACCEPTED"

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@dfattal
dfattal force-pushed the fix/linux-weave-refuse-unreadable-overlay branch from 2f05e2d to 5f70016 Compare October 5, 2026 08:25
@dfattal
dfattal merged commit e3246f0 into main Oct 5, 2026
40 checks passed
@dfattal
dfattal deleted the fix/linux-weave-refuse-unreadable-overlay branch October 5, 2026 08:41
dfattal added a commit that referenced this pull request Oct 7, 2026
…g it (XR_DXR_weave v17) (#1837)

In a resize race a present-owner keeps handing its old overlay buffer, still
the OLD window's size, for a frame or more after the window (input, woven
output) changed size. Seen on the DS1 with the browser's whole-page overlay as
a black border and black 2D on maximise / unmaximise.

The black margin is the runtime's own post-weave blend, not the display
processor: the DP offer (set_overlay_2d) is gated on the layer being exactly
the output size, so the mismatched layer fell through to the v4 post-weave
blend, which drew it over the whole output as if it were window-sized. The
stage-A OPAQUE_FD path was worse: it imported the overlay at the window's size
whatever the buffer's real size (a smaller allocation failed the import and,
since #1825, refused the submit; a larger one was read with the wrong layout).

Both submit paths now treat an overlay whose size is not this submit's window
size as ABSENT for that frame: no DP offer, no post-weave blend, no off-panel /
flat-region redraw, content woven normally. The submit is accepted, not
refused (refusing would hold frames through every resize); #1825's refusal of
an overlay that cannot be imported is unchanged. One WARN on entering the
stale state, one when the overlay matches again.

Window size: the input for a batch submit, else the bound geometry (legacy
without geometry: rect offset + extent; v6 without geometry: unchecked).

Real overlay size:
- dma-buf: the descriptor's declared width x height (not imported if stale).
- stage A: nothing carries a size (XrWeaveSubmitOverlaysDXR has no extent, an
  fd has none), so a wire field would have nothing to fill it from. The engine
  recovers it instead: an inode it already imported IS that import's size
  (the cache keeps the allocation alive; allocations never change size), and
  an unseen fd smaller than W x H x 4 bytes cannot be window-sized. An unseen
  stage-A buffer LARGER than the window remains undetectable (documented).

v14: a frame whose overlay was ignored forces layer_unchanged = false on the
next overlay offered to the DP, whose cached layer predates the ignored frames.

XR_DXR_weave SPEC_VERSION 17, no new structure: it qualifies v16's "accepted
with an overlay chained means composited" for a caller whose overlay is its
2D, which needs to know whether a mismatched overlay is dropped (v17) or
stretched (older desktop-Linux runtimes). Spec version field in the doc
header brought up from a stale 13.

weave_probe_vk_linux gains the case in both runs, headless on sim_display
(DXR_PLUGIN_EXCLUSIVE=sim-display, active plug-in confirmed in the log,
private runtime dir socket):
- stage A: never-seen 960x540 overlay under 1280x720 (allocation path),
  cached 1280x720 overlay under 1600x900 (identity path), each followed by
  a matching overlay: ignored -> composited, no refusal. PASS.
- dma-buf: two frames with a declared 960x540 overlay, then the matching
  one declared "unchanged": ignored -> composited, no refusal. PASS (also
  --linear); fd counts flat over 600 frames.
- main (negative control): dma-buf step 0 reads opaque green (the stale
  layer stretched over everything); stage A step 0 is REFUSED. FAIL.

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
dfattal added a commit that referenced this pull request Oct 7, 2026
…refuses (XR_DXR_weave v18)

Two gaps in the Android weave engine's v4 overlay handling, found while
porting the browser's whole-page overlay (browser-pvt#226) to Android:

1. A cached overlay was a STANDING overlay. The engine caches the overlay
   import by AHardwareBuffer identity (the caller re-sends the same buffer
   every frame), and composited mc->weave.overlay_image whenever it was set
   - so after one submit that chained an overlay, every later submit was
   composited with it, chained or not. The browser's whole-page overlay
   switches itself off while the page scrolls (its 0351 safety floor); on
   those frames the last page it sent would be composited over a page that
   has moved. The import is still cached (no re-import for the same
   buffer), but the overlay is now composited only on a submit that chains
   one (mc->weave.overlay_this_submit).

2. An overlay that could not be imported was DROPPED and the frame woven
   anyway, returning success. A caller that hands its whole 2D over as the
   overlay reads "accepted with the overlay chained" as "composited" and
   presents the woven output as the complete frame. The submit is now
   refused (XRT_ERROR_WEAVE_REFUSED -> XR_ERROR_RUNTIME_FAILURE) before
   anything is recorded, so the woven output is left exactly as the
   previous accepted submit left it: the desktop-Linux v16 rule (#1825) and
   the D3D11 service's. One WARN on the first refusal, then 1 in 300.

XR_DXR_weave SPEC_VERSION 18: no new structure, the Android gate for the
one kind of caller that needs it (overlay == its 2D), exactly as v16 is on
desktop Linux. The macOS engine still drops an unreadable overlay.

Callers whose overlay decorates content they present anyway (0099's
over-plane mode) see no difference except on a frame that chains no
overlay, which no longer shows the previous frame's.

Not tested on hardware: the NP02J is off the bus right now. Compiled by CI
(build-android). The branch feat/android-overlay-2d-dp (DP-side overlay
offer) touches the same block; its offer must also require
overlay_this_submit once both land.

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

Renumbered v17 -> v18 on rebase: main took v17 for the desktop-Linux stale-size
overlay rule (#1837).
dfattal added a commit that referenced this pull request Oct 7, 2026
…refuses (XR_DXR_weave v18) (#1835)

Two gaps in the Android weave engine's v4 overlay handling, found while
porting the browser's whole-page overlay (browser-pvt#226) to Android:

1. A cached overlay was a STANDING overlay. The engine caches the overlay
   import by AHardwareBuffer identity (the caller re-sends the same buffer
   every frame), and composited mc->weave.overlay_image whenever it was set
   - so after one submit that chained an overlay, every later submit was
   composited with it, chained or not. The browser's whole-page overlay
   switches itself off while the page scrolls (its 0351 safety floor); on
   those frames the last page it sent would be composited over a page that
   has moved. The import is still cached (no re-import for the same
   buffer), but the overlay is now composited only on a submit that chains
   one (mc->weave.overlay_this_submit).

2. An overlay that could not be imported was DROPPED and the frame woven
   anyway, returning success. A caller that hands its whole 2D over as the
   overlay reads "accepted with the overlay chained" as "composited" and
   presents the woven output as the complete frame. The submit is now
   refused (XRT_ERROR_WEAVE_REFUSED -> XR_ERROR_RUNTIME_FAILURE) before
   anything is recorded, so the woven output is left exactly as the
   previous accepted submit left it: the desktop-Linux v16 rule (#1825) and
   the D3D11 service's. One WARN on the first refusal, then 1 in 300.

XR_DXR_weave SPEC_VERSION 18: no new structure, the Android gate for the
one kind of caller that needs it (overlay == its 2D), exactly as v16 is on
desktop Linux. The macOS engine still drops an unreadable overlay.

Callers whose overlay decorates content they present anyway (0099's
over-plane mode) see no difference except on a frame that chains no
overlay, which no longer shows the previous frame's.

Not tested on hardware: the NP02J is off the bus right now. Compiled by CI
(build-android). The branch feat/android-overlay-2d-dp (DP-side overlay
offer) touches the same block; its offer must also require
overlay_this_submit once both land.

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

Renumbered v17 -> v18 on rebase: main took v17 for the desktop-Linux stale-size
overlay rule (#1837).
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.

1 participant