Skip to content

feat(d3d11): multi-screen segments under the weave-on-scanout split (M6 on hybrid boxes) - #1878

Merged
dfattal merged 3 commits into
mainfrom
feat/win-segments-under-split
Oct 9, 2026
Merged

dfattal merged 3 commits into
mainfrom
feat/win-segments-under-split

Conversation

@dfattal

@dfattal dfattal commented Oct 9, 2026

Copy link
Copy Markdown
Collaborator

What

Multi-screen M6 on Windows now works on the default in-process D3D11 path on hybrid boxes, i.e. under the #918 weave-on-scanout split (ADR-039). A window straddling two panels is woven per screen without DXR_D3D_FORCE_GPU=scanout / DXR_SPLIT_SAME_ADAPTER=0.

Design

  • One segment path, no third path. d3d11_dp_weave is the weave site on both sides of the split (the "split bypasses the windowed weave site" trap is a Vulkan-compositor thing, not D3D11). The !c->split_active gate is removed.
  • Segment DPs on the device that presents. The segment manager was already created on d3d11_out_device() and hands its DPs d3d11_out_context(); under the split that is the output (scanout) device, where the primary DP already lives. create_dp_d3d11_for_screen(inst, device, context, NULL, binding, ...) is simply handed the output device. No plug-in change: the Leia factory (leiasr_d3d11_create_for_screen) uses whatever device/context it is given, and the primary DP has always been made on the output device the same way (d3d11_make_dp(..., out_dev, out_ctx, true), which tells the plug-in nothing extra).
  • Atlas source = the egress slot. Under the split atlas_srv at the segment site is the egress slot being woven: the output-side copy of the composed atlas, already cropped to the box that slot was painted at. The per-segment crop, viewport + scissor, canvas = segment and set_present_origin are exactly the non-split ones.
  • Flat-2D fill moved to the output composite unit. It went through comp_d3d11_renderer_blit_rect, which records on the renderer's (app) context: wrong device under the split. It is now comp_d3d11_outcomp_blit_rect, the same HLSL / linear sampler / opaque blend / rasterizer / depth state re-made on the outcomp's device (compiled on first use). Off the split the outcomp's device and context are the app's, so the pixels are unchanged; the renderer's blit_rect had no other caller and is removed.
  • feat(d3d11): multi-screen M3 on Windows — per-segment views in the in-process D3D11 compositor #1863 per-segment views unchanged. The mosaic is painted by the renderer on the app device and crosses the bridge like any atlas.
  • R2 resize lag: a slot one frame behind the window is cropped at its own view size against the live canvas: the same one-frame lag the single-DP split weave already has. Unit-tested (the crop still partitions the slot's tile exactly).

What is byte-identical

  • A window on one screen under the split (the common case): comp_d3d11_segments_update returns false, and process_atlas runs with the same arguments as before. The only additions on that path are the segment manager's per-weave table update (no GPU work) and, once per manager on a split session that has ≥2 screens, one WARN line.
  • The non-split segment path: same crops, same DP calls; the flat-2D fill uses the same shaders and states on the same context.
  • The single-DP non-split path is untouched.

Not changed / out of scope

Zones / Local2D frames (non-whole-window canvas), zero-copy frames, shared-texture path, pinned sessions, service/hosted path, HWND-follows-majority.

Tests

  • tests_comp_segments: new case, a split egress slot one frame behind a resizing window (tile 800/824/840 against a 1649 px canvas, seam at an odd column) still partitions its tile with the seam where the live window puts it.
  • ctest -C Release: all segment / compositor tests pass. 3 tests_oxr_view_space_mode_* cases fail identically on unmodified main on the win box (environment: they need a >2-view DP and this box registers Leia SR).

Rig check (needs the two-panel win rig; not run here)

  1. Dev runtime from this branch, no DXR_D3D_FORCE_GPU / DXR_SPLIT_SAME_ADAPTER in the env. cube_handle_d3d11_win launched non-elevated (via explorer.exe <bat> so XR_RUNTIME_JSON is honoured).
  2. Place the client area across the seam at desktop x = 3840, e.g. client x 3016..4664 (1648 wide), with a per-monitor-v2 DPI aware SetWindowPos.
  3. In %LOCALAPPDATA%\DisplayXR\DisplayXR_cube_handle_d3d11_win.*.log expect, in order:
    • D3D11 output-device split ACTIVE: weave/repaint/present move to '<Intel ...>' (or ADR-039 same-adapter ENGAGE)
    • D3D11 display processor created via factory on the SCANOUT device
    • segments: under the #918 output-device split — segment DPs are created on the OUTPUT device (LUID=..., device 0x...) (new)
    • segments: created a segment DP for screen 0x... ('\.\DISPLAY...', plug-in 'leia-sr') via create_dp_d3d11_for_screen
    • the plug-in's per-DP lines canvas=(0,0 824x...) and canvas=(824,0 824x...)
    • per-segment views: segments=2 ... -> total=4 (oxr_session)
    • absent: any segments: could not create a DP, D3D11 debug-layer device-mismatch errors, #918 R1 refusals beyond the first frame.
  4. By eye: each half weaves and looks around from its own viewer, as with the knobs on 2026-10-08. Drag back to the laptop panel only: segments: retiring the segment DP ... after ~0.5 s and the frame is the plain single-DP split weave.

🤖 Generated with Claude Code

dfattal and others added 3 commits October 9, 2026 00:16
…omposite unit

The flat-2D fill of a segment no DP can weave went through
comp_d3d11_renderer_blit_rect, which records on the renderer's (app)
context. The destination is the weave target, which under the #918
weave-on-scanout split lives on the output device the renderer knows
nothing about, so the fill could not run there.

Move the blit to comp_d3d11_outcomp (comp_d3d11_outcomp_blit_rect), the
unit that already owns the output-side composite on the device the
target lives on. Same HLSL (the renderer's projection VS/PS), same linear
sampler desc, same opaque blend / rasterizer / depth state, so the pixels
are unchanged off the split, where the outcomp's device and context ARE
the app's. Its shaders are compiled on first use, so a session that never
blits pays nothing. The renderer's blit_rect had no other caller and is
removed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…plit (multi-screen M6)

The segment path bailed whenever the #918 output-device split was
engaged, and ADR-039 makes the split the default on every hybrid box
(and keeps it on one adapter), so the shipping configuration never wove
a window per screen without DXR_D3D_FORCE_GPU=scanout +
DXR_SPLIT_SAME_ADAPTER=0.

Nothing in the path actually needed the gate: d3d11_dp_weave is the
weave site on both sides of the split, the segment manager is already
created on d3d11_out_device() and hands its DPs d3d11_out_context() (the
plug-in's create_dp_d3d11_for_screen is given the output device), and
atlas_srv there is the egress slot being woven, the output-side copy of
the composed atlas cropped to the box that slot was painted at. With the
flat-2D fill on the output composite unit (previous commit) the gate is
removed: one segment path, per-segment crop / viewport / scissor /
present origin unchanged, #1863 routing unchanged (the mosaic is painted
on the app device and crosses the bridge like any atlas).

A window on one screen is still the single-DP split weave, byte for
byte: comp_d3d11_segments_update returns false and process_atlas runs
with the arguments it always had. A split session logs one WARN when its
segment manager is made, naming the output device it lives on (the rig
check's proof).

Test: a split egress slot one frame behind a resizing window (its own
tile size, the live canvas) still partitions its tile exactly, the seam
where the live window puts it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…dmap M6, ADR-047 Amendment 1)

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.

1 participant