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
13 changes: 13 additions & 0 deletions docs/architecture/comp-segments.md
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,19 @@ buffer, in order). What differs on macOS:
- Test knob: `cube_handle_metal_macos` honours `DXR_TEST_WINDOW_RECT=x,y,w,h` (content rect, top-down
points) and, once at frame `DXR_TEST_WINDOW_MOVE_FRAME` (default 300), `DXR_TEST_WINDOW_RECT2`.

## Windows / D3D12 (M6)

Status: the in-process D3D12 compositor (the Unity display provider's path) carries the same
model in `d3d12/comp_d3d12_segments.{h,cpp}`, behaving like the pre-split D3D11 path: two more
appended slots, no ABI bump (`xrt_plugin_iface::create_dp_d3d12_for_screen`,
`xrt_display_processor_d3d12::set_present_origin`), every segment recorded onto the one weave
command list (crop per screen, each crop with its own SRV descriptor, the DP sets viewport +
scissor from the canvas itself), the same non-segmented cases (plus the #1264 reroute and a back
buffer that does not match the client area), M3 per-segment views through
`comp_d3d12_compositor_get_segment_metrics` / `set_view_routing` and a mosaic in the D3D12
renderer, and `oxr_system` counting a D3D11 **or** D3D12 factory toward the view-set capacity.
sim_display implements both D3D12 slots. Not yet run on hardware.

## Per-segment views (M3)

Under `PRIMARY_MULTIVIEW_DXR` each segment gets its **own** views instead of a crop of
Expand Down
33 changes: 31 additions & 2 deletions docs/reference/xrt_plugin_iface.md
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,35 @@ Appended after `get_display_info_for_monitor` per ADR-020 (`struct_size`-gated,
belong to the ACTIVE plug-in; mixed vendors are M4. Reference implementation:
`sim_display_dp_factory_vk_for_screen` (`drivers/sim_display/sim_display_processor.c`).

### `create_dp_d3d11_for_screen` / `create_dp_d3d12_for_screen` (multi-screen M6, Windows)

```c
xrt_result_t (*create_dp_d3d11_for_screen)(struct xrt_plugin_instance *inst, void *d3d11_device,
void *d3d11_context, void *window_handle,
const struct xrt_screen_binding *binding,
struct xrt_display_processor_d3d11 **out_xdp);
xrt_result_t (*create_dp_d3d12_for_screen)(struct xrt_plugin_instance *inst, void *d3d12_device,
void *d3d12_command_queue, void *window_handle,
const struct xrt_screen_binding *binding,
struct xrt_display_processor_d3d12 **out_xdp);
```

The Windows twins of `create_dp_vk_for_screen`, for the in-process D3D11 and D3D12 compositors.
Same `binding` rules; `window_handle` is the session's real HWND only for the DP that keeps the
vendor's drag phase-snap and NULL for every other screen's DP, whose phase comes from the
appended per-API `set_present_origin` slot (`xrt_display_processor_d3d11::set_present_origin`,
`xrt_display_processor_d3d12::set_present_origin`, both `struct_size`-gated, announced by
`XRT_DP_D3D11_HAS_PRESENT_ORIGIN` / `XRT_DP_D3D12_HAS_PRESENT_ORIGIN`), called before every weave,
plus the canvas offset. `process_atlas` gets `canvas = the segment` and a pre-cropped atlas
holding exactly that segment's views; confine viewport AND scissor to the canvas and do not
assume you are the first writer. On D3D12 every segment records onto ONE command list, which
carries no viewport/scissor a DP could inherit — the DP sets both itself from the canvas; the
atlas arrives as a plain 2D texture in `COMMON` with its own SRV descriptor. Appended after
`create_dp_vk_for_screen` (D3D11) and `create_dp_metal_for_screen` (D3D12) per ADR-020 (ABI stays
5); guard with `#ifdef XRT_PLUGIN_IFACE_HAS_CREATE_DP_D3D11_FOR_SCREEN` /
`XRT_PLUGIN_IFACE_HAS_CREATE_DP_D3D12_FOR_SCREEN`. Reference implementations:
`sim_display_dp_factory_d3d11_for_screen` / `sim_display_dp_factory_d3d12_for_screen`.

**The 1:1 capability.** Report through the existing `get_scanout_caps` slot whether your output
survives a display-server resample: `XRT_DP_SCANOUT_FLAG_TOLERATES_RESAMPLE` in
`xrt_dp_scanout_caps::flags` (carved from the former `reserved[0]`, so an older plug-in reads
Expand Down Expand Up @@ -839,8 +868,8 @@ Flags (`XRT_PLUGIN_STEREO_CAMERA_*`) mirror the XR bits: `SHARED_WITH_EYE_TRACKI
everything that was on `main` before them: `… vk_bundle_fn_table_offset` → `create_dp_d3d11_lift`
(ADR-042) → `get_platform_state` (ADR-045) → `stereo_camera_enumerate … stereo_camera_close`
→ `get_display_info_for_monitor` (multi-screen M1) → `create_dp_vk_for_screen` (multi-screen M2)
→ `create_dp_d3d11_for_screen` (M6, Windows) → `create_dp_metal_for_screen` (macOS, now the last
member).
→ `create_dp_d3d11_for_screen` (M6, D3D11) → `create_dp_metal_for_screen` (macOS) →
`create_dp_d3d12_for_screen` (M6, D3D12, now the last member).
`tests_stereo_camera` pins that order (lift right after the vk fingerprint, platform-state right
after lift, the camera block right after platform-state and ending the struct), so a reorder or a
slot squeezed in between fails on the host, not on a vendor box.
Expand Down
2 changes: 1 addition & 1 deletion docs/roadmap/multi-screen.md
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ multiview + display_info v22. An app may also pin itself to one display
| **M3** Per-segment views | Same window: `xrLocateViews` returns 4 views, each half rendered from its own frustum | oxr: per-segment Kooima + eyes from the segment DP; `XrViewDisplayBindingsDXR`; `activeViewCount`; tile routing by segment; legacy stereo = majority segment + 2D blit | none | cube renders the correct perspective on each half; `check_displayxr_app.py` rule for v22 |
| **M4 = T1** Mixed vendor on `ds1-linux` | **Leia weave + DS1 tracking on the DS1 half, sim anaglyph on the laptop half** | loader: two plug-ins resident, DPs created from each screen's factory; plug-in display info per screen | leia-linux: `window = 0` always (one line, `leia_sr_linux_sdk.c:1470`); per-DP display state instead of `g_ctx.display` / statics; `srLensDisable` right after `srCreateLens` if the lens must start off. Confirmed by the SR side: a windowless Linux Vulkan weaver fed the DS1 segment + present origin weaves exactly that viewport, votes the lens on once, then the DP owns it. Works on today's SDK for **one** Leia panel. With SR-P0 (verified on the DS1 2026-10-07, branch `ST-5481-weaver-external-routing`): check `SrWeaverRoutingCapabilities` (tag 24) chained on `SrRuntimeCapabilities.pNext`, then chain `SrWeaverRoutingInfo{mode=SR_WEAVER_ROUTING_EXTERNAL}` (tag 22) on `SrWeaverCreateInfoVulkan.pNext`, `window = 0`, present origin per frame, lens via `srLensEnable/Disable` | David eyeballs it; screenshot both halves |
| **M5 = T2** Two Leia panels | Each panel tracked by its own camera | per-screen tracker plumbing already in place from M3 | **SR-P1** enumeration + identity, **SR-P2** bind display/weaver/lens/tracker by `displayId`, **SR-P3** N trackers + lens per display (SRService refactor, ST-3160 camera pairing). Plug-in: per-DP SR display + tracker, claims from `srEnumerateDisplays` | needs a second panel on a dev box |
| **M6 = T3** Service / hosted / Windows | Shell and hosted apps on N screens; Windows parity | ADR-035 amendment: one presenter per 3D screen in the service, clients segmented across presenters; hosted window per screen (#715 fix folds in); Windows: HWND to majority segment DP, windowless secondaries | SR-P0 **already ships on q3**, so no SDK gate: plug-in Windows DP chains `SrWeaverRoutingInfo{EXTERNAL, KEEP_DRAG_SNAP}` on the HWND DP and `{EXTERNAL}` + NULL HWND on secondaries, forwards `set_present_origin` → `srWeaverSetPresentOrigin` in **device px relative to that SR display, rotation applied** (a DPI-virtualised origin weaves on the wrong lattice and looks plausible — see `docs/reference/dpi-awareness.md`). **In-process D3D11 half: runtime `comp_d3d11_segments` + two appended slots (`create_dp_d3d11_for_screen`, D3D11 `set_present_origin`, no ABI bump) + sim_display D3D11; plug-in: screen-bound D3D11 DP chains EXTERNAL (+KEEP_DRAG_SNAP with the HWND) + `SrDisplayBindingInfo`, per-frame `srWeaverSetPresentOrigin` when windowless, scissor = canvas, and pins `srWeaverSetSimulatedViewer` on a non-active display (lens left to the active panel until SR D3). the HWND follows the majority screen (ADR-047 Amendment 2: 20 % margin held 0.5 s, windowless replacement first, rollback on failure; rig-unverified); M3 per-segment views ported too (D3D11 mosaic routing, `get_segment_metrics` / `set_view_routing`), so a straddling window's second panel renders from its own viewer; works under the #918 weave-on-scanout split (ADR-039, the hybrid-box default — segment DPs on the output device, fed from the egress slot; ADR-047 Amendment 1), so no `DXR_D3D_FORCE_GPU` / `DXR_SPLIT_SAME_ADAPTER` knobs are needed. Service half: a direct (non-workspace) IPC client's window is segmented by the service's direct pipeline, with per-segment views over IPC (ADR-047 Amendment 3); compose/shell mode and hosted windows stay single-screen.** | shell with a window straddling two panels |
| **M6 = T3** Service / hosted / Windows | Shell and hosted apps on N screens; Windows parity | ADR-035 amendment: one presenter per 3D screen in the service, clients segmented across presenters; hosted window per screen (#715 fix folds in); Windows: HWND to majority segment DP, windowless secondaries | SR-P0 **already ships on q3**, so no SDK gate: plug-in Windows DP chains `SrWeaverRoutingInfo{EXTERNAL, KEEP_DRAG_SNAP}` on the HWND DP and `{EXTERNAL}` + NULL HWND on secondaries, forwards `set_present_origin` → `srWeaverSetPresentOrigin` in **device px relative to that SR display, rotation applied** (a DPI-virtualised origin weaves on the wrong lattice and looks plausible — see `docs/reference/dpi-awareness.md`). **In-process D3D11 half: runtime `comp_d3d11_segments` + two appended slots (`create_dp_d3d11_for_screen`, D3D11 `set_present_origin`, no ABI bump) + sim_display D3D11; plug-in: screen-bound D3D11 DP chains EXTERNAL (+KEEP_DRAG_SNAP with the HWND) + `SrDisplayBindingInfo`, per-frame `srWeaverSetPresentOrigin` when windowless, scissor = canvas, and pins `srWeaverSetSimulatedViewer` on a non-active display (lens left to the active panel until SR D3). the HWND follows the majority screen (ADR-047 Amendment 2: 20 % margin held 0.5 s, windowless replacement first, rollback on failure; rig-unverified); M3 per-segment views ported too (D3D11 mosaic routing, `get_segment_metrics` / `set_view_routing`), so a straddling window's second panel renders from its own viewer; works under the #918 weave-on-scanout split (ADR-039, the hybrid-box default — segment DPs on the output device, fed from the egress slot; ADR-047 Amendment 1), so no `DXR_D3D_FORCE_GPU` / `DXR_SPLIT_SAME_ADAPTER` knobs are needed. In-process D3D12 (the Unity provider's path): the same, pre-split — `comp_d3d12_segments`, appended `create_dp_d3d12_for_screen` + D3D12 `set_present_origin`, sim_display D3D12, D3D12 mosaic routing; rig-checked with the split off; single-DP under the #918 split for now. Service half: a direct (non-workspace) IPC client's window is segmented by the service's direct pipeline, with per-segment views over IPC (ADR-047 Amendment 3); compose/shell mode and hosted windows stay single-screen.** | shell with a window straddling two panels |

Ordering rationale: M0–M3 are hardware-free beyond "two monitors" and exercise everything
with sim_display, so the model is proven before any SR dependency. M4 is David's stated
Expand Down
2 changes: 2 additions & 0 deletions src/xrt/compositor/d3d12/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ if(WIN32 AND XRT_HAVE_D3D12)
comp_d3d12_outcomp.h
comp_d3d12_renderer.cpp
comp_d3d12_renderer.h
comp_d3d12_segments.cpp
comp_d3d12_segments.h
comp_d3d12_swapchain.cpp
comp_d3d12_swapchain.h
comp_d3d12_target.cpp
Expand Down
Loading
Loading