Side quest 3: fix the Horizon Lines determinism flake at the cause

Phase 7's determinism check reported Horizon Lines at 2/255 against a 1/255
tolerance, but only when phase 7 ran in isolation — a full run warmed the GPU
first and it passed. A gate whose verdict depends on preceding load will
eventually stay green through a real regression, and determinism is the one
property this whole project is built on.

The cause turned out to be geometry, not the driver. The lines were drawn
with `smoothstep(u_thickness * (0.5 + u_sigLine), 0.0, d)` where the width is
0.008 scene units against a 0.028-unit pixel at 720p — a line under a THIRD
of a pixel wide, ramping from full brightness to nothing across that third.
The parameter range goes down to 0.002, which is a fourteenth of a pixel.
That is a near-vertical cliff, and a cliff turns a float wobble of 1e-7 in a
cancelling subtraction into a whole byte of colour. Nothing else in the
library sums 40 such terms.

Replaced with analytic coverage: `sat((w - d) / px + 0.5)`, where px is the
exact height of a pixel in scene units. The transition now always spans one
pixel, so no pixel sits on a discontinuity — and a line thinner than a pixel
comes out DIM instead of being drawn at full brightness wherever a pixel
centre happens to land on it. That second part is a real image fix as well as
a determinism one: it is the end of the shimmer this scene has always had.

Width stays in scene units, so resolution independence is untouched — the
320x180 vs 1280x720 diff still reads 0.00183 against a 0.06 limit.

Measured: worst delta over two identical 20-frame captures drops from 2 to at
most 1, usually 0. Phase 7 now passes in isolation, repeatedly, which is the
condition that was failing. Horizon Lines is no longer even the worst scene
on that metric.

Also removed the dead `total` accumulator while in there.

104/104 checks pass including the slow set. See SIDE-QUESTS.md §3.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Dejvino 2026-08-06 19:41:58 +02:00
parent 17a583a87f
commit c44b527fe8

View File

@ -39,7 +39,10 @@ vec4 scene(vec2 uv, vec2 p) {
float offset = sin(p.x * 2.2 + t * 2.0) * u_bend * envelope;
vec3 col = pal(0) * 0.06;
float total = 0.0;
// One pixel, in the scene units p is measured in. p spans [-1, 1] vertically
// regardless of output size, so this is the exact height of a pixel.
float px = 2.0 / u_resolution.y;
for (int i = 0; i < 40; i++) {
if (float(i) >= u_count) break;
@ -49,12 +52,28 @@ vec4 scene(vec2 uv, vec2 p) {
float y = slot + offset * (0.4 + fract(fi * 0.37));
float d = abs(p.y - y);
float line = smoothstep(u_thickness * (0.5 + u_sigLine), 0.0, d);
// Analytic coverage rather than a smoothstep to zero.
//
// The width here is genuinely sub-pixel: 0.008 scene units against a
// 0.028-unit pixel at 720p is a line under a third of a pixel wide, and
// the range goes down to a fourteenth. Ramping from full to nothing
// across a third of a pixel is a near-vertical cliff, and a cliff turns
// any float wobble in d into a whole byte of colour — which is what
// made this the one scene in the library that failed the determinism
// gate, intermittently, at 2/255.
//
// Coverage fixes the cause rather than the symptom. The transition
// always spans exactly one pixel, so nothing sits on a discontinuity;
// and a line thinner than a pixel now comes out DIM rather than being
// drawn at full brightness wherever a pixel centre happens to land on
// it, which is both correct and the end of the shimmer it used to have.
// Width stays in scene units, so resolution independence is unchanged.
float w = u_thickness * (0.5 + u_sigLine);
float line = sat((w - d) / px + 0.5);
float halo = exp(-d * 26.0) * u_glow;
vec3 c = pal(i);
col += c * (line + halo * 0.55);
total += line;
}
// Keep the far edges dark so the lines read as a subject, not wallpaper.