From 69e1407876595188151f15f7ce085cf4b835a704 Mon Sep 17 00:00:00 2001 From: Dmitrii Vasilev Date: Sun, 18 Oct 2026 18:14:02 +0700 Subject: [PATCH] t27f token_floor: expose only the pixels a splat visited; the empty scene's bytes are computed once (Closes #10407) The empty scene's light depends on the pixel, not on the frame. main now computes the background bytes and each row's key, glint and fade factors once. A row gives a pixel the empty scene's light on the first splat visit, marks it dirty, and exposes only dirty pixels; every other pixel is copied from the cached bytes. Splat order and every float sum are unchanged, so frames are byte-identical (sha256 7e0c6662...391e07, one process and 48 shards). New pure helpers row_key, row_glint, row_fade, light_byte with two tests asserting exact values, including bytes read off frame 0. Lab, -O ReleaseSafe -lc: one process 32.8 s / 33.2 s -> 18.8 s / 17.9 s. test-report 28/28, 0 vacuous; resealed. Co-Authored-By: Claude Opus 5.5 --- .trinity/seals/t27f_token_floor.json | 16 +-- specs/t27f/token_floor.t27 | 158 +++++++++++++++++++++++---- 2 files changed, 147 insertions(+), 27 deletions(-) diff --git a/.trinity/seals/t27f_token_floor.json b/.trinity/seals/t27f_token_floor.json index 06ddeab5e6..386e5f045f 100644 --- a/.trinity/seals/t27f_token_floor.json +++ b/.trinity/seals/t27f_token_floor.json @@ -19,23 +19,23 @@ } ], "config": "gen=c,rust,verilog,zig;tests=zig", - "gen_hash_c": "sha256:632fc0ea1caeb3bbed598e2e31b34ed225334135591742f6e38961effe996465", - "gen_hash_rust": "sha256:58c03090b40543238379889a621acf0e621e5bbd74f9782102afaa57f46b3183", - "gen_hash_verilog": "sha256:a3c7af823504d83104bdd66cdf8c98a2cbc6876fe499a0ed9a6079c483ac1f35", - "gen_hash_zig": "sha256:ca0fe77f7b7aea34d4eb0f5ad461fd12dac9b53c532dd8b78c2c306d3bc4f803", + "gen_hash_c": "sha256:4f16c015b460b005332d006930f66dd64bba91477c3c33b7fc7040cc1a6cea34", + "gen_hash_rust": "sha256:b1f0266f0bd7d407b6f3ae20fb7d79b054774604b359c8c4308eec30eeebcce7", + "gen_hash_verilog": "sha256:5f23d216ff552e61ae387df63bbab05d616894d32665ef3232316da20905c0d3", + "gen_hash_zig": "sha256:ba680b8e5c6d54e34d2ce7f4c2ba073a7bc2376b53e7c90d0eed0327fdf7809d", "module": "token_floor", "ring": 12, "seal_schema": 2, - "sealed_at": "2026-10-18T11:00:36Z", + "sealed_at": "2026-10-18T11:13:39Z", "sealed_by": "t27c-bootstrap@0.5.2", - "spec_hash": "sha256:073d8dfb6e490e426ea4ae1c7df0233f0e9c575a75b15938060ded38d7723bed", + "spec_hash": "sha256:2ac0e9d3046d86cf3d2f10974b1ebb9c474cc4e2d44fa3226a4ec0de3d7bedf3", "spec_path": "specs/t27f/token_floor.t27", "tests": { "failed": 0, "failing": [], "forced": false, - "passed": 26, - "total": 26, + "passed": 28, + "total": 28, "vacuous": 0 }, "toolchain": { diff --git a/specs/t27f/token_floor.t27 b/specs/t27f/token_floor.t27 index dae68fbeec..7ac808680c 100644 --- a/specs/t27f/token_floor.t27 +++ b/specs/t27f/token_floor.t27 @@ -46,6 +46,8 @@ // were splatted; the raster's own check (touches_row) still decides, so the index only skips rows a // splat cannot light and the bytes are those of a scan over every splat. Without the index each of // the 1920 rows checked all ~42k splats and one process took 51 s; with it, 33 s on one core. +// The empty scene is the same in every frame, so its bytes and each row's light factors are +// computed once; a row exposes only the pixels a splat visited and copies the rest, 19 s. // To use more cores, `main` renders shard k of N // (environment TF_SHARD and TF_SHARDS; unset, 0 of 1, the whole reel): the frames // [shard_a(k, N), shard_b(k, N)). Frame f's state is the integral from frame 0, so every shard steps @@ -683,6 +685,35 @@ fn back_light(kx: f64, ky: f64, gx: f64, gy: f64, below: bool) -> f64 { return BG * FLOOR_DIM + KEY * FLOOR_KEY * kx * ky + GLINT * gx * gy; } +// What the row whose centre is y gives the light of the empty scene, from y alone: the key light's +// vertical factor (seen in the mirror below the floor), the horizon glint's, and the share of a +// mirror image that survives at that depth. Above the floor the glint and the fade are 0. +pub fn row_key(y: f64) -> f64 { + if (y > FLOOR_PX) { + return key_w(2.0 * FLOOR_PX - y - KEY_Y); + } + return key_w(y - KEY_Y); +} + +pub fn row_glint(y: f64) -> f64 { + if (y > FLOOR_PX) { + return glint_y(y - FLOOR_PX); + } + return 0.0; +} + +pub fn row_fade(y: f64) -> f64 { + if (y > FLOOR_PX) { + return @exp(0.0 - (y - FLOOR_PX) / FADE); + } + return 0.0; +} + +// The byte one channel of light l is written as. +pub fn light_byte(l: f64) -> u8 { + return quantize(expose(l)); +} + fn backdrop(x: f64, y: f64) -> f64 { if (y <= FLOOR_PX) { return back_light(key_w(x - KEY_X), key_w(y - KEY_Y), 0.0, 0.0, false); @@ -981,6 +1012,35 @@ pub fn main() -> void { gxa[px] = glint_x(x - TUBE_X); px = px + 1; } + // the empty scene is the same in every frame: its row factors and its bytes are computed once, + // and a pixel no splat visits in a frame is written from them + var kya : [1920]f64 = undefined; + var gya : [1920]f64 = undefined; + var fra : [1920]f64 = undefined; + var bgq : [6220800]u8 = undefined; + var dirty : [1080]bool = undefined; + var py0 : usize = 0; + while (py0 < 1920) { + const y = @as(f64, @floatFromInt(py0)) + 0.5; + kya[py0] = row_key(y); + gya[py0] = row_glint(y); + fra[py0] = row_fade(y); + px = 0; + while (px < 1080) { + const b = back_light(kxa[px], kya[py0], gxa[px], gya[py0], y > FLOOR_PX); + const o = py0 * ROW_BYTES + px * 3; + bgq[o] = light_byte(b * TINT_R); + bgq[o + 1] = light_byte(b * TINT_G); + bgq[o + 2] = light_byte(b * TINT_B); + px = px + 1; + } + py0 = py0 + 1; + } + px = 0; + while (px < 1080) { + dirty[px] = false; + px = px + 1; + } const mir0 = reflect_alpha(FLOOR_PX); // the physics runs from frame 0 to the shard's last frame; the picture only inside [fa, fb) @@ -1211,27 +1271,17 @@ pub fn main() -> void { } // ---- raster: one row at a time, over the splats its index lists ---- + // A pixel takes the empty scene's light the first time a splat visits it in the row and is + // marked dirty; only dirty pixels are exposed, the rest are copied from bgq. A visit that adds + // nothing leaves the light as it was, so a dirty pixel writes the byte bgq holds for it. var py : i32 = 0; while (py < FRAME_H) { const y = @as(f64, @floatFromInt(py)) + 0.5; const below = y > FLOOR_PX; - var ky : f64 = key_w(y - KEY_Y); - var gy : f64 = 0.0; - var fr : f64 = 0.0; - if (below) { - ky = key_w(2.0 * FLOOR_PX - y - KEY_Y); - gy = glint_y(y - FLOOR_PX); - fr = @exp(0.0 - (y - FLOOR_PX) / FADE); - } - px = 0; - while (px < 1080) { - const b = back_light(kxa[px], ky, gxa[px], gy, below); - accr[px] = b * TINT_R; - accg[px] = b * TINT_G; - accb[px] = b * TINT_B; - px = px + 1; - } const pu = @as(usize, @intCast(py)); + const ky = kya[pu]; + const gy = gya[pu]; + const fr = fra[pu]; var ei : usize = roff[pu]; while (ei < roff[pu + 1]) { j = @as(usize, @intCast(rsp[ei])); @@ -1252,6 +1302,13 @@ pub fn main() -> void { while (q <= qe) { const ddx = @as(f64, @floatFromInt(q)) + 0.5 - sx[j]; const kk = @as(usize, @intCast(q)); + if (dirty[kk] == false) { + const b = back_light(kxa[kk], ky, gxa[kk], gy, below); + accr[kk] = b * TINT_R; + accg[kk] = b * TINT_G; + accb[kk] = b * TINT_B; + dirty[kk] = true; + } if (k == K_BALL || k == K_BALLF) { const dd = @sqrt(ddx * ddx + dyy * dyy); const cov = clamp01(sr[j] - dd + 0.5) * sa[j]; @@ -1293,11 +1350,19 @@ pub fn main() -> void { } ei = ei + 1; } + const o = pu * ROW_BYTES; px = 0; while (px < 1080) { - row[px * 3] = quantize(expose(accr[px])); - row[px * 3 + 1] = quantize(expose(accg[px])); - row[px * 3 + 2] = quantize(expose(accb[px])); + if (dirty[px]) { + row[px * 3] = light_byte(accr[px]); + row[px * 3 + 1] = light_byte(accg[px]); + row[px * 3 + 2] = light_byte(accb[px]); + dirty[px] = false; + } else { + row[px * 3] = bgq[o + px * 3]; + row[px * 3 + 1] = bgq[o + px * 3 + 1]; + row[px * 3 + 2] = bgq[o + px * 3 + 2]; + } px = px + 1; } std.debug.print("{s}", .{row}); @@ -1567,6 +1632,61 @@ test "the field is near-black and a lit dot is bright without clipping" { assert(expose(COL[2]) < expose(COL[1])); } +test "a row's light comes from its centre alone" { + // on the key light's centre line its factor is exactly 1, and the mirror sees that line as far + // below the floor as the key is above it + assert(row_key(KEY_Y) == 1.0); + assert(row_key(2.0 * FLOOR_PX - KEY_Y) == 1.0); + assert(row_key(FLOOR_PX + 0.5) == key_w(FLOOR_PX - 0.5 - KEY_Y)); + // no glint and no image above the floor or on the floor line itself + assert(row_glint(FLOOR_PX) == 0.0); + assert(row_fade(FLOOR_PX) == 0.0); + assert(row_glint(FLOOR_PX - 0.5) == 0.0); + assert(row_fade(FLOOR_PX - 0.5) == 0.0); + // one glint width below the floor the glint is e^-1/2, one fade length below the image e^-1 + assert(row_glint(FLOOR_PX + GLINT_W) == @exp(0.0 - 0.5)); + assert(row_fade(FLOOR_PX + FADE) == @exp(0.0 - 1.0)); + // the raster's row factors give the backdrop, bit for bit, on every 7th row and 13th column + var bad : i64 = 0; + var seen : i64 = 0; + var py : i32 = 0; + while (py < FRAME_H) { + const y = @as(f64, @floatFromInt(py)) + 0.5; + var px : i32 = 0; + while (px < FRAME_W) { + const x = @as(f64, @floatFromInt(px)) + 0.5; + const b = back_light(key_w(x - KEY_X), row_key(y), glint_x(x - TUBE_X), row_glint(y), y > FLOOR_PX); + if (b != backdrop(x, y)) { + bad = bad + 1; + } + seen = seen + 1; + px = px + 13; + } + py = py + 7; + } + assert(bad == 0); + assert(seen == 23100); +} + +test "the empty scene's bytes are the ones the reel writes where no splat has been" { + assert(light_byte(0.0) == 0); + assert(light_byte(100.0) == 255); + assert(light_byte(BG) == 10); + // read off frame 0 of the reel, whose pixels here no splat reaches: the corners, the key + // light's centre, and the floor line by the tube, just above it and just below + assert(light_byte(backdrop(0.5, 0.5) * TINT_R) == 11); + assert(light_byte(backdrop(0.5, 0.5) * TINT_G) == 10); + assert(light_byte(backdrop(0.5, 0.5) * TINT_B) == 9); + assert(light_byte(backdrop(1079.5, 0.5) * TINT_R) == 12); + assert(light_byte(backdrop(1079.5, 1919.5) * TINT_G) == 8); + assert(light_byte(backdrop(0.5, 1919.5) * TINT_B) == 7); + assert(light_byte(backdrop(760.5, 979.5) * TINT_R) == 18); + assert(light_byte(backdrop(859.5, 1639.5) * TINT_R) == 14); + assert(light_byte(backdrop(859.5, 1640.5) * TINT_R) == 35); + assert(light_byte(backdrop(859.5, 1640.5) * TINT_G) == 32); + assert(light_byte(backdrop(859.5, 1640.5) * TINT_B) == 29); +} + test "a ball is lit from up-left, has a rim, and its mirror image is lit from below" { const hx = -0.23; const hy = -0.30;