feat(capture): host arm of the fence ring #538

Merged
enricobuehler merged 3 commits from worktree-wp7-host-fence-v3 into main 2026-09-02 09:52:41 +00:00
Owner

Immunity plan WP7, step 3 of 3 — stacked on #535 (driver arm) ← #525 (protocol layer). Retarget down the stack as they merge. Replaces #537 (same tree plus the fence DACL fix, as one gate-clean commit; no force-push by house rule). With this, the fence protocol is live end to end wherever both sides open the fences; every other pairing keeps the keyed-mutex arm.

Negotiation (no flag day)

  • Every ring generation carries two fresh shared ID3D11Fences (fresh, so both value sequences restart at zero on both sides; created with the SYSTEM-only DACL like every other frame object — design/idd-push-security.md item 10) in SetFrameChannelRequestV2, and a v4 header with the slot table. A pre-D3D11.4 device gets neither and stays on the mutex arm.
  • The first ring on a box is a keyed-mutex probe carrying fences with CAP_FENCE_RING clear. The open-time attach's capability word says whether the driver opened them — remembered process-wide (DRIVER_FENCE_CAPABLE) — and a capable driver's probe ring is rebuilt on the fence protocol before any frame flows (one extra recreate on the first session per process). A fence-mode attach that fails falls back to the mutex arm and remembers that.
  • Fence-mode textures are SHARED | NTHANDLE without a keyed mutex (HostSlot.mutex: Option); FrameChannelSender / send_frame_channel carry the v2 request (a pre-fence driver reads the v1 prefix).

Consume (D5)

Newest PUBLISHED slot above the last delivery from the slot table (fence::consumer_pick) — older publishes freed (newest-wins, the S2 finding), and any PUBLISHED record whose packed token names another generation freed too (a leftover of a superseded publisher; closes the race a ring rebuild opens). CAS PUBLISHED → READING, GPU Wait(ready, record.ready_value), the existing convert/blend path unchanged, Signal(retire, ++retire_value), publish the value in the record, then FREE (Release). A cursor regen re-reads the last slot only while it is FREE and retires it the same way. FenceSlotGuard puts a READING slot back to FREE on any early return.

Recreate

Fresh fences + slot table zeroed before the generation bump, CAP_FENCE_RING set/cleared per mode, v2 delivery.

Verification

  • Writing gate passes. pf-capture / pf-vdisplay / punktfunk-host cannot build on macOS; windows-host dispatch on this branch is the compile proof. The driver arm's windows-drivers / driver-build already passed (run 22731).
  • Live proof pending on the .173 box (both vendors, BGRA/FP16, chaos, process death); the S2 spike passed 10/10 there with the same shape, and WP4's attach log will show negotiated & CAP_FENCE_RING.
