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
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>
This commit is contained in:
@@ -60,7 +60,8 @@ fn flag(name: &str) -> bool {
|
||||
|
||||
/// True when `PUNKTFUNK_ZEROCOPY` is explicitly truthy — the operator forced the dmabuf offer, so
|
||||
/// a failed negotiation keeps erroring loudly instead of silently downgrading to the CPU path.
|
||||
pub fn vaapi_dmabuf_forced() -> bool {
|
||||
/// Read by BOTH negotiation-timeout latches (the raw passthrough's and the EGL→CUDA offer's).
|
||||
pub fn zerocopy_forced() -> bool {
|
||||
flag_opt("PUNKTFUNK_ZEROCOPY") == Some(true)
|
||||
}
|
||||
|
||||
@@ -70,11 +71,12 @@ pub fn vaapi_dmabuf_forced() -> bool {
|
||||
/// Vulkan (LINEAR) imports now run in a per-capture worker subprocess
|
||||
/// (`design/zerocopy-worker-isolation.md`), so a driver fault on a producer-invalidated dmabuf kills
|
||||
/// the worker and the host degrades to its capture-loss rebuild instead of dying — the reason the
|
||||
/// NVENC path stayed opt-in is gone. Fallbacks stay in place: VAAPI has a one-shot CPU downgrade if
|
||||
/// the LINEAR-dmabuf offer never negotiates ([`note_raw_dmabuf_negotiation_failed`]); NVENC falls back per
|
||||
/// capture when no importer/importable modifier is available and latches the import off after
|
||||
/// repeated worker deaths. `PUNKTFUNK_ZEROCOPY=0` opts out; `PUNKTFUNK_FORCE_SHM` forces the
|
||||
/// race-free SHM path.
|
||||
/// NVENC path stayed opt-in is gone. Fallbacks stay in place: VAAPI has a one-shot CPU downgrade
|
||||
/// if the LINEAR-dmabuf offer never negotiates ([`note_raw_dmabuf_negotiation_failed`]); NVENC
|
||||
/// falls back per capture when no importer/importable modifier is available, and latches the
|
||||
/// import off after repeated worker deaths or its own negotiation timeout
|
||||
/// ([`note_gpu_dmabuf_negotiation_failed`]). `PUNKTFUNK_ZEROCOPY=0` opts out;
|
||||
/// `PUNKTFUNK_FORCE_SHM` forces the race-free SHM path.
|
||||
///
|
||||
/// This is the GLOBAL switch and nothing but the env var moves it. It used to fall back to
|
||||
/// `!VAAPI_DMABUF_FAILED`, a latch a capture-side dmabuf *negotiation* timeout set — which made one
|
||||
@@ -311,6 +313,35 @@ pub fn raw_dmabuf_import_disabled() -> bool {
|
||||
RAW_DMABUF_DISABLED.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
/// The EGL→CUDA twin of the raw-passthrough negotiation latch: the capture advertised the GPU
|
||||
/// importer's dmabuf-only offer and the compositor never accepted it. Without this, the raw
|
||||
/// passthrough stopped being asked after one timeout while the GPU-import offer re-ran the same
|
||||
/// 10 s negotiation timeout on every session, forever — the mirror image of the hybrid-Intel case
|
||||
/// [`note_raw_dmabuf_negotiation_failed`] was written for.
|
||||
static GPU_DMABUF_NEGOTIATION_FAILED: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
/// Latch the EGL→CUDA dmabuf offer off because it *never negotiated*. One timeout is conclusive
|
||||
/// for this offer too: a compositor that cannot allocate any of the advertised EGL-importable
|
||||
/// modifiers refuses them identically on every retry. Scoped deliberately — this gates only
|
||||
/// `build_importer` (the capture-side EGL→CUDA offer); the raw passthrough, the worker-death
|
||||
/// latch, and the encoder are untouched.
|
||||
pub fn note_gpu_dmabuf_negotiation_failed() {
|
||||
if !GPU_DMABUF_NEGOTIATION_FAILED.swap(true, Ordering::Relaxed) {
|
||||
tracing::warn!(
|
||||
"zero-copy EGL→CUDA dmabuf offer disabled for this host process: the compositor never \
|
||||
accepted the GPU importer's dmabuf-only capture offer, so later captures negotiate \
|
||||
the CPU path instead of repeating that timeout (the raw-dmabuf passthrough is NOT \
|
||||
affected)"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// True once a negotiation timeout latched the EGL→CUDA dmabuf offer off (see
|
||||
/// [`note_gpu_dmabuf_negotiation_failed`]).
|
||||
pub fn gpu_dmabuf_negotiation_disabled() -> bool {
|
||||
GPU_DMABUF_NEGOTIATION_FAILED.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
/// DRM FourCC for a packed 32-bit format name (little-endian, e.g. `b"XR24"`).
|
||||
const fn fourcc(c: &[u8; 4]) -> u32 {
|
||||
(c[0] as u32) | ((c[1] as u32) << 8) | ((c[2] as u32) << 16) | ((c[3] as u32) << 24)
|
||||
|
||||
Reference in New Issue
Block a user