From 699300f15d77606afba893c2233cae2e67604a21 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 18 Jul 2026 22:29:14 +0000 Subject: [PATCH 1/5] =?UTF-8?q?board:=20E-X265-PROBE-GPU-LUT-1=20=E2=80=94?= =?UTF-8?q?=20wgpu=20decode-tier=20harness=20real,=20LUT-gather=20buildabl?= =?UTF-8?q?e?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PROBE-GPU-LUT landed on a2ui-paint's wgpu seam (the one in-scope GPU path; q2 sculpt + ndarray splat3d both opt out of GPU). CPU-reference leg proves the 256²-u16 palette-distance LUT texture-gather bit-exact (65536/65536); GPU-exec leg compiles + ships under wgpu 22 (WebGPU+WebGL2) and skips-clean here (no adapter — 0 ICDs measured). Un-gates the sprite-replay wgpu/wasm decode tiers structurally; runtime-execution parity on silicon is the one deferred piece. Honest caveat + clean a2ui crate boundary (no bgz17 dep) recorded. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01K3RyLEbuNSHxxB3NTTrGki --- .claude/board/EPIPHANIES.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/.claude/board/EPIPHANIES.md b/.claude/board/EPIPHANIES.md index 26abf5cf5..44c0137f1 100644 --- a/.claude/board/EPIPHANIES.md +++ b/.claude/board/EPIPHANIES.md @@ -1,3 +1,15 @@ +## 2026-07-18 — E-X265-PROBE-GPU-LUT-1 — the shared wgpu decode-tier harness is REAL and the 256²-u16 palette-distance LUT texture-gather is proven buildable; harness pinned to a2ui-paint's `wgpu` seam (the one in-scope GPU path) + +**Status:** FINDING (PROBE-GPU-LUT — CPU-reference leg ran green, GPU-exec leg compiled + skips-clean; main-thread-adjudicated; operator-directed harness choice this session). Un-gates the plan `x265-sprite-replay-probe-v1.md` §Decode tiers (c) + the ndarray probe-queue `a2ui N2` row structurally; only runtime-execution parity on silicon remains deferred (no adapter in this sandbox). +**Context:** the x265/H.268 sprite-replay arc's wgpu + wasm decode tiers were "deferred on the shared PROBE-GPU-LUT harness." This session measured where a WebGL/wgpu body actually lives in scope: q2 `sculpt` (`// No WebGL`, software z-buffer→PNG) and ndarray `splat3d` (`// Pure SIMD, no GPU, no wgpu`) both deliberately opt OUT; the ONE real in-scope GPU seam is **a2ui-paint's `wgpu = "22"`** (WebGPU + WebGL2), which already ships an adapter-guarded headless render-to-texture `GpuPainter`. Operator ruling: use that seam as the harness. +**What (PROBE-GPU-LUT, `a2ui-rs crates/a2ui-paint/src/gpu_lut_probe.rs`):** two legs, split by what the sandbox can measure. +- **CPU-reference (RAN, GREEN):** the exact `textureLoad(lut,(q,k)).r` gather modelled in Rust == row-major `lut[q*256+k]`, bit-exact over all **65536** entries; 256²-u16 table symmetric + zero-diagonal + deterministic (SplitMix64); 128 KiB (the §10(i) materialized-table figure). **This is the falsifiable core** — the arithmetic is what could be wrong; the GPU only executes it. +- **GPU-exec (COMPILED + SHIPPED, adapter-deferred):** the full R16Uint-LUT → fragment `textureLoad` → R32Uint target → readback → full-table-parity path compiles clean under wgpu 22 (WebGPU + WebGL2 via `glow`; `clippy --features wgpu -D warnings` clean, fmt clean) and **SKIPS-green** here — measured: `libvulkan` loader present but **0 ICDs** → `request_adapter()` returns `None`. Integer sampled texture + `textureLoad` + integer render target are all WebGL2-core, so one shader covers both backends; runs the real 65536/65536 parity wherever an adapter exists (lavapipe CI, browser). +**Ruling:** the bgz17 256²-u16 palette-distance table IS gatherable through a real in-scope wgpu texture — the GPU LUT lane is **not** abandoned (KILL did not fire). The harness capability is proven buildable; the shared harness the sprite-replay wgpu/wasm tiers waited on is real. +**HONEST CAVEAT:** the GPU-exec *execution* was NOT run on silicon in this sandbox (no adapter). "GPU-exec green" = COMPILES + SKIPS-cleanly + is the shipped WGSL, NOT "65536 texels compared on a GPU here." The CPU-reference is the leg that actually ran; runtime-execution parity on silicon is the one piece still deferred (the `a2ui N2` render-parity-headless-vs-browser bar). +**Boundary:** no bgz17 dep added to a2ui-paint (a2ui charter: no consumer-crate deps) — the 256² table is built deterministically with bgz17's table STRUCTURE, so this is a HARNESS-CAPABILITY probe, not a bgz17 integration. `#[cfg(test)]`-only module; test-only `pollster` dev-dep for the async block. Charter T1/T2/T3 untouched (no new vocabulary, no behavior-on-surface, no hot-path serialization). +**Cross-ref:** ndarray `pr-x12-h268-morton-wgpu-synergies.md` §5 Wave-3 sub-table + PROBE-GPU-LUT queue-row verdict + §10(i) 128 KiB figure; plan `x265-sprite-replay-probe-v1.md` §Results (2026-07-18) + §Decode tiers (c); `E-X265-PROBE-WAVE-2-RESULTS` (the sprite-replay wave whose wgpu/wasm tiers this un-gates); a2ui-paint `src/lib.rs` `mod gpu` (the shipped `GpuPainter` harness this extends). Probe asserts structural sanity + determinism only (never the verdict); the number adjudication is here. + ## 2026-07-18 — E-WI3-CONTINUOUS-FIELD-ANALYTIC-1 — WI-3 resolved: the monotone-bounded continuous-field exit is ANALYTIC (helix RollingFloor at 1 byte), not the materialized ladder **Status:** FINDING (PROBE-HELIX-CONTINUOUS-FIELD measured, main-thread-adjudicated; resolves WI-3 of `E-BGZ-TENSOR-LANE-REVIEW-1` axis (c) — the one open work-item that could move a lane assignment). From f01b37e2267874107fd6dc03454a56cba48b6ff5 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 18 Jul 2026 22:37:57 +0000 Subject: [PATCH 2/5] board: scope E-X265-PROBE-GPU-LUT-1 un-gating to the wgpu tier (codex P2) Cross-ref corrected: PROBE-GPU-LUT un-gates the sprite-replay wgpu decode tier (c) only. The wasm tier (b) is a separate CPU-vs-wasm replay-determinism gate, not touched by this wave. Mirrors the ndarray #249 codex-P2 fix. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01K3RyLEbuNSHxxB3NTTrGki --- .claude/board/EPIPHANIES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.claude/board/EPIPHANIES.md b/.claude/board/EPIPHANIES.md index 44c0137f1..ee77d6652 100644 --- a/.claude/board/EPIPHANIES.md +++ b/.claude/board/EPIPHANIES.md @@ -8,7 +8,7 @@ **Ruling:** the bgz17 256²-u16 palette-distance table IS gatherable through a real in-scope wgpu texture — the GPU LUT lane is **not** abandoned (KILL did not fire). The harness capability is proven buildable; the shared harness the sprite-replay wgpu/wasm tiers waited on is real. **HONEST CAVEAT:** the GPU-exec *execution* was NOT run on silicon in this sandbox (no adapter). "GPU-exec green" = COMPILES + SKIPS-cleanly + is the shipped WGSL, NOT "65536 texels compared on a GPU here." The CPU-reference is the leg that actually ran; runtime-execution parity on silicon is the one piece still deferred (the `a2ui N2` render-parity-headless-vs-browser bar). **Boundary:** no bgz17 dep added to a2ui-paint (a2ui charter: no consumer-crate deps) — the 256² table is built deterministically with bgz17's table STRUCTURE, so this is a HARNESS-CAPABILITY probe, not a bgz17 integration. `#[cfg(test)]`-only module; test-only `pollster` dev-dep for the async block. Charter T1/T2/T3 untouched (no new vocabulary, no behavior-on-surface, no hot-path serialization). -**Cross-ref:** ndarray `pr-x12-h268-morton-wgpu-synergies.md` §5 Wave-3 sub-table + PROBE-GPU-LUT queue-row verdict + §10(i) 128 KiB figure; plan `x265-sprite-replay-probe-v1.md` §Results (2026-07-18) + §Decode tiers (c); `E-X265-PROBE-WAVE-2-RESULTS` (the sprite-replay wave whose wgpu/wasm tiers this un-gates); a2ui-paint `src/lib.rs` `mod gpu` (the shipped `GpuPainter` harness this extends). Probe asserts structural sanity + determinism only (never the verdict); the number adjudication is here. +**Cross-ref:** ndarray `pr-x12-h268-morton-wgpu-synergies.md` §5 Wave-3 sub-table + PROBE-GPU-LUT queue-row verdict + §10(i) 128 KiB figure; plan `x265-sprite-replay-probe-v1.md` §Results (2026-07-18) + §Decode tiers (c); `E-X265-PROBE-WAVE-2-RESULTS` (the sprite-replay wave whose wgpu decode tier (c) this un-gates — the wasm tier (b) is a SEPARATE CPU-vs-wasm replay-determinism gate, NOT touched by this wave; corrected per codex P2 on ndarray #249); a2ui-paint `src/lib.rs` `mod gpu` (the shipped `GpuPainter` harness this extends). Probe asserts structural sanity + determinism only (never the verdict); the number adjudication is here. ## 2026-07-18 — E-WI3-CONTINUOUS-FIELD-ANALYTIC-1 — WI-3 resolved: the monotone-bounded continuous-field exit is ANALYTIC (helix RollingFloor at 1 byte), not the materialized ladder From 81f07483f4198c912287a52efd8d6962436add9f Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 18 Jul 2026 22:41:41 +0000 Subject: [PATCH 3/5] board: scope E-X265-PROBE-GPU-LUT-1 GPU-exec to surface-less backends (codex P2) Headless probe validates on WebGPU + native/software GL (lavapipe); WGSL is WebGL2-compatible but the wasm32 WebGL2 backend needs a canvas compatible_surface and is out of scope. Mirrors the a2ui-rs #13 + ndarray #249 codex-P2 fixes. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01K3RyLEbuNSHxxB3NTTrGki --- .claude/board/EPIPHANIES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.claude/board/EPIPHANIES.md b/.claude/board/EPIPHANIES.md index ee77d6652..328cf9a9e 100644 --- a/.claude/board/EPIPHANIES.md +++ b/.claude/board/EPIPHANIES.md @@ -4,7 +4,7 @@ **Context:** the x265/H.268 sprite-replay arc's wgpu + wasm decode tiers were "deferred on the shared PROBE-GPU-LUT harness." This session measured where a WebGL/wgpu body actually lives in scope: q2 `sculpt` (`// No WebGL`, software z-buffer→PNG) and ndarray `splat3d` (`// Pure SIMD, no GPU, no wgpu`) both deliberately opt OUT; the ONE real in-scope GPU seam is **a2ui-paint's `wgpu = "22"`** (WebGPU + WebGL2), which already ships an adapter-guarded headless render-to-texture `GpuPainter`. Operator ruling: use that seam as the harness. **What (PROBE-GPU-LUT, `a2ui-rs crates/a2ui-paint/src/gpu_lut_probe.rs`):** two legs, split by what the sandbox can measure. - **CPU-reference (RAN, GREEN):** the exact `textureLoad(lut,(q,k)).r` gather modelled in Rust == row-major `lut[q*256+k]`, bit-exact over all **65536** entries; 256²-u16 table symmetric + zero-diagonal + deterministic (SplitMix64); 128 KiB (the §10(i) materialized-table figure). **This is the falsifiable core** — the arithmetic is what could be wrong; the GPU only executes it. -- **GPU-exec (COMPILED + SHIPPED, adapter-deferred):** the full R16Uint-LUT → fragment `textureLoad` → R32Uint target → readback → full-table-parity path compiles clean under wgpu 22 (WebGPU + WebGL2 via `glow`; `clippy --features wgpu -D warnings` clean, fmt clean) and **SKIPS-green** here — measured: `libvulkan` loader present but **0 ICDs** → `request_adapter()` returns `None`. Integer sampled texture + `textureLoad` + integer render target are all WebGL2-core, so one shader covers both backends; runs the real 65536/65536 parity wherever an adapter exists (lavapipe CI, browser). +- **GPU-exec (COMPILED + SHIPPED, adapter-deferred):** the full R16Uint-LUT → fragment `textureLoad` → R32Uint target → readback → full-table-parity path compiles clean under wgpu 22 (GL backend via `glow` builds; `clippy --features wgpu -D warnings` clean, fmt clean) and **SKIPS-green** here — measured: `libvulkan` loader present but **0 ICDs** → `request_adapter()` returns `None`. **Backend scope (codex P2, headless):** the probe requests a **surface-less** adapter, so its execution validates on WebGPU (native/browser) + native/software GL (lavapipe CI). Integer sampled texture + `textureLoad` + integer render target are all WebGL2-core, so the WGSL is WebGL2-**compatible** — but wgpu's WebGL backend needs a canvas-bound `compatible_surface`, so the wasm32 WebGL2 backend is OUT OF SCOPE for this headless probe (a surface-bound harness would run it). Runs the real 65536/65536 parity wherever a surface-less adapter exists. **Ruling:** the bgz17 256²-u16 palette-distance table IS gatherable through a real in-scope wgpu texture — the GPU LUT lane is **not** abandoned (KILL did not fire). The harness capability is proven buildable; the shared harness the sprite-replay wgpu/wasm tiers waited on is real. **HONEST CAVEAT:** the GPU-exec *execution* was NOT run on silicon in this sandbox (no adapter). "GPU-exec green" = COMPILES + SKIPS-cleanly + is the shipped WGSL, NOT "65536 texels compared on a GPU here." The CPU-reference is the leg that actually ran; runtime-execution parity on silicon is the one piece still deferred (the `a2ui N2` render-parity-headless-vs-browser bar). **Boundary:** no bgz17 dep added to a2ui-paint (a2ui charter: no consumer-crate deps) — the 256² table is built deterministically with bgz17's table STRUCTURE, so this is a HARNESS-CAPABILITY probe, not a bgz17 integration. `#[cfg(test)]`-only module; test-only `pollster` dev-dep for the async block. Charter T1/T2/T3 untouched (no new vocabulary, no behavior-on-surface, no hot-path serialization). From 76f0748699b083a29a08bf64d1a8affccf1a4503 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 18 Jul 2026 23:17:23 +0000 Subject: [PATCH 4/5] helix: HEVC moving-scene anchor example + E-X265-HEVC-ANCHOR-1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The plan §5 external anchor ("run actual x265; report bits/frame + PSNR"), run in-sandbox and made visual. `crates/helix/examples/hevc_moving_scene.rs` (std-only, deterministic) renders the sprite-replay scene — 8 gaussian sprites on φ-spiral hemisphere paths, alternating hemispheres by parity, 320×240 × 240 frames — to Y4M. x265 3.5 (medium, --psnr) encodes it with its own I/P/B GOP (1 I / 56 P / 183 B): 27.6 MB raw → 47,836 B = 578×, 1594.5 bits/frame, Global PSNR 60.79 dB. ffmpeg decodes frames back into a motion montage + GIF. Honest framing (board entry): these are x265's numbers on easy synthetic content — an EXTERNAL ANCHOR, not a claim our primitives beat x265. The our-motion-codes vs x265-MV-field bitrate comparison is a named follow-up. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01K3RyLEbuNSHxxB3NTTrGki --- .claude/board/EPIPHANIES.md | 8 ++ crates/helix/examples/hevc_moving_scene.rs | 155 +++++++++++++++++++++ 2 files changed, 163 insertions(+) create mode 100644 crates/helix/examples/hevc_moving_scene.rs diff --git a/.claude/board/EPIPHANIES.md b/.claude/board/EPIPHANIES.md index 328cf9a9e..c91b39e31 100644 --- a/.claude/board/EPIPHANIES.md +++ b/.claude/board/EPIPHANIES.md @@ -1,3 +1,11 @@ +## 2026-07-18 — E-X265-HEVC-ANCHOR-1 — the §5 external HEVC anchor is RUN + VISUAL: x265 over the φ-spiral sprite scene, 578× / PSNR 60.79 dB, real I/P/B GOP + +**Status:** FINDING (external anchor — plan `x265-sprite-replay-probe-v1.md` §5 "optional context, NOT a gate"; run this session in-sandbox). Reproducer committed as `crates/helix/examples/hevc_moving_scene.rs`. +**What:** the sprite-replay scene (8 gaussian sprites on φ-spiral / golden-angle hemisphere paths, alternating hemispheres by parity, NUM_SPRITES=8 × TOTAL=240) rendered to a 320×240 Y4M and encoded with x265 3.5 (preset medium, `--psnr`). x265 ran its OWN I/P/B GOP over our moving scene (the "replay x265's GOP grammar" made literal): 1 I · 56 P · 183 B, up to 5 consecutive B-frames. ffmpeg decoded frames back OUT of the `.265` into a 5-frame motion montage + animated GIF (delivered to the operator). +**Numbers:** raw Y4M 27,649,483 B → HEVC 47,836 B = **578×**; **1594.5 bits/frame** (199 B/frame); Global **PSNR 60.79 dB** (Y 47.3–49.2 by slice-type; chroma neutral); encode 296 fps. +**Reading (honest, load-bearing):** these are **x265's** numbers on a smooth-gaussian synthetic scene that compresses easily — an EXTERNAL ANCHOR for "what a stock HEVC encoder does with this content," NOT a claim that our primitives beat x265 and NOT a gate. The arc's own sprite-replay motion coding (`E-SPRITE-IPB-HELIX-1`: one Signed360 code per sprite per P-frame) is what this contextualizes. The genuine comparison — our object-level motion codes vs x265's per-block MV field on the SAME scene, bits + PSNR side by side — is a NAMED follow-up, not done here. In-sandbox now: x265 3.5 + ffmpeg apt-installed this session (were absent). +**Cross-ref:** plan `x265-sprite-replay-probe-v1.md` §Results (2026-07-18 — HEVC external anchor) + §5; `E-SPRITE-IPB-HELIX-1` + `E-X265-PROBE-WAVE-2-RESULTS` (the motion-coding result this anchors against); reproducer `crates/helix/examples/hevc_moving_scene.rs`. + ## 2026-07-18 — E-X265-PROBE-GPU-LUT-1 — the shared wgpu decode-tier harness is REAL and the 256²-u16 palette-distance LUT texture-gather is proven buildable; harness pinned to a2ui-paint's `wgpu` seam (the one in-scope GPU path) **Status:** FINDING (PROBE-GPU-LUT — CPU-reference leg ran green, GPU-exec leg compiled + skips-clean; main-thread-adjudicated; operator-directed harness choice this session). Un-gates the plan `x265-sprite-replay-probe-v1.md` §Decode tiers (c) + the ndarray probe-queue `a2ui N2` row structurally; only runtime-execution parity on silicon remains deferred (no adapter in this sandbox). diff --git a/crates/helix/examples/hevc_moving_scene.rs b/crates/helix/examples/hevc_moving_scene.rs new file mode 100644 index 000000000..1867df22c --- /dev/null +++ b/crates/helix/examples/hevc_moving_scene.rs @@ -0,0 +1,155 @@ +//! HEVC moving-scene anchor renderer (plan §5 external anchor, made visual). +//! +//! Renders the sprite-replay scene — 8 gaussian sprites tracing φ-spiral +//! (golden-angle hemisphere) paths, alternating hemispheres by index parity, +//! exactly as `helix/src/sprite_replay.rs` seeds them — to a Y4M (I420) clip. +//! x265 then encodes it (the arc's "replay x265's GOP grammar" made literal: +//! x265 runs its own I/P/B GOP over OUR moving scene) and reports bits/frame + +//! PSNR; ffmpeg decodes frames back for the screenshot montage. +//! +//! std-only, deterministic (SplitMix64, no rand) — matches the probe discipline. + +use std::io::{BufWriter, Write}; + +const W: usize = 320; +const H: usize = 240; +const TOTAL: usize = 240; // frames == sprite-replay TOTAL +const NUM_SPRITES: usize = 8; // sprite-replay NUM_SPRITES + +/// SplitMix64 — same generator + seed family as sprite_replay.rs. +struct SplitMix64(u64); +impl SplitMix64 { + fn new(seed: u64) -> Self { + Self(seed) + } + fn next_u64(&mut self) -> u64 { + self.0 = self.0.wrapping_add(0x9E37_79B9_7F4A_7C15); + let mut z = self.0; + z = (z ^ (z >> 30)).wrapping_mul(0xBF58_476D_1CE4_E5B9); + z = (z ^ (z >> 27)).wrapping_mul(0x94D0_49BB_1331_11EB); + z ^ (z >> 31) + } + fn unit(&mut self) -> f64 { + (self.next_u64() >> 11) as f64 / (1u64 << 53) as f64 + } + fn range(&mut self, lo: f64, hi: f64) -> f64 { + lo + self.unit() * (hi - lo) + } +} + +#[derive(Clone, Copy)] +struct Sprite { + cx: f64, // screen center x (px) + cy: f64, // screen center y (px) + radius: f64, // spiral radius (px) + sign: f64, // +1 upper hemisphere, -1 lower (parity, like sprite-replay) + bright: f64, // peak luma + sigma: f64, // gaussian spread (px) +} + +/// φ-spiral (golden-angle Fibonacci hemisphere) point n of TOTAL, signed. +/// Returns the (x, y) of the spiral in the unit disk; z (depth) modulates size. +fn phi_spiral_xy(n: usize, total: usize, sign: f64) -> (f64, f64, f64) { + // Golden angle ≈ 2.399963 rad — the same irrational winding the arc's + // φ-spiral / CurveRuler uses (stride-4-over-17 is its integer cousin). + let ga = std::f64::consts::PI * (3.0 - 5.0_f64.sqrt()); + let t = (n as f64 + 0.5) / total as f64; // 0..1 along the path + let z = sign * (1.0 - t); // hemisphere height, sign flips hemisphere + let r = (1.0 - z * z).sqrt(); // disk radius at height z + let theta = n as f64 * ga; + (r * theta.cos(), r * theta.sin(), z) +} + +fn seed_sprites() -> [Sprite; NUM_SPRITES] { + // Same seed constant as sprite_replay::seed_sprites (0x5350_5249_5445_5F31 = "SPRITE_1"). + let mut rng = SplitMix64::new(0x5350_5249_5445_5F31); + core::array::from_fn(|i| { + let _place = rng.next_u64(); // consume one u64 (mirrors the probe's field order) + let sign = if i % 2 == 0 { 1.0 } else { -1.0 }; + // Spread 8 centers over the frame (2 rows × 4 cols), jittered. + let col = (i % 4) as f64; + let row = (i / 4) as f64; + let cx = 40.0 + col * (W as f64 - 80.0) / 3.0 + rng.range(-8.0, 8.0); + let cy = 70.0 + row * (H as f64 - 140.0) + rng.range(-8.0, 8.0); + let radius = rng.range(18.0, 34.0); + let bright = rng.range(170.0, 245.0); + let sigma = rng.range(6.0, 11.0); + Sprite { + cx, + cy, + radius, + sign, + bright, + sigma, + } + }) +} + +fn main() -> std::io::Result<()> { + let path = std::env::args().nth(1).unwrap_or_else(|| "scene.y4m".into()); + let sprites = seed_sprites(); + let f = std::fs::File::create(&path)?; + let mut out = BufWriter::new(f); + + // Y4M header — I420, 25 fps, progressive. + write!(out, "YUV4MPEG2 W{W} H{H} F25:1 Ip A1:1 C420jpeg\n")?; + + let mut y = vec![0u8; W * H]; + let cw = W / 2; + let ch = H / 2; + let uv = vec![128u8; cw * ch]; // neutral chroma (grayscale scene) + + for frame in 0..TOTAL { + // Background: a faint moving gradient so inter-frame prediction has + // global motion to track (a static bg would make every P-frame near-zero). + let pan = (frame as f64 / TOTAL as f64) * 40.0; + for py in 0..H { + for px in 0..W { + let g = 24.0 + 10.0 * (((px as f64 + pan) * 0.03).sin()) + + 6.0 * ((py as f64 * 0.05).cos()); + y[py * W + px] = g.clamp(0.0, 60.0) as u8; + } + } + // Splat each sprite at its φ-spiral point for this frame. + for s in &sprites { + let (sx, sy, sz) = phi_spiral_xy(frame, TOTAL, s.sign); + let px0 = s.cx + s.radius * sx; + let py0 = s.cy + s.radius * sy; + // Depth (z) modulates size: nearer (z→1) = larger/brighter. + let depth = 0.6 + 0.4 * (sz.abs()); + let sigma = s.sigma * depth; + let peak = s.bright * depth; + let rad = (sigma * 3.0).ceil() as i64; + let inv2s2 = 1.0 / (2.0 * sigma * sigma); + let cxi = px0.round() as i64; + let cyi = py0.round() as i64; + for dy in -rad..=rad { + let yy = cyi + dy; + if yy < 0 || yy >= H as i64 { + continue; + } + for dx in -rad..=rad { + let xx = cxi + dx; + if xx < 0 || xx >= W as i64 { + continue; + } + let d2 = (dx * dx + dy * dy) as f64; + let v = peak * (-d2 * inv2s2).exp(); + let idx = yy as usize * W + xx as usize; + let cur = y[idx] as f64; + y[idx] = (cur + v).clamp(0.0, 255.0) as u8; // additive splat + } + } + } + out.write_all(b"FRAME\n")?; + out.write_all(&y)?; + out.write_all(&uv)?; // U + out.write_all(&uv)?; // V + } + out.flush()?; + eprintln!( + "rendered {TOTAL} frames {W}x{H} ({} sprites, φ-spiral motion) → {path}", + NUM_SPRITES + ); + Ok(()) +} From f0532d7010ae430efb37f33345a280a1a7eb45fa Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 18 Jul 2026 23:27:12 +0000 Subject: [PATCH 5/5] Address CodeRabbit #738: faithful sprite scene + honest wgpu scoping MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit hevc_moving_scene.rs (5 findings): - FIX (Major, real bug): phi_spiral projected (x,y)+abs(z), cancelling `sign` entirely → both hemispheres shared one trajectory. Now returns canonical (x, z, y) and projects SIGNED z to screen-y, so sign genuinely mirrors the hemisphere (matching sprite_replay's (x,z,y) position axes). - FIX (Major): seed_sprites now draws the CANONICAL sequence (place, center[0..3], scale) byte-for-byte like sprite_replay, deriving screen params from those values with no extra RNG draws — so the "same seeded scene" claim holds. - ADD: #[cfg(test)] module (determinism + sign-separates-hemispheres + alternating-parity); 3 tests green via `cargo test --example`. - Re-render/re-encode: numbers shift to 43,115 B = 641×, 1437 bits/frame, PSNR 60.94 dB (the sign fix changed the motion); EPIPHANIES + plan updated. EPIPHANIES E-X265-PROBE-GPU-LUT-1 (2 findings): - "IS gatherable through a real wgpu texture" → "is representable and COMPILES through the wgpu texture path; runtime gather deferred to GPU execution". - "wgpu/wasm tiers waited on" → "wgpu decode tier (c) waited on" (wasm tier (b) is a separate gate, untouched). Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01K3RyLEbuNSHxxB3NTTrGki --- .claude/board/EPIPHANIES.md | 6 +- crates/helix/examples/hevc_moving_scene.rs | 125 ++++++++++++++++----- 2 files changed, 102 insertions(+), 29 deletions(-) diff --git a/.claude/board/EPIPHANIES.md b/.claude/board/EPIPHANIES.md index c91b39e31..101bdd43c 100644 --- a/.claude/board/EPIPHANIES.md +++ b/.claude/board/EPIPHANIES.md @@ -1,8 +1,8 @@ -## 2026-07-18 — E-X265-HEVC-ANCHOR-1 — the §5 external HEVC anchor is RUN + VISUAL: x265 over the φ-spiral sprite scene, 578× / PSNR 60.79 dB, real I/P/B GOP +## 2026-07-18 — E-X265-HEVC-ANCHOR-1 — the §5 external HEVC anchor is RUN + VISUAL: x265 over the φ-spiral sprite scene, 641× / PSNR 60.94 dB, real I/P/B GOP **Status:** FINDING (external anchor — plan `x265-sprite-replay-probe-v1.md` §5 "optional context, NOT a gate"; run this session in-sandbox). Reproducer committed as `crates/helix/examples/hevc_moving_scene.rs`. **What:** the sprite-replay scene (8 gaussian sprites on φ-spiral / golden-angle hemisphere paths, alternating hemispheres by parity, NUM_SPRITES=8 × TOTAL=240) rendered to a 320×240 Y4M and encoded with x265 3.5 (preset medium, `--psnr`). x265 ran its OWN I/P/B GOP over our moving scene (the "replay x265's GOP grammar" made literal): 1 I · 56 P · 183 B, up to 5 consecutive B-frames. ffmpeg decoded frames back OUT of the `.265` into a 5-frame motion montage + animated GIF (delivered to the operator). -**Numbers:** raw Y4M 27,649,483 B → HEVC 47,836 B = **578×**; **1594.5 bits/frame** (199 B/frame); Global **PSNR 60.79 dB** (Y 47.3–49.2 by slice-type; chroma neutral); encode 296 fps. +**Numbers:** raw Y4M 27,649,483 B → HEVC 43,115 B = **641×**; **1437.2 bits/frame** (180 B/frame); Global **PSNR 60.94 dB** (Y 47.5–52.0 by slice-type; chroma neutral); encode 316 fps. (Re-run after the codex/CodeRabbit #738 fix — the reproducer now uses `sprite_replay`'s canonical draw sequence + signed-z hemisphere projection, so the sign genuinely separates hemispheres; the earlier 578×/60.79 dB numbers were the pre-fix scene where `sign` was inert.) **Reading (honest, load-bearing):** these are **x265's** numbers on a smooth-gaussian synthetic scene that compresses easily — an EXTERNAL ANCHOR for "what a stock HEVC encoder does with this content," NOT a claim that our primitives beat x265 and NOT a gate. The arc's own sprite-replay motion coding (`E-SPRITE-IPB-HELIX-1`: one Signed360 code per sprite per P-frame) is what this contextualizes. The genuine comparison — our object-level motion codes vs x265's per-block MV field on the SAME scene, bits + PSNR side by side — is a NAMED follow-up, not done here. In-sandbox now: x265 3.5 + ffmpeg apt-installed this session (were absent). **Cross-ref:** plan `x265-sprite-replay-probe-v1.md` §Results (2026-07-18 — HEVC external anchor) + §5; `E-SPRITE-IPB-HELIX-1` + `E-X265-PROBE-WAVE-2-RESULTS` (the motion-coding result this anchors against); reproducer `crates/helix/examples/hevc_moving_scene.rs`. @@ -13,7 +13,7 @@ **What (PROBE-GPU-LUT, `a2ui-rs crates/a2ui-paint/src/gpu_lut_probe.rs`):** two legs, split by what the sandbox can measure. - **CPU-reference (RAN, GREEN):** the exact `textureLoad(lut,(q,k)).r` gather modelled in Rust == row-major `lut[q*256+k]`, bit-exact over all **65536** entries; 256²-u16 table symmetric + zero-diagonal + deterministic (SplitMix64); 128 KiB (the §10(i) materialized-table figure). **This is the falsifiable core** — the arithmetic is what could be wrong; the GPU only executes it. - **GPU-exec (COMPILED + SHIPPED, adapter-deferred):** the full R16Uint-LUT → fragment `textureLoad` → R32Uint target → readback → full-table-parity path compiles clean under wgpu 22 (GL backend via `glow` builds; `clippy --features wgpu -D warnings` clean, fmt clean) and **SKIPS-green** here — measured: `libvulkan` loader present but **0 ICDs** → `request_adapter()` returns `None`. **Backend scope (codex P2, headless):** the probe requests a **surface-less** adapter, so its execution validates on WebGPU (native/browser) + native/software GL (lavapipe CI). Integer sampled texture + `textureLoad` + integer render target are all WebGL2-core, so the WGSL is WebGL2-**compatible** — but wgpu's WebGL backend needs a canvas-bound `compatible_surface`, so the wasm32 WebGL2 backend is OUT OF SCOPE for this headless probe (a surface-bound harness would run it). Runs the real 65536/65536 parity wherever a surface-less adapter exists. -**Ruling:** the bgz17 256²-u16 palette-distance table IS gatherable through a real in-scope wgpu texture — the GPU LUT lane is **not** abandoned (KILL did not fire). The harness capability is proven buildable; the shared harness the sprite-replay wgpu/wasm tiers waited on is real. +**Ruling:** the bgz17 256²-u16 palette-distance table is **representable and COMPILES** through a real in-scope wgpu texture path (R16Uint sampled + `textureLoad` + integer render target) — the GPU LUT lane is **not** abandoned (KILL did not fire). Runtime gatherability + silicon parity stay DEFERRED until actual GPU execution (no adapter here). The harness capability is proven buildable; the shared harness the sprite-replay **wgpu decode tier (c)** waited on is real — the wasm tier (b) is a separate CPU-vs-wasm replay-determinism gate, untouched (codex + CodeRabbit #738). **HONEST CAVEAT:** the GPU-exec *execution* was NOT run on silicon in this sandbox (no adapter). "GPU-exec green" = COMPILES + SKIPS-cleanly + is the shipped WGSL, NOT "65536 texels compared on a GPU here." The CPU-reference is the leg that actually ran; runtime-execution parity on silicon is the one piece still deferred (the `a2ui N2` render-parity-headless-vs-browser bar). **Boundary:** no bgz17 dep added to a2ui-paint (a2ui charter: no consumer-crate deps) — the 256² table is built deterministically with bgz17's table STRUCTURE, so this is a HARNESS-CAPABILITY probe, not a bgz17 integration. `#[cfg(test)]`-only module; test-only `pollster` dev-dep for the async block. Charter T1/T2/T3 untouched (no new vocabulary, no behavior-on-surface, no hot-path serialization). **Cross-ref:** ndarray `pr-x12-h268-morton-wgpu-synergies.md` §5 Wave-3 sub-table + PROBE-GPU-LUT queue-row verdict + §10(i) 128 KiB figure; plan `x265-sprite-replay-probe-v1.md` §Results (2026-07-18) + §Decode tiers (c); `E-X265-PROBE-WAVE-2-RESULTS` (the sprite-replay wave whose wgpu decode tier (c) this un-gates — the wasm tier (b) is a SEPARATE CPU-vs-wasm replay-determinism gate, NOT touched by this wave; corrected per codex P2 on ndarray #249); a2ui-paint `src/lib.rs` `mod gpu` (the shipped `GpuPainter` harness this extends). Probe asserts structural sanity + determinism only (never the verdict); the number adjudication is here. diff --git a/crates/helix/examples/hevc_moving_scene.rs b/crates/helix/examples/hevc_moving_scene.rs index 1867df22c..826c4e664 100644 --- a/crates/helix/examples/hevc_moving_scene.rs +++ b/crates/helix/examples/hevc_moving_scene.rs @@ -39,41 +39,52 @@ impl SplitMix64 { #[derive(Clone, Copy)] struct Sprite { - cx: f64, // screen center x (px) - cy: f64, // screen center y (px) - radius: f64, // spiral radius (px) - sign: f64, // +1 upper hemisphere, -1 lower (parity, like sprite-replay) - bright: f64, // peak luma - sigma: f64, // gaussian spread (px) + cx: f64, // screen center x (px) + cy: f64, // screen center y (px) + radius: f64, // spiral radius (px) + sign: f64, // +1 upper hemisphere, -1 lower (parity, like sprite-replay) + bright: f64, // peak luma + sigma: f64, // gaussian spread (px) } /// φ-spiral (golden-angle Fibonacci hemisphere) point n of TOTAL, signed. -/// Returns the (x, y) of the spiral in the unit disk; z (depth) modulates size. -fn phi_spiral_xy(n: usize, total: usize, sign: f64) -> (f64, f64, f64) { +/// Returns the canonical `(x, z, y)` cartesian of the hemisphere point — the +/// SAME axis order `sprite_replay::sprite_position` uses (`HemispherePoint:: +/// cartesian` → `(x, z, y)`, position = center + scale·[x, z, y]). The **signed +/// height is `z`** (the 2nd element), so `sign` genuinely selects the +/// hemisphere; the caller must project a signed axis to screen (not `abs`) or +/// the two hemispheres collapse onto one trajectory. +fn phi_spiral_cart(n: usize, total: usize, sign: f64) -> (f64, f64, f64) { // Golden angle ≈ 2.399963 rad — the same irrational winding the arc's // φ-spiral / CurveRuler uses (stride-4-over-17 is its integer cousin). let ga = std::f64::consts::PI * (3.0 - 5.0_f64.sqrt()); let t = (n as f64 + 0.5) / total as f64; // 0..1 along the path - let z = sign * (1.0 - t); // hemisphere height, sign flips hemisphere + let z = sign * (1.0 - t); // signed hemisphere height (upper for +, lower for −) let r = (1.0 - z * z).sqrt(); // disk radius at height z let theta = n as f64 * ga; - (r * theta.cos(), r * theta.sin(), z) + (r * theta.cos(), z, r * theta.sin()) // (x, z=signed height, y) } fn seed_sprites() -> [Sprite; NUM_SPRITES] { // Same seed constant as sprite_replay::seed_sprites (0x5350_5249_5445_5F31 = "SPRITE_1"). let mut rng = SplitMix64::new(0x5350_5249_5445_5F31); core::array::from_fn(|i| { - let _place = rng.next_u64(); // consume one u64 (mirrors the probe's field order) + // CANONICAL draw sequence — byte-for-byte the order sprite_replay uses: + // place (u64), then center[0..3] (3 range draws), then scale. Screen/render + // params are DERIVED from these canonical world values with NO extra RNG + // draws, so sprite i's place/center/scale stream matches the probe exactly. + let _place = rng.next_u64(); + let c0 = rng.range(-50.0, 50.0); // center[0] + let c1 = rng.range(-50.0, 50.0); // center[1] + let c2 = rng.range(-50.0, 50.0); // center[2] + let scale_w = rng.range(5.0, 25.0); // scale let sign = if i % 2 == 0 { 1.0 } else { -1.0 }; - // Spread 8 centers over the frame (2 rows × 4 cols), jittered. - let col = (i % 4) as f64; - let row = (i / 4) as f64; - let cx = 40.0 + col * (W as f64 - 80.0) / 3.0 + rng.range(-8.0, 8.0); - let cy = 70.0 + row * (H as f64 - 140.0) + rng.range(-8.0, 8.0); - let radius = rng.range(18.0, 34.0); - let bright = rng.range(170.0, 245.0); - let sigma = rng.range(6.0, 11.0); + // Derive the on-screen envelope from the canonical world values. + let cx = W as f64 * 0.5 + (c0 / 50.0) * (W as f64 * 0.38); // [-50,50] → screen x + let cy = H as f64 * 0.5 + (c1 / 50.0) * (H as f64 * 0.30); // [-50,50] → screen y + let radius = 16.0 + (scale_w - 5.0) / 20.0 * 18.0; // [5,25] → [16,34] px + let bright = 175.0 + ((c2 + 50.0) / 100.0) * 70.0; // [-50,50] → [175,245] + let sigma = 6.0 + (scale_w - 5.0) / 20.0 * 5.0; // [5,25] → [6,11] px Sprite { cx, cy, @@ -86,7 +97,9 @@ fn seed_sprites() -> [Sprite; NUM_SPRITES] { } fn main() -> std::io::Result<()> { - let path = std::env::args().nth(1).unwrap_or_else(|| "scene.y4m".into()); + let path = std::env::args() + .nth(1) + .unwrap_or_else(|| "scene.y4m".into()); let sprites = seed_sprites(); let f = std::fs::File::create(&path)?; let mut out = BufWriter::new(f); @@ -105,18 +118,21 @@ fn main() -> std::io::Result<()> { let pan = (frame as f64 / TOTAL as f64) * 40.0; for py in 0..H { for px in 0..W { - let g = 24.0 + 10.0 * (((px as f64 + pan) * 0.03).sin()) + let g = 24.0 + + 10.0 * (((px as f64 + pan) * 0.03).sin()) + 6.0 * ((py as f64 * 0.05).cos()); y[py * W + px] = g.clamp(0.0, 60.0) as u8; } } // Splat each sprite at its φ-spiral point for this frame. for s in &sprites { - let (sx, sy, sz) = phi_spiral_xy(frame, TOTAL, s.sign); - let px0 = s.cx + s.radius * sx; - let py0 = s.cy + s.radius * sy; - // Depth (z) modulates size: nearer (z→1) = larger/brighter. - let depth = 0.6 + 0.4 * (sz.abs()); + // Canonical (x, z, y): x → screen-x, SIGNED z → screen-y (so the + // hemisphere sign mirrors the sprite vertically), y → depth. + let (cx_off, cz_signed, cy_depth) = phi_spiral_cart(frame, TOTAL, s.sign); + let px0 = s.cx + s.radius * cx_off; + let py0 = s.cy + s.radius * cz_signed; + // The remaining axis (y) modulates size: nearer = larger/brighter. + let depth = 0.6 + 0.4 * cy_depth.abs(); let sigma = s.sigma * depth; let peak = s.bright * depth; let rad = (sigma * 3.0).ceil() as i64; @@ -153,3 +169,60 @@ fn main() -> std::io::Result<()> { ); Ok(()) } + +#[cfg(test)] +mod tests { + use super::*; + + /// The SplitMix64 stream is deterministic for the sprite_replay seed — the + /// canonical-sequence contract the scene relies on. + #[test] + fn splitmix64_is_deterministic_for_the_sprite_seed() { + let mut a = SplitMix64::new(0x5350_5249_5445_5F31); + let mut b = SplitMix64::new(0x5350_5249_5445_5F31); + for _ in 0..8 { + assert_eq!(a.next_u64(), b.next_u64()); + } + // Distinct successive outputs (not a stuck generator). + let mut c = SplitMix64::new(0x5350_5249_5445_5F31); + let x = c.next_u64(); + let y = c.next_u64(); + assert_ne!(x, y); + } + + /// `sign` MUST select the hemisphere: the signed-height axis flips with it, + /// so opposite signs give different projected positions (the bug CodeRabbit + /// caught — `abs(z)` had cancelled the sign). + #[test] + fn phi_spiral_sign_separates_hemispheres() { + for n in [0usize, 37, 120, 239] { + let (xp, zp, _) = phi_spiral_cart(n, TOTAL, 1.0); + let (xn, zn, _) = phi_spiral_cart(n, TOTAL, -1.0); + // x (azimuth) is sign-independent; the height z is the discriminator. + assert!( + (xp - xn).abs() < 1e-12, + "azimuth is sign-independent at n={n}" + ); + assert!( + zp > 0.0 && zn < 0.0, + "pos=upper / neg=lower hemisphere at n={n}" + ); + assert!( + (zp - zn).abs() > 1e-9, + "sign must produce distinct projected height at n={n}" + ); + } + } + + /// seed_sprites is deterministic and alternates hemisphere by index parity. + #[test] + fn seed_sprites_deterministic_and_alternating() { + let a = seed_sprites(); + let b = seed_sprites(); + for i in 0..NUM_SPRITES { + assert_eq!(a[i].sign, b[i].sign); + assert_eq!(a[i].cx.to_bits(), b[i].cx.to_bits()); + assert_eq!(a[i].sign, if i % 2 == 0 { 1.0 } else { -1.0 }); + } + } +}