Skip to content

test(shader-transitions): add midpoint (p=0.5) regression invariants for all shaders#378

Open
vanceingalls wants to merge 3 commits intovance/srgb-hdr-reference-testsfrom
vance/shader-midpoint-tests
Open

test(shader-transitions): add midpoint (p=0.5) regression invariants for all shaders#378
vanceingalls wants to merge 3 commits intovance/srgb-hdr-reference-testsfrom
vance/shader-midpoint-tests

Conversation

@vanceingalls
Copy link
Copy Markdown
Collaborator

@vanceingalls vanceingalls commented Apr 21, 2026

Summary

Add four midpoint (p=0.5) regression invariants applied via a describe loop over ALL_SHADERS, so every existing and future shader transition automatically gets coverage at the most viewer-visible point in the animation.

Why

Chunk 9G of plans/hdr-followups.md. Existing smoke tests cover only the endpoints (p=0 ≈ from, p=1 ≈ to), which miss a class of regressions that surface specifically at the midpoint and let shaders silently rot in CI:

  • A shader becomes a no-op (returns input as-is)
  • A shader prematurely completes (returns target at midpoint)
  • A shader doesn't write to the output buffer at all
  • A shader loses determinism (Math.random / Date.now / leaked state)

What changed

packages/engine/src/utils/shaderTransitions.test.ts: a single describe loop over ALL_SHADERS that asserts at p=0.5:

  1. output ≠ from — catches no-ops
  2. output ≠ to — catches premature completion
  3. output is non-zero — catches blank output
  4. output is deterministic — catches accidental non-determinism

Uses two distinct uniform input colors (40000/30000/20000 vs 10000/10000/10000) so equality checks have distinct byte patterns to compare against. Even shaders that warp UVs (which would be no-ops on uniform input alone) produce mix16(from, to, 0.5) at every pixel, distinct from both inputs.

Test plan

  • 60 new tests (4 invariants × 15 shaders), all passing.
  • Any new transition added to the registry automatically picks up the same coverage.

Stack

Chunk 9G of plans/hdr-followups.md. Test-only change, independent of all other chunks.

Copy link
Copy Markdown
Collaborator Author

vanceingalls commented Apr 21, 2026

Warning

This pull request is not mergeable via GitHub because a downstack PR is open. Once all requirements are satisfied, merge this PR as a stack on Graphite.
Learn more

This stack of pull requests is managed by Graphite. Learn more about stacking.

@vanceingalls vanceingalls force-pushed the vance/srgb-hdr-reference-tests branch from a1d1fdd to afe3517 Compare April 21, 2026 20:48
@vanceingalls vanceingalls force-pushed the vance/shader-midpoint-tests branch 2 times, most recently from 2163cd0 to ca9c359 Compare April 21, 2026 20:54
@vanceingalls vanceingalls marked this pull request as ready for review April 21, 2026 20:57
@vanceingalls vanceingalls force-pushed the vance/shader-midpoint-tests branch from ca9c359 to d8c43e8 Compare April 21, 2026 22:37
@vanceingalls vanceingalls force-pushed the vance/srgb-hdr-reference-tests branch from 2af7fa2 to f675c92 Compare April 22, 2026 01:16
@vanceingalls vanceingalls force-pushed the vance/shader-midpoint-tests branch from d8c43e8 to 1733e38 Compare April 22, 2026 01:16
@vanceingalls vanceingalls force-pushed the vance/srgb-hdr-reference-tests branch from f675c92 to b09ef42 Compare April 22, 2026 02:03
@vanceingalls vanceingalls force-pushed the vance/shader-midpoint-tests branch from 1733e38 to 6881964 Compare April 22, 2026 02:03
Copy link
Copy Markdown
Collaborator

@jrusso1020 jrusso1020 left a comment

Choose a reason for hiding this comment

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

Midpoint (p=0.5) regression invariants are the right surface to pin for a shader-transitions library. Shaders at p=0 and p=1 have strong invariants (equal to fromScene / toScene respectively), but the middle is where actual transition logic runs — wipe direction, crossfade alpha, shader-specific parameters. Without a p=0.5 pin, a shader rewrite can flip direction, swap source/destination, or change easing curve without being detected by the existing endpoint tests.

"Invariants for all shaders" is the right scope — a single shader-by-shader snapshot would be noisy and brittle, but invariant assertions (e.g. "the midpoint output is a mix of both sources" or "no pixel is fully black") are durable and meaningful.

Approved.

Rames Jusso

@vanceingalls vanceingalls force-pushed the vance/srgb-hdr-reference-tests branch from b09ef42 to 4ea3875 Compare April 22, 2026 03:03
@vanceingalls vanceingalls force-pushed the vance/shader-midpoint-tests branch 2 times, most recently from 0483635 to 1d14220 Compare April 22, 2026 03:34
@vanceingalls vanceingalls force-pushed the vance/srgb-hdr-reference-tests branch 2 times, most recently from 17e374e to 8499b89 Compare April 22, 2026 04:45
@vanceingalls vanceingalls force-pushed the vance/shader-midpoint-tests branch from 1d14220 to 2f8e87f Compare April 22, 2026 04:45
@vanceingalls
Copy link
Copy Markdown
Collaborator Author

Thanks for the approval @jrusso1020.

You nailed the rationale — endpoint pins (p=0 ≡ fromScene, p=1 ≡ toScene) are strong but say nothing about the actual transition logic. The midpoint is where direction, crossfade alpha, and shader-specific parameters live, so that's the surface a shader rewrite is most likely to silently break.

The "invariants over snapshots" framing is what kept the harness durable: assertions like "midpoint output is a non-trivial mix of both sources", "no pixel is fully black/saturated", and "midpoint is symmetric for crossfade-class shaders" survive shader refactors that don't change semantics, but reliably trip on direction flips, source/destination swaps, or easing-curve regressions.

