Repository navigation
feat(display_info): per-segment views and view display bindings (multi-screen M3) - #1854
Merged
Merged
Conversation
dfattal
force-pushed
the
feat/multi-screen-m2-segments
branch
from
October 7, 2026 21:57
d137c45 to
1be22f0
Compare
…multi-screen M3) XR_DXR_display_info v22 (append-only, same version): XrViewDisplayBindingsDXR (1004999217, the reserved value) chained on XrViewState at xrLocateViews -- which views belong to which display, with the segment rect in window px. xrt_display_metrics.h gains XRT_MAX_SEGMENTS (2), xrt_segment_metrics (the compositor's segment table for the view math) and xrt_segment_view_routing (which views of a frame go to which segment). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
- PRIMARY_MULTIVIEW_DXR reports device max x XRT_MAX_SEGMENTS views (capped at XRT_MAX_VIEWS); a one-segment window locates exactly as before and aliases the longer tail. The #1499 mode floor asks one segment's share. - xrLocateViews: when the compositor weaves the window per segment, each segment gets the active mode's views, located from that display's eyes (DP eyes when tracking, else its nominal viewer) with the segment as the Kooima canvas, contiguous, in one frame (the majority display's), seam- continuous. activeViewCount is the sum; XrViewDisplayBindingsDXR filled. - PRIMARY_STEREO keeps 2 views, framed from the majority display. - Camera rigs, zone-scoped locates, >2 segments keep one view set. - The routing rides to the compositor at xrEndFrame. - oxr_segment_views.{h,c}: the pure geometry, unit-tested. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… plug-in DPs (multi-screen M3) - The weave publishes its segment table (geometry, each screen's size and nominal viewer, woven or flat) for xrLocateViews; eyes are predicted per query, segment DPs guarded against the weave. - The renderer builds a mosaic atlas: segment k's local view j lands at the segment's rect inside tile j (blit and compose paths), using the same comp_segments_tile_rect mapping the M2 crop reads with, so each segment DP's input is its own views. Routed frames never zero-copy. - Mixed vendors (closes the M4 runtime gap): a segment DP is created from ITS screen's registry entry (owning iface/instance, ABI-checked factory), whichever plug-in won that screen; one INFO line for a mixed table. - Every segment DP follows the session's 2D/3D mode (request_display_mode). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ay bindings (multi-screen M3) Begins PRIMARY_MULTIVIEW_DXR when offered (DXR_STEREO_FIXED_APP=1 keeps stereo), renders every active view, chains XrViewActivityStateDXR + XrViewDisplayBindingsDXR, renders each display's views into its segment's part of every tile, and logs the bindings once per change. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…tes (multi-screen M3) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…dors (multi-screen M3) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…MAX_SEGMENTS (multi-screen M3) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…n be split (multi-screen M3) PRIMARY_MULTIVIEW_DXR now reports device max x view-set capacity, where the capacity is min(XRT_MAX_SEGMENTS, DP-registry screens with a VK DP factory), computed once in oxr_system_fill_in -- and 1 wherever no compositor segments windows (every platform but desktop Linux, any service session). A single- screen box, Windows, macOS and Android keep exactly the pre-M3 count, so existing multiview apps and the CTS see no change; ds1-linux gets device max x 2. The locate never hands out more view sets than the capacity; the aliasing rule is unchanged. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…gment views (multi-screen M3) Under per-segment views each view covers only its segment's part of a tile, so a submission narrower than the tile is the layout; the lifecycle WARN now names the segment count instead of claiming the compose pass upscales it. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… (multi-screen M4, #793 phase 3) DXR_SCREEN_PLUGIN=<match>=<plugin-id>[,...], <match> = RandR output, DRM connector (case-insensitive) or monitor id hex. For that monitor only a pin outranks the PreferredPlugin override (#791) and the active-plug-in rule (#1521), provided the pinned plug-in claims it; everything else resolves as before. So XRT_PREFERRED_PLUGIN_ID=sim-display DXR_SCREEN_PLUGIN=HDMI-1=leia-sr keeps the DS1 woven by leia-sr. One INFO per applied pin; WARN for a pin whose plug-in has no claim or no DP factory there, an unknown monitor, a malformed entry. The per-monitor winner rule is now target_screen_pick() (pin > preferred > active > confidence, ties to the lower ProbeOrder) in target_screen_pin.{h,c}, unit-tested with the parser by tests_target_screen_pin. Documented in plugin-discovery.md section 3.4. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…every active view (multi-screen M3 review 1) A segment accepted its DP's eyes only while tracking, unlike the single-screen path, so an untracked DP (sim_display) was replaced by the registry nominal viewer and the primary panel's views jumped when the window crossed the seam. And the nominal branch always produced 2 eyes, so a 4-view mode left views 2..3 with zero FOVs (the save/restore then wrote them into every segment). Now: any valid DP eye set counts (oxr_segment_views_accept_eyes), and the nominal branch fills one eye per active view (the pair repeated; 2 views = exactly the old pair). Quad-mode test added. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… (multi-screen M3 review 2) Every non-splitting locate used to zero the routing, so an app that located its projection per segment and then did a zone-scoped or camera-rig locate in the same frame lost the routing: segment 0's subimage stretched over every tile. The routing is now a per-frame record: a splitting locate writes it, other locates leave it, and xrEndFrame takes it (and logs a change) and resets it for the next frame. Test added. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…t's camera (multi-screen M3 review 3) Under per-segment views a mosaic tile holds every segment's view j, but a quad or equirect2 was drawn once per tile with projection view j's camera — segment 0's frustum across the whole tile, so a world-locked quad (a HUD) landed mis-scaled and mis-placed on every segment. Routed frames now draw each such layer once per routed view (first[k]+j) with that view's camera, viewport and scissor confined to the segment's rect in tile j; visibility and the painter's tile state use the local view index. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
m2v was V / h_seg per segment, so vertically stacked screens magnified each half ~2x and side-by-side panels of different pitch got different m2v (a gap or overlap at the seam). A rig's virtual display height now sizes the whole window: each segment takes V * h_seg / union_h (oxr_segment_views_vdh_scale), so m2v = V / union_h for every segment. Tests for both layouts. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…t (multi-screen M3 review 5+7) The #1502 VIEW-space offset averaged the whole reported array, so the longer aliased multiview tail dragged VIEW toward view 0 even when nothing was split; during a split it was measured on the majority segment's temporary array only. Now it is the centroid of the active views — (L+R)/2 unsplit, and over every segment's active views when split (published once by the per-segment wrapper, and for a stereo session framed from a non-primary majority screen). The centroid test checks the active views. view-configuration-model.md documents that DP-backed screens include sim's FALLBACK claim on plain monitors (intended on multi-monitor desktop Linux) and the VIEW rule. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…set (multi-screen M3 review 6) The header said bindingCountOutput is 1 for a window on one display; the spec and the runtime report 0 whenever the views are one set for the whole window. Fixed the header text (it ships to the public mirror). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…s the pre-M3 count (multi-screen M3 review 8) is_multiview_count accepted the device max OR x2 on every platform. The test binary now runs with DXR_SEGMENTS=0 and asserts exactly the device max everywhere; the segmentation kill switch now also keeps PRIMARY_MULTIVIEW_DXR at the pre-M3 count (no window can be split). Deriving the Linux x2 from the box is not exact (a screen counts only when its plug-in's VK factory passed the vk_bundle ABI check, invisible to xrEnumerateDisplaysDXR); the x2 arithmetic stays pinned by tests_oxr_segment_views. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…-screen M3 review, minor) When the mode changed between the locate and the commit, a segment's view count no longer equalled the tile count and the whole frame went unrouted (segment 0's views stretched over the window). Route min(located, tiles) views per segment instead. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ively (multi-screen M3 review, minor) Like the monitor names a pin is matched against; PreferredPlugin keeps its exact match. Test added. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… mixed-vendor layouts (multi-screen M4 eyeball) Mode A on ds1-linux (Leia primary, sim on eDP): the eDP half looked flat 2D. The sim segment DP reported eyes from the process-wide sim view count, which only the sim head device sets -- absent when leia-sr is active -- so it stayed 1: one eye, duplicated into both views by the #615 coherence guard, an anaglyph of two identical images. - sim_display: a screen-bound (segment) DP reports one eye per view of the atlas it last wove (2 before the first); the session DP is unchanged. - oxr: a segment uses its DP's eyes only when they cover its views (oxr_segment_views_segment_eyes); an under-reporting DP falls back to that screen's nominal viewer, which fills every view. - Logging: every segment of one locate now logs on the same throttled frame ('Window-relative Kooima [segment 0x…]', 'Segment 0x… eyes from its DP' / '…nominal viewer'); before, the per-call counter only ever let the majority segment log, which hid the other segment's (correct) pitch. - The segment mode log says when a DP has no 2D/3D switch instead of '-> 0'. Test: two screens of different pitch, segment 1 uses its own pitch and its own eye pair; an under-reporting DP is not used. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
dfattal
force-pushed
the
feat/multi-screen-m3-per-segment-views
branch
from
October 7, 2026 22:18
9c11926 to
e7b8464
Compare
This was referenced Oct 7, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What / why
Multi-screen M3: per-segment views (ADR-047 decision D3). A window that spans two screens is already woven per segment by M2, but every segment wove a crop of ONE view pair, so the off-primary half had the wrong perspective. Now, under
PRIMARY_MULTIVIEW_DXR, each segment gets its own views: located from that screen's eyes, with that segment (relative to its own screen, in its own metres) as the Kooima canvas, and routed to that segment's DP.Also closes the M4 runtime gap: M2's one-vendor rule is lifted. A segment DP is created from its screen's registry entry (owning iface/instance + ABI-checked VK factory), whichever plug-in won that screen; one INFO line when a mixed-vendor table is built. Every segment DP follows the session's 2D/3D mode (
request_display_mode) and getsset_present_originper frame.DXR_SEGMENTS=0remains the kill switch.The view-count contract
PRIMARY_MULTIVIEW_DXRreports device max × view-set capacity (capped atXRT_MAX_VIEWS= 8). Capacity =min(XRT_MAX_SEGMENTS (2), DP-registry screens with a VK DP factory), computed once inoxr_system_fill_in, and 1 wherever no compositor can segment a window (every platform but desktop Linux, any service session). So a single-screen box, Windows, macOS and Android keep exactly the pre-M3 count (no change for existing multiview apps or the CTS); ds1-linux gets device max × 2. Per-view recommended sizes stay the worst-case envelope.[0,n)left segment,[n,2n)right (n= active mode count; mode is session-wide).activeViewCount= Σ. NewXrViewDisplayBindingsDXR(1004999217, chained onXrViewState, two-call) names display / segment rect / view range.XR_DXR_display_infostays v22 (append).oxr_segment_views_per_segment_capacity) — same verdict as pre-M3.PRIMARY_STEREOstays exactly 2.The legacy-stereo rule
A
PRIMARY_STEREO(or MONO) session never splits its views: its 2 views are framed from the segment holding most of the window (that screen's eyes, the whole window relative to it — identical to today's path when the majority is the primary screen), and the compositor crops them per segment as shipped in M2.Routing / atlas choice
One atlas, per-segment tile ranges, as a mosaic: atlas layout unchanged (
cols × rowstiles ofcanvas × scale), but local viewjof segmentkis drawn at segmentk's rect inside tilej(blit + compose paths). The rect comes fromcomp_segments_tile_rect— the exact mapping M2's crop reads with — so cropping each segment out of every tile yields that segment's own views and the M2 split path runs unchanged. Routed frames never zero-copy. The routing travels oxr → compositor atxrEndFrame(what the app's last locate handed out).Keeps one view set (M2 crop): STEREO/MONO, camera-rig and zone-scoped locates, >2 segments, unknown screen size. IPC/service: in-process only; the service never segments, so service sessions see one view set and 0 bindings (documented).
Tests
tests_oxr_segment_views(view-count contract, per-segment capacity, majority selection, seam-continuous placement across different pitches, segment window metrics, two screens of different size/nominal viewer → two different frusta, checked analytically through the shared Kooima core, stereo whole-window framing, binding ranges, tail alias).tests_comp_segments: shared tile-rect mapping (seam column shared).tests_oxr_view_space*: MULTIVIEW count updated to the new contract; remaining failures are the pre-existing ones (identical set on the M2 base: :953 centroid,device_max > 2, mode-floor cases), plus pre-existingtests_rig_composer../scripts/build_linux.sh --hybrid --apps+ selftest green;check_displayxr_app.py test_apps/cube_handle_vk_linuxclean;gen_extensions_index.py --check,check_doc_paths.pygreen.cube_handle_vk_linuxnow begins MULTIVIEW (DXR_STEREO_FIXED_APP=1keeps stereo), renders every active view, chainsXrViewActivityStateDXR+XrViewDisplayBindingsDXR, renders each display's views into its segment of each tile, logs bindings once per change.Gate (hub runs it — nothing was put on the panel)
Expect: app log
Views: activeViewCount=4; display 0x… views 0..1 segment 0,0 …; display 0x… views 2..3 segment …,0 …; runtime INFOper-segment views: 2 segment(s)andsegments: per-segment views routed — 2 segment(s) x 2 view(s). Eachseg<i>.pngholds that segment's OWN two views from its own frustum (the cube continues across the seam, with each half's perspective from its own screen's nominal viewer), not a crop of a shared pair.Per-screen plug-in pin —
DXR_SCREEN_PLUGIN(M4, #793 phase 3)DXR_SCREEN_PLUGIN=<match>=<plugin-id>[,…](<match>= RandR outputHDMI-1, DRM connectorHDMI-A-1, or monitor id hex) pins one monitor to a plug-in. For that monitor only it outranks PreferredPlugin (#791) and the active-plug-in rule (#1521) as long as the pinned plug-in claims it; all other monitors resolve as before. The winner rule is now one pure function (target_screen_pick: pin > preferred > active > confidence) intarget_screen_pin.{h,c}, unit-tested with the parser (tests_target_screen_pin). One INFO per applied pin; WARN for no claim / no DP factory / unknown monitor / malformed entry. Documented indocs/specs/runtime/plugin-discovery.md§3.4.M4 gate mode B:
XRT_PREFERRED_PLUGIN_ID=sim-display DXR_SCREEN_PLUGIN=HDMI-1=leia-sr+ the M3 gate command.Gate result — PASSED on ds1-linux
Run by the hub with the command above (sim-display exclusive, anaglyph, X11, window 1600x900+2656+400, straddling eDP-1 / HDMI-1). Log:
Captures:
seg0.png= segment 0's own two views (400x450 each), the cube cut at its right edge;seg1.png= segment 1's own two views, the cube continuing from its left edge — each from its own frustum, not a crop of one shared pair.Follow-up from the run: the pre-existing VIEW_DIMS WARN claimed the 400x450 per-view submission was "upscaled"; under the mosaic it now reports the segment count instead.
M4 gate result — PASSED on ds1-linux (mixed vendor, both modes)
Run by the hub on ds1-linux (eDP-1 laptop panel + Acer DS1 on HDMI-1), with a Debug dev Leia plug-in matching the runtime build (vk_bundle ABI, #1243).
Mode A — leia-sr active: mixed-vendor screen table built; the eDP segment DP created from sim-display via
create_dp_vk_for_screen; the DS1 woven by the Leia main DP;activeViewCount=4, per-segment views routed,seg0/seg1captures written.Mode B —
XRT_PREFERRED_PLUGIN_ID=sim-display DXR_SCREEN_PLUGIN=HDMI-1=leia-sr:Leia windowless weaver created, lens enabled,
activeViewCount=4, per-segment views routed,seg0/seg1captures written.Human-verified — mode A eyeball on
9c119262e(David at the DS1): PASSED. "M4 fix works, I see anaglyph now laptop side and woven 3D DS1 side." The first eyeball (on6554af85b) showed the laptop half flat: the sim segment DP reported one eye (sim's process-wide view count is only set by the sim head device, absent with leia-sr active), fixed in9c119262e. Log lines from the passing run:Each segment uses its own pitch (eDP 0.300 m / 3456 px, DS1 0.344 m / 3840 px) and its own eyes (sim's nominal pair on the eDP, the DS1's live tracked eyes). Capture: the eDP segment's two views now differ (mean |v0 − v1| ≈ 1.3 per channel, 0.0 before the fix).
Part of #69 (M3). Refs #1849 #1853.
🤖 Generated with Claude Code