28c50d1c5b5616b08a998e21e7be8f56bd2cab7b
3
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
9b3ec9204c |
fix(capture): a refused EGL→CUDA dmabuf offer stops being asked
ci / rust (push) Failing after 41s
android / android (push) Canceled after 1m19s
apple / screenshots (push) Canceled after 0s
apple / swift (push) Canceled after 1m20s
arch / build-publish (push) Canceled after 1m23s
ci / rust-arm64 (push) Canceled after 1m24s
ci / web (push) Canceled after 1m24s
ci / docs-site (push) Canceled after 1m23s
ci / bench (push) Canceled after 1m22s
deb / build-publish-client-arm64 (push) Canceled after 0s
deb / build-publish (push) Canceled after 46s
deb / build-publish-host (push) Canceled after 2s
decky / build-publish (push) Canceled after 1s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Canceled after 0s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Canceled after 0s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Canceled after 0s
docker / build-push (ci, ci/rust-ci-noble.Dockerfile, punktfunk-rust-ci-noble) (push) Canceled after 0s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Canceled after 0s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Canceled after 0s
docker / build-push-arm64cross (push) Canceled after 0s
docker / deploy-docs (push) Canceled after 0s
windows-host / winget-source (push) Canceled after 0s
windows-host / package (push) Canceled after 1m35s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Canceled after 7s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Canceled after 5s
The raw-dmabuf passthrough has had a negotiation-timeout latch since the hybrid-Intel case: one conclusive timeout and later captures negotiate the CPU path instead of re-paying it. The EGL→CUDA dmabuf-only offer had no equivalent — a compositor that accepts none of the importer's modifiers timed out the same 10 s negotiation on every session, forever, under the generic 'format negotiation never completed' diagnosis. Now the symmetric latch (note_gpu_dmabuf_negotiation_failed) gates build_importer in the one negotiation resolver, scoped to this offer alone (raw passthrough, worker-death latch, encoder untouched), with the same operator escape as the raw arm: an explicit PUNKTFUNK_ZEROCOPY=1 keeps erroring loudly instead of downgrading. One correctness detail beyond the handoff's sketch: whether the GPU offer was ACTUALLY advertised is a runtime fact of the PipeWire thread (the importer may fail to construct — no CUDA — in which case no dmabuf was offered and a timeout must not latch it off). plan.build_importer alone cannot answer that, so the thread records the made-offer on CaptureSignals (gpu_dmabuf_offer) and the timeout diagnosis branches on the offer that really happened, not the plan's intent. Also renames vaapi_dmabuf_forced → zerocopy_forced (it now guards both timeout arms; single caller). New plan invariant pinned in tests: the latch gates only the importer, never the raw passthrough. (V3 from design/pf-zerocopy-sweep-handoff.md — the deferred design call, now decided in favour of the symmetric latch.) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
143a707f76 |
fix(zerocopy): the mechanical sweep batch — truthful fence waits, forgiving env flags, no leaked planes
From the pf-zerocopy review sweep (design/pf-zerocopy-sweep-handoff.md), the compile-verifiable batch: - dmabuf_fence: the blocking poll's result was discarded — EINTR silently skipped the wait (reopening the stale-frame race a SIGCHLD away) and a timeout reported as waited. Now EINTR retries with the remaining budget and the caller gets Signaled/TimedOut/NoFence, so the one diagnostic operators have about implicit fencing stops lying. (C2) - env flags: PUNKTFUNK_ZEROCOPY=TRUE meant *off* — values are case-folded now, and an unrecognised spelling falls back to the flag's default with a one-shot warning instead of silently inverting the operator's intent. (C3) - cuda: alloc_pitched_nv12 leaked the Y plane when the UV allocation failed (per-frame under VRAM pressure — the worst possible time to leak); a failed async-copy enqueue now drains the stream before returning, so a recycled pool buffer can't race an orphaned in-flight copy. (C4, C5) - worker: --fd is validated (>= 3, fstat + S_ISSOCK) before OwnedFd adoption — 'zerocopy-worker --fd -1' was constructing OwnedFd's niche value. (V1) - docs: all 15 rustdoc warnings fixed, including the link to the renamed note_raw_dmabuf_negotiation_failed. (D1) - CI: a SPIR-V drift gate — the committed .spv blobs are include_bytes!'d and rebuilt by hand; the gate diffs disassembly (filtering only the shaderc/glslang generator difference) so a forgotten rebuild fails CI instead of shipping the old kernel. Both blobs verified in sync. (S1) - tests: bt709_limited pinned to external BT.709 anchors (it is the sole oracle for the GPU colour self-test); blend_geometry gets its first tests — empty rect, CURSOR_MAX clamp, per-format group counts, and negative-ox floor alignment. (T2, T3) Verified on .25: clippy -D warnings clean, 27/27 tests, cargo doc 0 warnings. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
306f4a514d |
refactor(pf-capture): structural splits + collapse the restated signal set (Phase 5)
Refactors LAST, after every defect fix, so no behaviour change hides in a move diff — and so the newly-exposed review surface was already clean when it landed (the WP7 discipline). 28f8fc71's recorded trap (an inline `use super::X` inside a moved body silently changing meaning) was audited first: every `use super::` in the moved code is either at module level — where `super` still means `linux` — or inside a `mod tests` that moved with its parent. 5.1 `mod pipewire` → `linux/pipewire.rs`. It was 1,900 of that file's lines. `mod.rs` is a directory module, so a plain `mod pipewire;` resolves with no `#[path]`. 5.2 out of that, `linux/pw_pods.rs` (the 7 param-pod builders + `serialize_pod` + the PQ constant and its test) and `linux/pw_cursor.rs` (`CursorState`, `decode_bitmap_pixel`, `update_cursor_meta`, `dst_offsets`, both `composite_cursor*`). The pods are the crate's WIRE surface — a missing property is not a compile error but a link that stalls in `negotiating`; the cursor half is producer-driven and bounds-critical. Both are now pure enough to unit-test without a compositor, which is what Phase 6 needs. 5.3 `linux/portal.rs`: the ScreenCast/RemoteDesktop handshake, the cursor-mode choice and GNOME's BT.2100 probe — the async/tokio/zbus control plane, separated from the realtime half. Zero re-exports changed: `mod.rs` re-exports `gnome_hdr_monitor_active` at its old path. 5.4 `idd_push/open.rs` (the whole one-time construction path + `SharedObjectSa` + `AttachTexFail`) and `idd_push/compose_kick.rs`. Read `open.rs` when a session will not START and the parent when one stops flowing; the steady state stays with the parent by decision. Also moved the ~65-line stall REPORTING block out of `try_consume` (the hot loop) into `StallWatch::report`, taking its two correlation counters with it — they were capturer fields nothing else touched. 5.5 `windows/dxgi/selftest.rs`: `p010_reference`, both self-tests, `hdr_p010_convert_bars_on_luid`, `f32_to_f16` and the two test modules — the validation path, none of which runs in a session. The two `pub` entry points are re-exported, so `main.rs`'s subcommand and pf-encode's live e2e keep their paths. (An explicit `#[path]`, like `idd_push`'s children: this file is itself reached through a `#[path]`, so a bare `mod` would resolve to `windows/selftest.rs`.) 5.6 `CaptureSignals`. The seven shared flags/slots were created in `spawn_pipewire`, `_cb`-cloned one by one, passed as seven of `pipewire_thread`'s parameters, and then re-declared as fields on `PwHandles`, `PortalCapturer` AND `UserData` — the same list written four times, each with its own drifting copy of the doc comment. One `#[derive(Clone)]` struct instead (a refcount bump per field), which also makes it structurally obvious that producer and consumer share ONE set. And `CaptureOpts` for the four trailing `bool`s: four adjacent same-typed positional arguments are a silent-transposition footgun — swap `want_444` and `want_hdr` and it compiles, negotiates the wrong pod family, and surfaces as a black screen ten seconds later. 5.7 trait shape, targeted: `set_active(&self)` → `&mut self` (it took `&self` only because the flag happened to be an `Arc<AtomicBool>` — an implementation detail leaking into the contract); `capture_target_id` ⇒ `resize_output`'s pairing rule stated on both methods and in a cluster note; one-line cluster headers over the 13 methods. NOT done from 5.7: taking the gamescope targets as an `open_*` option instead of the trait method. It would put a Linux-only parameter on the cross-platform `open_virtual_output` and on the host's `capture_virtual_output` facade — a `#[cfg]`-shaped wart in two shared signatures to delete one already-`cfg`'d defaulted method whose lifecycle rule 2.5c had already made harmless. Wrong trade under 5.7's own "no churn for no defect fixed" principle. File sizes: `linux/mod.rs` 2,778 → 770 (target ≤900 ✓), `windows/dxgi.rs` 1,374 → 954, and `windows/idd_push.rs` 2,694 → 1,922. The last two miss the plan's ≤850/≤1,300 targets, which were computed against the ORIGINAL line counts — Phases 1–4 added several hundred lines of SAFETY proofs and defect rationale to exactly these files before the split. The moves themselves are the ones the plan specifies; closing the rest would mean inventing new splits it does not call for. Zero behaviour delta. pf-capture 20/20; workspace clippy --all-targets clean on Linux and on windows-msvc. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |