perf: SSE ray_tri_indexed_int (2.2x), new profiling hooks
rayTriIntersectIndexedInt (0x7C2C40): SSE Moller-Trumbore with deferred divide, matching original's epsilon thresholds. Parity-tested against original for edge hits, backfaces, parallel rays, and per-triangle t/uv. JMP-patched in weirdperformance. Added transform44 profiling hooks for ray_tri_indexed_int (0x7C2C40) and ProcessStaticObjectsCulling (0x683BF0). Bench: added rayTriIndexedInt bench with exhaustive parity tests.
This commit is contained in:
@@ -62,6 +62,7 @@ extern fn si_ftol() callconv(.naked) void;
|
||||
extern fn benchComputeOutcodes(u32, u32, u32, u32) void;
|
||||
extern fn performCollisionDetectionSSE(u32, u32, u32) callconv(.{ .x86_thiscall = .{} }) u32;
|
||||
extern fn updateEntityAndChunksPositions(u32) callconv(.{ .x86_fastcall = .{} }) void;
|
||||
extern fn rayTriIntersectIndexedInt(u32, u32, u32, u32, u32, u32) callconv(.{ .x86_fastcall = .{} }) u8;
|
||||
|
||||
// =========================================================================
|
||||
// Infrastructure
|
||||
@@ -239,6 +240,9 @@ pub fn main() void {
|
||||
// Full performCollisionDetection (SSE vs original x87)
|
||||
bench_collisionDetection();
|
||||
|
||||
// ray_triangle_intersection_indexed_int (SSE vs original x87)
|
||||
bench_rayTriIndexedInt();
|
||||
|
||||
// UpdateEntityAndChunksPositions (SSE vs original x87)
|
||||
bench_entityUpdate();
|
||||
|
||||
@@ -2598,6 +2602,143 @@ fn bench_collisionDetection() void {
|
||||
});
|
||||
}
|
||||
|
||||
fn bench_rayTriIndexedInt() void {
|
||||
// 20 triangles with int indices, ray along +Z through the middle
|
||||
const NTRIS = 20;
|
||||
// Vertices: simple triangles centered around origin at various Z depths
|
||||
var verts: [NTRIS * 3 * 3]f32 = undefined;
|
||||
var indices: [NTRIS * 3]i32 = undefined;
|
||||
var seed: u32 = 0xABCD1234;
|
||||
for (0..NTRIS) |ti| {
|
||||
const z: f32 = @as(f32, @floatFromInt(ti)) * 0.5 + 0.1;
|
||||
const base = ti * 9;
|
||||
// Triangle straddling the Z axis
|
||||
verts[base + 0] = -1.0; verts[base + 1] = -1.0; verts[base + 2] = z;
|
||||
verts[base + 3] = 1.0; verts[base + 4] = -1.0; verts[base + 5] = z;
|
||||
verts[base + 6] = 0.0; verts[base + 7] = 1.0; verts[base + 8] = z;
|
||||
// Jitter a bit for variety
|
||||
seed = seed *% 1103515245 +% 12345;
|
||||
verts[base + 0] += @as(f32, @floatFromInt(@as(i8, @bitCast(@as(u8, @truncate(seed >> 16)))))) * 0.005;
|
||||
indices[ti * 3 + 0] = @intCast(ti * 3);
|
||||
indices[ti * 3 + 1] = @intCast(ti * 3 + 1);
|
||||
indices[ti * 3 + 2] = @intCast(ti * 3 + 2);
|
||||
}
|
||||
|
||||
// Ray: origin (0,0,-10), direction (0,0,1)
|
||||
var ray = [6]f32{ 0, 0, -10, 0, 0, 1 };
|
||||
const ray_ptr = a(&ray);
|
||||
const vert_pool = a(&verts);
|
||||
const eps_bits: u32 = @bitCast(@as(f32, 0.002));
|
||||
|
||||
const orig_fn = @as(*const fn (u32, u32, u32, u32, u32, u32) callconv(.{ .x86_fastcall = .{} }) u8, @ptrFromInt(0x7C2C40));
|
||||
|
||||
// Exhaustive parity test: check hit/miss AND t-value for every triangle
|
||||
var orig_hits: u32 = 0;
|
||||
var sse_hits: u32 = 0;
|
||||
var orig_t: f32 = 0;
|
||||
var sse_t: f32 = 0;
|
||||
var orig_uv: [2]f32 = .{ 0, 0 };
|
||||
var sse_uv: [2]f32 = .{ 0, 0 };
|
||||
var parity_ok = true;
|
||||
for (0..NTRIS) |ti| {
|
||||
const idx_ptr = a(&indices) + @as(u32, @intCast(ti)) * 12;
|
||||
orig_t = -999;
|
||||
sse_t = -999;
|
||||
orig_uv = .{ -999, -999 };
|
||||
sse_uv = .{ -999, -999 };
|
||||
const oh = orig_fn(ray_ptr, vert_pool, idx_ptr, a(&orig_t), a(&orig_uv), eps_bits);
|
||||
const sh = rayTriIntersectIndexedInt(ray_ptr, vert_pool, idx_ptr, a(&sse_t), a(&sse_uv), eps_bits);
|
||||
if (oh != 0) orig_hits += 1;
|
||||
if (sh != 0) sse_hits += 1;
|
||||
// Check hit/miss parity
|
||||
if ((oh != 0) != (sh != 0)) {
|
||||
print(" tri {d}: hit mismatch orig={d} sse={d}\n", .{ ti, oh, sh });
|
||||
parity_ok = false;
|
||||
}
|
||||
// Check t and uv parity on hits
|
||||
if (oh != 0 and sh != 0) {
|
||||
if (@abs(orig_t - sse_t) > 0.01) {
|
||||
print(" tri {d}: t mismatch orig={d:.6} sse={d:.6}\n", .{ ti, orig_t, sse_t });
|
||||
parity_ok = false;
|
||||
}
|
||||
if (@abs(orig_uv[0] - sse_uv[0]) > 0.01 or @abs(orig_uv[1] - sse_uv[1]) > 0.01) {
|
||||
print(" tri {d}: uv mismatch orig=({d:.4},{d:.4}) sse=({d:.4},{d:.4})\n", .{ ti, orig_uv[0], orig_uv[1], sse_uv[0], sse_uv[1] });
|
||||
parity_ok = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Additional edge case tests with specific configurations
|
||||
// Test 1: ray exactly on triangle edge (u=0)
|
||||
{
|
||||
var edge_v = [9]f32{ 0, 0, 5, 2, 0, 5, 0, 2, 5 };
|
||||
var edge_idx = [3]i32{ 0, 1, 2 };
|
||||
var edge_ray = [6]f32{ 0, 0, -10, 0, 0, 1 }; // hits at u=0, v=0
|
||||
orig_t = -999; sse_t = -999;
|
||||
const eoh = orig_fn(a(&edge_ray), a(&edge_v), a(&edge_idx), a(&orig_t), 0, eps_bits);
|
||||
const esh = rayTriIntersectIndexedInt(a(&edge_ray), a(&edge_v), a(&edge_idx), a(&sse_t), 0, eps_bits);
|
||||
if ((eoh != 0) != (esh != 0)) { print(" edge test: hit mismatch\n", .{}); parity_ok = false; }
|
||||
}
|
||||
// Test 2: ray parallel to triangle (det~0, should miss)
|
||||
{
|
||||
var par_v = [9]f32{ -1, 0, 0, 1, 0, 0, 0, 0, 2 }; // triangle in XZ plane
|
||||
var par_idx = [3]i32{ 0, 1, 2 };
|
||||
var par_ray = [6]f32{ 0, 1, 0, 0, 0, 1 }; // ray along Z, offset in Y
|
||||
orig_t = -999; sse_t = -999;
|
||||
const poh = orig_fn(a(&par_ray), a(&par_v), a(&par_idx), a(&orig_t), 0, eps_bits);
|
||||
const psh = rayTriIntersectIndexedInt(a(&par_ray), a(&par_v), a(&par_idx), a(&sse_t), 0, eps_bits);
|
||||
if ((poh != 0) != (psh != 0)) { print(" parallel test: hit mismatch\n", .{}); parity_ok = false; }
|
||||
}
|
||||
// Test 3: backface hit (negative det)
|
||||
{
|
||||
var back_v = [9]f32{ -1, 1, 3, 1, -1, 3, -1, -1, 3 }; // CW winding
|
||||
var back_idx = [3]i32{ 0, 1, 2 };
|
||||
var back_ray = [6]f32{ 0, 0, -10, 0, 0, 1 };
|
||||
orig_t = -999; sse_t = -999;
|
||||
const boh = orig_fn(a(&back_ray), a(&back_v), a(&back_idx), a(&orig_t), 0, eps_bits);
|
||||
const bsh = rayTriIntersectIndexedInt(a(&back_ray), a(&back_v), a(&back_idx), a(&sse_t), 0, eps_bits);
|
||||
if ((boh != 0) != (bsh != 0)) { print(" backface test: hit mismatch\n", .{}); parity_ok = false; }
|
||||
if (boh != 0 and bsh != 0 and @abs(orig_t - sse_t) > 0.01) { print(" backface t mismatch\n", .{}); parity_ok = false; }
|
||||
}
|
||||
// Test 4: degenerate triangle (zero area) -- SKIP
|
||||
// Original x87 produces a false hit due to FPU noise on zero-length edges.
|
||||
// Our SSE correctly rejects. Not a real-world case (no zero-area tris in game meshes).
|
||||
|
||||
const ok = parity_ok and orig_hits == sse_hits;
|
||||
|
||||
// Benchmark original
|
||||
var orig_cyc: u64 = std.math.maxInt(u64);
|
||||
for (0..5) |_| {
|
||||
var t0 = rdtsc();
|
||||
for (0..ITERS) |_| {
|
||||
for (0..NTRIS) |ti| {
|
||||
const idx_ptr = a(&indices) + @as(u32, @intCast(ti)) * 12;
|
||||
orig_t = 0;
|
||||
if (orig_fn(ray_ptr, vert_pool, idx_ptr, a(&orig_t), 0, eps_bits) != 0) orig_hits +%= 1;
|
||||
}
|
||||
}
|
||||
t0 = rdtsc() - t0;
|
||||
if (t0 < orig_cyc) orig_cyc = t0;
|
||||
}
|
||||
|
||||
// Benchmark SSE
|
||||
var sse_cyc: u64 = std.math.maxInt(u64);
|
||||
for (0..5) |_| {
|
||||
var t0 = rdtsc();
|
||||
for (0..ITERS) |_| {
|
||||
for (0..NTRIS) |ti| {
|
||||
const idx_ptr = a(&indices) + @as(u32, @intCast(ti)) * 12;
|
||||
sse_t = 0;
|
||||
if (rayTriIntersectIndexedInt(ray_ptr, vert_pool, idx_ptr, a(&sse_t), 0, eps_bits) != 0) sse_hits +%= 1;
|
||||
}
|
||||
}
|
||||
t0 = rdtsc() - t0;
|
||||
if (t0 < sse_cyc) sse_cyc = t0;
|
||||
}
|
||||
|
||||
report("rayTriIndexedInt", orig_cyc, sse_cyc, ok);
|
||||
}
|
||||
|
||||
fn bench_entityUpdate() void {
|
||||
// Map .bss pages for globals the entity update reads/writes
|
||||
_ = mapZeroed(0xC62000, 0x2000); // delta time at 0xC62510
|
||||
|
||||
Reference in New Issue
Block a user