Skip to content

IPC clients can capture their frame: MCP capture_frame, per-exe trigger, service + shell triggers (#1810) - #1811

Merged
dfattal merged 1 commit into
mainfrom
fix/1810-ipc-capture
Oct 3, 2026
Merged

dfattal merged 1 commit into
mainfrom
fix/1810-ipc-capture

Conversation

@dfattal

@dfattal dfattal commented Oct 3, 2026

Copy link
Copy Markdown
Collaborator

Fixes #1810: an IPC client (forced IPC, or an app under the shell) had no working capture route. All four routes now work; hardware-verified on the win box, 2026-10-03.

Root causes and fixes

  1. App MCP capture_frame reported "not wired".
    • Cause: the IPC client compositor never registered a capture handler.
    • Fix: it now installs one (Windows). It requests this client's own per-client atlas from the service over the existing workspace_capture_frame IPC call, which xrCaptureAtlasDXR's IPC branch already used. The service gains comp_d3d11_service_capture_frame_for_client; the old entry point forwards with xc=NULL, so existing callers are unchanged.
  2. The per-exe trigger displayxr_atlas_trigger.<exe> was never consumed.
    • Fix: the IPC client now polls it after each commit (the same u_capture_intent semantics as in-process) and writes the same output path the in-process compositors use.
  3. The service workspace_screenshot_trigger never fired for a client-present (ADR-029 client-texture) presenter.
    • Cause: pipeline_default_policy_render returns early for that presenter kind, and the client's weave runs in pipeline_client_texture_weave, which had no trigger check.
    • Fix: the check was added there, at most 4 times a second, before render_mutex, consuming the trigger only after a woven frame. No new waits.
  4. The shell MCP capture_frame returned ok:false.
    • Cause: the shell sends flags=0 ("runtime picks"), which capture_frame rejected.
    • Fix: flags==0 now defaults to the full atlas, and a failed capture logs a WARN.
  • Also: a request-only IPC_CAPTURE_FLAG_RAW_ALPHA bit, so DXR_ATLAS_CAPTURE_RAW_ALPHA=1 in the client env works over IPC. The wire structs are unchanged.

In-process capture paths are untouched.

Verified (dev service from this branch, cube_zones_d3d11_win, XRT_FORCE_MODE=ipc DISPLAYXR_ZONES=1 DISPLAYXR_MCP=1)

route result
per-exe trigger displayxr_atlas.cube_zones_d3d11_win.png written (144 KB)
app MCP capture_frame {"path": "...displayxr-mcp-capture-<pid>-<frame>.png", "size_bytes": 145155}
service workspace_screenshot_trigger workspace_screenshot.png + workspace_screenshot_atlas_2_2x1.png
shell MCP capture_frame (installed shell v2.3.2 on this dev service) ok:true, displayxr_capture_atlas_2_2x1.png

Used straight away: these captures numerically verified #1809's service pure-zones path. The UNORM-strip leg reads exactly one encode above the _SRGB leg in the per-client atlas: greys 60→133, 210→234, red (38,8,8)→(108,50,50).

Not covered

🤖 Generated with Claude Code

An IPC client (forced IPC or shell-launched) had no working capture route.

- Per-app MCP capture_frame + %TEMP%\displayxr_atlas_trigger[.<exe>][.projection]:
  the IPC client compositor now installs the same mcp_capture hand-off the
  in-process compositors do and polls u_capture_intent after each layer
  commit. A request asks the SERVICE for this client's per-client atlas
  (pre-weave, DP-input crop, encoded bytes as stored) over the existing
  workspace_capture_frame call, then moves the PNG to the exact path the
  in-process compositors write. Windows-only (only the D3D11 service answers).
- Service: an APP caller now captures its OWN compositor's atlas
  (comp_d3d11_service_capture_frame_for_client) instead of whichever client
  rendered last; CONTROLLER/DIAG and the debug triggers are unchanged.
- IPC_CAPTURE_FLAG_RAW_ALPHA carries the client's DXR_ATLAS_CAPTURE_RAW_ALPHA
  to the service (which reads its own env). Request-only modifier bit; the
  wire struct and proto.json are unchanged; an old service ignores it.
  xrCaptureAtlasDXR's IPC branch sends it too.
- workspace_screenshot_trigger is now consumed for an ADR-029 CLIENT_TEXTURE
  presenter (pipeline_client_texture_weave): the render thread's poll sits
  past the PRESENTER_SELF/CLIENT_TEXTURE early return, so it was dead there.
  Stat before render_mutex at 4 Hz; dump only after a frame was woven.
- workspace_capture_frame with flags == 0 (what the shell's MCP capture_frame
  sends) was rejected before any log; the stage now defaults to
  IPC_CAPTURE_FLAG_ALL, and a failed capture logs a WARN.

In-process capture paths are untouched.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@dfattal
dfattal requested a review from a team as a code owner October 3, 2026 18:17
@dfattal
dfattal merged commit d26b80b into main Oct 3, 2026
40 checks passed
@dfattal
dfattal deleted the fix/1810-ipc-capture branch October 3, 2026 18:51
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.

IPC clients cannot capture their frame: MCP capture_frame 'not wired', per-exe atlas trigger and service screenshot trigger not serviced

1 participant