No outstanding action items from the review.

@vanceingalls vanceingalls force-pushed the vance/shader-midpoint-tests branch from 2f8e87f to abe64f1 Compare April 22, 2026 18:01
@vanceingalls vanceingalls force-pushed the vance/srgb-hdr-reference-tests branch from 8499b89 to 87bce21 Compare April 22, 2026 18:01
@vanceingalls vanceingalls force-pushed the vance/shader-midpoint-tests branch 2 times, most recently from a8a7d7f to e3ea8d6 Compare April 22, 2026 18:50
@vanceingalls vanceingalls force-pushed the vance/srgb-hdr-reference-tests branch 2 times, most recently from bd45f57 to b1e97ae Compare April 22, 2026 18:59
@vanceingalls vanceingalls force-pushed the vance/shader-midpoint-tests branch from e3ea8d6 to 230c5df Compare April 22, 2026 18:59
@vanceingalls vanceingalls force-pushed the vance/srgb-hdr-reference-tests branch from b1e97ae to f07a20c Compare April 22, 2026 19:34
@vanceingalls vanceingalls force-pushed the vance/shader-midpoint-tests branch from 230c5df to aa5be19 Compare April 22, 2026 19:34
@vanceingalls vanceingalls force-pushed the vance/srgb-hdr-reference-tests branch from f07a20c to 57d8296 Compare April 22, 2026 20:45
@vanceingalls vanceingalls force-pushed the vance/shader-midpoint-tests branch 2 times, most recently from 456f47f to bb1d031 Compare April 22, 2026 20:48
@vanceingalls vanceingalls force-pushed the vance/srgb-hdr-reference-tests branch from 4ac7856 to f1bcae5 Compare April 22, 2026 22:13
@vanceingalls vanceingalls force-pushed the vance/shader-midpoint-tests branch from bb1d031 to c1d8c12 Compare April 22, 2026 22:13
@vanceingalls vanceingalls force-pushed the vance/srgb-hdr-reference-tests branch from f1bcae5 to f152c4e Compare April 22, 2026 22:50
…d-transfer caller error

PR #370 review feedback (jrusso1020):

- chunkEncoder: when codec=h264 and hdr is set, log a warning and strip
  hdr instead of emitting a half-HDR file (BT.2020 container tags +
  BT.709 VUI inside the bitstream). libx264 has no HDR support; the only
  honest output is SDR/BT.709. Caller is told to use codec=h265.

- videoFrameExtractor: comment at the convertSdrToHdr call site clarifying
  that dominantTransfer is majority-wins; mixing PQ and HLG sources in a
  single composition is caller-error and the minority transfer's videos
  will be converted with the wrong curve. Render two compositions if you
  need both transfers.

- docs/guides/hdr.mdx: limitations section now documents (a) H.264 + HDR
  is rejected at the encoder layer, and (b) GPU H.265 (nvenc, videotoolbox,
  qsv, vaapi) emits BT.2020 + transfer tags but does NOT embed master-display
  or max-cll SEI, since ffmpeg won't pass x265-params through hardware
  encoders. Acceptable for previews, not for HDR10 delivery.
Mirrors buildSrgbToHdrLut() in Python so future intentional LUT changes
(transfer-function constants, BT.709→BT.2020 matrix, OOTF, SDR-white nit
reference) can regenerate the byte-exact SRGB_TO_HDR_REFERENCE table in
one shot instead of hand-editing 12 inline literals.

Addresses PR #377 review feedback (jrusso1020): "reference values are
currently inline literals. If the LUT ever gets regenerated for a
legitimate reason, updating 97 lines of literals by hand is noisy. A
helper pattern — scripts/generate-lut-reference.py that emits the current
LUT values as a test-fixture JSON — is worth ~5 minutes to write and
would make future regenerations one-shot."

Usage:
  python3 packages/engine/scripts/generate-lut-reference.py --probes

Produces a paste-ready TS snippet matching the existing reference array.
Default mode (no flag) emits full 256-entry HLG/PQ LUTs as JSON. The
script uses int(math.floor(x + 0.5)) to mirror JS Math.round() for
non-negative numbers, which is what guarantees byte-identical output.

Verified: --probes regenerates the existing 12 reference rows exactly.
…for all shaders

Existing smoke tests cover only the endpoints (p=0 ≈ from, p=1 ≈ to),
which miss a class of regressions that surface specifically at the
midpoint — where the transition is most visible to viewers — and let
shaders silently rot in CI:

  • A shader becomes a no-op (returns input as-is)
  • A shader prematurely completes (returns target at midpoint)
  • A shader doesn't write to the output buffer at all
  • A shader loses determinism (Math.random / Date.now / leaked state)

Add four invariants every shader must satisfy at p=0.5, applied via a
describe loop over ALL_SHADERS so any new transition added to the
registry automatically picks up the same coverage:

  1. output ≠ from           catches no-ops
  2. output ≠ to             catches premature completion
  3. output is non-zero      catches blank output
  4. output is deterministic catches accidental non-determinism

Uses two distinct uniform input colors (40000/30000/20000 vs
10000/10000/10000) so equality checks have distinct byte patterns to
compare against. Even shaders that warp UVs (which would be no-ops on
uniform input alone) produce mix16(from, to, 0.5) at every pixel,
distinct from both inputs.

60 new tests (4 invariants × 15 shaders), all passing.

Follow-up to plans/hdr-followups.md Chunk 9G.
@vanceingalls vanceingalls force-pushed the vance/shader-midpoint-tests branch from c1d8c12 to 1f9a361 Compare April 22, 2026 22:50
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.

2 participants