Skip to content

test(engine): the blur trail mask covers the shutter's sweep, not only whole frames - #7624

Merged
vanceingalls merged 1 commit into
mainfrom
test/blur-trail-shutter-sweep
Nov 12, 2026
Merged

vanceingalls merged 1 commit into
mainfrom
test/blur-trail-shutter-sweep

Conversation

@vanceingalls

@vanceingalls vanceingalls commented Nov 12, 2026 •

Copy link
Copy Markdown
Collaborator

What

Fixes #7596. The nightly generated-composition run failed seed 63098 (leftTop, svgClip, onUpdateMover, tlCall) with "outside the trail at 0.2: 2 bad tiles, worst MAE 4.0" on both Chrome flavours. It is a test-oracle defect, not a blur defect.

Root cause

The outside check masks the trail with trailMask over the blur-off whole frames at t − 2 … t + 2, dilated by 2 px.

  • #lt is a 30 px box moving about 33–46 px per frame (552 px in 0.4 s).
  • Consecutive whole-frame poses therefore leave unmasked gaps along its path.
  • The 240° shutter's smear, centred on t, legitimately covers part of a gap.

Measured on row 135 at t = 0.2 (Chrome 153, local):

  • mask: x 104–137 and from 145, gap x 138–144;
  • blur-off #lt: x 147–176;
  • blurred frame: x 140–183, so the tail lands in the gap.

A tile fails only when a static object nearby leaves enough of it unmasked to be checked. In 63098 that is #fl, the tlCall box. Removing #fl keeps the 209 leaking pixels but unchecks the tile. That is why only some fast, narrow movers trip it, and why svgClip is a coincidence here.

Fix

measureOutside adds the shutter's sub-frame blur-off poses (shutterTimes(t), the same samples measureInside uses) to the mask. All times are captured in ascending order, so callback state is never seeked backwards. The reference frame and tile rules are unchanged.

Evidence

  • Seed 63098: fails 3 of 3 before, passes 2 of 2 after. It is now in the default seeds (1,2,3,63098). With the fix reverted those give 1 failed / 3 passed; with it, 4 passed.
  • Seeds 1, 2, 3, 129 and 62660 pass.
  • captureMotionBlur.differential.browser.test.ts and .differential.filter.browser.test.ts: 76 passed.
  • Mutation: a 12 px stray mark added to the blur-on page only, at (280, 150) and at (200, 160), is still flagged outside the trail with the new mask.
  • Engine tsc --noEmit and the comment ratchet pass.
  • Seeds 63050–63099 (a slice of the 2026-11-12 nightly range, including 63098): 50 of 50 pass with the fix (Chrome 153, macOS, 227 s).

Not fixed: #7486 (seed 62660)

Its bad tiles (x 208–271, y 0–47) are exactly a static, non-moving #sibling: a gradient box with clip-path:url(#genClip). Its edge differs by MAE ≤ 2 with blur on, against a floor of 1.

  • It fails in CI on both flavours but passes 3 of 3 locally on Chrome 153 (macOS), before and after this change.
  • Task 8 saw the same signature on seed 129 and found blur-off renders that differ between runs.
  • My guess, unverified: the filters and defs that blur adds change how Chrome layers the page, which moves the clip-path edge's anti-aliasing. It needs a Linux repro to confirm.

What I did NOT exercise

  • The nightly's full 200-seed range: only a 50-seed slice was run.
  • Linux, and the CI stable Chrome flavour.

🤖 Generated with Claude Code

…y whole frames

A mover that moves further than its own width between frames leaves gaps in a
trail built from whole-frame blur-off poses; the shutter's smear lands in them and
read as a leak outside the trail (nightly seed 63098). The mask now adds the
shutter's sub-frame poses, captured in time order. Seed 63098 joins the default
seeds as the regression case.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@heygengenesis heygengenesis Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verdict: APPROVE · 0 blocker · 0 important · 1 nit

I checked the mask by hand. The new poses are the 8 shutter samples at t ± 1/90 s. That's strictly inside the t ± 1/30 frame neighbours, so the ascending capture order holds and wholeFrames[radiusFrames] is still frame t. 8 samples is even, so no sample lands on t and whole.includes can't misfile one. At about 46 px per frame the samples are about 4 px apart, and the 2 px dilation closes those gaps. The added area is a subset of where a correct blur is allowed to land, so the outside check loses no real sensitivity. The CI run Test (runtime) ran the generated file with 4 seeds, including 63098, on Linux, and it passed.

Scope / notes
  • Read in full: differentialPage.ts and the generated test's seed default.
  • Not part of this PR: the producer render-path oracle (packages/producer/src/services/render/__test_utils__/blurRender.ts:129) still builds its outside mask from whole frames only. It can't add sub-frame poses from a render. Today's hand-written fixtures pass there, but a fast, narrow mover added to FIXTURES would hit the same gap.
  • I did not reproduce #7486 / seed 62660. I'm going on the PR body's statement that it's out of scope.

— Genesis

import { INVARIANTS } from "./__fixtures__/motionBlur/thresholds.js";

const SEEDS = parseSeeds(process.env.HF_BLUR_SEEDS ?? "1,2,3");
const SEEDS = parseSeeds(process.env.HF_BLUR_SEEDS ?? "1,2,3,63098");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: seed 63098 only reproduces #7596 while generate.ts keeps producing the same composition for it. A change to the generator (a new feature, a reordered draw) quietly turns this into a different random case, and the test still passes. If you want the check to last, add a cheap assertion that generateComposition(63098) still has the leftTop/svgClip/onUpdateMover/tlCall features, or copy the case into a fixed fixture.

@vanceingalls
vanceingalls added this pull request to the merge queue Nov 12, 2026
Merged via the queue into main with commit 2df837b Nov 12, 2026
124 checks passed
@vanceingalls
vanceingalls deleted the test/blur-trail-shutter-sweep branch November 12, 2026 23:11
@miguel-heygen miguel-heygen mentioned this pull request Nov 13, 2026
3 of 5 tasks
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.

motion blur nightly: seed 63098 motion blur nightly: seed 62660

1 participant