Immunity plan **WP7**, step 3 of 3 — stacked on #535 (driver arm) ← #525 (protocol layer). Retarget down the stack as they merge. Replaces #537 (same tree plus the fence DACL fix, as one gate-clean commit; no force-push by house rule). With this, the fence protocol is live end to end wherever both sides open the fences; every other pairing keeps the keyed-mutex arm. ## Negotiation (no flag day) - Every ring generation carries **two fresh shared `ID3D11Fence`s** (fresh, so both value sequences restart at zero on both sides; created with the SYSTEM-only DACL like every other frame object — `design/idd-push-security.md` item 10) in `SetFrameChannelRequestV2`, and a **v4 header** with the slot table. A pre-D3D11.4 device gets neither and stays on the mutex arm. - The **first ring on a box is a keyed-mutex probe** carrying fences with `CAP_FENCE_RING` clear. The open-time attach's capability word says whether the driver opened them — remembered process-wide (`DRIVER_FENCE_CAPABLE`) — and a capable driver's probe ring is **rebuilt on the fence protocol before any frame flows** (one extra recreate on the first session per process). A fence-mode attach that fails falls back to the mutex arm and remembers that. - Fence-mode textures are `SHARED | NTHANDLE` without a keyed mutex (`HostSlot.mutex: Option`); `FrameChannelSender` / `send_frame_channel` carry the v2 request (a pre-fence driver reads the v1 prefix). ## Consume (D5) Newest PUBLISHED slot above the last delivery from the slot table (`fence::consumer_pick`) — older publishes freed (newest-wins, the S2 finding), and **any PUBLISHED record whose packed token names another generation freed too** (a leftover of a superseded publisher; closes the race a ring rebuild opens). CAS PUBLISHED → READING, GPU `Wait(ready, record.ready_value)`, the existing convert/blend path unchanged, `Signal(retire, ++retire_value)`, publish the value in the record, then FREE (Release). A cursor regen re-reads the last slot only while it is FREE and retires it the same way. `FenceSlotGuard` puts a READING slot back to FREE on any early return. ## Recreate Fresh fences + slot table zeroed before the generation bump, `CAP_FENCE_RING` set/cleared per mode, v2 delivery. ## Verification - Writing gate passes. `pf-capture` / `pf-vdisplay` / `punktfunk-host` cannot build on macOS; **`windows-host` dispatch on this branch is the compile proof**. The driver arm's `windows-drivers / driver-build` already passed (run 22731). - **Live proof pending** on the `.173` box (both vendors, BGRA/FP16, chaos, process death); the S2 spike passed 10/10 there with the same shape, and WP4's attach log will show `negotiated & CAP_FENCE_RING`.
enricobuehler changed target branch from worktree-wp7-driver-fence-v2 to main 2026-09-02 09:13:49 +00:00
enricobuehler added 2 commits 2026-09-02 09:13:49 +00:00
feat(capture): host arm of the fence ring
macos-host / check (pull_request) Successful in 1m12s
android / android (pull_request) Canceled after 1m37s
ci / rust (pull_request) Canceled after 1m19s
ci / rust-arm64 (pull_request) Canceled after 0s
ci / web (pull_request) Canceled after 0s
ci / docs-site (pull_request) Canceled after 0s
ci / bun-nix (pull_request) Canceled after 0s
ci / docs-drift (pull_request) Canceled after 0s
secret-scan / gitleaks (pull_request) Successful in 16s
f5c0c5fd32
Immunity plan WP7, step 3 of 3. The IDD-push capturer runs the CAS plus
shared-fence slot protocol once the driver has proven it opens the
fences; every other pairing stays on the keyed-mutex arm.

Every ring generation carries two fresh shared ID3D11Fences (fresh so
both value sequences restart at zero on both sides), created with the
SYSTEM-only DACL and delivered in the v2 request, plus a v4 header with
the slot table. The first ring on a box is a keyed-mutex probe: the
attach's capability word says whether the driver opened the fences,
remembered process-wide; a capable driver's probe ring is rebuilt on the
fence protocol before any frame flows, and a failed fence-mode attach
falls back to the mutex arm.

Fence-mode consume: newest PUBLISHED slot above the last delivery from
the table (older publishes freed; a record whose packed token names
another generation freed too), CAS to READING, GPU-wait producer-ready,
convert as before, signal consumer-retire, publish the value, then FREE.
A RAII guard frees a slot on any early return. Fence textures are SHARED
plus NTHANDLE without a keyed mutex; the sender carries the v2 request.
fix(capture): open the attach wait to the fence-mode upgrade
macos-host / check (pull_request) Successful in 1m7s
android / android (pull_request) Canceled after 0s
ci / rust (pull_request) Canceled after 0s
ci / rust-arm64 (pull_request) Canceled after 0s
ci / web (pull_request) Canceled after 0s
ci / docs-site (pull_request) Canceled after 0s
ci / bun-nix (pull_request) Canceled after 0s
ci / docs-drift (pull_request) Canceled after 0s
secret-scan / gitleaks (pull_request) Successful in 14s
6884833bb5
windows-host run 22795 on the host arm: wait_for_attach lives in the open child module as a private method, and the fence-mode upgrade after the probe ring calls it from the parent. pub(super), like create_ring_slots.
enricobuehler added 1 commit 2026-09-02 09:15:39 +00:00
Merge remote-tracking branch 'origin/main' into worktree-wp7-host-fence-v3
macos-host / check (pull_request) Successful in 1m11s
ci / docs-site (pull_request) Successful in 1m25s
ci / bun-nix (pull_request) Successful in 37s
ci / docs-drift (pull_request) Successful in 28s
secret-scan / gitleaks (pull_request) Successful in 14s
android / android (pull_request) Successful in 6m47s
ci / web (pull_request) Successful in 7m43s
ci / rust-arm64 (pull_request) Successful in 9m51s
ci / rust (pull_request) Successful in 42m41s
754146b5c4
# Conflicts:
#	CHANGELOG.md
enricobuehler merged commit af11b908db into main 2026-09-02 09:52:41 +00:00
Sign in to join this conversation.
No Reviewers
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: unom/punktfunk#538