Repository navigation
Conversation
15167e5 to
b9925bc
Compare
|
Follow-ups from the first SR-hardware run (pushed; R2 rebased onto this):
Verified: macOS build, |
…04999300-310, catalog note (ADR-043) Type values sit at 1004999300-310 (not 290-300 as first drafted): on the rebase onto post-lift main, 290 had been taken by XR_TYPE_WEAVE_SUBMIT_OVERLAY_UNCHANGED_DXR (weave spec v14). Safe to move: spec v1, nothing shipped chains these. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ator, NV12/BGRA/GRAY8 conversion, persistent id, disparity probe Pure, OS-free building blocks shared by the service camera manager, the sim_display fake and displayxr-cli camera (ADR-043). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…_STEREO_CAMERA) + sim_display FAKE stereo camera Six optional slots appended to xrt_plugin_iface per ADR-020 (struct_size-gated, no ABI bump): enumerate, get_calibration (OpenCV R,T of the ACTIVE device), open (= tracker keep-alive), wait_frame, release_frame, close. They sit after a one-pointer placeholder for ADR-042's create_dp_d3d11_lift, which claims the offset right after the vk fingerprint on feat/lift-ext but is not on main yet: the camera slots are therefore at their final offsets whichever branch merges first, and tests_stereo_camera asserts the adjacency (against the real lift slot once it exists). sim_display fills the slots; SIM_DISPLAY_FAKE_STEREO_CAMERA=1 advertises one 640x480-per-eye 30 Hz GRAY8 camera rendering a random-dot pair with a 12 px background and a 40 px bar (68 deg HFOV, 50 mm, 2.0/0.6 m) plus a frame counter; _SIZE / _FPS / _FORMAT / _SUSPEND_PERIOD_MS knobs. xrt_instance gains the stereo_camera client aspect (IPC instance only) and get_active_plugin (native instance) for the service's camera manager. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…mera_* calls) ipc_server_stereo_camera: the service opens each plug-in camera once, on the first stream start, and closes it 2 s after the last stop. One runtime-owned thread per open camera pulls wait_frame and fans each frame out to every started stream: per stream a 3-slot latest-wins pinned ring in shared memory (u_stereo_camera_ring), format conversion, decimation, and a per-stream wake handle (auto-reset event on Windows, non-blocking pipe on POSIX). States WAITING / AVAILABLE / SUSPENDED / UNAVAILABLE follow the plug-in; a suspension clears every pin. Streams are owned by the connection and die with it. Privacy hooks, deny by default: start and calibration reads are refused (XRT_ERROR_NOT_AUTHORIZED) unless DXR_STEREO_CAMERA_DEV_ALLOW=1 is set in the SERVICE environment (R3 replaces it with OS + DisplayXR consent); DXR_STEREO_CAMERA=0 is the kill switch (zero cameras); RAW is refused to PRESENT_OWNER clients (the browser); the foreground rule and in-use indicator are marked hook points. Windows hands out a FILE_MAP_READ section duplicate and a SYNCHRONIZE-only event duplicate. The D3D11 / AHardwareBuffer transports are designed in the file comment (same ring, texture slots + fence / sync_file). Client side: ipc_client_stereo_camera (connection-level calls) and the xrt_stereo_camera_client aspect the IPC instance now carries. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…n its open time Re-anchoring the pacing clock after a pause also moved the suspend phase, so every SUSPENDED half-period stretched (36 frames in 5 s instead of ~75 at a 1 s period). Separate origins; now 92 frames in 6 s, 3 suspensions. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A real DIAG consumer over IPC: probe creates + starts a stream (through the service's authorisation), maps the ring READ-ONLY, waits on the per-stream wake handle, acquires, and reports the measured delivery rate, frame-index gaps, the SBS layout, block-matched disparities of the last frame and the service's stream stats; --out writes the last frame as a PNG. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ce-level, service-only) Nine entry points over the instance's xrt_stereo_camera_client aspect. An in-process instance enumerates zero (maintainer decision: service clients only in R1). Consent refusal maps to XR_ERROR_PERMISSION_INSUFFICIENT (retryable), a dead pipe to XR_ERROR_INSTANCE_LOST; capture times are converted to XrTime; handles returned through XrStereoCameraStreamTransportDXR are the caller's (closed here when nothing is chained to receive them). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Enumerates the active plug-in's cameras through the iface and validates each description (even non-zero eye extent, known format, positive rate, CALIBRATED backed by a real calibration). Absence never fails. sim_display: 'PASS: stereo_camera_caps — 0 cameras (OK)'; with the fake: '1 camera(s); [0] "Sim 3D camera (fake)" 640x480/eye @ 30.0 Hz, flags 0x1f'. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…stant expressions for MSVC/GCC) Caught by the MinGW syntax check: the XR flag bits are static const XrFlags64, which only clang accepts in _Static_assert. Pin the xrt mirrors to the header's literal values at compile time and cross-check the real constants once at runtime. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…oid facts - spec: status R1, the DXR_STEREO_CAMERA_DEV_ALLOW dev gate, privacy hook status, slot placement behind the lift placeholder, the OpenCV R,T plug-in convention and the wait_frame result enum, the fake as built, CLI output. - roadmap: §C Android rewritten from the NP02J/K68 measurements (logical cameras 4/5, two raw 1280x720 NV12 streams, LeiaCameraBuilder is not CNSDK, opening the front pair evicts the tracker for good), design consequence: the runtime owns the pair and runs in-app tracking from the same capture; §E R1 as built + merge order; §F Android risk + CNSDK asks; §G the four R1 maintainer decisions. - ADR-043: R1 status, Android context/consequence updated. - xrt_plugin_iface reference: the stereo_camera_* slot contract. - env census: DXR_STEREO_CAMERA, DXR_STEREO_CAMERA_DEV_ALLOW. - ADR index regenerated (it was stale on the docs branch). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…SVC C5287)
MSVC /W4 raised C5287 ("operands are different enum types") on the
XrStereoCamera*DXR vs xrt_stereo_camera_* _Static_asserts, even through the
(int) casts. Pin each side against the header's literal instead: two
enum-vs-literal checks carry the same proof with no cross-enum operator.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ver a guess Field test (Leia SR tracking camera): the descriptor said "up to 30.0 Hz" while the source ran 58-64 Hz — the plug-in's enumerate-time rate was a hard-coded placeholder. The service now measures every source over the first 60 frames after each open (u_stereo_camera_rate_meter; a >0.5 s gap restarts the window) and the descriptor, the decimator and the stream info use that; until a window completes they use the plug-in's value, where 0 now means "unknown" (xrt_plugin doc, XR header doc, spec). One WARN on the first measurement, INFO after. - selftest: max_frame_rate 0 is legal; negative / NaN / > 1000 is malformed. - camera list prints "rate unknown (measured once a stream runs)"; probe prints "cap = source rate" instead of "cap 0.0 Hz". - sim fake: SIM_DISPLAY_FAKE_STEREO_CAMERA_ADVERTISED_FPS (0 = unknown) to exercise the measured path; tests_stereo_camera covers the meter (jitter, suspension gap, restart keeps the last value). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…, not a fixed 64 px
On the Leia SR tracking camera (baseline 120 mm, f ~513 px) a face at 0.6 m is
~95 px of disparity, so every block of the fixed 0..64 px search clamped at 64
and the probe reported "d = 64". The bound is now f*B/0.4 m from the camera's
own baseline + HFOV (quarter eye width when uncalibrated; clamped to
[64, 0.6*eye]), and a block whose best match sits ON the bound is counted and
excluded ("AT THE SEARCH BOUND") instead of silently reported as the bound.
sim fake: SIM_DISPLAY_FAKE_STEREO_CAMERA_BASELINE_MM (default 50). At 120 mm
the probe reads 28 px (background) / 95 px (bar at 0.6 m), search 0..143.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…slots after platform-state, values 300-310 Mechanical follow-through of the rebase onto main (lift v2.22.0 + ADR-045): - docs no longer describe the reserved_adr042_create_dp_d3d11_lift placeholder; the camera slots append after create_dp_d3d11_lift AND get_platform_state. - type values quoted as 1004999300-310 everywhere (290 is weave v14's). - cli CMake: one stb include for lift + camera, not two. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
8a8c30f to
cd2927a
Compare
Rebased onto post-lift
|
| branch | old head | new head | base |
|---|---|---|---|
feat/stereo-camera-r1 |
8a8c30f1a |
cd2927aae |
origin/main @ 1f64f78bf (v2.25.3) |
feat/stereo-camera-r2 |
f433d31fc |
0191f5481 |
new R1 — f433d31fc (PRESENT_OWNER owner quota) survives as 0191f5481 |
leia feat/stereo-camera-l1 |
aa3253d |
e9fe9f7 |
plug-in main @ 48afe3f (v2.9.0+2) |
What moved where
xrt_plugin_iface (ADR-020 append-only). reserved_adr042_create_dp_d3d11_lift is deleted. Main had appended ADR-045's get_platform_state after lift's factory, so the camera block goes after both — create_dp_d3d11_lift is no longer the last pre-camera member:
| member | index | offset |
|---|---|---|
vk_bundle_fn_table_offset |
18 | 132 |
create_dp_d3d11_lift (ADR-042) |
19 | 136 |
get_platform_state (ADR-045) |
20 | 144 |
stereo_camera_enumerate |
21 | 152 |
stereo_camera_get_calibration |
22 | 160 |
stereo_camera_open |
23 | 168 |
stereo_camera_wait_frame |
24 | 176 |
stereo_camera_release_frame |
25 | 184 |
stereo_camera_close |
26 | 192 |
sizeof(struct xrt_plugin_iface) |
200 |
tests_stereo_camera's ABI case now pins the whole chain (lift right after the vk fingerprint → platform-state right after lift → camera block right after platform-state and ending the struct) with lift REAL, no #ifdef fallback. No DP vtable slot is touched (D3D11 lift's BASE+25..29 untouched).
Type values relocated 1004999290–300 → 1004999300–310. Not a lift collision — 290 was taken on main by XR_TYPE_WEAVE_SUBMIT_OVERLAY_UNCHANGED_DXR (weave spec v14). Safe: spec v1, draft PR, nothing shipped chains them. Registry (openxr/README.md) rows 300–309 / 310–319, next free 320+. gen_extensions_index.py --check and gen_adr_index.py --check pass.
Other resolutions. CLI_SELFTEST_BAD_STEREO_CAMERA = exit 12 (11 is lift's). Append-at-end conflicts in xrt_openxr_includes.h, ipc_server.h, per-client teardown, oxr_api_funcs.h / oxr_api_negotiate.c / oxr_api_verify.h / oxr_defines.h / oxr_extension_support.h / oxr_objects.h, cli CMake/main/common, tests/CMakeLists.txt, env census (100 → 102 names), sim_display_plugin.c iface init — all lift-first, camera-second. proto.json's stereo_camera_* keys already sat after instance_declare_peer (after lift's); the generated IPC files are build-time, nothing checked in.
L1 (leia). Rebased over leia_platform_state / lift-NeurD / compose-colour-test additions (keep-both in leia_plugin.c iface, CMakeLists, build-linux.{sh,yml}, CLAUDE.md). The camera slots stay #ifdef XRT_PLUGIN_IFACE_HAS_STEREO_CAMERA; the committed pins (v2.16.9 Win / v2.18.0 Linux) are untouched, so a stock build is byte-identical. Build it against the rebased R2 head via DXR_RUNTIME_SOURCE_DIR (PR #273 step 0).
Verified on macOS (R2 worktree, Debug hybrid build)
tests_stereo_camera,tests_stereo_rectify,tests_stereo_vrefine,tests_ipc_proto,tests_lift_mailbox: all pass.displayxr-cli camera probe --rectifiedagainst the DISTORTED sim fake (fake +DXR_STEREO_CAMERA_DEV_ALLOW=1in the service env,XRT_PLUGIN_SEARCH_PATHat the fresh dylib): 116 frames at 30.00 Hz, disparity modes 11.30 px → 2.005 m / 37.81 px → 0.599 m, signed-dy median +0.001 px, refinement ALIGNED, PASS — rows aligned.camera calib 1 --rectifiedprints P1/P2.displayxr-cli selftestin-process with the fake:PASS: stereo_camera_caps — 1 camera(s) … flags 0x17, exit 0.build-mingw-check.sh aux_util drv_sim_display: PASSED.- L1:
test_stereo_camera_parsepasses natively (-std=c99 -pedantic); MinGW-fsyntax-onlyofleia_plugin.cagainst R2 headers (slots filled) andmainheaders (slots out) shows only the two pre-existingleiasr_geometry_*implicit-declaration errors thatorigin/main's file has too.
Windows box, please re-run
MSVC build of R2 (dev-setup.bat --leia with the plug-in's L1 checkout next to it), tests_stereo_camera (the ABI case with lift real), displayxr-cli selftest, then the #273 on-hardware recipe (camera probe --rectified on the SR tracker, exit-code check: a malformed camera is now 12). Also confirm the owner-quota fix still admits the browser's sibling processes.
|
Landed on |
Phase R1 of the stereo camera source (ADR-043,
XR_DXR_stereo_camera, roadmap). No hardware is needed: the display's stereo camera becomes a plug-in-provided source that the service owns and clients consume, end to end against asim_displayfake.Merge order (important)
Rebased onto post-lift
main(2026-10-03). Lift (ADR-042, #1781) shipped in v2.22.0 and ADR-045 (get_platform_state) followed, so the placeholder scheme below is history. Order now: #1746 (R1) → #1747 (R2) → DisplayXR/displayxr-leia-plugin#273 (L1) → B1 (browser).What the rebase moved (details in the rebase comment):
xrt_plugin_iface: thereserved_adr042_create_dp_d3d11_liftplaceholder is gone; the six camera slots are appended AFTERcreate_dp_d3d11_lift(member 19, offset 136) andget_platform_state(member 20, offset 144) — members 21–26, offsets 152–192,sizeof200.tests_stereo_camerapins that order (lift right after the vk fingerprint, platform-state right after lift, camera block right after platform-state and ending the struct).1004999290–300→1004999300–310:290had meanwhile been taken byXR_TYPE_WEAVE_SUBMIT_OVERLAY_UNCHANGED_DXR(weave spec v14). Safe: spec v1, nothing shipped chains them. Registry rows added; next free is320+.displayxr-cli selftest:CLI_SELFTEST_BAD_STEREO_CAMERAis exit 12 (11 is lift'sBAD_LIFT_CAPS).xrt_openxr_includes.h,ipc_server.h, per-client teardown,oxr_*, cli,tests/CMakeLists.txt, env census count 100 → 102) resolved lift-first, camera-second.proto.json'sstereo_camera_*calls already sat afterinstance_declare_peer, i.e. after lift's.BASE+25..29are untouched); the camera lives on the iface only.Original (pre-rebase) merge-order note, for the record
15ff2dfe2is its commit).feat/lift-ext, ADR-042) before this one. Lift claims thexrt_plugin_ifaceslot right aftervk_bundle_fn_table_offset. This PR holds that offset with a same-size placeholder (reserved_adr042_create_dp_d3d11_lift), so the camera slots already sit at their final offsets and either merge order is ABI-safe.tests_stereo_cameraasserts that the camera block starts right after the lift slot, or after its placeholder while lift is absent. The rebase onto lift is mechanical:xrt_plugin.h: delete the placeholder; lift'screate_dp_d3d11_lifttakes its place.openxr/README.mdregistry: drop the reserved270–289row in favour of lift's two rows.xrt_openxr_includes.h,ipc_server.h(ipc_client_statetail),ipc_server_per_client_thread.cteardown,oxr_api_negotiate.c/oxr_api_funcs.h/oxr_objects.h/oxr_api_verify.h/oxr_defines.h,tests/CMakeLists.txt.lint(dead link) is red until lift lands: ADR-043 from docs(stereo-camera): plug-in-provided stereo camera source (ADR-043, XR_DXR_stereo_camera design) #1743 linksADR-042-…md, which only exists on the lift branch.What's in it
openxr/XR_DXR_stereo_camera.h(spec v1, type values1004999300–310) and itsindex.jsoncatalog note.gen_extensions_index.py --checkpasses.XRT_PLUGIN_IFACE_HAS_STEREO_CAMERA):enumerate,get_calibration(OpenCVR,Tof the active device),open(which doubles as the tracker keep-alive),wait_frame,release_frame,close.sim_displayfake, turned on bySIM_DISPLAY_FAKE_STEREO_CAMERA=1in the service environment. It renders a random-dot pair: background at 12 px disparity, a bar at 40 px (68° HFOV, 50 mm, 2.0 / 0.6 m), and a frame counter in both halves. Knobs:_SIZE,_FPS,_FORMAT,_SUSPEND_PERIOD_MS.ipc/server/ipc_server_stereo_camera.c, platform-neutral):FILE_MAP_READ) duplicate.stereo_camera_*calls). Streams are owned by the connection. The client-sidexrt_stereo_camera_clientaspect is carried by the IPCxrt_instance.displayxr-cli camera list | calib | probe, and astereo_camera_capscheck inselftest.XR_ERROR_PERMISSION_INSUFFICIENTunless the service hasDXR_STEREO_CAMERA_DEV_ALLOW=1.DXR_STEREO_CAMERA=0is a kill switch.PRESENT_OWNERclients (the browser).xrt_plugin_ifacereference and the env-var census.Test evidence (macOS, Debug, hybrid build)
tests_stereo_camera: 9 cases, 1616 assertions pass. They cover:tests_ipc_protopasses.camera probeagainst the fake:Three concurrent probes, fanned out from one open:
--rawcapped at 10 Hz → 10.00 HzSuspend square wave (1 s): 3 suspensions, 92 frames in 6 s, and the stream resumes after each one.
Killing the service mid-stream: it exits cleanly and the source closes.
Without
DEV_ALLOW, calib and start return-39(NOT_AUTHORIZED). WithDXR_STEREO_CAMERA=0, zero cameras are listed.OpenXR level: a scratch app dlopens the runtime with
XRT_FORCE_MODE=ipcand runs enumerate → calibration → create → start → transport → 60 × acquire at 30.00 Hz ofcaptureTime. The consent refusal comes back asXR_ERROR_PERMISSION_INSUFFICIENT, and in-process gets 0 cameras.selftest:PASS: stereo_camera_caps — 1 camera(s); … flags 0x1fwith the fake, and0 cameras (OK)without it.MinGW-w64:
build-mingw-check.sh aux_util drv_sim_displaypasses. A-fsyntax-onlypass over the Windows branches of the server, client, CLI and oxr files is clean. That pass also caught_Static_assertonstatic const XrFlags64, which is fixed.Deviations from the design
wait_framereturnsenum xrt_plugin_stereo_camera_waitinstead ofxrt_result_tvalues, so no new result codes were needed.x_R = R·x_L + T; the service converts them to the XRrightFromLeftpose.NATIVELY_RECTIFIED. The distorted variant arrives with R2's rectifier. A calibrated-but-not-rectified source delivers RAW with the frame flagged as such.persistentIdis not keyed yet (R3).Not tested
Windows MSVC, Linux and Android builds and runs (CI covers the builds). Any real hardware. The GPU transports (not implemented). The malformed-description path of
stereo_camera_caps(the fake cannot produce one).🤖 Generated with Claude Code