All bitwise & and | comparisons were missing parentheses — Zig's ==
and != bind tighter than & and |, so `flags & 0x8 == 0` parsed as
`flags & (0x8 == 0)` = `flags & 0` = always 0.
Affected: si_frustumCullBBox (behind-camera check never ran, occlusion
flag check always passed), si_processLinkedListCollision (early-out
never triggered, node skip broken, AABB hit test only checked X-axis).
Also adds fastRecip (vrcpss+NR) and cvtss2si helpers for frustumCull
perspective divide optimization (2.0x speedup, was 1.6x).
- bone_sse: deduplicate findInterpIdx calls in bone loop — rotation's
search result reused for scale/translation when tracks share temporal
structure (canReuseInterp guard). Est. ~23% bone loop cycle reduction.
- silicon: SSE replacement for processLinkedListCollision (0x6ABC40,
1.57% CPU). V4 AABB overlap test replaces 6 x87 FCOMP/FNSTSW.
Benched at 3.2x speedup (378→115 cyc/call, 8 nodes).
- transform44: remove A/B toggle, always use SSE path (teardown guard
kept). A/B infrastructure remains for other hooks.
- bench: add processLinkedListCollision benchmark with fake linked list
test fixture and stubbed addGeometryToBuffer.
si_ftol: 9-byte naked asm using FISTTP (SSE3 truncate-from-x87) replaces
the 39-byte FSTCW/FLDCW/FISTP rounding mode dance. 4 vs 7 cycles (1.7x).
13.2M calls/7.5s in-game -- ~13ms savings per period.
Benchmark: patch-in-place at mapped 0x40A2B0, test parity across 19 values,
best-of-5 timing with varying inputs. Framework for all silicon functions.
Also disabled h67 (ConvertPixelsToScreenAlt) probe -- game passes ECX=0
as valid input, thiscall probe crashes on null this.
mulMat3x4 / mulMat3x4InPlace: translation row had A*B operands swapped.
Original computes A_translation * B_rotation + B_translation, but our
code was doing A_rotation * B_translation + A_translation. Verified by
tracing x87 disassembly: first element loads A[9]*B[col] pattern.
Fixed in both silicon_sse.zig and silicon.zig.
packParticleColor: x87 rounds 127.5 to 128 (round-to-nearest), but SSE
@intFromFloat truncates to 127. Added @round() before @intFromFloat.
42/42 benchmarks now pass correctness. 0 MISMATCHes.