Files
WeirdUtils/src/performance/inflate_hook.zig
T
MarcelineVQ 14ba112b0d perf: fix deferred divide precision, threadlocal TLS, MSVC target
Removed deferred divide from both ray-tri functions -- the det-scaled
epsilon comparisons lose precision for near-parallel rays, potentially
accepting triangles the original rejects. Downstream SetupBoxFrustum
then writes to wrong globals near 0xCE6738 (SGroupPtr), causing
ERROR #124 on exit.

inflate_hook: replaced manual FS:0x24 thread ID pool with Zig native
threadlocal. Fixes potential crash on Wine where FS segment layout
may differ.

Build: switched from GNU to MSVC ABI (4-5K smaller per DLL, no
.eh_frame unwind tables). Added setjmp.h stub for libdeflate MSVC
build. Added noperf build variant (zig build noperf).
2026-03-26 11:38:33 -07:00

119 lines
4.0 KiB
Zig

//! inflate_hook — libdeflate replacement for WoW's zlib inflate.
//!
//! Hooks DecompressData_WithOptions (0x661A80). For type 0x02 (zlib) streams,
//! uses libdeflate (~2.2x faster than stock zlib). Falls back to original on failure.
//!
//! Thread-safety: the decompressor struct contains mutable decode tables rebuilt
//! per block, so each thread gets its own cached decompressor via Zig's native
//! threadlocal (OS-managed TLS). This avoids both the race condition
//! (shared decompressor -> wild writes) and the per-call alloc overhead.
//!
//! Benchmark results (84k calls, heavy load):
//! stock=2681ms | per-call-alloc=1304ms (2.05x) | tls-cached=1194ms (2.2x)
const hook_lib = @import("zhook");
const logging = @import("../logging.zig");
const std = @import("std");
// libdeflate C API
extern fn libdeflate_alloc_decompressor() ?*anyopaque;
extern fn libdeflate_free_decompressor(?*anyopaque) void;
extern fn libdeflate_zlib_decompress(?*anyopaque, [*]const u8, usize, [*]u8, usize, *usize) c_int;
var lib_available: bool = false;
var log: logging.Logger = .{};
// --- Thread-local decompressor ---
// Each thread lazily allocates its own decompressor on first use.
// OS-managed TLS via Zig's threadlocal -- works correctly on both
// native Windows and Wine without manual FS segment access.
threadlocal var tls_decomp: ?*anyopaque = null;
fn getTlsDecompressor() ?*anyopaque {
if (tls_decomp) |d| return d;
const d = libdeflate_alloc_decompressor() orelse return null;
tls_decomp = d;
return d;
}
// --- Timing ---
var fast_total_cycles: u64 = 0;
var orig_total_cycles: u64 = 0;
var call_count: u64 = 0;
var success_count: u64 = 0;
var fallback_count: u64 = 0;
inline fn rdtsc() u64 {
var lo: u32 = undefined;
var hi: u32 = undefined;
asm volatile ("rdtsc" : [lo] "={eax}" (lo), [hi] "={edx}" (hi));
return (@as(u64, hi) << 32) | lo;
}
const DecompressFn = fn (u32, u32, u32, u32, u32) callconv(.{ .x86_stdcall = .{} }) u32;
pub var decompress_hook: hook_lib.Detour(DecompressFn) = .{};
pub fn decompressDetour(out_buf: u32, out_size_ptr: u32, in_buf: u32, in_size: u32, flags: u32) callconv(.{ .x86_stdcall = .{} }) u32 {
const in_ptr: [*]const u8 = @ptrFromInt(in_buf);
const out_capacity = @as(*const u32, @ptrFromInt(out_size_ptr)).*;
// Accept type 0x02 with valid zlib CMF (deflate method = low nibble 0x08)
if (lib_available and in_size > 3 and out_capacity > 0 and
in_ptr[0] == 0x02 and (in_ptr[1] & 0x0F) == 0x08)
{
if (getTlsDecompressor()) |decomp| {
var ld_out: usize = 0;
const t1 = rdtsc();
const ld_ret = libdeflate_zlib_decompress(
decomp,
in_ptr + 1,
in_size - 1,
@ptrFromInt(out_buf),
out_capacity,
&ld_out,
);
fast_total_cycles +|= rdtsc() - t1;
if (ld_ret == 0) {
call_count +|= 1;
success_count +|= 1;
return 1;
}
}
}
// Fallback to original
call_count +|= 1;
const t0 = rdtsc();
const ret = decompress_hook.callOriginal(.{ out_buf, out_size_ptr, in_buf, in_size, flags });
orig_total_cycles +|= rdtsc() - t0;
fallback_count +|= 1;
return ret;
}
pub fn dumpStats() void {
if (call_count == 0) return;
const MS = 3_000_000;
log.fmt("inflate: {d} calls | fast={d} ({d}ms) fallback={d} ({d}ms)\n", .{
call_count, success_count, fast_total_cycles / MS, fallback_count, orig_total_cycles / MS,
});
fast_total_cycles = 0;
orig_total_cycles = 0;
call_count = 0;
success_count = 0;
fallback_count = 0;
}
pub fn install() bool {
const test_decomp = libdeflate_alloc_decompressor();
if (test_decomp == null) return false;
libdeflate_free_decompressor(test_decomp);
lib_available = true;
return decompress_hook.attach(0x661A80, &decompressDetour) == .ok;
}
pub fn remove() void {
decompress_hook.detach();
lib_available = false;
}