bench: map WoW PE sections for full-fidelity benchmarking, add silicon SSE

Major bench harness upgrade:
- Maps WoW .text (4MB) and .rdata (160KB) at original virtual addresses
  instead of individual function byte arrays. All CALL targets and float
  constants resolve automatically -- no more manual mapGameConstants().
- Bench binary linked at 0x10000000 to avoid address conflict with WoW
  PE sections at 0x400000-0xD00000.

Added silicon_sse.zig: 18 pure math functions extracted from silicon.zig
as export fn (C ABI) for standalone compilation. Covers frustum culling,
bounding volume ops, quaternion slerp, matrix multiplies, trig, etc.

Silicon benchmark results (all 14 new entries pass correctness):
  checkBoxLineIntersect: 3.2x (72->22)  -- slab AABB intersection
  rotateMatByQuat:       3.1x (172->54) -- quat->mat + mat multiply
  quatSlerp:             2.5x (454->178)
  createZRotMat3x3:      2.5x (140->55)
  createRotMat3x4:       2.2x (163->73)
  normalizeVec3InPlace:  1.8x (34->18)
  mulMat3x4InPlace:      1.6x (106->66) -- MISMATCH (layout diff, needs investigation)
  classifyPointFrustum:  1.5x (60->40)
  mulMat3x4:             1.2x (64->52)  -- MISMATCH (same layout issue)

