Skip to content

fix(render): the motion-vector pass waits for a reader - #868

Merged
lobinuxsoft merged 2 commits into
developmentfrom
fix/motion-vectors-need-a-reader
Aug 17, 2026
Merged

fix(render): the motion-vector pass waits for a reader#868
lobinuxsoft merged 2 commits into
developmentfrom
fix/motion-vectors-need-a-reader

Conversation

@lobinuxsoft

Copy link
Copy Markdown
Owner

~2 ms per frame, measured, with no quality cost. The first item of the graphics Phase 0 to produce code.

Measured on the OneXFly, many_lights.scene, temporal resolve off:

motion vectors   1.994 ms   ← of a 20.5 ms GPU frame, budget 13.9

taa.wgsl is the only shader that binds the buffer, so with the resolve off the pass writes a full-resolution target nobody samples. The comment above it explained why it runs in every debug mode — true, and silent about whether anything reads the result.

  • The predicate is needs_motion(), not self.taa_enabled at the call site: FSR (feat(render): FSR over the FidelityFX C ABI (FFI), behind the upscaler trait #536) becomes the second consumer of the same condition, so it extends one gate instead of adding a second.
  • The history the vectors are computed from is maintained by the scene upload, not by this pass — so turning the resolve back on mid-session gets correct vectors on its first frame. Documented on the predicate.

On the test, because the obvious one is unsound. "The motion vectors scope is absent" cannot be asserted in a shared test binary: puffin's scope_delta is a delta, the first test to register a name drains it, and gpu_scopes.rs already documents that asserting absence there is asserting which test ran first. So this reads the texture — a fresh wgpu texture is zeroed — in its own binary, and the resolve-on half is what stops the resolve-off half from passing vacuously (a gate that skipped the pass unconditionally would pass one and fail the other).

🔴 Verified failing with the gate reverted: 147684 of 160000 bytes are non-zero, so the pass is still running for nobody.

temporal_motion, half_rate_shading and compute_shading_parity all still pass. Refs #481.

lobinuxsoft and others added 2 commits August 17, 2026 10:16
Measured on the OneXFly with the temporal resolve off: 1.994 ms of a
20.5 ms GPU frame, writing a full-resolution buffer nobody sampled.
taa.wgsl is the only shader that binds it.

Unlike the resolve itself, which is a quality trade, this is an if. The
predicate is needs_motion() rather than the field at the call site
because FSR (#536) becomes the second consumer of the same condition.

The test is its own binary: asserting a GPU scope is ABSENT is unsound in
a shared one, because puffin's scope_delta is a delta and the first test
to register a name drains it. It reads the texture instead - a fresh wgpu
texture is zeroed - and the resolve-on half is what stops the resolve-off
half from passing vacuously. Verified failing: 147684 of 160000 bytes
non-zero with the gate reverted.
@lobinuxsoft
lobinuxsoft merged commit 6b8e816 into development Aug 17, 2026
@lobinuxsoft
lobinuxsoft deleted the fix/motion-vectors-need-a-reader branch August 17, 2026 14:15
lobinuxsoft added a commit that referenced this pull request Aug 17, 2026
The user's directive decides the architecture rather than a preference:
the engine ships for Windows as well as Linux and for NVIDIA and Intel as
well as AMD, and it gets its own upscaler. A vendor SDK cannot be the
path a build depends on, so the always-there path is ours (#481) and the
vendor backends (#536) become optional on top of it — inverting what #536
said, which was that a vendor's ships and ours is the untested fallback.

It also happens to be the largest measured lever here. Per-pixel shading
dominates the frame (10.969 ms of 24.8), all shadows total 1.153, and
rendering at 67 % linear is 44 % of the pixels: roughly -5 ms, and it
compounds with every per-pixel cost added later. #866 is demoted to
Phase 2 because its payoff is scale and memory, not cost per pixel.

Phase 0 item 1 is closed with its numbers, including the finding it was
not looking for: motion vectors ran for nobody at 1.994 ms (#868).

Auto exposure moves out of Phase 4 and into Phase 1 as a prerequisite:
every temporal resolve takes an exposure input and rejects history in a
perceptual space, and this engine's radiance is in the hundreds.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant