perf: make all verified optimizations permanent, remove A/B toggles
- Glyph cache: unconditional (was A/B toggled) - RenderParticleSprites SSE: unconditional (was A/B toggled) - transform44 bone SSE: already permanent (teardown guard only) - processLinkedListCollision: already permanent (JMP patch) - frustumCullBoundingBox: already permanent (JMP patch) - silicon functions (ftol, normalize, matmul, etc.): already permanent Also adds decompilation of UpdateEntityAndChunksPositions — analyzed but not optimizable (game function calls dominate, math is ~50 cycles of the ~375 cycle total).
This commit is contained in:
@@ -0,0 +1,121 @@
|
||||
|
||||
/* WARNING: Globals starting with '_' overlap smaller symbols at the same address */
|
||||
|
||||
void __fastcall UpdateEntityAndChunksPositions(int entityPtr)
|
||||
|
||||
{
|
||||
float *pfVar1;
|
||||
int iVar2;
|
||||
float fVar3;
|
||||
undefined *puVar4;
|
||||
int *piVar5;
|
||||
uint uVar6;
|
||||
int iVar7;
|
||||
undefined *local_38;
|
||||
undefined *local_34;
|
||||
undefined *local_30;
|
||||
undefined *local_2c;
|
||||
undefined *local_28;
|
||||
undefined *local_24;
|
||||
undefined *local_18;
|
||||
undefined *local_14;
|
||||
undefined *local_10;
|
||||
undefined *local_c;
|
||||
undefined *local_8;
|
||||
|
||||
*(float *)(entityPtr + 0x78) =
|
||||
(_DAT_00c7bcb0 * *(float *)(entityPtr + 0x5c) +
|
||||
_DAT_00c7bcb8 * *(float *)(entityPtr + 100) + _DAT_00c7bcb4 * *(float *)(entityPtr + 0x60) +
|
||||
_DAT_00c7bcbc) - *(float *)(entityPtr + 0x68);
|
||||
*(undefined **)(entityPtr + 0xb8) = PTR_00867964;
|
||||
if ((((byte)g_renderFlags & 4) != 0) && (_DAT_00867960 < *(float *)(entityPtr + 0x78))) {
|
||||
*(undefined **)(entityPtr + 0xb8) = PTR_00867968;
|
||||
}
|
||||
fVar3 = (float)PTR_00c62510 + *(float *)(entityPtr + 0xac);
|
||||
*(float *)(entityPtr + 0xac) = fVar3;
|
||||
if ((_DAT_0080a1e8 < fVar3) && (*(int *)(entityPtr + 0x14c) != 0)) {
|
||||
recycleVertexBuffer((int *)(entityPtr + 0x14c),(undefined **)(entityPtr + 0x150));
|
||||
}
|
||||
if (*(int *)(entityPtr + 0xc0) != 0) {
|
||||
if ((StaticFloat1_0 < *(float *)(entityPtr + 0xac)) &&
|
||||
(puVar4 = check_instances_active(*(int *)(entityPtr + 0xc0)), puVar4 != (undefined *)0x0)) {
|
||||
store_all_instance_buffers(*(int *)(entityPtr + 0xc0));
|
||||
}
|
||||
if (_DAT_0080a1e8 < *(float *)(entityPtr + 0xac)) {
|
||||
return_object_to_pool(*(undefined ***)(entityPtr + 0xc0));
|
||||
*(undefined4 *)(entityPtr + 0xc0) = 0;
|
||||
}
|
||||
}
|
||||
piVar5 = (int *)(entityPtr + 0x118);
|
||||
iVar7 = 4;
|
||||
local_8 = (undefined *)piVar5;
|
||||
do {
|
||||
iVar2 = *piVar5;
|
||||
if (iVar2 != 0) {
|
||||
fVar3 = (float)PTR_00c62510 + *(float *)(iVar2 + 0x30);
|
||||
*(float *)(iVar2 + 0x30) = fVar3;
|
||||
if ((*(int *)(iVar2 + 0x400) != 0) && (_DAT_0080a1e8 < fVar3)) {
|
||||
ReturnChunkBuffers((int *)(iVar2 + 0x400),(undefined **)(iVar2 + 0x404));
|
||||
}
|
||||
}
|
||||
piVar5 = piVar5 + 1;
|
||||
iVar7 = iVar7 + -1;
|
||||
} while (iVar7 != 0);
|
||||
if ((((*(float *)(entityPtr + 0x44) < _DAT_00c7cb68 !=
|
||||
(*(float *)(entityPtr + 0x44) == _DAT_00c7cb68)) &&
|
||||
(*(float *)(entityPtr + 0x48) < _DAT_00c7cb6c !=
|
||||
(*(float *)(entityPtr + 0x48) == _DAT_00c7cb6c))) &&
|
||||
(*(float *)(entityPtr + 0x4c) < _DAT_00c7cb70 !=
|
||||
(*(float *)(entityPtr + 0x4c) == _DAT_00c7cb70))) &&
|
||||
(puVar4 = IsPointInsideBounds((float *)(entityPtr + 0x50),(float *)&PTR_00c7cb5c),
|
||||
(char)puVar4 != '\0')) {
|
||||
pfVar1 = (float *)(entityPtr + 0x83c + *(int *)(&DAT_0086b580 + (int)PTR_00c7f294 * 4) * 0xc);
|
||||
local_c = (undefined *)
|
||||
(*(float *)(entityPtr + 0x844 + *(int *)(&DAT_0086b580 + (int)PTR_00c7f294 * 4) * 0xc)
|
||||
+ *(float *)(entityPtr + 0x74));
|
||||
local_10 = (undefined *)(pfVar1[1] + *(float *)(entityPtr + 0x70));
|
||||
local_14 = (undefined *)(*pfVar1 + *(float *)(entityPtr + 0x6c));
|
||||
AddObjectToSpatialList(entityPtr,(float *)&local_14);
|
||||
uVar6 = *(uint *)(entityPtr + 0xe4);
|
||||
if (((uVar6 & 1) != 0) || (uVar6 == 0)) {
|
||||
uVar6 = 0;
|
||||
}
|
||||
for (; ((uVar6 & 1) == 0 && (uVar6 != 0));
|
||||
uVar6 = *(uint *)(*(int *)(entityPtr + 0xdc) + uVar6 + 4)) {
|
||||
iVar7 = *(int *)(uVar6 + 4);
|
||||
if ((*(char *)(iVar7 + 0xc) < '\0') &&
|
||||
((*(int *)(iVar7 + 0x88) != 0 || (*(int *)(iVar7 + 0x174) != 0)))) {
|
||||
AddToSpatialGrid(iVar7);
|
||||
}
|
||||
}
|
||||
}
|
||||
uVar6 = 0;
|
||||
piVar5 = (int *)local_8;
|
||||
do {
|
||||
if ((void *)*piVar5 != (void *)0x0) {
|
||||
local_38 = (undefined *)0x0;
|
||||
local_34 = (undefined *)0x0;
|
||||
local_30 = (undefined *)0x0;
|
||||
local_2c = (undefined *)0x0;
|
||||
local_28 = (undefined *)0x0;
|
||||
local_24 = (undefined *)0x0;
|
||||
CopyChunkBounds((void *)*piVar5,&local_38);
|
||||
local_18 = (undefined *)((float)local_30 + (float)local_24);
|
||||
local_14 = (undefined *)(((float)local_2c + (float)local_38) * StaticFloat0_5);
|
||||
local_10 = (undefined *)(((float)local_34 + (float)local_28) * StaticFloat0_5);
|
||||
local_c = (undefined *)((float)local_18 * StaticFloat0_5);
|
||||
if ((((((float)local_38 < _DAT_00c7cb68 != ((float)local_38 == _DAT_00c7cb68)) &&
|
||||
((float)local_34 < _DAT_00c7cb6c != ((float)local_34 == _DAT_00c7cb6c))) &&
|
||||
((float)local_30 < _DAT_00c7cb70 != ((float)local_30 == _DAT_00c7cb70))) &&
|
||||
(((float)PTR_00c7cb5c <= (float)local_2c && (_DAT_00c7cb60 <= (float)local_28)))) &&
|
||||
(_DAT_00c7cb64 <= (float)local_24)) {
|
||||
AddToLayeredSpatialGrid(*piVar5,uVar6,(float *)&local_14);
|
||||
}
|
||||
}
|
||||
uVar6 = uVar6 + 1;
|
||||
piVar5 = piVar5 + 1;
|
||||
} while (uVar6 < 4);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -890,64 +890,46 @@ fn glyphDetour(a: u32, b: u32, c: u32, d: u32) callconv(hook.cc.fastcall) ?*anyo
|
||||
|
||||
const s = rdtsc();
|
||||
|
||||
if (ab_use_custom) {
|
||||
// a=ECX=FontObject*, b=EDX=unused, c=charCode, d=param2
|
||||
// Fast integer mix — golden ratio hash. Murmur2 was overkill for 3 integer keys.
|
||||
const hash = ((a ^ c *% 0x9E3779B9) ^ d) & GLYPH_CACHE_MASK;
|
||||
const entry = &glyph_cache[hash];
|
||||
// a=ECX=FontObject*, b=EDX=unused, c=charCode, d=param2
|
||||
const hash = ((a ^ c *% 0x9E3779B9) ^ d) & GLYPH_CACHE_MASK;
|
||||
const entry = &glyph_cache[hash];
|
||||
|
||||
if (entry.font_ptr == a and entry.char_code == c and entry.param2 == d) {
|
||||
// Cache hit — load cached width into ST(0) for caller
|
||||
asm volatile ("flds (%[p])"
|
||||
:: [p] "r" (&entry.width_bits)
|
||||
);
|
||||
prof.glyph_hits +|= 1;
|
||||
prof.glyph_cycles +|= rdtsc() - s;
|
||||
prof.glyph_calls +|= 1;
|
||||
return null; // EAX unused by callers, they read ST(0)
|
||||
}
|
||||
|
||||
// Cache miss — call original (sets ST(0)), then capture the result
|
||||
const ret = glyph_hook.callOriginal(.{ a, b, c, d });
|
||||
|
||||
// Read ST(0) without popping — original's float return is still on FPU stack
|
||||
var width_bits: u32 = undefined;
|
||||
asm volatile ("fsts (%[p])"
|
||||
:: [p] "r" (&width_bits)
|
||||
if (entry.font_ptr == a and entry.char_code == c and entry.param2 == d) {
|
||||
asm volatile ("flds (%[p])"
|
||||
:: [p] "r" (&entry.width_bits)
|
||||
);
|
||||
|
||||
entry.* = .{
|
||||
.font_ptr = a,
|
||||
.char_code = c,
|
||||
.param2 = d,
|
||||
.width_bits = width_bits,
|
||||
};
|
||||
|
||||
prof.glyph_misses +|= 1;
|
||||
prof.glyph_hits +|= 1;
|
||||
prof.glyph_cycles +|= rdtsc() - s;
|
||||
prof.glyph_calls +|= 1;
|
||||
return ret;
|
||||
return null;
|
||||
}
|
||||
|
||||
// BASELINE: just call original, measure cycles
|
||||
const ret = glyph_hook.callOriginal(.{ a, b, c, d });
|
||||
|
||||
var width_bits: u32 = undefined;
|
||||
asm volatile ("fsts (%[p])"
|
||||
:: [p] "r" (&width_bits)
|
||||
);
|
||||
|
||||
entry.* = .{
|
||||
.font_ptr = a,
|
||||
.char_code = c,
|
||||
.param2 = d,
|
||||
.width_bits = width_bits,
|
||||
};
|
||||
|
||||
prof.glyph_misses +|= 1;
|
||||
prof.glyph_cycles +|= rdtsc() - s;
|
||||
prof.glyph_calls +|= 1;
|
||||
return ret;
|
||||
}
|
||||
fn particleDetour(a: u32, b: u32, c: u32, d: u32) callconv(hook.cc.fastcall) ?*anyopaque {
|
||||
// a=ECX(emitter), b=EDX(unused), c=particleData, d=vertexBuffers
|
||||
fn particleDetour(a: u32, _: u32, c: u32, d: u32) callconv(hook.cc.fastcall) ?*anyopaque {
|
||||
// a=ECX(emitter), _=EDX(unused), c=particleData, d=vertexBuffers
|
||||
const s = rdtsc();
|
||||
if (ab_use_custom) {
|
||||
const result = renderParticleSprites_SSE(a, c, d);
|
||||
prof.particle_cycles +|= rdtsc() - s;
|
||||
prof.particle_calls +|= 1;
|
||||
return @ptrFromInt(result);
|
||||
}
|
||||
const ret = particle_hook.callOriginal(.{ a, b, c, d });
|
||||
const result = renderParticleSprites_SSE(a, c, d);
|
||||
prof.particle_cycles +|= rdtsc() - s;
|
||||
prof.particle_calls +|= 1;
|
||||
return ret;
|
||||
return @ptrFromInt(result);
|
||||
}
|
||||
fn collisionDetour(a: u32, b: u32, c: u32, d: u32) callconv(hook.cc.fastcall) ?*anyopaque {
|
||||
const s = rdtsc();
|
||||
|
||||
Reference in New Issue
Block a user