Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .claude/blackboard.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
## 2026-10-10 — `tests/splat3d_correctness.rs`: clippy 1.99 clean under `--features splat3d`

- Five lints, all in the test file: an unneeded `mut` on a closure, an over-precise f32 literal (`0.28209479177387814` → `0.282_094_8`; same bits, `0x3e906ebb`), and three `chunks_exact(3)` → `as_chunks::<3>().0.iter()`.
- No behaviour change: `cargo test --features splat3d --test splat3d_correctness` 5/5.
- The file is `#![cfg(feature = "splat3d")]`, so a plain `cargo clippy --test splat3d_correctness` compiles nothing and reports success. The feature has to be on to see these lints.

## 2026-10-10 — `simd_avx.rs`: AVX-without-AVX2 realization (Sandy/Ivy Bridge, Bulldozer–Jaguar), plus three pre-existing x86 fixes

- **What:** a seventh realization, selected at compile time by `all(target_feature = "avx", not(target_feature = "avx2"))`. You get it with `target-cpu=native` on such a CPU, or with `.cargo/config-avx.toml` (`sandybridge`). The types and the `simd.rs` exports are unchanged; the 49 AVX2-only items in `simd_avx2.rs` and 19 in `simd_avx512.rs` are cfg-gated, and `simd_avx.rs` supplies the same methods from SSE2–SSE4.1 halves plus AVX1 float-domain ops. There are no runtime feature checks. The `cpu_guard` AVX2 startup floor applies only when the AVX2 arm is compiled in. Baseline builds (no `avx` cfg) are UNCHANGED: they keep the AVX2 arm and the floor. Whether the SSE tier should take them over is an open operator question.
Expand Down
14 changes: 9 additions & 5 deletions tests/splat3d_correctness.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ use ndarray::hpc::splat3d::{Camera, Gaussian3D, SplatFrame, SplatRenderer, SH_CO
fn build_synthetic_cube_scene(frame: &mut SplatFrame) {
let n = 10;
let mut state = 0xC0FFEEu32;
let mut xor_advance = |s: &mut u32| {
let xor_advance = |s: &mut u32| {
*s ^= *s << 13;
*s ^= *s >> 17;
*s ^= *s << 5;
Expand All @@ -45,7 +45,7 @@ fn build_synthetic_cube_scene(frame: &mut SplatFrame) {
// R = ix/(n-1), G = iy/(n-1), B = iz/(n-1)
// Pre-divide by SH_C0 ≈ 0.282 so the output (which is
// SH_C0 · sh[0] + 0.5) lands at the intended color.
let sh_c0: f32 = 0.28209479177387814;
let sh_c0: f32 = 0.282_094_8;
sh[0] = (ix as f32) / (n - 1) as f32 / sh_c0;
sh[16] = (iy as f32) / (n - 1) as f32 / sh_c0;
sh[32] = (iz as f32) / (n - 1) as f32 / sh_c0;
Expand Down Expand Up @@ -116,7 +116,9 @@ fn end_to_end_synthetic_cube_renders_without_panic() {
// nothing.
let lit_pixels = frame
.framebuffer
.chunks_exact(3)
.as_chunks::<3>()
.0
.iter()
.filter(|p| p[0] > 0.01 || p[1] > 0.01 || p[2] > 0.01)
.count();
assert!(lit_pixels > 100, "expected > 100 lit pixels from a 1000-gaussian cube scene, got {lit_pixels}");
Expand All @@ -125,7 +127,9 @@ fn end_to_end_synthetic_cube_renders_without_panic() {
// total saturation bug or an early-out failure).
let saturated_pixels = frame
.framebuffer
.chunks_exact(3)
.as_chunks::<3>()
.0
.iter()
.filter(|p| p[0] > 0.99 && p[1] > 0.99 && p[2] > 0.99)
.count();
assert!(
Expand Down Expand Up @@ -195,7 +199,7 @@ fn end_to_end_empty_scene_yields_pure_background() {
let bg = [0.25_f32, 0.5, 0.75];
frame.tick(&camera, bg);

for (i, chunk) in frame.framebuffer.chunks_exact(3).enumerate() {
for (i, chunk) in frame.framebuffer.as_chunks::<3>().0.iter().enumerate() {
assert!(
(chunk[0] - bg[0]).abs() < 1e-6 && (chunk[1] - bg[1]).abs() < 1e-6 && (chunk[2] - bg[2]).abs() < 1e-6,
"pixel {i}: expected bg = {bg:?}, got [{}, {}, {}]",
Expand Down
Loading