Skip to content

fix(multi/linux): route a present-owner's hardware 2D/3D request to the weave engine's DP (#1774) - #1775

Merged
dfattal merged 2 commits into
mainfrom
fix/linux-weave-display-mode
Oct 2, 2026
Merged

dfattal merged 2 commits into
mainfrom
fix/linux-weave-display-mode

Conversation

@dfattal

@dfattal dfattal commented Oct 1, 2026 •

Copy link
Copy Markdown
Collaborator

Fixes #1774. On desktop Linux, switching the DisplayXR browser from an inline-3D tab to a 2D tab left the panel's lens on.

Call chains

Windows (reference): browser tab policy → xrRequestDisplayModeDXR → oxr_session_request_display_mode (IPC branch) → ipc_handle_compositor_request_display_mode → compositor_request_display_mode (comp_d3d11_service.cpp) → service_apply_pending_mode (applied immediately on the IPC thread, #815) → apply_mode_transition → the DP's request_display_mode → broadcast_hardware_state (only after the DP accepts, #961).

Linux before this PR: … → ipc_handle_compositor_request_display_mode → multi_compositor_request_display_mode, which returns false when !mc->session_render.initialized. A present-owner has no window, so that condition is always true for it.

Drop point: the weave engine's DP (mc->weave.dp, created in weave_ensure_engine) is never called, and no HARDWARE_DISPLAY_STATE_CHANGE event is sent anywhere on the Linux service path.

Change

  • comp_multi_compositor.c: on desktop Linux with the weave service, a client that has no per-session render is routed to the weave engine.
  • comp_multi_weave_linux.c: comp_multi_weave_linux_request_display_mode applies the request immediately, under weave.mutex, the same lock order every submit uses.
    • Before the engine exists, the request is recorded and applied when the engine starts. A later 3D request withdraws a recorded 2D, so start-up applies nothing stale.
    • Event: sent once the DP accepts a change, carrying the requested state (service: panel lease — a single mode/DP owner; non-owners request or are denied with a reason; vetoed requests are dropped, not latched; mode event only after the DP confirms (#761) #961). A DP with no request_display_mode slot counts as accepted, the same rule as dp_request_display_mode_confirmed. A request for the state the panel is already in sends no event.
    • Read-back is logged, not used: the vendor lens can switch asynchronously, so get_hardware_3d_state right after the request can still show the old state.
    • DP call is timed: it runs on the IPC thread under the mutex, so a slow vendor service would stall this client's submits.
    • One WARN per transition, for example: weave(#1699): hardware 3D -> 2D (xrRequestDisplayModeDXR) on the weave engine's display processor (request_display_mode accepted) in N ms[ (slow)]; DP readback: 2D (may lag — the lens switches asynchronously). (slow) is added above 50 ms.
  • sim_display Vulkan DP: SIM_DISPLAY_FAKE_LENS=1 turns on a fake lens that records each request, so a headless run can see where the request lands. It is off by default, so default behaviour does not change.
  • weave_present_vk_linux --headless N --test-display-mode[=stale|deferred]: sends batches of requests and requires the matching events, in order, with each batch's events present before the next batch.
    • stale (default): 2D then 3D before the first submit (no event at start-up), then 2D, a fast 3D, 2D, 3D within one frame, a repeated 3D (must send no duplicate event), and 2D. Expected events: 2D, 3D, 2D, 3D, 2D.
    • deferred: 2D before the first submit (applied at start-up), then 3D, then 2D. Expected events: 2D, 3D, 2D.

Windows, macOS and Android are unchanged: the new branch only compiles under COMP_MULTI_HAVE_WEAVE && XRT_OS_LINUX_DESKTOP.

Verification (headless, sim_display)

The dev service ran in a private XDG_RUNTIME_DIR with DXR_PLUGIN_EXCLUSIVE=sim-display (log shows active plug-in: id=sim-display), and the client used the same env.

[weave_present] frame 0: xrRequestDisplayModeDXR(2D) -> 0
[weave_present] frame 0: xrRequestDisplayModeDXR(3D) -> 0
[weave_present] frame 20: xrRequestDisplayModeDXR(2D) -> 0
[weave_present] event: hardware display state -> 2D (frame 21)
[weave_present] frame 30: xrRequestDisplayModeDXR(3D) / (2D) / (3D) -> 0
[weave_present] event: hardware display state -> 3D (frame 31)
[weave_present] event: hardware display state -> 2D (frame 31)
[weave_present] event: hardware display state -> 3D (frame 31)
[weave_present] frame 40: xrRequestDisplayModeDXR(3D) -> 0
[weave_present] frame 50: xrRequestDisplayModeDXR(2D) -> 0
[weave_present] event: hardware display state -> 2D (frame 51)
[weave_present]   display-mode: events 2D,3D,2D,3D,2D (no stale 2D, triple, no-op) OK
[weave_present] PASS
service:
 WARN weave(#1699): hardware 2D requested before the weave engine exists — recorded, applied when the engine comes up
 WARN weave(#1699): hardware 3D requested before the weave engine exists — the recorded 2D request is withdrawn, nothing to apply at bring-up
 WARN sim_display: fake lens 3D -> 2D (request_display_mode on DP 0x…)
 WARN weave(#1699): hardware 3D -> 2D (xrRequestDisplayModeDXR) … in 0.0 ms; DP readback: 2D (may lag — the lens switches asynchronously)
 … (one fake-lens + one weave WARN per transition, 5 in total)
  • stale with SIM_DISPLAY_FAKE_LENS=1: PASS.
  • deferred with SIM_DISPLAY_FAKE_LENS=1: PASS.
  • stale with no fake lens (the DP has no mode slot, so it counts as accepted): PASS.
  • The plain --expect-anaglyph self-check passes, and fd counts stay flat.
  • ctest: 73/75 pass. The two failures (tests_rig_composer, tests_oxr_view_space) also fail on an unmodified origin/main build on this box.

Not yet verified on hardware. sim_display only shows the request reaches the right DP and the events follow. A run on the Leia panel still has to confirm the lens actually drops on a 2D tab and comes back on an inline-3D tab. On the switch, the service journal should show the plug-in's lens OFF (request_display_mode(2D)) line and the new weave(#1699): hardware 3D -> 2D … in N ms WARN. The browser log should show display-mode event: panel hardware is now 2D.

Known scope limit: the event goes only to the session that asked. The Linux service has no arbiter for which session controls the panel, and the Leia lens state is shared across the whole service process, so with two present-owners the last request wins.

🤖 Generated with Claude Code

@dfattal
dfattal requested a review from a team as a code owner October 1, 2026 04:54
dfattal and others added 2 commits October 1, 2026 00:10
…he weave engine's DP (#1774)

On the desktop-Linux service an XR_DXR_weave present-owner (the DisplayXR
browser) has no window, so session_render is never initialised and
multi_compositor_request_display_mode returned false at its first gate. Every
xrRequestDisplayModeDXR from the browser's tab policy (browser#55) was dropped
silently, and no hardware-state event was ever sent. The weave engine's own DP
(mc->weave.dp), the only DP in the service and the one holding the vendor
lens, never heard about it, so the panel stayed lensed over a 2D tab.

- comp_multi_compositor.c: under COMP_MULTI_HAVE_WEAVE && XRT_OS_LINUX_DESKTOP,
  a client without per-session render goes to the weave engine.
- comp_multi_weave_linux.c: comp_multi_weave_linux_request_display_mode applies
  the request inline on the IPC thread under weave.mutex, as the D3D11
  service's #815 does: a present-owner asking for 2D has stopped submitting,
  so a frame-gated apply would never run. Before the engine exists the wish is
  recorded and applied at engine bring-up. The hardware-state event is sent to
  the session only once the DP confirmed a change (#961; a DP with no slot is
  mode-neutral and counts as accepted). There is one WARN per transition, with
  the DP's get_hardware_3d_state read-back.
- sim_display (Vulkan DP): SIM_DISPLAY_FAKE_LENS=1 fills request_display_mode
  and get_hardware_3d_state with a recording fake lens (one WARN per
  transition). It is off by default, so default behaviour is unchanged.
- weave_present_vk_linux --test-display-mode (headless) requests 2D before
  the first submit, then 3D, then 2D, and requires exactly the events 2D, 3D,
  2D, each one before the next request.

Windows, macOS and Android are untouched: the new branch is compiled only on
desktop Linux with the weave service.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…cover stale/fast switches (#1774)

Review refinements on top of the present-owner display-mode routing:

- The hardware-state event carries the REQUESTED state once the DP accepted
  it (#961), never a read-back. A vendor lens may switch asynchronously (Leia
  SR flips it via LENS_ON/OFF events), so an immediate get_hardware_3d_state
  can still show the old state. The read-back is now logged as information
  only: "DP readback: 3D (may lag — the lens switches asynchronously)".
- The DP call runs on the IPC thread under weave.mutex, so it is timed with
  os_monotonic_get_ns and the duration goes into the per-transition (and
  rejection) WARN, "in N ms", with "(slow)" past 50 ms. No behaviour change.
- A 3D request before the engine exists withdraws a recorded 2D (one WARN),
  so bring-up applies nothing stale.
- weave_present_vk_linux --test-display-mode is now table-driven:
  - stale (default): 2D,3D before the first submit (no event at bring-up),
    then 2D, the fast triple 3D,2D,3D in one frame, a no-op 3D (no
    duplicate event) and 2D. It expects the events 2D,3D,2D,3D,2D in order.
  - =deferred: the original pre-submit 2D applied at bring-up.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@dfattal
dfattal force-pushed the fix/linux-weave-display-mode branch from 950bfae to 981ea6e Compare October 1, 2026 07:10
@dfattal

dfattal commented Oct 2, 2026

Copy link
Copy Markdown
Collaborator Author

Hardware check passed (pre-merge, 2026-10-02) on a Leia 3D display, GNOME 50 Wayland: this branch's hybrid build as the service + a locally built Leia Linux plug-in (real srSDK backend) + the released browser v1.0.10 pointed at it. Switching from an inline-3D tab to a 2D tab turned the lens off, and back on when returning — confirmed by eye. Service log: the plug-in's lens OFF (request_display_mode(2D)) — DisplayXR now owns this SR context's lens preference, then weave(#1699): hardware 3D -> 2D … accepted in 10.3 ms; DP readback: 3D (may lag …) and 2D -> 3D … in 10.2 ms; DP readback: 2D (may lag …) — the readback does lag, so sending the event from the accepted request is the right rule. The browser received display-mode event: panel hardware is now 2D / 3D; no weave failures. Fast back-to-back switching was not specifically stressed in this run.

@dfattal
dfattal merged commit bcc2f36 into main Oct 2, 2026
40 checks passed
@dfattal
dfattal deleted the fix/linux-weave-display-mode branch October 2, 2026 07:08
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.

Linux service: xrRequestDisplayModeDXR from a weave present-owner never reaches the weave engine's DP (panel stays 3D on a 2D browser tab)

1 participant