Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion docs/specs/extensions/XR_DXR_display_info.md
Original file line number Diff line number Diff line change
Expand Up @@ -941,7 +941,9 @@ XrResult xrRequestDisplayModeDXR(
changes. Requesting `XR_DISPLAY_MODE_2D_DXR` over an active 3D mode therefore shows the
*woven* atlas flat (blurry); an application fading its parallax to zero converges back
to a sharp image — the building block for app-authored 2D↔3D transitions such as the
MANUAL eye-tracking loss flow.
MANUAL eye-tracking loss flow. (A weave present-owner that wants the content flat instead —
one view, sharp — opts in per submit with `XrWeaveSubmitMonoIn2DDXR`, `XR_DXR_weave` v13 §5f;
this function's own semantics do not change.)
- **Lifetime of the override.** The override holds until the next
`xrRequestDisplayRenderingModeDXR` call, whose mode's default hardware state then
applies (re-requesting the current mode index restores its default).
Expand Down
74 changes: 70 additions & 4 deletions docs/specs/extensions/XR_DXR_weave.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
| Field | Value |
|---|---|
| **Extension Name** | `XR_DXR_weave` |
| **Spec Version** | 12 |
| **Spec Version** | 13 |
| **Extension Type** | Instance extension (service path — Windows/D3D11, macOS/comp_multi-Vulkan #759, Android/comp_multi-Vulkan #1036, desktop Linux/comp_multi-Vulkan dma-buf #1699 when the service carries its engine; the snap and its bulk grid form also work in-process on desktop Linux, §5c / #1588 / #1723) |
| **Header** | `src/external/openxr_includes/openxr/XR_DXR_weave.h` (canonical; auto-syncs to `displayxr-extensions`) |
| **Status** | Provisional (`1004999190–198` type block, pending Khronos registry; `199` reserved, see §2c; v9+ additions in a fresh `1004999240–249` decade — 240 v9, 241–245 v10, 246 v11, 247–248 v12) |
Expand Down Expand Up @@ -681,16 +681,16 @@ duplicates rather than transfers. Engine contract: `src/xrt/include/xrt/xrt_weav

**Off-panel 2D (#1654).** The part of the bound window that the published geometry puts off the
panel comes back flat, not woven: the runtime paints each off-panel band of the output with the
centre view of the submitted content (for a batch rect, its left half unsqueezed; for v6, the
centre tile), in register with the window and under the v4 overlay, and skips the weave entirely
flat view of the submitted content (§5f's table: for a stereo pair the left view — a batch rect's
left half unsqueezed; for v6 with more views, the view nearest the display axis), in register with the window and under the v4 overlay, and skips the weave entirely
for a window wholly off the panel — so a caller composites the output as usual and never clips it
itself. `DXR_SPAN_2D=0` restores the fully woven output.

**Flat regions (v8) on desktop Linux.** With no per-region lens on any desktop-Linux display
processor to wish to, the runtime instead paints the union of the submit's
`XrWeaveSubmitFlatRegionsDXR` rects and the latched `xrWeaveSetScreenFlatRegionsDXR` rects
(clipped to the bound window, taking effect at the next submit) flat in the woven output — the
input pixels 1:1 for a batch submit, the centre view for v6 — a platform exception to §2c's
input pixels 1:1 for a batch submit, the flat view (§5f) for v6 — a platform exception to §2c's
"pixels unaffected" that `DXR_WEAVE_FLAT_2D=0` switches off.

## 5e. Woven origin per output — present-owner move sync (v12, browser-pvt#180)
Expand Down Expand Up @@ -776,6 +776,67 @@ service): the app re-binds a stepped origin every 4 frames with a logical origin
checks on every output that the reported origin equals the last bind, the logical pair is echoed
verbatim and the serial strictly increases.

## 5f. One flat view while the panel is in hardware 2D (v13)

```c
#define XR_TYPE_WEAVE_SUBMIT_MONO_IN_2D_DXR ((XrStructureType)1004999249)

typedef struct XrWeaveSubmitMonoIn2DDXR {
XrStructureType type; // XR_TYPE_WEAVE_SUBMIT_MONO_IN_2D_DXR
const void* XR_MAY_ALIAS next;
XrBool32 monoIn2D; // XR_TRUE: one view flat, no weave, while hardware is 2D
} XrWeaveSubmitMonoIn2DDXR;
```

**Why.** `xrRequestDisplayModeDXR` is hardware-only (`XR_DXR_display_info`): 2D over woven
content turns the lens off and keeps the weave, so every view shows interlaced through no lens,
soft and doubled. Apps build fade-to-flat transitions on exactly that, so it stays the default.
A present-owner that drops the lens for its OWN reason — the browser holding the panel 2D while
one of its menus is open over an inline-3D page — wants the content flat and sharp instead, as if
a 2D rendering mode were active, without the page being told anything.

**Why not a 2D rendering mode.** `xrRequestDisplayRenderingModeDXR` is the page's channel and is
display-global: on Windows the service broadcasts `XrEventDataRenderingModeChangedDXR` to every
session and re-forces a 3D mode for a weave client that keeps submitting; on desktop Linux the
content mode a present-owner pushes is recorded and read by nothing in the weave engine, and its
seed at `xrBeginSession` is whatever mode the head device holds. Neither weave engine reads the
content mode at all. A per-submit flag scoped to the weave keeps the page's mode, the events and
every other session out of it.

**Semantics.** Chained on `XrWeaveSubmitInfoDXR`. While `monoIn2D` is `XR_TRUE` **and** this
session's hardware display state is 2D — the state the runtime last confirmed to it, the one its
`XrEventDataHardwareDisplayStateChangedDXR` events report — the runtime skips the weave and paints
the whole output with ONE view of the submitted content at full output resolution, in register with
the window. The path is the off-panel bands' (§5d): the whole output becomes one band, painted from
the same view the bands use, so the two flat paths always match. Flat regions and the v4 overlay are
applied on top as usual.

**Mono follows the runtime's display-processor-CONFIRMED hardware-2D state for this session, not
the request.** It starts on the first submit after the display processor has accepted the 2D
request, and it ends on the first submit after the confirmed state is 3D again — no stale flat frame
after a release, no woven frame while held. A 2D request the display processor rejects never
starts it.

**Which view.** One rule for every flat path of the engine (`u_weave_flat_view_index`):

| submitted views | flat view |
|---|---|
| 1 or 2 — every batch submit, and a 2-view v6 layout | **view 0, the LEFT view** (a batch rect's left half, unsqueezed). Deterministic: both eyes sit about equally far from the display axis, so a nearest-axis rule would flip between them as the head moves and the flat image would jump by the disparity. |
| more than 2 (v6) | the view whose eye is **nearest the display axis** — smallest \|x\| of the per-view eye offset the display processor reports; a tie goes to the lower index |
| more than 2, no per-view eye offsets | the centre index, `(N − 1) / 2` |

- In hardware 3D the struct has no effect: the output is woven. A caller can therefore chain it for
the whole of its own 2D hold without racing the lens, and a 2D request the display processor
rejected keeps the weave.
- Per submit, no latch. Omitting the struct, or `monoIn2D = XR_FALSE`, is the pre-v13 behaviour.
- Nothing is reported to the session: no event, no change to the active rendering mode, to
`xrLocateViews`, or to the hardware state.

**Platforms.** The desktop-Linux weave engine implements it (`comp_multi_weave_linux.c`,
`weave_run_frame`; the IPC submit carries it as `ipc_arg_weave_submit::mono_in_2d`). Windows,
macOS and Android accept and ignore it — the output stays woven — as a pre-v13 runtime does with
any unknown chained struct. One `weave(v13)` WARN in the service log per edge.

## 6. Version history

| Version | Change |
Expand All @@ -792,6 +853,11 @@ verbatim and the serial strictly increases.
| 10 | Desktop-Linux dma-buf transport (§5d, #1699): `XR_WEAVE_HANDLE_KIND_DMABUF_DXR` / `_OPAQUE_FD_DXR`, `XrWeaveDmabufDescDXR` + `XrWeaveOverlayDmabufDescDXR` in, `XrWeaveOutputDmabufDXR` out, `sync_file` fences `XrWeaveSubmitSyncDXR` (acquire) / `XrWeaveOutputSyncDXR` (release, per frame). Desktop Linux becomes a full weave platform when the service carries its engine. |
| 11 | `xrWeaveSnapWindowGridDXR` + `XrWeaveSnapGridInfoDXR` / `XrWeaveSnapGridPointDXR` — bulk grid snap: the per-point snap evaluated over a grid by the runtime, one call (one IPC round trip) instead of one per point (§5c, #1723). |
| 12 | `XrWeaveOutputOriginDXR` (out, per frame: the origin each woven output was woven for + a serial) and `XrWeaveWindowLogicalOriginDXR` (in: the caller's logical origin + scale, echoed verbatim) — so a present-owner can tag its own Wayland commit for move-synchronised drag (§5e, browser-pvt#180). |
| 13 | `XrWeaveSubmitMonoIn2DDXR` — one flat view (stereo: the left view; N > 2: the view nearest the display axis) instead of the weave while the session's hardware state is 2D (§5f). Desktop Linux; accepted and ignored elsewhere. |

**v13 is a bump**: one structure type a caller gates on `extensionVersion >= 13`. Against an
older runtime the struct is skipped and the output stays woven, which is also what every
non-Linux v13 runtime does — so the gate decides only whether the caller can expect the flat view.

**v12 is a bump**: two structure types a caller must be able to test for. An older runtime skips
both chains — the logical origin is simply not latched, and the OUT struct keeps whatever the
Expand Down
2 changes: 1 addition & 1 deletion src/external/openxr_includes/openxr/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ Rules:
| 1004999210–219 | `XR_DXR_display_info` (v16+ additions) | 210 = `XR_TYPE_DISPLAY_DESKTOP_POSITION_DXR` (#715); fresh decade rather than reusing the 005/009 gaps in the original block. 211 = `XR_TYPE_DISPLAY_DESKTOP_INFO_DXR` (spec v18, #1301) — full panel desktop rect + GDI device name, superseding 210. 212 = `XR_VIEW_CONFIGURATION_TYPE_PRIMARY_MULTIVIEW_DXR` (spec v19, #1486) — an `XrViewConfigurationType`, not an `XrStructureType`: the block is shared across every extended core enum. 213 = `XR_TYPE_VIEW_ACTIVITY_STATE_DXR` (spec v21, ADR-041) — per-frame `activeViewCount` chained on `XrViewState`, the other half of the fixed-view-count submission contract |
| 1004999220–229 | `XR_DXR_android_surface_binding` | 220 = `XR_TYPE_ANDROID_WINDOW_GEOMETRY_DXR` (#1037), 221 = `XR_TYPE_EVENT_DATA_ANDROID_WINDOW_LAYOUT_HINT_DXR` (spec v2, #1396). The extension's `XR_TYPE_ANDROID_SURFACE_BINDING_CREATE_INFO_DXR` keeps **1004999005** — one of the two unused gaps in the `XR_DXR_display_info` decade — because that value was already published in the (then unimplemented) sketch in `docs/specs/extensions/XR_DXR_display_info.md` §4 |
| 1004999230–239 | `XR_DXR_result_codes` | Not an extension — runtime-wide `XrResult` values returned from core entry points, where no extension can have been enabled. 230 = `XR_ERROR_RUNTIME_VERSION_SKEW_DXR` (browser#103), an **error**-class result, so it appears in the header negated as `-1004999230` |
| 1004999240–249 | `XR_DXR_weave` (v9+ additions) | 240 = `XR_TYPE_WEAVE_IPC_CONNECTION_DXR` (`xrWeaveExportIpcConnectionDXR`, spec v9, browser#103); 241 = `XR_TYPE_WEAVE_DMABUF_DESC_DXR`, 242 = `XR_TYPE_WEAVE_OVERLAY_DMABUF_DESC_DXR`, 243 = `XR_TYPE_WEAVE_OUTPUT_DMABUF_DXR`, 244 = `XR_TYPE_WEAVE_SUBMIT_SYNC_DXR`, 245 = `XR_TYPE_WEAVE_OUTPUT_SYNC_DXR` (desktop-Linux dma-buf transport + sync_file fences, spec v10, #1699); 246 = `XR_TYPE_WEAVE_SNAP_GRID_INFO_DXR` (`xrWeaveSnapWindowGridDXR`, bulk grid snap, spec v11, #1723); 247 = `XR_TYPE_WEAVE_OUTPUT_ORIGIN_DXR`, 248 = `XR_TYPE_WEAVE_WINDOW_LOGICAL_ORIGIN_DXR` (woven origin per output for present-owner move sync, spec v12, browser-pvt#180). A fresh decade because the original 190–199 block is exhausted (199 is reserved for the wish mask) |
| 1004999240–249 | `XR_DXR_weave` (v9+ additions) | 240 = `XR_TYPE_WEAVE_IPC_CONNECTION_DXR` (`xrWeaveExportIpcConnectionDXR`, spec v9, browser#103); 241 = `XR_TYPE_WEAVE_DMABUF_DESC_DXR`, 242 = `XR_TYPE_WEAVE_OVERLAY_DMABUF_DESC_DXR`, 243 = `XR_TYPE_WEAVE_OUTPUT_DMABUF_DXR`, 244 = `XR_TYPE_WEAVE_SUBMIT_SYNC_DXR`, 245 = `XR_TYPE_WEAVE_OUTPUT_SYNC_DXR` (desktop-Linux dma-buf transport + sync_file fences, spec v10, #1699); 246 = `XR_TYPE_WEAVE_SNAP_GRID_INFO_DXR` (`xrWeaveSnapWindowGridDXR`, bulk grid snap, spec v11, #1723); 247 = `XR_TYPE_WEAVE_OUTPUT_ORIGIN_DXR`, 248 = `XR_TYPE_WEAVE_WINDOW_LOGICAL_ORIGIN_DXR` (woven origin per output for present-owner move sync, spec v12, browser-pvt#180); 249 = `XR_TYPE_WEAVE_SUBMIT_MONO_IN_2D_DXR` (one flat view instead of the weave while the panel is in hardware 2D, spec v13). The decade is now full. A fresh decade because the original 190–199 block is exhausted (199 is reserved for the wish mask) |
| 1004999250–259 | `XR_DXR_wayland_surface_binding` | 250 = `XR_TYPE_WAYLAND_SURFACE_BINDING_CREATE_INFO_DXR` (#757), 251 = `XR_TYPE_WAYLAND_SURFACE_GEOMETRY_DXR` (spec v2) — the app-declared surface size + output refresh, because a `wl_surface` has no intrinsic size and the compositor geometry service cannot bootstrap one. **250 was renumbered from 1004999210**, which collided with `XR_TYPE_DISPLAY_DESKTOP_POSITION_DXR` — the 210–219 decade was already `XR_DXR_display_info`'s. Safe to move: SPEC_VERSION 1, no shipped app chains it |
| 1004999260–269 | `XR_DXR_depth_budget` | 260 = `XR_TYPE_REAR_DEPTH_BUDGET_DXR`, 261 = `XR_TYPE_CONTENT_BOUNDS_DXR`, 262 = `XR_TYPE_EVENT_DATA_REAR_DEPTH_BUDGET_STATE_CHANGED_DXR`, 263 = `XR_TYPE_CONTENT_MASK_DXR` (ADR-040). Was taken without a registry row — recorded retroactively in #1486 PR 2 |
| 1004999270–279 | `XR_DXR_lift` | 270 = `XR_TYPE_LIFT_PROPERTIES_DXR`, 271 = `XR_TYPE_LIFT_STREAM_CREATE_INFO_DXR`, 272 = `XR_TYPE_LIFT_FRAME_SUBMIT_INFO_DXR`, 273 = `XR_TYPE_LIFT_OPTIONS_DXR`, 274 = `XR_TYPE_LIFT_RESULT_DXR`, 275 = `XR_TYPE_WEAVE_SUBMIT_LIFT_RECTS_DXR`, 276 = `XR_TYPE_WEAVE_RECT_LIFT_DXR`, 277 = `XR_TYPE_LIFT_BLOB_DXR` (spec v1, ADR-042). 278 = `XR_OBJECT_TYPE_LIFT_STREAM_DXR` — an `XrObjectType`, not an `XrStructureType`. 279 = `XR_LIFT_NOT_READY_DXR` — a **success**-class `XrResult` (positive) |
Expand Down
53 changes: 52 additions & 1 deletion src/external/openxr_includes/openxr/XR_DXR_weave.h
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,11 @@
* back verbatim), so a present-owner can tag its own Wayland commit for
* move-synchronised drag.
*
* Mono in hardware 2D (SPEC_VERSION 13). XrWeaveSubmitMonoIn2DDXR, chained on
* XrWeaveSubmitInfoDXR: while the session's hardware state is 2D, the output is
* one view (stereo: the left view), flat, instead of the weave. Desktop Linux;
* ignored elsewhere.
*
* Woven origin per output (SPEC_VERSION 12, browser-pvt#180). On native Wayland
* (GNOME extension version 9+) a drag is MOVE-SYNCHRONISED: the extension holds
* the window's actor at the position the buffer it is about to paint was woven
Expand Down Expand Up @@ -264,7 +269,7 @@ extern "C" {
#endif

#define XR_DXR_weave 1
#define XR_DXR_weave_SPEC_VERSION 12
#define XR_DXR_weave_SPEC_VERSION 13
#define XR_DXR_WEAVE_EXTENSION_NAME "XR_DXR_weave"

// Reserved 1004999190..199. Final values reconcile with the Khronos registry
Expand Down Expand Up @@ -296,6 +301,9 @@ extern "C" {
// Spec v12 (browser-pvt#180): woven origin per output, for present-owner move sync.
#define XR_TYPE_WEAVE_OUTPUT_ORIGIN_DXR ((XrStructureType)1004999247)
#define XR_TYPE_WEAVE_WINDOW_LOGICAL_ORIGIN_DXR ((XrStructureType)1004999248)
// Spec v13: one flat view instead of the weave while the panel is in hardware 2D.
// Last value of the 240..249 decade.
#define XR_TYPE_WEAVE_SUBMIT_MONO_IN_2D_DXR ((XrStructureType)1004999249)

//! Upper bound on eye positions carried by XrWeaveSubmitInfoDXR (mirrors the
//! runtime's XRT_MAX_VIEWS). Phase 1: carried but unused.
Expand Down Expand Up @@ -950,6 +958,49 @@ typedef struct XrWeaveOutputOriginDXR {
uint64_t serial; //!< monotonic per woven output; 0 = not reported
} XrWeaveOutputOriginDXR;

/*!
* @brief Spec v13: present ONE flat view instead of the weave while the panel is
* in hardware 2D (chain on XrWeaveSubmitInfoDXR::next).
*
* xrRequestDisplayModeDXR is hardware-only (XR_DXR_display_info): a 2D request
* over woven content turns the lens off and keeps the weave running, so the
* panel shows every view interlaced through no lens — soft and doubled. Apps
* rely on that for transitions that fade parallax to zero. A present-owner that
* drops the lens for a reason of its OWN (a browser holding the panel 2D while
* one of its menus is open) wants the opposite: the content flat and sharp.
*
* With @c monoIn2D XR_TRUE, and while THIS session's hardware display state is
* 2D (the last state the runtime confirmed to it — the one its
* XrEventDataHardwareDisplayStateChangedDXR events report), the runtime skips
* the weave and paints the whole output with ONE view of the submitted content
* at full output resolution, in register with the window — the same view the
* off-panel 2D bands use (§5d), so the two flat paths match: for a stereo pair
* (every batch submit, and a 2-view v6 layout) view 0, the LEFT view — a batch
* rect's left half, unsqueezed; for a v6 layout of more than two views, the
* view whose eye is nearest the display axis (smallest |x| of the per-view eye
* offset). Flat regions and the v4 overlay are applied on top as usual. Mono
* follows the runtime's display-processor-CONFIRMED hardware-2D state for this
* session, not the request: it starts on the first submit after the 2D state
* is confirmed and ends on the first submit after the confirmed state is 3D
* again. While the hardware state is 3D the struct has no effect, so a
* caller can chain it for the whole of its own 2D hold without racing the
* lens: the output is flat exactly while the runtime holds the panel 2D, and a
* request the display processor rejected keeps the weave.
*
* Per submit, no latch: omitting the struct (or @c monoIn2D XR_FALSE) is the
* pre-v13 behaviour. Nothing is reported to the session — no rendering-mode
* event, no change to the active mode or to xrLocateViews.
*
* Honoured by the desktop-Linux weave engine. Every other platform of a v13
* runtime accepts and ignores it (the output stays woven), as a pre-v13 runtime
* does with any unknown chained struct.
*/
typedef struct XrWeaveSubmitMonoIn2DDXR {
XrStructureType type; //!< XR_TYPE_WEAVE_SUBMIT_MONO_IN_2D_DXR
const void* XR_MAY_ALIAS next;
XrBool32 monoIn2D; //!< XR_TRUE: one view flat, no weave, while hardware is 2D
} XrWeaveSubmitMonoIn2DDXR;

typedef XrResult (XRAPI_PTR *PFN_xrWeaveBindWindowDXR)(
XrSession session, void* windowHandle);

Expand Down
38 changes: 38 additions & 0 deletions src/xrt/auxiliary/util/u_weave_span2d.h
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,44 @@ u_wl_offpanel_bands_to_output(const struct u_wl_rect_px *bands,
return n;
}

/*!
* Which view of the atlas the weave engine paints FLAT — the single source of
* every flat path on the desktop-Linux weave engine: the off-panel bands
* (#1654), the flat regions of a non-batch submit (v8), and mono in hardware 2D
* (XR_DXR_weave v13), so all three are in register with each other.
*
* - 1 or 2 views: view 0. For a stereo pair that is the LEFT view (a batch
* rect's left half). Deterministic on purpose: both eyes sit about equally
* far from the display axis, so a nearest-axis rule would flip between the
* two as the head moves and the flat image would jump by the disparity.
* - N > 2 views with one eye offset per view (@p eye_x, metres, display-plane
* x, 0 = the display's centre axis): the view whose eye is NEAREST THE
* DISPLAY AXIS (smallest |x|; a tie goes to the lower index).
* - N > 2 without a matching offset per view: the centre index, (N - 1) / 2.
*
* @return a view index in [0, @p view_count), or 0 when @p view_count is 0.
*/
static inline uint32_t
u_weave_flat_view_index(uint32_t view_count, const float *eye_x, uint32_t eye_count)
{
if (view_count <= 2) {
return 0;
}
if (eye_x == NULL || eye_count != view_count) {
return (view_count - 1) / 2;
}
uint32_t best = 0;
float best_abs = eye_x[0] < 0.0f ? -eye_x[0] : eye_x[0];
for (uint32_t i = 1; i < view_count; i++) {
const float a = eye_x[i] < 0.0f ? -eye_x[i] : eye_x[i];
if (a < best_abs) {
best_abs = a;
best = i;
}
}
return best;
}

#ifdef __cplusplus
}
#endif
Loading
Loading