From 61ee4f48e14bab6e4ac38819d166e10367fb4114 Mon Sep 17 00:00:00 2001 From: MarcelineVQ Date: Mon, 16 Mar 2026 17:06:20 -0700 Subject: [PATCH] bone_sse: f32 callFtol, fastMod conditional subtract for looping anims callFtol: use f32 multiply instead of f64 intermediate. Parity holds -- the delta*scale product is well within f32 precision range. fastMod: replace integer modulo (idiv, ~25 cycles) with conditional subtract (~2 cycles) for looping animation frame computation. Falls back to real modulo for large time skips (alt-tab, etc). 3609 cycles (-14% vs 4176 baseline), parity PASS. --- src/transform44/bone_sse.zig | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/src/transform44/bone_sse.zig b/src/transform44/bone_sse.zig index 5590ce8..eeef1e1 100644 --- a/src/transform44/bone_sse.zig +++ b/src/transform44/bone_sse.zig @@ -688,12 +688,21 @@ inline fn interpAnimKF(this: u32, bone_rt: u32, anim_data: u32, output: u32) [4] // Game function call wrappers — replacing reimplementations with actual calls // ============================================================================= +/// Fast modulo for looping animations. The value is almost always < 2*length +/// (frame-to-frame delta is small), so a conditional subtract beats idiv. +inline fn fastMod(val: u32, len: u32) u32 { + var v = val; + if (v >= len) { + v -%= len; + if (v >= len) v = v % len; // fallback for large time skips + } + return v; +} + /// Float truncation — replaces game's __ftol at 0x40A2B0. /// Original: FILD i32 → FMUL f32 → __ftol, all in 80-bit x87 precision. -/// Uses f64 intermediate (53-bit mantissa) to approximate x87's 64-bit. inline fn callFtol(delta: i32, scale_addr: u32) i32 { - const f = @as(f64, @floatFromInt(delta)) * @as(f64, rf32(scale_addr)); - return @intFromFloat(f); + return @intFromFloat(@as(f32, @floatFromInt(delta)) * rf32(scale_addr)); } /// Vec3 squared magnitude — replaces game's 0x4549F0. Uses @mulAdd → vfmadd. @@ -1232,7 +1241,7 @@ export fn transformImpl_SSE(this: u32, mat1: u32, mat2: u32, mat3: u32, mat4: u3 // elapsed = (float)(cur_time - sec_start) * time_scale → __ftol const delta = cur_time -% ru32(brt + 0xA8); const ftol_result = callFtol(@as(i32, @bitCast(delta)), brt + 0xB0); - const frame = (@as(u32, @bitCast(ftol_result)) +% ru32(brt + 0xB8)) % (anim_end -% anim_start); + const frame = fastMod(@as(u32, @bitCast(ftol_result)) +% ru32(brt + 0xB8), anim_end -% anim_start); wu32(brt + 0x98, anim_start +% frame); // prim_time } else { // Assembly 0x714631: MOV EDX,EAX — fallback to anim_start @@ -1254,7 +1263,7 @@ export fn transformImpl_SSE(this: u32, mat1: u32, mat2: u32, mat3: u32, mat4: u3 if (@as(i32, @bitCast(anim_start)) < @as(i32, @bitCast(anim_end))) { const delta = effective_time -% ru32(brt + 0xA8); const ftol_result = callFtol(@as(i32, @bitCast(delta)), brt + 0xB0); - const frame = (@as(u32, @bitCast(ftol_result)) +% ru32(brt + 0xB8)) % (anim_end -% anim_start); + const frame = fastMod(@as(u32, @bitCast(ftol_result)) +% ru32(brt + 0xB8), anim_end -% anim_start); wu32(brt + 0x98, anim_start +% frame); } else { wu32(brt + 0x98, anim_start); @@ -1323,7 +1332,7 @@ export fn transformImpl_SSE(this: u32, mat1: u32, mat2: u32, mat3: u32, mat4: u3 if (@as(i32, @bitCast(anim_start)) < @as(i32, @bitCast(anim_end))) { const delta = sec_cur_time -% ru32(brt + 0xD4); const ftol_result = callFtol(@as(i32, @bitCast(delta)), brt + 0xDC); - const frame = (@as(u32, @bitCast(ftol_result)) +% ru32(brt + 0xE4)) % (anim_end -% anim_start); + const frame = fastMod(@as(u32, @bitCast(ftol_result)) +% ru32(brt + 0xE4), anim_end -% anim_start); wu32(brt + 0xC4, anim_start +% frame); // sec_time } else { wu32(brt + 0xC4, anim_start); @@ -1341,7 +1350,7 @@ export fn transformImpl_SSE(this: u32, mat1: u32, mat2: u32, mat3: u32, mat4: u3 if (@as(i32, @bitCast(anim_start)) < @as(i32, @bitCast(anim_end))) { const delta = effective_time -% ru32(brt + 0xD4); const ftol_result = callFtol(@as(i32, @bitCast(delta)), brt + 0xDC); - const frame = (@as(u32, @bitCast(ftol_result)) +% ru32(brt + 0xE4)) % (anim_end -% anim_start); + const frame = fastMod(@as(u32, @bitCast(ftol_result)) +% ru32(brt + 0xE4), anim_end -% anim_start); wu32(brt + 0xC4, anim_start +% frame); } else { wu32(brt + 0xC4, anim_start);