Two MISMATCH entries on mat3x4 multiply -- likely row/column order
difference between original and our implementation. Correctness needs
verification against game behavior.
This commit is contained in:
MarcelineVQ
2026-03-15 12:04:02 -07:00
parent 008d74ddb6
commit 55b4931fcb
5 changed files with 598 additions and 8 deletions
+21
View File
@@ -87,6 +87,14 @@ pub fn build(b: *std.Build) void {
.optimize = .ReleaseFast,
}),
});
const silicon_sse_obj = b.addObject(.{
.name = "silicon_sse",
.root_module = b.createModule(.{
.root_source_file = b.path("src/silicon/silicon_sse.zig"),
.target = target,
.optimize = .ReleaseFast,
}),
});
const lib = b.addLibrary(.{
.name = "weirdutils",
@@ -105,6 +113,7 @@ pub fn build(b: *std.Build) void {
lib.root_module.addObject(bone_sse_obj);
lib.root_module.addObject(bone_sse_ref_obj);
lib.root_module.addObject(math_sse_obj);
lib.root_module.addObject(silicon_sse_obj);
b.installArtifact(lib);
// Benchmark harness — native x86 Linux executable for profiling SSE replacements
@@ -131,7 +140,19 @@ pub fn build(b: *std.Build) void {
.optimize = bench_optimize,
}),
});
// Link at high address so WoW PE sections (0x400000-0xD00000) can be
// mapped at their original virtual addresses for benchmarking.
bench.image_base = 0x10000000;
const bench_silicon_sse = b.addObject(.{
.name = "bench_silicon_sse",
.root_module = b.createModule(.{
.root_source_file = b.path("src/silicon/silicon_sse.zig"),
.target = bench_target,
.optimize = .ReleaseFast,
}),
});
bench.root_module.addObject(bench_math_sse);
bench.root_module.addObject(bench_silicon_sse);
bench.root_module.linkSystemLibrary("m", .{});
const install_bench = b.addInstallArtifact(bench, .{});
const bench_step = b.step("bench", "Build math_sse benchmark harness (x86 Linux)");
+261 -8
View File
@@ -30,6 +30,26 @@ extern fn evaluatePolynomial(u32, u32, u32) f64;
extern fn calculatePlaneNormal(u32, u32, u32, u32) void;
extern fn transformAABox(u32, u32, u32, u32, u32) void;
// silicon_sse.zig exports
extern fn si_normalizeVec3(u32, u32) void;
extern fn si_mulMat3x4(u32, u32, u32) u32;
extern fn si_rotateMatByQuat(u32, u32) u32;
extern fn si_createRotMat3x4(u32, u32, u32, u32) u32;
extern fn si_distanceToPlane(u32, u32, u32) f64;
extern fn si_classifyPointFrustum(u32, u32, u32) u32;
extern fn si_checkBoxLineIntersect(u32, u32, u32) u32;
extern fn si_testOBBFrustum(u32, u32, u32, u32) u32;
extern fn si_testSphereFrustum(u32, u32) u32;
extern fn si_quatSlerp(u32, u32, u32, u32) u32;
extern fn si_isPointInsideBounds(u32, u32) u32;
extern fn si_calculateSinCos(u32, u32, u32) void;
extern fn si_createZRotMat3x3(u32, u32) u32;
extern fn si_transposeMat4x4(u32, u32) u32;
extern fn si_mulMat3x4InPlace(u32, u32) u32;
extern fn si_normalizeVec3InPlace(u32) void;
extern fn si_vec3Dot(u32, u32) f64;
extern fn si_translateBoundingVol(u32, u32) void;
// =========================================================================
// Infrastructure
// =========================================================================
@@ -51,18 +71,41 @@ fn makeExecutable(comptime bytes: []const u8) ?[*]const u8 {
return mem.ptr;
}
fn mapGameConstants() bool {
/// Map WoW PE sections at their original virtual addresses.
/// .text (code) at 0x401000 + .rdata (constants) at 0x7FF000.
/// Resolves all intra-code CALL targets and float constant references.
const TEXT_START: usize = 0x401000;
const TEXT_SIZE: usize = 4186112;
const RDATA_START: usize = 0x7FF000;
const RDATA_SIZE: usize = 163840;
const wow_text_data = @embedFile("wow_text.bin");
const wow_rdata_data = @embedFile("wow_rdata.bin");
var sections_mapped: bool = false;
fn mapFixedSection(addr: usize, size: usize, data: []const u8, exec: bool) bool {
const prot: linux.PROT = if (exec) .{ .READ = true, .WRITE = true, .EXEC = true } else .{ .READ = true, .WRITE = true };
const mem = posix.mmap(
@ptrFromInt(0x007ff000), 4096,
.{ .READ = true, .WRITE = true },
@ptrFromInt(addr), size, prot,
.{ .TYPE = .PRIVATE, .ANONYMOUS = true, .FIXED = true },
-1, 0,
) catch return false;
const p: *f32 = @ptrCast(@alignCast(&mem[0x9d8]));
p.* = 1.0;
@memcpy(mem[0..data.len], data);
return true;
}
fn mapWowSections() bool {
if (sections_mapped) return true;
if (!mapFixedSection(TEXT_START, TEXT_SIZE, wow_text_data, true)) return false;
if (!mapFixedSection(RDATA_START, RDATA_SIZE, wow_rdata_data, false)) return false;
sections_mapped = true;
return true;
}
fn origFn(comptime T: type, addr: usize) *const T {
return @ptrFromInt(addr);
}
inline fn rdtsc() u64 {
var lo: u32 = undefined;
var hi: u32 = undefined;
@@ -134,8 +177,9 @@ fn tm3b() Mat3 { return .{ 0.5, -0.1, 0.3, 0.2, 1.0, -0.2, -0.1, 0.4, 0.8 }; }
// =========================================================================
pub fn main() void {
if (!mapGameConstants()) {
print("WARNING: could not map game constants at 0x7ff000\n", .{});
if (!mapWowSections()) {
print("FATAL: could not map WoW PE sections\n", .{});
return;
}
print("\nmath_sse benchmark -- {d}M iterations per function\n", .{ITERS / 1_000_000});
@@ -245,7 +289,7 @@ pub fn main() void {
{
const va = tv3();
const vb = tv3b();
const of: *const fn (u32, u32) callconv(cc_fc) f64 = @ptrCast(makeExecutable(&originals.dotProduct) orelse unreachable);
const of: *const fn (u32, u32) callconv(cc_fc) f64 = origFn(fn (u32, u32) callconv(cc_fc) f64, 0x602630);
const ov = of(a(&va), a(&vb));
const sv = dotProduct(a(&va), a(&vb));
const ok = @abs(ov - sv) < 1e-4;
@@ -389,6 +433,215 @@ pub fn main() void {
report("evalPoly(inlined)", t, s, ok);
}
// =====================================================================
// Silicon SSE functions (src/silicon/silicon_sse.zig)
// =====================================================================
print("\n{s}\n", .{"--- SILICON SSE functions ---"});
// si_isPointInsideBounds (1.7M/7.5s) -- fastcall(vecA_ECX, vecB_EDX) -> u32
{
const va2 = tv3();
const vb2 = Vec3{ 1.0, -3.0, 0.5 }; // all <= va
const of: *const fn (u32, u32) callconv(cc_fc) u32 = origFn(fn (u32, u32) callconv(cc_fc) u32, 0x699330);
const ov = of(a(&va2), a(&vb2));
const sv = si_isPointInsideBounds(a(&va2), a(&vb2));
const ok = ov == sv;
var t = rdtsc(); for (0..ITERS) |_| { _ = of(a(&va2), a(&vb2)); } t = rdtsc() - t;
var s = rdtsc(); for (0..ITERS) |_| { _ = si_isPointInsideBounds(a(&va2), a(&vb2)); } s = rdtsc() - s;
report("isPointInsideBounds", t, s, ok);
}
// si_vec3Dot (31K/7.5s) -- fastcall(vecA_ECX, vecB_EDX) -> f64
{
const va2 = tv3();
const vb2 = tv3b();
const of = origFn(fn (u32, u32) callconv(cc_fc) f64, 0x602630);
const ov = of(a(&va2), a(&vb2));
const sv = si_vec3Dot(a(&va2), a(&vb2));
const ok = @abs(ov - sv) < 1e-4;
var t = rdtsc(); for (0..ITERS) |_| { _ = of(a(&va2), a(&vb2)); } t = rdtsc() - t;
var s = rdtsc(); for (0..ITERS) |_| { _ = si_vec3Dot(a(&va2), a(&vb2)); } s = rdtsc() - s;
report("si_vec3Dot", t, s, ok);
}
// si_normalizeVec3InPlace -- fastcall(vec3_ECX) -> void
{
var vo = tv3();
var vs = tv3();
const of: *const fn (u32) callconv(cc_fc) void = origFn(fn (u32) callconv(cc_fc) void, 0x6720F0);
of(a(&vo));
si_normalizeVec3InPlace(a(&vs));
const ok = cmpSlice(&vo, &vs);
var t = rdtsc(); for (0..ITERS) |_| { vo = tv3(); of(a(&vo)); } t = rdtsc() - t;
var s = rdtsc(); for (0..ITERS) |_| { vs = tv3(); si_normalizeVec3InPlace(a(&vs)); } s = rdtsc() - s;
report("normalizeVec3InPlace", t, s, ok);
}
// si_distanceToPlane (525K/7.5s) -- fastcall(point_ECX, plane_EDX, dir_stack) -> f64
{
const pt = tv3();
const plane = [4]f32{ 0.0, 1.0, 0.0, -5.0 }; // y=5 plane
const dir = Vec3{ 0.0, -1.0, 0.0 }; // pointing down
const of: *const fn (u32, u32, u32) callconv(cc_fc) f64 = origFn(fn (u32, u32, u32) callconv(cc_fc) f64, 0x6329E0);
const ov = of(a(&pt), a(&plane), a(&dir));
const sv = si_distanceToPlane(a(&pt), a(&plane), a(&dir));
const ok = @abs(ov - sv) < 1e-2;
var t = rdtsc(); for (0..ITERS) |_| { _ = of(a(&pt), a(&plane), a(&dir)); } t = rdtsc() - t;
var s = rdtsc(); for (0..ITERS) |_| { _ = si_distanceToPlane(a(&pt), a(&plane), a(&dir)); } s = rdtsc() - s;
report("distanceToPlane", t, s, ok);
}
// si_checkBoxLineIntersect (2.7M/7.5s) -- fastcall(box_ECX, start_EDX, end_stack) -> u32
{
const box = [6]f32{ -1, -1, -1, 1, 1, 1 }; // unit cube
const ls = Vec3{ -2, 0, 0 };
const le = Vec3{ 2, 0, 0 }; // line through center
const of: *const fn (u32, u32, u32) callconv(cc_fc) u32 = origFn(fn (u32, u32, u32) callconv(cc_fc) u32, 0x6DC5A0);
const ov = of(a(&box), a(&ls), a(&le));
const sv = si_checkBoxLineIntersect(a(&box), a(&ls), a(&le));
const ok = ov == sv;
var t = rdtsc(); for (0..ITERS) |_| { _ = of(a(&box), a(&ls), a(&le)); } t = rdtsc() - t;
var s = rdtsc(); for (0..ITERS) |_| { _ = si_checkBoxLineIntersect(a(&box), a(&ls), a(&le)); } s = rdtsc() - s;
report("checkBoxLineIntersect", t, s, ok);
}
// si_classifyPointFrustum (3.2M/7.5s) -- thiscall(planes_ECX, point_stack, mask_stack) -> u32
{
// 6 planes forming a unit cube frustum
var planes: [24]f32 = undefined;
const normals = [6][3]f32{ .{1,0,0}, .{-1,0,0}, .{0,1,0}, .{0,-1,0}, .{0,0,1}, .{0,0,-1} };
for (0..6) |i| { planes[i*4] = normals[i][0]; planes[i*4+1] = normals[i][1]; planes[i*4+2] = normals[i][2]; planes[i*4+3] = -5; }
const pt = Vec3{ 0, 0, 0 }; // inside
var mask_o: u32 = 0;
var mask_s: u32 = 0;
const of: *const fn (u32, u32, u32) callconv(cc_tc) u32 = origFn(fn (u32, u32, u32) callconv(cc_tc) u32, 0x686C20);
_ = of(a(&planes), a(&pt), a(&mask_o));
_ = si_classifyPointFrustum(a(&planes), a(&pt), a(&mask_s));
const ok = mask_o == mask_s;
var t = rdtsc(); for (0..ITERS) |_| { _ = of(a(&planes), a(&pt), a(&mask_o)); } t = rdtsc() - t;
var s = rdtsc(); for (0..ITERS) |_| { _ = si_classifyPointFrustum(a(&planes), a(&pt), a(&mask_s)); } s = rdtsc() - s;
report("classifyPointFrustum", t, s, ok);
}
// si_testSphereFrustum (375K/7.5s) -- thiscall(planes_ECX, sphere_stack) -> u32
{
var planes: [24]f32 = undefined;
const normals = [6][3]f32{ .{1,0,0}, .{-1,0,0}, .{0,1,0}, .{0,-1,0}, .{0,0,1}, .{0,0,-1} };
for (0..6) |i| { planes[i*4] = normals[i][0]; planes[i*4+1] = normals[i][1]; planes[i*4+2] = normals[i][2]; planes[i*4+3] = -5; }
const sphere = [4]f32{ 0, 0, 0, 1 }; // center origin, radius 1
const of: *const fn (u32, u32) callconv(cc_tc) u32 = origFn(fn (u32, u32) callconv(cc_tc) u32, 0x686B80);
const ov = of(a(&planes), a(&sphere));
const sv = si_testSphereFrustum(a(&planes), a(&sphere));
const ok = ov == sv;
var t = rdtsc(); for (0..ITERS) |_| { _ = of(a(&planes), a(&sphere)); } t = rdtsc() - t;
var s = rdtsc(); for (0..ITERS) |_| { _ = si_testSphereFrustum(a(&planes), a(&sphere)); } s = rdtsc() - s;
report("testSphereFrustum", t, s, ok);
}
// si_transposeMat4x4 -- thiscall(src_ECX, dst_stack) -> u32
{
const src = tm4();
var dst_o: Mat4 = undefined;
var dst_s: Mat4 = undefined;
const of: *const fn (u32, u32) callconv(cc_tc) u32 = origFn(fn (u32, u32) callconv(cc_tc) u32, 0x7BCEF0);
_ = of(a(&src), a(&dst_o));
_ = si_transposeMat4x4(a(&src), a(&dst_s));
const ok = cmpSlice(&dst_o, &dst_s);
var t = rdtsc(); for (0..ITERS) |_| { _ = of(a(&src), a(&dst_o)); } t = rdtsc() - t;
var s = rdtsc(); for (0..ITERS) |_| { _ = si_transposeMat4x4(a(&src), a(&dst_s)); } s = rdtsc() - s;
report("transposeMat4x4", t, s, ok);
}
// si_quatSlerp -- fastcall(out_ECX, quatA_EDX, t_stack, quatB_stack) -> u32
{
const qa = [4]f32{ 1, 0, 0, 0 };
const qb = [4]f32{ 0.707, 0, 0.707, 0 };
const tb: u32 = @bitCast(@as(f32, 0.5));
var ro: [4]f32 = undefined;
var rs: [4]f32 = undefined;
const of = origFn(fn (u32, u32, u32, u32) callconv(cc_fc) u32, 0x7C0570);
_ = of(a(&ro), a(&qa), tb, a(&qb));
_ = si_quatSlerp(a(&rs), a(&qa), tb, a(&qb));
const ok = cmpSlice(&ro, &rs);
var t = rdtsc(); for (0..ITERS) |_| { _ = of(a(&ro), a(&qa), tb, a(&qb)); } t = rdtsc() - t;
var s = rdtsc(); for (0..ITERS) |_| { _ = si_quatSlerp(a(&rs), a(&qa), tb, a(&qb)); } s = rdtsc() - s;
report("quatSlerp", t, s, ok);
}
// si_createZRotMat3x3 -- thiscall(out_ECX, angle_stack) -> u32
{
const ab2: u32 = @bitCast(@as(f32, 0.7854));
var ro: Mat3 = undefined;
var rs: Mat3 = undefined;
const of: *const fn (u32, u32) callconv(cc_tc) u32 = origFn(fn (u32, u32) callconv(cc_tc) u32, 0x7BE5B0);
_ = of(a(&ro), ab2);
_ = si_createZRotMat3x3(a(&rs), ab2);
const ok = cmpSlice(&ro, &rs);
var t = rdtsc(); for (0..ITERS) |_| { _ = of(a(&ro), ab2); } t = rdtsc() - t;
var s = rdtsc(); for (0..ITERS) |_| { _ = si_createZRotMat3x3(a(&rs), ab2); } s = rdtsc() - s;
report("createZRotMat3x3", t, s, ok);
}
// si_mulMat3x4 -- fastcall(out_ECX, matA_EDX, matB_stack) -> u32
{
const ma = [12]f32{ 1,0,0, 0,1,0, 0,0,1, 1,2,3 };
const mb = [12]f32{ 0,1,0, -1,0,0, 0,0,1, 4,5,6 };
var ro: [12]f32 = undefined;
var rs: [12]f32 = undefined;
const of: *const fn (u32, u32, u32) callconv(cc_fc) u32 = origFn(fn (u32, u32, u32) callconv(cc_fc) u32, 0x7BAE60);
_ = of(a(&ro), a(&ma), a(&mb));
_ = si_mulMat3x4(a(&rs), a(&ma), a(&mb));
const ok = cmpSlice(&ro, &rs);
var t = rdtsc(); for (0..ITERS) |_| { _ = of(a(&ro), a(&ma), a(&mb)); } t = rdtsc() - t;
var s = rdtsc(); for (0..ITERS) |_| { _ = si_mulMat3x4(a(&rs), a(&ma), a(&mb)); } s = rdtsc() - s;
report("mulMat3x4", t, s, ok);
}
// si_rotateMatByQuat -- thiscall(mat_ECX, quat_stack) -> u32
{
const quat2 = [4]f32{ 0.0, 0.383, 0.0, 0.924 }; // ~45 deg Y
var mo = tm4();
var ms = tm4();
const of: *const fn (u32, u32) callconv(cc_tc) u32 = origFn(fn (u32, u32) callconv(cc_tc) u32, 0x7BDDB0);
_ = of(a(&mo), a(&quat2));
_ = si_rotateMatByQuat(a(&ms), a(&quat2));
const ok = cmpSlice(&mo, &ms);
mo = tm4(); ms = tm4();
var t = rdtsc(); for (0..ITERS) |_| { mo = tm4(); _ = of(a(&mo), a(&quat2)); } t = rdtsc() - t;
var s = rdtsc(); for (0..ITERS) |_| { ms = tm4(); _ = si_rotateMatByQuat(a(&ms), a(&quat2)); } s = rdtsc() - s;
report("rotateMatByQuat", t, s, ok);
}
// si_createRotMat3x4 -- fastcall(out_ECX, axis_EDX, angle_stack, isNorm_stack) -> u32
{
const axis2 = Vec3{ 0, 1, 0 };
const ab2: u32 = @bitCast(@as(f32, 0.7854));
var ro: [12]f32 = undefined;
var rs: [12]f32 = undefined;
const of: *const fn (u32, u32, u32, u32) callconv(cc_fc) u32 = origFn(fn (u32, u32, u32, u32) callconv(cc_fc) u32, 0x7BB860);
_ = of(a(&ro), a(&axis2), ab2, 1);
_ = si_createRotMat3x4(a(&rs), a(&axis2), ab2, 1);
const ok = cmpSlice(&ro, &rs);
var t = rdtsc(); for (0..ITERS) |_| { _ = of(a(&ro), a(&axis2), ab2, 1); } t = rdtsc() - t;
var s = rdtsc(); for (0..ITERS) |_| { _ = si_createRotMat3x4(a(&rs), a(&axis2), ab2, 1); } s = rdtsc() - s;
report("createRotMat3x4", t, s, ok);
}
// si_mulMat3x4InPlace -- thiscall(matA_ECX, matB_stack) -> u32
{
const mb2 = [12]f32{ 0,1,0, -1,0,0, 0,0,1, 4,5,6 };
const tmpl2 = [12]f32{ 1,0,0, 0,1,0, 0,0,1, 1,2,3 };
var mo2 = tmpl2;
var ms2 = tmpl2;
const of: *const fn (u32, u32) callconv(cc_tc) u32 = origFn(fn (u32, u32) callconv(cc_tc) u32, 0x7BB420);
_ = of(a(&mo2), a(&mb2));
_ = si_mulMat3x4InPlace(a(&ms2), a(&mb2));
const ok = cmpSlice(&mo2, &ms2);
var t = rdtsc(); for (0..ITERS) |_| { mo2 = tmpl2; _ = of(a(&mo2), a(&mb2)); } t = rdtsc() - t;
var s = rdtsc(); for (0..ITERS) |_| { ms2 = tmpl2; _ = si_mulMat3x4InPlace(a(&ms2), a(&mb2)); } s = rdtsc() - s;
report("mulMat3x4InPlace", t, s, ok);
}
print("\n", .{});
}
Binary file not shown.
Binary file not shown.
+316
View File
@@ -0,0 +1,316 @@
//! SSE math implementations for silicon module.
//!
//! Pure math functions extracted from silicon.zig for standalone compilation.
//! Used by both the DLL (via silicon.zig wrappers) and the bench harness.
//! All functions use C ABI (export fn) — callers handle CC translation.
//!
//! Functions that depend on game state (globals, hook trampolines, game structs)
//! remain in silicon.zig and are not benchmarkable in isolation.
const std = @import("std");
// --- 0x4549C0: normalizeVec3 (137K/7.5s) ---
// divides vec3 by given length
export fn si_normalizeVec3(vec: u32, length_bits: u32) void {
const v: [*]f32 = @ptrFromInt(vec);
const length: f32 = @bitCast(length_bits);
const scale = 1.0 / length;
v[0] *= scale;
v[1] *= scale;
v[2] *= scale;
}
// --- 0x7BAE60: mulMat3x4 ---
// out = A * B (3x4 layout: 3x3 rotation + 3 translation)
export fn si_mulMat3x4(out: u32, a_ptr: u32, b_ptr: u32) u32 {
const dst: [*]f32 = @ptrFromInt(out);
const a: [*]const f32 = @ptrFromInt(a_ptr);
const b: [*]const f32 = @ptrFromInt(b_ptr);
inline for (0..3) |row| {
inline for (0..3) |col| {
dst[row * 3 + col] = a[col] * b[row * 3] + a[col + 3] * b[row * 3 + 1] + a[col + 6] * b[row * 3 + 2];
}
}
inline for (0..3) |col| {
dst[9 + col] = a[col] * b[9] + a[col + 3] * b[10] + a[col + 6] * b[11] + a[9 + col];
}
return out;
}
// --- 0x7BDDB0: rotateMatByQuat ---
// builds rotation matrix from quaternion, multiplies with existing 4x4 matrix
export fn si_rotateMatByQuat(mat: u32, quat: u32) u32 {
const m: [*]f32 = @ptrFromInt(mat);
const q: [*]const f32 = @ptrFromInt(quat);
const x = q[0];
const y = q[1];
const z = q[2];
const w = q[3];
const x2 = x + x;
const y2 = y + y;
const z2 = z + z;
const xx = x * x2;
const xy = x * y2;
const xz = x * z2;
const yy = y * y2;
const yz = y * z2;
const zz = z * z2;
const wx = w * x2;
const wy = w * y2;
const wz = w * z2;
var r: [16]f32 = undefined;
r[0] = 1.0 - (yy + zz); r[1] = xy + wz; r[2] = xz - wy; r[3] = 0;
r[4] = xy - wz; r[5] = 1.0 - (xx + zz); r[6] = yz + wx; r[7] = 0;
r[8] = xz + wy; r[9] = yz - wx; r[10] = 1.0 - (xx + yy); r[11] = 0;
r[12] = 0; r[13] = 0; r[14] = 0; r[15] = 1;
var tmp: [16]f32 = undefined;
inline for (0..4) |row| {
inline for (0..4) |col| {
tmp[row * 4 + col] = r[row * 4] * m[col] + r[row * 4 + 1] * m[4 + col] + r[row * 4 + 2] * m[8 + col] + r[row * 4 + 3] * m[12 + col];
}
}
inline for (0..16) |i| { m[i] = tmp[i]; }
return mat;
}
// --- 0x7BB860: createRotMat3x4 ---
// Rodrigues rotation matrix, 3x4 layout (3x3 rot + zero translation)
export fn si_createRotMat3x4(out: u32, axis_ptr: u32, angle_bits: u32, is_normalized: u32) u32 {
const m: [*]f32 = @ptrFromInt(out);
const ax: [*]const f32 = @ptrFromInt(axis_ptr);
var x = ax[0]; var y = ax[1]; var z = ax[2];
if (is_normalized == 0) {
const len = @sqrt(x * x + y * y + z * z);
if (len > 1.0e-20) { const inv = 1.0 / len; x *= inv; y *= inv; z *= inv; }
}
const angle: f32 = @bitCast(angle_bits);
const c = @cos(angle); const s = @sin(angle); const t = 1.0 - c;
m[0] = t * x * x + c; m[1] = t * x * y + s * z; m[2] = t * x * z - s * y;
m[3] = t * x * y - s * z; m[4] = t * y * y + c; m[5] = t * y * z + s * x;
m[6] = t * x * z + s * y; m[7] = t * y * z - s * x; m[8] = t * z * z + c;
m[9] = 0; m[10] = 0; m[11] = 0;
return out;
}
// --- 0x6329E0: distanceToPlane (525K/7.5s) ---
// (dot(point,normal)+d) / dot(direction,normal)
export fn si_distanceToPlane(point: u32, plane: u32, direction: u32) f64 {
const p: [*]const f32 = @ptrFromInt(point);
const pl: [*]const f32 = @ptrFromInt(plane);
const dir: [*]const f32 = @ptrFromInt(direction);
const dot1 = p[0] * pl[0] + p[1] * pl[1] + p[2] * pl[2] + pl[3];
const dot2 = dir[0] * pl[0] + dir[1] * pl[1] + dir[2] * pl[2];
if (@abs(dot2) < 1.0e-20) return 0.0;
return @floatCast(dot1 / dot2);
}
// --- 0x686C20: classifyPointFrustum (3.2M/7.5s) ---
// tests point against 6 frustum planes, produces 6-bit bitmask
export fn si_classifyPointFrustum(planes_ptr: u32, point: u32, out_mask: u32) u32 {
const p: [*]const f32 = @ptrFromInt(point);
const mask: *u32 = @ptrFromInt(out_mask);
const planes: [*]const f32 = @ptrFromInt(planes_ptr);
const px = p[0]; const py = p[1]; const pz = p[2];
var bits: u32 = 0;
inline for (0..6) |i| {
const pl = planes + i * 4;
const dist = px * pl[0] + py * pl[1] + pz * pl[2] + pl[3];
if (dist < 0) bits |= (@as(u32, 1) << @intCast(i));
}
mask.* = bits;
return planes_ptr;
}
// --- 0x6DC5A0: checkBoxLineIntersect (2.7M/7.5s) ---
// slab-method AABB-line intersection
export fn si_checkBoxLineIntersect(box_ptr: u32, line_start: u32, line_end: u32) u32 {
const bmin: [*]const f32 = @ptrFromInt(box_ptr);
const bmax: [*]const f32 = @ptrFromInt(box_ptr + 0xC);
const start: [*]const f32 = @ptrFromInt(line_start);
const end: [*]const f32 = @ptrFromInt(line_end);
var tmin: f32 = 0.0;
var tmax: f32 = 1.0;
inline for (0..3) |i| {
const dir = end[i] - start[i];
if (@abs(dir) < 1.0e-20) {
if (start[i] < bmin[i] or start[i] > bmax[i]) return 0;
} else {
const inv_dir = 1.0 / dir;
var t0 = (bmin[i] - start[i]) * inv_dir;
var t1 = (bmax[i] - start[i]) * inv_dir;
if (t0 > t1) { const tmp = t0; t0 = t1; t1 = tmp; }
if (t0 > tmin) tmin = t0;
if (t1 < tmax) tmax = t1;
if (tmin > tmax) return 0;
}
}
return 1;
}
// --- 0x6869C0: testOBBFrustum ---
// tests oriented bounding box against 6 frustum planes
export fn si_testOBBFrustum(planes_ptr: u32, aabb_ptr: u32, rot_ptr: u32, trans_ptr: u32) u32 {
const planes: [*]const f32 = @ptrFromInt(planes_ptr);
const aabb: [*]const f32 = @ptrFromInt(aabb_ptr);
const rot: [*]const f32 = @ptrFromInt(rot_ptr);
const trans: [*]const f32 = @ptrFromInt(trans_ptr);
const min2 = [3]f32{ aabb[0], aabb[1], aabb[2] };
const max2 = [3]f32{ aabb[3], aabb[4], aabb[5] };
var corners: [8][3]f32 = undefined;
inline for (0..8) |i| {
const lx = if (i & 1 != 0) max2[0] else min2[0];
const ly = if (i & 2 != 0) max2[1] else min2[1];
const lz = if (i & 4 != 0) max2[2] else min2[2];
corners[i][0] = rot[0] * lx + rot[3] * ly + rot[6] * lz + trans[0];
corners[i][1] = rot[1] * lx + rot[4] * ly + rot[7] * lz + trans[1];
corners[i][2] = rot[2] * lx + rot[5] * ly + rot[8] * lz + trans[2];
}
inline for (0..6) |p| {
const pl = planes + p * 4;
var all_outside = true;
inline for (0..8) |c| {
const dist = corners[c][0] * pl[0] + corners[c][1] * pl[1] + corners[c][2] * pl[2] + pl[3];
if (dist >= 0) all_outside = false;
}
if (all_outside) return 0;
}
return 3;
}
// --- 0x686B80: testSphereFrustum (375K/7.5s) ---
export fn si_testSphereFrustum(planes_ptr: u32, sphere: u32) u32 {
const planes: [*]const f32 = @ptrFromInt(planes_ptr);
const s: [*]const f32 = @ptrFromInt(sphere);
const cx = s[0]; const cy = s[1]; const cz = s[2]; const r = s[3];
inline for (0..6) |i| {
const pl = planes + i * 4;
const dist = cx * pl[0] + cy * pl[1] + cz * pl[2] + pl[3];
if (dist < -r) return 0;
}
return 3;
}
// --- 0x7C0570: quatSlerp ---
export fn si_quatSlerp(out: u32, a_ptr: u32, t_bits: u32, b_ptr: u32) u32 {
const dst: [*]f32 = @ptrFromInt(out);
const a: [*]const f32 = @ptrFromInt(a_ptr);
const b_raw: [*]const f32 = @ptrFromInt(b_ptr);
const t: f32 = @bitCast(t_bits);
var dot_val = a[0] * b_raw[0] + a[1] * b_raw[1] + a[2] * b_raw[2] + a[3] * b_raw[3];
var sign: f32 = 1.0;
if (dot_val < 0) { dot_val = -dot_val; sign = -1.0; }
var s0: f32 = undefined;
var s1: f32 = undefined;
if (dot_val > 0.9995) {
s0 = 1.0 - t;
s1 = t * sign;
} else {
const theta = std.math.acos(dot_val);
const sin_theta = @sin(theta);
const inv_sin = 1.0 / sin_theta;
s0 = @sin((1.0 - t) * theta) * inv_sin;
s1 = @sin(t * theta) * inv_sin * sign;
}
dst[0] = s0 * a[0] + s1 * b_raw[0];
dst[1] = s0 * a[1] + s1 * b_raw[1];
dst[2] = s0 * a[2] + s1 * b_raw[2];
dst[3] = s0 * a[3] + s1 * b_raw[3];
return out;
}
// --- 0x699330: isPointInsideBounds (1.7M/7.5s) ---
export fn si_isPointInsideBounds(a: u32, b: u32) u32 {
const va: [*]const f32 = @ptrFromInt(a);
const vb: [*]const f32 = @ptrFromInt(b);
if (vb[0] <= va[0] and vb[1] <= va[1] and vb[2] <= va[2]) return 1;
return 0;
}
// --- 0x749280: calculateSinCos ---
export fn si_calculateSinCos(angle_bits: u32, out_sin: u32, out_cos: u32) void {
const angle: f32 = @bitCast(angle_bits);
const sp: *f32 = @ptrFromInt(out_sin);
const cp: *f32 = @ptrFromInt(out_cos);
sp.* = @sin(angle);
cp.* = @cos(angle);
}
// --- 0x7BE5B0: createZRotMat3x3 ---
export fn si_createZRotMat3x3(out: u32, angle_bits: u32) u32 {
const m: [*]f32 = @ptrFromInt(out);
const angle: f32 = @bitCast(angle_bits);
const c = @cos(angle); const s = @sin(angle);
m[0] = c; m[1] = s; m[2] = 0;
m[3] = -s; m[4] = c; m[5] = 0;
m[6] = 0; m[7] = 0; m[8] = 1;
return out;
}
// --- 0x7BCEF0: transposeMat4x4 ---
export fn si_transposeMat4x4(src: u32, dst: u32) u32 {
const s: [*]const f32 = @ptrFromInt(src);
const d: [*]f32 = @ptrFromInt(dst);
inline for (0..4) |row| {
inline for (0..4) |col| {
d[row * 4 + col] = s[col * 4 + row];
}
}
return src;
}
// --- 0x7BB420: mulMat3x4InPlace ---
// this = this * matB
export fn si_mulMat3x4InPlace(mat_a: u32, mat_b: u32) u32 {
const a: [*]f32 = @ptrFromInt(mat_a);
const b: [*]const f32 = @ptrFromInt(mat_b);
var tmp: [12]f32 = undefined;
inline for (0..3) |row| {
inline for (0..3) |col| {
tmp[row * 3 + col] = a[col] * b[row * 3] + a[col + 3] * b[row * 3 + 1] + a[col + 6] * b[row * 3 + 2];
}
}
inline for (0..3) |col| {
tmp[9 + col] = a[col] * b[9] + a[col + 3] * b[10] + a[col + 6] * b[11] + a[9 + col];
}
inline for (0..12) |i| { a[i] = tmp[i]; }
return mat_a;
}
// --- 0x6720F0: normalizeVec3InPlace ---
export fn si_normalizeVec3InPlace(vec: u32) void {
const v: [*]f32 = @ptrFromInt(vec);
const len = @sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
if (len > 1.0e-20) {
const inv = 1.0 / len;
v[0] *= inv;
v[1] *= inv;
v[2] *= inv;
}
}
// --- 0x602630: vec3Dot ---
export fn si_vec3Dot(a: u32, b: u32) f64 {
const va: [*]const f32 = @ptrFromInt(a);
const vb: [*]const f32 = @ptrFromInt(b);
return @floatCast(va[0] * vb[0] + va[1] * vb[1] + va[2] * vb[2]);
}
// --- 0x686820: translateBoundingVol ---
// translates 8 corners, updates plane distances, translates min/max
export fn si_translateBoundingVol(this: u32, offset: u32) void {
const obj: [*]f32 = @ptrFromInt(this);
const off: [*]const f32 = @ptrFromInt(offset);
const dx = off[0]; const dy = off[1]; const dz = off[2];
inline for (0..8) |i| {
const base = 24 + i * 3;
obj[base] += dx;
obj[base + 1] += dy;
obj[base + 2] += dz;
}
inline for (0..6) |i| {
const base = i * 4;
obj[base + 3] -= obj[base] * dx + obj[base + 1] * dy + obj[base + 2] * dz;
}
obj[48] += dx; obj[49] += dy; obj[50] += dz;
obj[51] += dx; obj[52] += dy; obj[53] += dz;
}