diff --git a/docs/specs/extensions/XR_DXR_weave.md b/docs/specs/extensions/XR_DXR_weave.md index 50b29007c6..2396942463 100644 --- a/docs/specs/extensions/XR_DXR_weave.md +++ b/docs/specs/extensions/XR_DXR_weave.md @@ -756,6 +756,16 @@ desktop-Linux runtime it keeps a mismatched overlay off the wire itself. With v14, a frame whose overlay was ignored also resets "unchanged": the first overlay the display processor is offered after one is passed as changed, whatever the caller declared, since the display processor's cached layer predates the ignored frames. +**Android: the overlay is per submit, and an unreadable one refuses (v18).** The Android engine +caches an overlay import by buffer identity (the caller re-sends the same `AHardwareBuffer` every +frame), and before v18 that cache was also a *standing* overlay: once imported, it was +composited on every later submit, including one that chained no overlay. From v17 an overlay is +composited only on a submit that chains one (the cache is kept, so re-sending the same buffer +costs no re-import), and a chained overlay that cannot be imported refuses the submit with +`XR_ERROR_RUNTIME_FAILURE` before the woven output is touched — the v16 rule above. A caller +whose overlay is its whole 2D (browser-pvt#226 on Android) switches the overlay off while its page +scrolls, so on a pre-v18 Android runtime it would see its last page composited over a page that +had moved; such a caller gates on `extensionVersion >= 18`. **The import cache.** An fd is not an identity: every hand-off is a new number. The service keys its input import cache on `bufferId` when non-zero and on the fd's `(st_dev, st_ino)` @@ -964,6 +974,9 @@ any unknown chained struct. One `weave(v13)` WARN in the service log per edge. **v17 is a gate for the same caller as v16**: one whose overlay IS its 2D must not rely on a mismatched overlay being dropped by an older desktop-Linux runtime, which composites it stretched; it keeps such an overlay off the wire there instead. +| 18 | No new structure: on Android an overlay is **per submit** (composited only on a submit that chains one; before v18 the last imported overlay was composited on every later submit) and a chained overlay the engine cannot import **refuses the submit before the woven output is touched** — the v16 rule. An Android caller that hands its whole 2D over as the overlay gates on `extensionVersion >= 18`. The macOS engine still drops an unreadable overlay. | + +**v18 is the same gate on Android**, for the same kind of caller. **v16 is a gate for one kind of caller**: one whose overlay IS its 2D (a whole-page overlay) must not send it to an older desktop-Linux runtime, which would weave without it and return diff --git a/src/external/openxr_includes/openxr/XR_DXR_weave.h b/src/external/openxr_includes/openxr/XR_DXR_weave.h index d237666181..301b677440 100644 --- a/src/external/openxr_includes/openxr/XR_DXR_weave.h +++ b/src/external/openxr_includes/openxr/XR_DXR_weave.h @@ -220,6 +220,15 @@ * overlay IS its 2D gates on extensionVersion >= 17 before relying on it, and * otherwise keeps a mismatched overlay off the wire itself. * + * Android: an overlay is per submit, and an unreadable one refuses (SPEC_VERSION + * 18). No new structure: from v18 the Android engine applies the v16 rule too + * (a chained overlay it cannot import refuses the submit, XR_ERROR_RUNTIME_FAILURE, + * before the woven output is touched) and composites an overlay only on a submit + * that chains one. Before v18 it composited the last overlay it had imported on + * every later submit, chained or not. An Android caller that hands the whole of + * its 2D over as the overlay gates on extensionVersion >= 18. The macOS engine + * still drops an unreadable overlay. + * * Overlay filter strength (SPEC_VERSION 15). XrWeaveSubmitOverlayFilterDXR, chained * next to an XrWeaveSubmitOverlaysDXR: how strongly a display processor that * band-limits the 2D layer for its lens filters it, 0..1 (0 = no lens filtering, @@ -303,7 +312,7 @@ extern "C" { #endif #define XR_DXR_weave 1 -#define XR_DXR_weave_SPEC_VERSION 17 +#define XR_DXR_weave_SPEC_VERSION 18 #define XR_DXR_WEAVE_EXTENSION_NAME "XR_DXR_weave" // Reserved 1004999190..199. Final values reconcile with the Khronos registry diff --git a/src/xrt/compositor/multi/comp_multi_private.h b/src/xrt/compositor/multi/comp_multi_private.h index 3000031423..87f8827531 100644 --- a/src/xrt/compositor/multi/comp_multi_private.h +++ b/src/xrt/compositor/multi/comp_multi_private.h @@ -793,6 +793,15 @@ struct multi_compositor bool overlay_first_use; struct vk_local2d_composite overlay_blend; bool overlay_blend_initialized; + /*! + * XR_DXR_weave v18: THIS submit chained an overlay. The import above is + * a cache (an AHardwareBuffer is re-sent every frame and matched by + * pointer), so @c overlay_image outlives the submit that brought it; the + * overlay is composited only when the current submit carries one. + */ + bool overlay_this_submit; + //! Submits refused because their chained overlay could not be imported. + uint32_t overlay_refusals; //! @} //! @name Arch-C weave satellite (#1277 P0) — service-presented weave diff --git a/src/xrt/compositor/multi/comp_multi_weave_android.c b/src/xrt/compositor/multi/comp_multi_weave_android.c index 02db386336..c06e20abd2 100644 --- a/src/xrt/compositor/multi/comp_multi_weave_android.c +++ b/src/xrt/compositor/multi/comp_multi_weave_android.c @@ -1828,6 +1828,24 @@ comp_multi_weave_submit(struct xrt_compositor *xc, } // v4 overlay: same, into the SEPARATE overlay cache. + // + // XR_DXR_weave v18 (browser-pvt#226), two rules the desktop-Linux engine + // has had since v16 and the D3D11 service always had: + // 1. PER SUBMIT. The cache is keyed by buffer identity because the + // caller re-sends the same AHardwareBuffer every frame, but a cached + // import is NOT a standing overlay: a submit that chains none is woven + // with none. Before v18 the last imported overlay was composited on + // every later submit, so a caller that stops sending one (the + // browser's whole-page overlay switches itself off while the page + // scrolls) got its last page composited over a page that had moved. + // 2. REFUSE, don't drop. A chained overlay that cannot be imported + // refuses the submit HERE, before anything is recorded, so the woven + // output is left exactly as the previous accepted submit left it. 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 frame; before v18 that frame would have had its 2D + // missing. + mc->weave.overlay_this_submit = overlay != XRT_GRAPHICS_BUFFER_HANDLE_INVALID; if (overlay != XRT_GRAPHICS_BUFFER_HANDLE_INVALID && (mc->weave.overlay_image == VK_NULL_HANDLE || mc->weave.overlay_ahb != (void *)overlay)) { weave_release_overlay(vk, mc); @@ -1840,6 +1858,14 @@ comp_multi_weave_submit(struct xrt_compositor *xc, overlay = XRT_GRAPHICS_BUFFER_HANDLE_INVALID; U_LOG_W("weave(#1036) v4: overlay import cached (%ux%u)", mc->weave.overlay_w, mc->weave.overlay_h); + } else { + const uint32_t n = mc->weave.overlay_refusals++; + if (n == 0 || (n % 300) == 0) { + U_LOG_W("weave(#1036) v18: overlay import failed — submit REFUSED, woven output " + "untouched (refusals=%u)", + n + 1); + } + break; } } @@ -2380,7 +2406,7 @@ comp_multi_weave_submit(struct xrt_compositor *xc, // blend, so crisp 2D lands on top of the interlaced 3D at screen depth. // The overlay is NOT woven — it is drawn after process_atlas onto the same // output attachment. - if (mc->weave.overlay_image != VK_NULL_HANDLE) { + if (mc->weave.overlay_image != VK_NULL_HANDLE && mc->weave.overlay_this_submit) { bool blend_ready = mc->weave.overlay_blend_initialized; if (!blend_ready) { blend_ready = vk_local2d_composite_init(&mc->weave.overlay_blend, vk, WEAVE_VK_FORMAT,