fix(compositor): honour force_live on the CPU mixer path - #745
Conversation
build_software_compositor discarded its force_live parameter, justified by a comment claiming compositor exposes force-live as read-only. That is wrong: gst-inspect-1.0 reports it as "readable, writable, can be set only at object construction time" — construct-only, the same flags as glvideomixerelement. The code built the element first and only then tried to set properties, which is why it concluded the property was unusable. Set it at construction instead, the pattern already used in vision_mixer/elements.rs. Before this, the CPU mixer always ran with force-live=false while the GL path received the caller's value. Adds two tests driving CompositorBuilder::build() with compositor_preference "cpu" and reading force-live back off the built mixer. Reverting the fix makes cpu_mixer_honours_force_live_true fail (verified locally). This is a correctness fix, not a behaviour fix — it does not address the vision mixer cold-start stall. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
srperens
left a comment
There was a problem hiding this comment.
Verdict: Comment — correct fix, brings the CPU path to parity with the pre-existing GL
path, but the current head's CI is not green.
Claims
| Claim | Verdict | Evidence |
|---|---|---|
force-live set at construction on the CPU mixer, matching the GL path |
CONFIRMED |
backend/src/blocks/builtin/compositor.rs:465 — `.property("force-live", force_live)`; the pre-existing GL path does the identical thing at backend/src/blocks/builtin/compositor.rs:247 — `.property("force-live", force_live)`, both fed from one force_live computed at :139 and passed to both builders at :157 and :168 |
build_software_compositor has one caller |
CONFIRMED |
only call site is backend/src/blocks/builtin/compositor.rs:160 |
| CI is green at the current head | CONTRADICTED |
gh pr checks 745 at 88a6a11: Build (Linux x86_64) is cancelled, run 33662772217, Build step alone ran 30m+ against the job's own timeout-minutes: 30 (.github/workflows/ci.yml:229) — a job timeout, not a compile failure; Build (Linux ARM64) on the identical SHA passed in 9m32s and Check (Linux) (which runs the new tests) passed |
| The new tests execute in CI, not skipped | CONFIRMED |
Check (Linux) runs cargo test --package strom --features efp,nvidia (.github/workflows/ci.yml step under that job); compositor.rs is not feature-gated, so cpu_mixer_honours_force_live_{true,false} ran there and that job is green |
Diagnosis — Matches the stated cause: force-live is construct-only on compositor
(same as glvideomixerelement), so the old post-build() no-op silently kept the CPU path at
the element's false default regardless of the caller's value. The fix is ABSOLUTE for this
bug: one property, one call site, and the two tests read the property back off the live element
rather than re-asserting the code's own logic. The PR is explicit that this does not touch the
separate cold-start-stall issue — correctly out of scope, not this diagnosis's problem to solve.
Radius — LOCAL. Single function, single call site, no new dependency, GL path untouched.
Tests & CI — The guard (cpu_mixer_honours_force_live_true) is real: the PR body states it
was verified to fail on a manual revert of the fix line, which is consistent with the element's
documented false default and the property being construct-only — reading it back after
build() cannot pick up a value that arrived too late. Check (Linux), Check & Build (WASM),
API Contract Check, Build (Linux ARM64), sccache preflight are all green at 88a6a11.
Build (Linux x86_64) is not — it needs a clean rerun before merge:
gh run rerun 33662772217 --failed. Build (macOS)/Build (Windows) skip on
push/pull_request as usual; nothing in this diff is platform-specific.
Repo rules — No BUFFER probe, no pipeline-element closure, no shared-type or endpoint
change. Nothing to flag.
Confidence: HIGH
The bug
build_software_compositorinbackend/src/blocks/builtin/compositor.rsdiscarded itsforce_liveparameter, justified by this comment:That is factually wrong.
gst-inspect-1.0 compositorreports:Construct-only, not read-only — the same flags as
glvideomixerelement. The code built theelement first and only then tried to set properties, which is why it concluded the property was
unusable.
Net effect before this change: the CPU mixer always ran with
force-live=false, while the GLpath received the caller's value. The parameter was silently dropped for CPU.
The fix
Set it at construction, the pattern already used in
vision_mixer/elements.rs, which evendocuments it: "force-live is construct-only and must be set via
ElementFactory::make().property()".The incorrect comment and the
let _ = force_live;line are removed, and theinfo!now reportsthe value the way the GL path already does. The GL path is otherwise unchanged.
What this does NOT do
This is a correctness fix, not a behaviour fix. It does not fix the vision mixer cold-start
stall. That was tested specifically. Isolated
gst-launch-1.0runs show a compositor withlinked-but-never-negotiating sink pads keeps producing frames in every configuration tried:
force-livetrue and falseignore-inactive-padstrue and falseudpsrc) and non-live (appsrc)All produced ~294–297 frames per 10s at 30fps, against a ~295 frame all-live control.
Also worth recording, and deliberately not built on here:
glvideomixerelementhas noignore-inactive-padsproperty at all — that escape hatch is CPU-only.Tests
Two tests in a new
mod testsincompositor.rs. They drive the publicCompositorBuilder::build()withcompositor_preference = "cpu"— pinning the backend so thetest exercises the CPU path regardless of whether the host has GL — and then read
force-liveback off the built mixer element.
cpu_mixer_honours_force_live_true— the guard. Reverting the one-line fix makes it fail,because a
compositorbuilt without the construct-time property stays at itsfalsedefault,and being construct-only nothing downstream can change it afterwards. Verified by actually
reverting the line locally and re-running: it failed with "CPU mixer must report
force-live=true when the block is built with force_live true", then passed again on restore.
cpu_mixer_honours_force_live_false— the counterpart. It does not fail on revert, so it isnot the guard; it earns its place because
parse_force_livedefaults totrue, so a wrongproperty key would make this one fail. It pins the key name and the plumbing.
compositorships ingst-plugins-base, already in the CI package list(
.github/workflows/ci.yml), so these tests execute in CI rather than skipping.Commands actually run
gst-inspect-1.0 compositorcargo fmt --all -- --checkcargo clippy --workspace --all-targets -- -D warningscargo test --lib blocks::builtin::compositor--no-verifynot usedAll runs used the dev profile with
CARGO_PROFILE_DEV_DEBUG=0andCARGO_INCREMENTAL=0.Not run
cargo test --workspace— not run. The build volume was nearly full for this work, and afull workspace test build did not fit. Only the two tests above were executed.
construct-time property receives, and the assertion reads that property back directly.
🤖 Generated with Claude Code