Add render pipeline profiling: 5 hooks across render/movement path

Hooks executeSceneRenderPass (0x708900), renderFrame (0x707680),
transformMatrix4x4 (0x714260), RenderTextureQuads (0x76FB00), and
CMovement::ProcessUnitMovementUpdate (0x616620).

Unified stats dump every 180 render passes shows per-frame call counts,
cycle costs, bone counts, recursion depth, and quad item counts.
This commit is contained in:
MarcelineVQ
2026-03-12 11:03:23 -07:00
parent b771d7a982
commit cfc252f161
5 changed files with 1266 additions and 59 deletions
@@ -0,0 +1,100 @@
/* Setting prototype: void CMovement_ProcessUnitMovementUpdate(CMovement *this, ulong timeNow, ulong
lastUpdate, CGUnit *unitObj) */
void __thiscall
CMovement__ProcessMovementWithCollisionAndServerValidation
(void *this,ulong timeNow,ulong lastUpdate,CGUnit *unitObj)
{
int *piVar1;
undefined *objectManager;
undefined *puVar2;
uint uVar3;
uint stepTime;
int local_14;
uint uStack_10;
undefined *processedTime;
undefined *deltaTime;
/* Main movement processing function - handles time-based movement updates,
collision detection, and position synchronization. Key offsets: +0x10-0x18
(position xyz), +0x1c (facing), +0x40 (movement flags), +0x15c (unit object
reference) */
deltaTime = (undefined *)(timeNow - lastUpdate);
processedTime = (undefined *)0x0;
objectManager = GetObjectManagerField();
if (deltaTime < (undefined *)0xfb) {
if (deltaTime == (undefined *)0x0) goto LAB_00616789;
}
else {
lastUpdate = (ulong)(deltaTime + -0xfa + lastUpdate);
UpdatePlayerActionCounter(this,(int)(deltaTime + -0xfa));
deltaTime = (undefined *)0xfa;
}
do {
stepTime = (int)deltaTime - (int)processedTime;
puVar2 = CheckNodeExistence((int)this + 0x150);
if ((char)puVar2 != '\0') {
uVar3 = ExtractNodeValue((int)this + 0x150);
uVar3 = *(int *)(uVar3 + 8) - lastUpdate;
if ((int)uVar3 < 0) {
uVar3 = 0;
}
if (uVar3 < stepTime) {
stepTime = uVar3;
}
}
piVar1 = *(int **)(*(int *)((int)this + 0x15c) + 8);
if (((((undefined *)*piVar1 == PTR_00c4da98) && ((undefined *)piVar1[1] == PTR_00c4da9c)) &&
((*(int *)((int)this + 0xa4) == 0 ||
((*(byte *)(*(int *)((int)this + 0xa4) + 0x18) & 4) != 0)))) &&
(((*(uint *)((int)this + 0x40) & 0x200f) != 0 &&
(*(int *)(objectManager + 0x130) - lastUpdate < stepTime)))) {
stepTime = *(int *)(objectManager + 0x130) - lastUpdate;
}
lastUpdate = lastUpdate + stepTime;
*(ulong *)(objectManager + 300) = lastUpdate;
if (stepTime != 0) {
if ((*(uint *)((int)this + 0x40) & 0x20ff) != 0) {
ProcessPlayerMovementWithServerValidationAndInterpolation(this,(float)lastUpdate,stepTime);
piVar1 = *(int **)((int)*(void **)((int)this + 0x15c) + 8);
if (((((undefined *)*piVar1 == PTR_00c4da98) && ((undefined *)piVar1[1] == PTR_00c4da9c)) &&
((*(uint *)((int)this + 0x40) & 0x200f) != 0)) &&
(((*(int *)((int)this + 0xa4) == 0 ||
((*(byte *)(*(int *)((int)this + 0xa4) + 0x18) & 4) != 0)) &&
(-1 < (int)(lastUpdate - *(int *)(objectManager + 0x130)))))) {
InitiatePlayerSpellCast
(*(void **)((int)this + 0x15c),lastUpdate,0xee,(void *)0x0,(void *)0x0);
}
}
processedTime = processedTime + stepTime;
}
puVar2 = ProcessPlayerActionQueue(this,(void *)lastUpdate);
if (puVar2 == (undefined *)0x0) {
SetAudioChannelSimple30(this,1);
break;
}
} while (processedTime < deltaTime);
LAB_00616789:
ProcessUnitPositionAndProximity(*(void **)((int)this + 0x15c),(void *)timeNow,0);
uStack_10 = (uint)((ulonglong)(double)*(float *)((int)this + 0x10) >> 0x20);
local_14 = SUB84((double)*(float *)((int)this + 0x10),0);
objectManager = IsInfiniteOrNaN(local_14,uStack_10);
if (objectManager == (undefined *)0x0) {
uStack_10 = (uint)((ulonglong)(double)*(float *)((int)this + 0x14) >> 0x20);
local_14 = SUB84((double)*(float *)((int)this + 0x14),0);
objectManager = IsInfiniteOrNaN(local_14,uStack_10);
if (objectManager == (undefined *)0x0) {
uStack_10 = (uint)((ulonglong)(double)*(float *)((int)this + 0x18) >> 0x20);
local_14 = SUB84((double)*(float *)((int)this + 0x18),0);
objectManager = IsInfiniteOrNaN(local_14,uStack_10);
if (objectManager == (undefined *)0x0) {
return;
}
}
}
RotateLogFile((byte *)s_Mover_at_invalid_position_008613fc);
return;
}
@@ -0,0 +1,66 @@
void __fastcall RenderTextureQuads(RenderBatch *param_1)
{
int stateValue;
int itemCount;
void *pvVar1;
int itemOffset;
undefined *local_c;
undefined *lastTexture;
char *currentTexture;
RenderItem *renderItems;
itemCount = param_1->item_count;
if (itemCount != 0) {
lastTexture = (undefined *)0xffffffff;
BeginRender();
SetRenderState(0xe,0);
SetRenderState(0xf,0);
SetRenderState(0x10,0);
SetRenderState(0x12,0);
if (itemCount != 0) {
itemOffset = 0;
do {
renderItems = param_1->items_ptr;
currentTexture = *(char **)((int)&renderItems->texture + itemOffset);
if (currentTexture != lastTexture) {
SetTexture(0x17,currentTexture);
lastTexture = currentTexture;
}
if (((*(int *)((int)&renderItems->additionalData + itemOffset) != 0) &&
(stateValue = 2, *(int *)((int)&renderItems->renderState + itemOffset) < 2)) ||
(stateValue = *(int *)((int)&renderItems->renderState + itemOffset), stateValue != 0xb))
{
SetRenderState(7,stateValue);
}
SetTexture(0x3f,*(char **)((int)&renderItems->secondaryTexture + itemOffset));
InitializeRenderingPipeline
(4,*(undefined ***)((int)&renderItems->vertices + itemOffset),0xc,
(undefined **)&g_defaultTexCoord_U1,0,
*(undefined ***)((int)&renderItems->additionalData + itemOffset),
*(int *)((int)&renderItems->dataStride + itemOffset),(undefined *)0x0,
(undefined *)0x0,*(undefined ***)((int)&renderItems->textureCoords + itemOffset),
8,(undefined **)0x0,0);
RenderVertexBuffer((undefined *)0x4,4,&g_quadVertexIndices);
EmptyRenderFunction();
itemOffset = itemOffset + 0x1c;
itemCount = itemCount + -1;
} while (itemCount != 0);
}
EndRender();
}
if (param_1->text_data != (void *)0x0) {
DrawString((int)param_1->text_data);
}
pvVar1 = param_1->callback_list;
if ((((uint)pvVar1 & 1) != 0) || (pvVar1 == (void *)0x0)) {
pvVar1 = (void *)0x0;
}
for (; (((uint)pvVar1 & 1) == 0 && (pvVar1 != (void *)0x0)); pvVar1 = *(void **)((int)pvVar1 + 4))
{
(**(code **)((int)pvVar1 + 8))();
}
return;
}
@@ -0,0 +1,36 @@
undefined * CM2Scene_ExecuteRenderPass(int renderPassIndex)
{
int renderer;
int iVar1;
undefined1 unaff_BP;
undefined4 *puVar2;
undefined1 renderContext [352];
undefined callbackBuffer [12768];
undefined4 uStackY_20;
StackProbe(unaff_BP);
if (*(int *)(renderer + 0x148) != *(int *)(*(int *)(renderer + 4) + 8)) {
puVar2 = (undefined4 *)(renderer + 0x14c);
for (iVar1 = 0x708; iVar1 != 0; iVar1 = iVar1 + -1) {
*puVar2 = 0xffffffff;
puVar2 = puVar2 + 1;
}
*(undefined4 *)(renderer + 0x148) = *(undefined4 *)(*(int *)(renderer + 4) + 8);
}
initializeRenderContext(renderContext,renderer);
uStackY_20 = 0x70896e;
CM2SceneRenderDraw(renderContext,(undefined *)renderPassIndex,*(int *)(renderer + 0x34),
*(int *)(renderPassIndex * 0x10 + 0x54 + renderer),
*(uint *)((renderPassIndex + 5) * 0x10 + renderer));
if (renderPassIndex == 0) {
uStackY_20 = 0x70898a;
CM2SceneRenderDraw(renderContext,(undefined *)0x0,*(int *)(renderer + 0x34),
*(int *)(renderer + 0x44),*(uint *)(renderer + 0x40));
}
uStackY_20 = 0x70899f;
CallbackIteratorDuplicate(callbackBuffer,&DAT_00000010,4,&DAT_0070dbf0);
return (undefined *)0x1;
}
@@ -0,0 +1,880 @@
/* WARNING: Globals starting with '_' overlap smaller symbols at the same address */
undefined * __thiscall renderFrame(void *this,float *cameraPosition)
{
Matrix4x4 *pMVar1;
float fVar2;
short sVar3;
ushort uVar4;
SceneObject *pSVar5;
void *pvVar6;
uint uVar7;
undefined *puVar8;
float *pfVar9;
uint uVar10;
undefined4 *puVar11;
undefined *puVar12;
uint uVar13;
int iVar14;
undefined *puVar15;
uint uVar16;
int *piVar17;
undefined4 *puVar18;
uint *puVar19;
byte *pbVar20;
int iVar21;
undefined4 *puVar22;
undefined **ppuVar23;
float transformedPosition [3];
float worldPosition [3];
undefined *local_74;
undefined *local_70;
undefined *local_6c;
undefined *local_64;
undefined *local_60;
undefined *local_5c;
undefined *local_58;
undefined *local_54;
undefined *local_50;
undefined *local_4c;
undefined *local_48;
undefined *local_44;
undefined *local_40;
undefined *local_3c;
undefined *local_38;
undefined *local_34;
undefined *local_30;
undefined *local_2c;
undefined *local_28;
undefined *local_24;
undefined *local_20;
undefined *local_1c;
undefined *local_18;
undefined *local_14;
undefined *local_10;
undefined *local_c;
undefined *local_8;
*(int *)((int)this + 0x10) = *(int *)((int)this + 0x10) + 1;
if (*(int *)((int)this + 0x148) != *(int *)(*(int *)((int)this + 4) + 8)) {
puVar11 = (undefined4 *)((int)this + 0x14c);
for (iVar14 = 0x708; iVar14 != 0; iVar14 = iVar14 + -1) {
*puVar11 = 0xffffffff;
puVar11 = puVar11 + 1;
}
*(undefined4 *)((int)this + 0x148) = *(undefined4 *)(*(int *)((int)this + 4) + 8);
}
pMVar1 = (Matrix4x4 *)((int)this + 0x9c);
local_8 = (undefined *)this;
SetTransformMatrix((undefined **)pMVar1);
*(float *)((int)this + 0xcc) =
*(float *)((int)this + 0xcc) -
(pMVar1->m00 * *cameraPosition +
*(float *)((int)this + 0xbc) * cameraPosition[2] +
*(float *)((int)this + 0xac) * cameraPosition[1]);
*(float *)((int)this + 0xd0) =
*(float *)((int)this + 0xd0) -
(*(float *)((int)this + 0xc0) * cameraPosition[2] +
*(float *)((int)this + 0xa0) * *cameraPosition +
*(float *)((int)this + 0xb0) * cameraPosition[1]);
*(float *)((int)this + 0xd4) =
*(float *)((int)this + 0xd4) -
(*(float *)((int)this + 0xc4) * cameraPosition[2] +
*(float *)((int)this + 0xa4) * *cameraPosition +
*(float *)((int)this + 0xb4) * cameraPosition[1]);
initPixelShaderDispatcher4();
if ((*(byte *)((int)*(void **)((int)this + 4) + 4) & 4) == 0) {
for (pSVar5 = *(SceneObject **)((int)this + 0x20); pSVar5 != (SceneObject *)0x0;
pSVar5 = (SceneObject *)pSVar5->data_offset_base) {
if (*(int *)&pSVar5->field_0x1cc == 0) {
local_4c = (undefined *)0x0;
local_48 = (undefined *)0x0;
local_44 = (undefined *)0x0;
local_58 = (undefined *)0x3f800000;
local_54 = (undefined *)0x3f800000;
local_50 = (undefined *)0x3f800000;
transformMatrix4x4(pSVar5,pMVar1,(Matrix4x4 *)&local_58,(Matrix4x4 *)&local_4c,
(Matrix4x4 *)0x3f800000);
}
}
}
else {
setSessionCallbacks(*(void **)((int)this + 4),processLinkedObjectList,(undefined *)this);
for (pSVar5 = *(SceneObject **)((int)this + 0x20); pSVar5 != (SceneObject *)0x0;
pSVar5 = *(SceneObject **)(pSVar5->data_offset_base + 0x48)) {
if (*(int *)&pSVar5->field_0x1cc == 0) {
local_58 = (undefined *)0x0;
local_54 = (undefined *)0x0;
local_50 = (undefined *)0x0;
local_4c = (undefined *)0x3f800000;
local_48 = (undefined *)0x3f800000;
local_44 = (undefined *)0x3f800000;
transformMatrix4x4(pSVar5,pMVar1,(Matrix4x4 *)&local_4c,(Matrix4x4 *)&local_58,
(Matrix4x4 *)0x3f800000);
}
if (pSVar5->data_offset_base == 0) break;
}
loadSessionKey(*(int *)((int)this + 4));
}
for (pSVar5 = *(SceneObject **)((int)this + 0x20); pSVar5 != (SceneObject *)0x0;
pSVar5 = (SceneObject *)pSVar5->data_offset_base) {
renderSceneNode(pSVar5);
}
pvVar6 = *(void **)((int)this + 0x20);
while (pvVar6 != (void *)0x0) {
*(undefined4 *)((int)this + 0x20) = *(undefined4 *)((int)pvVar6 + 0x48);
*(undefined4 *)((int)pvVar6 + 0x44) = 0;
*(undefined4 *)((int)pvVar6 + 0x48) = 0;
setupSceneRenderState(pvVar6);
pvVar6 = *(void **)((int)this + 0x20);
}
*(undefined4 *)((int)this + 0x40) = 0;
puVar11 = (undefined4 *)((int)this + 0x50);
iVar14 = 3;
do {
*puVar11 = 0;
puVar11 = puVar11 + 4;
iVar14 = iVar14 + -1;
} while (iVar14 != 0);
*(undefined4 *)((int)this + 0x30) = 0;
puVar12 = *(undefined **)((int)this + 0x24);
cameraPosition = (float *)0x0;
local_64 = (undefined *)0x0;
local_18 = puVar12;
if (puVar12 != (undefined *)0x0) {
do {
*(undefined4 *)(local_8 + 0x24) = *(undefined4 *)(puVar12 + 0x58);
*(undefined4 *)(puVar12 + 0x54) = 0;
*(undefined4 *)(puVar12 + 0x58) = 0;
*(undefined4 *)(puVar12 + 0x50) = 0;
local_18 = puVar12;
puVar8 = PrepareModelForRender(puVar12,0,0);
if (puVar8 != (undefined *)0x0) {
puVar8 = *(undefined **)(*(int *)(puVar12 + 0x30) + 0x130);
local_5c = *(undefined **)(puVar12 + 0x3b8);
local_64 = local_64 + 1;
local_10 = puVar8;
if (*(int *)(local_5c + 0x19c) == 0) {
local_28 = (undefined *)0x1;
LAB_00707a17:
local_24 = (undefined *)0x0;
}
else {
fVar2 = *(float *)(puVar12 + 0xfc);
local_38 = (undefined *)
(*(float *)(puVar12 + 0x104) * *(float *)(puVar12 + 0x104) +
*(float *)(puVar12 + 0x100) * *(float *)(puVar12 + 0x100) + fVar2 * fVar2);
SetVector3((Vector3 *)&local_4c,*(float *)(puVar8 + 0xc0) + *(float *)(puVar8 + 0xb4),
*(float *)(puVar8 + 0xc4) + *(float *)(puVar8 + 0xb8),
*(float *)(puVar8 + 200) + *(float *)(puVar8 + 0xbc));
ScaleVector3D((float *)&local_58,(float *)&local_4c,0.5);
local_c = (undefined *)(SQRT((float)local_38) * *(float *)(puVar8 + 0xcc));
pfVar9 = (float *)transformVector3ByMatrix4x4
(transformedPosition,(float *)&local_58,
(float *)(puVar12 + 0xfc));
local_74 = (undefined *)*pfVar9;
local_70 = (undefined *)pfVar9[1];
local_6c = (undefined *)pfVar9[2];
fVar2 = (float)local_74 * *(float *)(local_5c + 0x1a0) +
(float)local_70 * *(float *)(local_5c + 0x1a4) +
(float)local_6c * *(float *)(local_5c + 0x1a8) + *(float *)(local_5c + 0x1ac);
if ((*(byte *)(*(int *)(local_8 + 4) + 4) & 2) == 0) {
local_28 = (undefined *)(uint)((float)COLLISION_PLANE_ZERO_THRESHOLD <= fVar2);
local_24 = (undefined *)(uint)(local_28 == (undefined *)0x0);
}
else {
local_28 = (undefined *)(uint)(-(float)local_c <= fVar2);
if (fVar2 < (float)local_c == (fVar2 == (float)local_c)) goto LAB_00707a17;
local_24 = (undefined *)0x1;
}
}
local_1c = (undefined *)(uint)(*(int *)(puVar12 + 0x404) != 0);
local_34 = *(undefined **)(*(int *)(puVar12 + 0x30) + 0x138);
if ((((*(byte *)(*(int *)(local_8 + 4) + 4) & 1) == 0) || ((puVar12[4] & 1) != 0)) ||
(local_c = (undefined *)0x1, *(int *)(puVar12 + 0x1c4) == 0)) {
local_c = (undefined *)0x0;
}
if (local_1c == (undefined *)0x0) {
local_38 = *(undefined **)(local_34 + 0x20);
}
else {
local_38 = *(undefined **)(puVar12 + 0x3f0);
}
local_2c = (undefined *)0x0;
if (local_38 != (undefined *)0x0) {
do {
puVar15 = local_8;
pfVar9 = cameraPosition;
if (local_1c == (undefined *)0x0) {
uVar16 = (uint)*(ushort *)(*(int *)(local_34 + 0x24) + 4 + (int)local_2c * 0x18);
pbVar20 = (byte *)(*(int *)(local_34 + 0x24) + (int)local_2c * 0x18);
local_20 = (undefined *)(uVar16 * 0x20 + *(int *)(*(int *)(puVar12 + 0x30) + 0x158));
if (*(int *)(*(int *)(puVar12 + 0x98) + uVar16 * 4) != 0) goto LAB_00707aea;
}
else {
pbVar20 = (byte *)(*(int *)(puVar12 + 0x3ec) + (int)local_2c * 0x18);
local_20 = (undefined *)
((uint)*(ushort *)(pbVar20 + 4) * 0x20 + *(int *)(puVar12 + 0x3f4));
LAB_00707aea:
local_30 = *(undefined **)(puVar12 + 0x19c);
if ((uint)*(ushort *)(pbVar20 + 8) < *(uint *)(puVar8 + 0x54)) {
local_30 = (undefined *)
((float)local_30 *
*(float *)((uint)*(ushort *)(pbVar20 + 8) * 0x50 + 0x3c +
*(int *)(puVar12 + 0xa0)));
}
if (*(short *)(pbVar20 + 0xe) != 0) {
local_30 = (undefined *)
((float)local_30 *
*(float *)((uint)*(ushort *)
(*(int *)(puVar8 + 0xa8) +
(uint)*(ushort *)(pbVar20 + 0x14) * 2) * 0x20 + 0xc +
*(int *)(puVar12 + 0xa8)));
}
if (((float)COLLISION_PLANE_ZERO_THRESHOLD <= (float)local_30) &&
((float)local_30 != (float)COLLISION_PLANE_ZERO_THRESHOLD)) {
local_60 = (undefined *)
(*(int *)(puVar8 + 0x88) + (uint)*(ushort *)(pbVar20 + 10) * 4);
if (((*pbVar20 & 4) == 0) ||
(local_40 = (undefined *)0x1, *(int *)(local_8 + 0x11c) == 0)) {
local_40 = (undefined *)0x0;
}
if ((1 < *(ushort *)(local_60 + 2)) ||
(local_3c = (undefined *)0x0, (float)local_30 < _DAT_00808120)) {
local_3c = (undefined *)0x1;
}
puVar12 = local_8 + 0x2c;
uVar16 = *(int *)(local_8 + 0x30) + 1;
if (*(uint *)(local_8 + 0x2c) < uVar16) {
uVar10 = *(uint *)(local_8 + 0x38);
if (uVar10 == 0) {
uVar10 = CalculateOptimalBlockSize(puVar12,uVar16);
}
uVar16 = AlignToBlockSize(uVar16,uVar10);
SetArrayCapacity(puVar12,uVar16);
}
puVar12 = local_8;
piVar17 = (int *)(*(int *)(puVar15 + 0x30) * 0x40 + *(int *)(puVar15 + 0x34));
*(int *)(puVar15 + 0x30) = *(int *)(puVar15 + 0x30) + 1;
if (piVar17 == (int *)0x0) {
return (undefined *)0x0;
}
iVar14 = 0;
if (local_40 == (undefined *)0x0) {
if ((((local_3c == (undefined *)0x0) && (*(int *)(local_18 + 0xb8) != 0)) &&
((*pbVar20 & 0x10) != 0)) &&
((*(int *)(*(int *)(local_18 + 0x3b8) + 0x180) == 0 &&
((*(byte *)(*(int *)(local_8 + 4) + 4) & 0x20) != 0)))) {
*piVar17 = 2;
}
else {
*piVar17 = 0;
}
}
else {
*piVar17 = 1;
}
piVar17[1] = (int)local_18;
piVar17[2] = 0;
if (((local_3c == (undefined *)0x1) && (local_28 != (undefined *)0x0)) &&
((local_24 != (undefined *)0x0 && (local_40 == (undefined *)0x0)))) {
iVar14 = 1;
}
piVar17[3] = iVar14;
piVar17[4] = (int)local_30;
piVar17[7] = (int)local_2c;
sVar3 = *(short *)(pbVar20 + 2);
piVar17[0xc] = (int)local_20;
piVar17[10] = (int)sVar3;
piVar17[0xb] = (int)pbVar20;
piVar17[0xd] = (int)local_1c;
piVar17[0xe] = -1;
piVar17[0xf] = -1;
if (((*(byte *)(*(int *)(local_8 + 4) + 4) & 8) != 0) &&
(local_40 == (undefined *)0x0)) {
if (local_1c == (undefined *)0x0) {
iVar14 = *(int *)((int)local_2c * 4 +
*(int *)(*(int *)(local_18 + 0x30) + 0x150));
}
else {
iVar14 = *(int *)((int)local_2c * 4 + *(int *)(local_18 + 0x408));
}
iVar21 = (int)local_2c * 4;
piVar17[0xe] = iVar14;
if ((*local_60 & 1) == 0) {
piVar17[0xe] = *(int *)(*(int *)(local_18 + 0x3b8) + 0x180) + iVar14;
}
uVar16 = calculateDepthValue(local_8,piVar17[0xe]);
piVar17[0xe] = uVar16;
if ((*(byte *)(*(int *)(puVar12 + 4) + 4) & 0x10) != 0) {
if (*(int *)(*(int *)(local_18 + 0x3b8) + 0x1b0) == 0) {
piVar17[0xe] = uVar16 - 900;
}
else if (local_1c == (undefined *)0x0) {
piVar17[0xf] = *(int *)(iVar21 + *(int *)(*(int *)(local_18 + 0x30) + 0x154));
}
else {
piVar17[0xf] = *(int *)(iVar21 + *(int *)(local_18 + 0x40c));
}
}
}
puVar8 = local_18;
puVar12 = local_3c;
if ((int)local_3c < 1) {
piVar17[6] = *(int *)(local_18 + 0x84);
}
else {
pfVar9 = (float *)transformVector3ByMatrix4x4
(worldPosition,(float *)(local_20 + 0x14),
(float *)((uint)*(ushort *)(local_20 + 0x12) * 0x40 +
*(int *)(local_18 + 0x94)));
piVar17[6] = (int)(pfVar9[2] * pfVar9[2] +
pfVar9[1] * pfVar9[1] + *pfVar9 * *pfVar9);
}
puVar15 = local_8;
if ((local_c == (undefined *)0x0) || (local_40 != (undefined *)0x0)) {
piVar17[5] = piVar17[6];
}
else {
piVar17[5] = *(int *)(puVar8 + 0x84);
}
if (*piVar17 == 2) {
puVar19 = (uint *)(local_8 + 0x3c);
uVar16 = *(int *)(local_8 + 0x40) + 1;
if (*(uint *)(local_8 + 0x3c) < uVar16) {
uVar10 = *(uint *)(local_8 + 0x48);
if (uVar10 == 0) {
uVar10 = calculateArrayCapacity(puVar19,uVar16);
}
uVar16 = alignToMultiple(uVar16,uVar10);
resizePointerArray(puVar19,uVar16);
}
puVar11 = (undefined4 *)(*(int *)(puVar15 + 0x44) + *(int *)(puVar15 + 0x40) * 4);
if (puVar11 != (undefined4 *)0x0) {
*puVar11 = cameraPosition;
}
goto LAB_00707f7a;
}
if (puVar12 == (undefined *)0x1) {
if (local_40 != (undefined *)0x0) {
if (*(int *)(local_5c + 0x19c) == 0) {
puVar19 = (uint *)(local_8 + 0x5c);
goto LAB_00707f3e;
}
puVar19 = (uint *)(local_8 + 0x6c);
uVar16 = *(int *)(local_8 + 0x70) + 1;
if (*(uint *)(local_8 + 0x6c) < uVar16) {
uVar10 = *(uint *)(local_8 + 0x78);
if (uVar10 == 0) {
uVar10 = calculateArrayCapacity(puVar19,uVar16);
}
uVar16 = alignToMultiple(uVar16,uVar10);
resizePointerArray(puVar19,uVar16);
}
puVar11 = (undefined4 *)
(*(int *)(puVar15 + 0x74) + *(int *)(puVar15 + 0x70) * 4);
if (puVar11 != (undefined4 *)0x0) {
*puVar11 = cameraPosition;
}
goto LAB_00707f7a;
}
if (local_28 != (undefined *)0x0) {
puVar19 = (uint *)(local_8 + 0x5c);
uVar16 = *(int *)(local_8 + 0x60) + 1;
if (*puVar19 < uVar16) {
uVar10 = *(uint *)(local_8 + 0x68);
if (uVar10 == 0) {
uVar10 = calculateArrayCapacity(puVar19,uVar16);
}
uVar16 = alignToMultiple(uVar16,uVar10);
resizePointerArray(puVar19,uVar16);
}
puVar11 = (undefined4 *)(*(int *)(puVar15 + 100) + *(int *)(puVar15 + 0x60) * 4)
;
if (puVar11 != (undefined4 *)0x0) {
*puVar11 = cameraPosition;
}
*(int *)(puVar15 + 0x60) = *(int *)(puVar15 + 0x60) + 1;
}
puVar12 = local_8;
if (local_24 != (undefined *)0x0) {
puVar19 = (uint *)(local_8 + 0x6c);
uVar16 = *(int *)(local_8 + 0x70) + 1;
if (*(uint *)(local_8 + 0x6c) < uVar16) {
uVar10 = *(uint *)(local_8 + 0x78);
if (uVar10 == 0) {
uVar10 = calculateArrayCapacity(puVar19,uVar16);
}
uVar16 = alignToMultiple(uVar16,uVar10);
resizePointerArray(puVar19,uVar16);
}
puVar11 = (undefined4 *)
(*(int *)(puVar12 + 0x74) + *(int *)(puVar12 + 0x70) * 4);
if (puVar11 != (undefined4 *)0x0) {
*puVar11 = cameraPosition;
}
goto LAB_00707f7a;
}
}
else {
puVar19 = (uint *)(local_8 + (int)local_3c * 0x10 + 0x4c);
LAB_00707f3e:
uVar16 = puVar19[1] + 1;
if (*puVar19 < uVar16) {
uVar10 = puVar19[3];
if (uVar10 == 0) {
uVar10 = calculateArrayCapacity(puVar19,uVar16);
}
uVar16 = alignToMultiple(uVar16,uVar10);
resizePointerArray(puVar19,uVar16);
}
puVar11 = (undefined4 *)(puVar19[2] + puVar19[1] * 4);
if (puVar11 != (undefined4 *)0x0) {
*puVar11 = cameraPosition;
}
LAB_00707f7a:
puVar19[1] = puVar19[1] + 1;
}
puVar12 = local_8;
pfVar9 = (float *)((int)cameraPosition + 1);
if ((((local_c != (undefined *)0x0) && (local_40 == (undefined *)0x0)) &&
(0 < (int)local_3c)) && ((*local_60 & 0x10) == 0)) {
piVar17[6] = 0x7f7fffff;
ResizeDynamicArray(local_8 + 0x2c,1,1);
puVar11 = (undefined4 *)
(*(int *)(puVar12 + 0x30) * 0x40 + *(int *)(puVar12 + 0x34));
*(int *)(puVar12 + 0x30) = *(int *)(puVar12 + 0x30) + 1;
if (puVar11 == (undefined4 *)0x0) {
return (undefined *)0x0;
}
puVar18 = (undefined4 *)((int)pfVar9 * 0x40 + -0x40 + *(int *)(puVar12 + 0x34));
puVar22 = puVar11;
for (iVar14 = 0x10; iVar14 != 0; iVar14 = iVar14 + -1) {
*puVar22 = *puVar18;
puVar18 = puVar18 + 1;
puVar22 = puVar22 + 1;
}
puVar11[2] = 1;
if (local_28 != (undefined *)0x0) {
uVar16 = *(int *)(puVar12 + 0x60) + 1;
if (*(uint *)(puVar12 + 0x5c) < uVar16) {
uVar10 = *(uint *)(puVar12 + 0x68);
if (uVar10 == 0) {
uVar10 = calculateArrayCapacity(puVar12 + 0x5c,uVar16);
}
uVar16 = alignToMultiple(uVar16,uVar10);
resizePointerArray(puVar12 + 0x5c,uVar16);
}
piVar17 = (int *)(*(int *)(puVar12 + 100) + *(int *)(puVar12 + 0x60) * 4);
if (piVar17 != (int *)0x0) {
*piVar17 = (int)pfVar9;
}
*(int *)(puVar12 + 0x60) = *(int *)(puVar12 + 0x60) + 1;
}
if (local_24 != (undefined *)0x0) {
expandTimerArray(puVar12 + 0x6c,1,1);
piVar17 = (int *)(*(int *)(puVar12 + 0x74) + *(int *)(puVar12 + 0x70) * 4);
if (piVar17 != (int *)0x0) {
*piVar17 = (int)pfVar9;
}
*(int *)(puVar12 + 0x70) = *(int *)(puVar12 + 0x70) + 1;
}
pfVar9 = (float *)((int)cameraPosition + 2);
}
}
}
cameraPosition = pfVar9;
local_2c = local_2c + 1;
puVar8 = local_10;
puVar12 = local_18;
} while (local_2c < local_38);
}
local_c = (undefined *)0x0;
if (*(int *)(puVar8 + 0x134) != 0) {
local_1c = (undefined *)0x0;
local_20 = (undefined *)0x0;
do {
puVar15 = local_8;
if (local_1c[*(int *)(puVar12 + 0x3c8) + 0xbc] != '\0') {
local_14 = *(undefined **)(puVar12 + 0x19c);
if (*(int *)(local_20 + *(int *)(puVar8 + 0x138) + 0x4c) != 0) {
local_14 = (undefined *)
((float)local_14 *
*(float *)(local_1c + *(int *)(puVar12 + 0x3c8) + 0x3c));
}
if ((float)local_14 < (float)COLLISION_PLANE_ZERO_THRESHOLD) {
local_14 = (undefined *)0x0;
}
uVar4 = **(ushort **)(local_20 + *(int *)(puVar8 + 0x138) + 0x20);
iVar14 = *(int *)(local_10 + 0x88);
puVar12 = local_8 + 0x2c;
uVar16 = *(int *)(local_8 + 0x30) + 1;
if (*(uint *)(local_8 + 0x2c) < uVar16) {
uVar10 = *(uint *)(local_8 + 0x38);
if (uVar10 == 0) {
uVar10 = CalculateOptimalBlockSize(puVar12,uVar16);
}
uVar16 = AlignToBlockSize(uVar16,uVar10);
SetArrayCapacity(puVar12,uVar16);
}
puVar12 = local_8;
puVar11 = (undefined4 *)(*(int *)(puVar15 + 0x30) * 0x40 + *(int *)(puVar15 + 0x34));
*(int *)(puVar15 + 0x30) = *(int *)(puVar15 + 0x30) + 1;
if (puVar11 != (undefined4 *)0x0) {
puVar11[4] = local_14;
*puVar11 = 3;
puVar11[1] = local_18;
puVar11[2] = 0;
puVar11[3] = 0;
puVar11[7] = local_c;
puVar11[10] = 0;
puVar11[5] = *(undefined4 *)(local_18 + 0x84);
puVar11[6] = *(undefined4 *)(local_18 + 0x84);
if ((1 < *(ushort *)(iVar14 + (uint)uVar4 * 4 + 2)) ||
((float)local_14 < _DAT_00808120)) {
if (local_28 == (undefined *)0x0) {
puVar19 = (uint *)(local_8 + 0x6c);
expandTimerArray(puVar19,1,1);
puVar11 = (undefined4 *)
(*(int *)(puVar12 + 0x74) + *(int *)(puVar12 + 0x70) * 4);
if (puVar11 != (undefined4 *)0x0) {
*puVar11 = cameraPosition;
}
}
else {
puVar19 = (uint *)(local_8 + 0x5c);
uVar16 = *(int *)(local_8 + 0x60) + 1;
if (*puVar19 < uVar16) {
uVar10 = *(uint *)(local_8 + 0x68);
if (uVar10 == 0) {
uVar10 = calculateArrayCapacity(puVar19,uVar16);
}
uVar16 = alignToMultiple(uVar16,uVar10);
resizePointerArray(puVar19,uVar16);
}
puVar11 = (undefined4 *)(*(int *)(puVar12 + 100) + *(int *)(puVar12 + 0x60) * 4)
;
if (puVar11 != (undefined4 *)0x0) {
*puVar11 = cameraPosition;
}
}
}
else {
puVar19 = (uint *)(local_8 + 0x4c);
uVar16 = *(int *)(local_8 + 0x50) + 1;
if (*puVar19 < uVar16) {
uVar10 = *(uint *)(local_8 + 0x58);
if (uVar10 == 0) {
uVar10 = calculateArrayCapacity(puVar19,uVar16);
}
uVar16 = alignToMultiple(uVar16,uVar10);
resizePointerArray(puVar19,uVar16);
}
puVar11 = (undefined4 *)(*(int *)(puVar12 + 0x54) + *(int *)(puVar12 + 0x50) * 4);
if (puVar11 != (undefined4 *)0x0) {
*puVar11 = cameraPosition;
}
}
puVar19[1] = puVar19[1] + 1;
cameraPosition = (float *)((int)cameraPosition + 1);
}
}
local_20 = local_20 + 0xdc;
local_c = local_c + 1;
local_1c = local_1c + 0xd0;
puVar8 = local_10;
puVar12 = local_18;
} while (local_c < *(undefined **)(local_10 + 0x134));
}
puVar8 = local_8;
if (*(int *)(puVar12 + 0x1b8) != 0) {
puVar19 = (uint *)(local_8 + 0x2c);
uVar16 = *(int *)(local_8 + 0x30) + 1;
if (*puVar19 < uVar16) {
uVar10 = *(uint *)(local_8 + 0x38);
if (uVar10 == 0) {
uVar10 = CalculateOptimalBlockSize(puVar19,uVar16);
}
uVar16 = AlignToBlockSize(uVar16,uVar10);
SetArrayCapacity(puVar19,uVar16);
}
puVar15 = local_8;
puVar11 = (undefined4 *)(*(int *)(puVar8 + 0x30) * 0x40 + *(int *)(puVar8 + 0x34));
*(int *)(puVar8 + 0x30) = *(int *)(puVar8 + 0x30) + 1;
if (puVar11 != (undefined4 *)0x0) {
*puVar11 = 5;
puVar11[1] = puVar12;
puVar11[2] = 0;
puVar11[3] = 0;
puVar11[4] = 0x3f800000;
puVar11[7] = 0;
puVar11[10] = 0;
puVar11[5] = *(undefined4 *)(puVar12 + 0x84);
puVar11[6] = *(undefined4 *)(puVar12 + 0x84);
if (*(int *)(puVar12 + 0x1c0) == 0) {
if (local_28 == (undefined *)0x0) {
puVar12 = local_8 + 0x6c;
goto LAB_0070835d;
}
puVar12 = local_8 + 0x5c;
expandTimerArray(puVar12,1,1);
puVar11 = (undefined4 *)(*(int *)(puVar15 + 100) + *(int *)(puVar15 + 0x60) * 4);
if (puVar11 != (undefined4 *)0x0) {
*puVar11 = cameraPosition;
}
}
else {
puVar12 = local_8 + 0x4c;
LAB_0070835d:
expandTimerArray(puVar12,1,1);
puVar11 = (undefined4 *)(*(int *)(puVar12 + 8) + *(int *)(puVar12 + 4) * 4);
if (puVar11 != (undefined4 *)0x0) {
*puVar11 = cameraPosition;
}
}
*(int *)(puVar12 + 4) = *(int *)(puVar12 + 4) + 1;
cameraPosition = (float *)((int)cameraPosition + 1);
}
}
}
puVar12 = *(undefined **)(local_8 + 0x24);
} while (puVar12 != (undefined *)0x0);
local_18 = (undefined *)0x0;
this = local_8;
}
local_14 = *(undefined **)((int)this + 0x28);
puVar12 = local_8;
do {
local_8 = puVar12;
if (local_14 == (undefined *)0x0) {
puVar8 = *(undefined **)(puVar12 + 0x40);
local_14 = (undefined *)0x0;
if ((undefined *)0x1 < puVar8) {
ppuVar23 = &PTR_00cefff8;
for (iVar14 = 0xfb; iVar14 != 0; iVar14 = iVar14 + -1) {
*ppuVar23 = (undefined *)0xffffffff;
ppuVar23 = ppuVar23 + 1;
}
local_c = (undefined *)0x0;
if (puVar8 != (undefined *)0x0) {
do {
local_38 = *(undefined **)(*(int *)(puVar12 + 0x44) + (int)local_c * 4);
iVar14 = (int)local_38 * 0x40;
uVar16 = calculateRenderStateHash(*(int *)(puVar12 + 0x34) + iVar14);
puVar15 = (undefined *)(uVar16 % 0xfb);
local_34 = puVar15;
do {
puVar15 = puVar15 + 1;
if ((undefined *)0xfa < puVar15) {
puVar15 = (undefined *)0x0;
}
if (((&PTR_00cefff8)[(int)puVar15] == (undefined *)0xffffffff) ||
(puVar15 == local_34)) {
(&PTR_00cefff8)[(int)puVar15] = local_38;
break;
}
uVar16 = compareRenderStates((int)local_38,(int)(&PTR_00cefff8)[(int)puVar15],
(int)puVar12);
} while (uVar16 != 0);
*(undefined **)(*(int *)(puVar12 + 0x34) + 0x24 + iVar14) =
(&PTR_00cefff8)[(int)puVar15];
local_c = local_c + 1;
} while (local_c < puVar8);
}
}
heapSort(compareRenderPriority,*(void ***)(puVar12 + 0x44),(uint)puVar8,puVar12);
uVar16 = 0;
local_10 = (undefined *)0x0;
if (puVar8 != (undefined *)0x0) {
do {
local_c = *(undefined **)(*(int *)(puVar12 + 0x44) + (int)local_10 * 4);
*(undefined **)(*(int *)(puVar12 + 0x44) + uVar16 * 4) = local_c;
puVar15 = local_10;
while( true ) {
puVar15 = puVar15 + 1;
uVar10 = uVar16 + 1;
if ((puVar8 <= puVar15) ||
(uVar13 = compareRenderPriority
((int)local_c,
*(int *)(*(int *)(puVar12 + 0x44) + (int)puVar15 * 4),
(int)puVar12), uVar13 != 0)) break;
*(undefined4 *)(uVar10 * 4 + *(int *)(puVar12 + 0x44)) =
*(undefined4 *)(*(int *)(puVar12 + 0x44) + (int)puVar15 * 4);
uVar16 = uVar10;
}
if ((uint)((int)puVar15 - (int)local_10) < 2) {
*(undefined4 *)((int)local_c * 0x40 + *(int *)(puVar12 + 0x34)) = 0;
uVar10 = *(uint *)(puVar12 + 0x50) + 1;
if (*(uint *)(puVar12 + 0x4c) < uVar10) {
uVar13 = *(uint *)(puVar12 + 0x58);
if (uVar13 == 0) {
if (uVar10 < 0x40) {
uVar13 = uVar10;
for (uVar7 = *(uint *)(puVar12 + 0x50) & uVar10; uVar7 != 0;
uVar7 = uVar7 - 1 & uVar7) {
uVar13 = uVar7;
}
if (uVar13 == 0) {
uVar13 = 1;
}
}
else {
*(undefined4 *)(puVar12 + 0x58) = 0x40;
uVar13 = 0x40;
}
}
uVar10 = alignToMultiple(uVar10,uVar13);
resizePointerArray(puVar12 + 0x4c,uVar10);
}
puVar11 = (undefined4 *)(*(int *)(puVar12 + 0x54) + *(int *)(puVar12 + 0x50) * 4);
if (puVar11 != (undefined4 *)0x0) {
*puVar11 = local_c;
}
*(int *)(puVar12 + 0x50) = *(int *)(puVar12 + 0x50) + 1;
}
else {
*(int *)((int)local_c * 0x40 + 0x20 + *(int *)(puVar12 + 0x34)) =
(int)puVar15 - (int)local_10;
local_10 = puVar15 + -1;
uVar16 = uVar10;
}
local_10 = local_10 + 1;
} while (local_10 < puVar8);
}
if ((*(uint *)(puVar12 + 0x40) < uVar16) && (*(uint *)(puVar12 + 0x3c) < uVar16)) {
uVar10 = *(uint *)(puVar12 + 0x48);
if (uVar10 == 0) {
if (uVar16 < 0x40) {
uVar13 = uVar16 - 1 & uVar16;
uVar10 = uVar16;
while (uVar7 = uVar13, uVar7 != 0) {
uVar10 = uVar7;
uVar13 = uVar7 - 1 & uVar7;
}
if (uVar10 == 0) {
uVar10 = 1;
}
}
else {
*(undefined4 *)(puVar12 + 0x48) = 0x40;
uVar10 = 0x40;
}
}
uVar10 = alignToMultiple(uVar16,uVar10);
resizePointerArray(puVar12 + 0x3c,uVar10);
}
*(uint *)(puVar12 + 0x40) = uVar16;
heapSort(compareRenderItems,*(void ***)(puVar12 + 0x54),*(uint *)(puVar12 + 0x50),puVar12);
heapSort(compareRenderItemsExtended,*(void ***)(puVar12 + 100),*(uint *)(puVar12 + 0x60),
puVar12);
heapSort(compareRenderItemsExtended,*(void ***)(puVar12 + 0x74),*(uint *)(puVar12 + 0x70),
puVar12);
return (undefined *)0x1;
}
*(undefined4 *)(puVar12 + 0x28) = *(undefined4 *)(local_14 + 0x3e0);
*(undefined4 *)(local_14 + 0x3dc) = 0;
*(undefined4 *)(local_14 + 0x3e0) = 0;
puVar12 = PrepareModelForRender(local_14,0,0);
if (puVar12 != (undefined *)0x0) {
iVar14 = *(int *)(local_14 + 0x3b8);
puVar12 = *(undefined **)(*(int *)(local_14 + 0x30) + 0x130);
local_24 = puVar12;
if (*(int *)(iVar14 + 0x19c) == 0) {
LAB_007084dd:
local_c = (undefined *)0x1;
}
else {
pfVar9 = (float *)(local_14 + 0xfc);
fVar2 = *pfVar9;
local_34 = (undefined *)
(*(float *)(local_14 + 0x104) * *(float *)(local_14 + 0x104) +
*(float *)(local_14 + 0x100) * *(float *)(local_14 + 0x100) + fVar2 * fVar2);
SetVector3((Vector3 *)&local_4c,*(float *)(puVar12 + 0xc0) + *(float *)(puVar12 + 0xb4),
*(float *)(puVar12 + 0xc4) + *(float *)(puVar12 + 0xb8),
*(float *)(puVar12 + 200) + *(float *)(puVar12 + 0xbc));
ScaleVector3D((float *)&local_58,(float *)&local_4c,0.5);
local_34 = (undefined *)(SQRT((float)local_34) * *(float *)(puVar12 + 0xcc));
pfVar9 = (float *)transformVector3ByMatrix4x4(worldPosition,(float *)&local_58,pfVar9);
local_74 = (undefined *)*pfVar9;
local_70 = (undefined *)pfVar9[1];
local_6c = (undefined *)pfVar9[2];
local_c = (undefined *)0x0;
fVar2 = (float)local_74 * *(float *)(iVar14 + 0x1a0) +
(float)local_70 * *(float *)(iVar14 + 0x1a4) +
(float)local_6c * *(float *)(iVar14 + 0x1a8) + *(float *)(iVar14 + 0x1ac);
if (-(float)local_34 < fVar2 != (-(float)local_34 == fVar2)) goto LAB_007084dd;
}
local_20 = (undefined *)0x0;
if (*(int *)(puVar12 + 0x13c) != 0) {
local_2c = (undefined *)0x0;
local_1c = (undefined *)0x0;
do {
puVar15 = local_8;
puVar8 = local_1c;
iVar14 = *(int *)(puVar12 + 0x140);
if (((*(int *)(local_2c + *(int *)(local_14 + 0x3d0) + 0x164) != 0) &&
(local_10 = *(undefined **)(local_14 + 0x19c),
(float)COLLISION_PLANE_ZERO_THRESHOLD <= (float)local_10)) &&
((float)local_10 != (float)COLLISION_PLANE_ZERO_THRESHOLD)) {
puVar12 = local_8 + 0x2c;
uVar16 = *(int *)(local_8 + 0x30) + 1;
if (*(uint *)(local_8 + 0x2c) < uVar16) {
uVar10 = *(uint *)(local_8 + 0x38);
if (uVar10 == 0) {
uVar10 = CalculateOptimalBlockSize(puVar12,uVar16);
}
uVar16 = AlignToBlockSize(uVar16,uVar10);
SetArrayCapacity(puVar12,uVar16);
}
puVar11 = (undefined4 *)(*(int *)(puVar15 + 0x30) * 0x40 + *(int *)(puVar15 + 0x34));
*(int *)(puVar15 + 0x30) = *(int *)(puVar15 + 0x30) + 1;
if (puVar11 != (undefined4 *)0x0) {
puVar11[4] = local_10;
*puVar11 = 4;
puVar11[1] = local_14;
puVar11[2] = 0;
puVar11[3] = 0;
puVar11[7] = local_20;
puVar11[10] = (uint)*(ushort *)(puVar8 + iVar14 + 0x2e);
puVar11[5] = *(undefined4 *)(local_14 + 0x84);
puVar11[6] = *(undefined4 *)(local_14 + 0x84);
if ((1 < *(ushort *)(puVar8 + iVar14 + 0x28)) || ((float)local_10 < _DAT_00808120)) {
puVar19 = (uint *)(local_8 + 0x5c);
if (local_c == (undefined *)0x0) {
puVar19 = (uint *)(local_8 + 0x6c);
}
}
else {
puVar19 = (uint *)(local_8 + 0x4c);
}
uVar16 = puVar19[1] + 1;
if (*puVar19 < uVar16) {
uVar10 = puVar19[3];
if (uVar10 == 0) {
uVar10 = calculateArrayCapacity(puVar19,uVar16);
}
uVar16 = alignToMultiple(uVar16,uVar10);
resizePointerArray(puVar19,uVar16);
}
puVar11 = (undefined4 *)(puVar19[2] + puVar19[1] * 4);
if (puVar11 != (undefined4 *)0x0) {
*puVar11 = cameraPosition;
}
puVar19[1] = puVar19[1] + 1;
cameraPosition = (float *)((int)cameraPosition + 1);
}
}
local_20 = local_20 + 1;
local_1c = local_1c + 0x1f8;
local_2c = local_2c + 0x16c;
puVar12 = local_24;
} while (local_20 < *(undefined **)(local_24 + 0x13c));
}
}
local_14 = *(undefined **)(local_8 + 0x28);
puVar12 = local_8;
} while( true );
}
+184 -59
View File
@@ -1,21 +1,14 @@
//! transform44 — profiling & optimization of transformMatrix4x4 (0x714260)
//! transform44 — render pipeline profiling & M2 bone transform optimization
//!
//! transformMatrix4x4 is the main per-frame bone transform engine for ALL visible
//! M2 models. 17703 bytes. Called from renderFrame, renderSceneNode,
//! updateAnimationTransform. Recursive for attached child objects (weapons, etc).
//! Hooks 5 functions in the render/update pipeline to measure per-frame costs:
//!
//! Phase 1: Profiling instrumentation — measures call frequency, early-exit rate,
//! cycle cost, and bone counts to identify optimization targets.
//! executeSceneRenderPass (0x708900) — top-level render pass dispatch
//! renderFrame (0x707680) — per-model render (calls transformMatrix4x4)
//! transformMatrix4x4 (0x714260) — per-model bone transform engine (17703 bytes)
//! RenderTextureQuads (0x76FB00) — batched quad rendering
//! CMovement::Process (0x616620) — per-unit movement update
//!
//! Key inner functions (call counts within transformMatrix4x4):
//! findInterpolationIndices (0x713d50) — 58 calls, binary/linear keyframe search
//! interpolateAnimationKeyframes (0x713ea0) — 2 calls, vec4 keyframe lerp
//! getInterpolatedFloat (0x71af20) — 4 calls, scalar keyframe lerp
//! getIndexOffset (0x71aff0) — 12 calls, trivial (ptr + idx*2)
//! setShortValue (0x71b010) — 12 calls, trivial (write u16)
//! scaleMatrix3x3ByVector (0x7bdca0) — 2 calls, x87 FPU (SSE candidate)
//! ApplyTranslationMatrix (0x7bdc40) — 5 calls, x87 FPU (SSE candidate)
//! rotateMatrixByQuaternion (0x7bddb0) — 1 call, builds rot matrix then SSE multiply
//! Stats are dumped every DUMP_FRAMES render passes (~2s at 60fps).
const std = @import("std");
const hook = @import("zhook");
@@ -33,21 +26,42 @@ pub fn isActive() bool {
}
// =============================================================================
// Profiling state
// Profiling state — unified dump every DUMP_FRAMES render passes
// =============================================================================
const DUMP_INTERVAL: u32 = 500;
const DUMP_FRAMES: u32 = 180;
var prof = ProfState{};
var t44_depth: u32 = 0; // recursion depth — survives resets
const ProfState = struct {
calls: u32 = 0,
early_exits: u32 = 0,
cycles: u64 = 0,
total_bones: u64 = 0,
max_bones: u32 = 0,
max_depth: u32 = 0,
depth: u32 = 0,
// Frame counter (incremented by executeSceneRenderPass)
frames: u32 = 0,
// transformMatrix4x4 (0x714260)
t44_calls: u32 = 0,
t44_early: u32 = 0,
t44_cycles: u64 = 0,
t44_bones: u64 = 0,
t44_max_bones: u32 = 0,
t44_max_depth: u32 = 0,
// renderFrame (0x707680)
rf_calls: u32 = 0,
rf_cycles: u64 = 0,
// executeSceneRenderPass (0x708900)
erp_calls: u32 = 0,
erp_cycles: u64 = 0,
// RenderTextureQuads (0x76FB00)
rtq_calls: u32 = 0,
rtq_cycles: u64 = 0,
rtq_items: u64 = 0,
// CMovement::ProcessUnitMovementUpdate (0x616620)
mov_calls: u32 = 0,
mov_cycles: u64 = 0,
};
inline fn rdtsc() u64 {
@@ -62,17 +76,12 @@ inline fn rdtsc() u64 {
// =============================================================================
// Hook: transformMatrix4x4 (0x714260)
// __thiscall(ECX=SceneObject*, stack: Matrix4x4*, Matrix4x4*, Matrix4x4*, Matrix4x4*)
// RET 0x10 (4 stack params × 4 bytes)
//
// Mapped to fastcall: ECX=this, EDX=unused, stack: mat1, mat2, mat3, mat4
// Stack cleanup is identical (4 stack params = RET 0x10 in both conventions).
// __thiscall(ECX=SceneObject*, stack: Matrix4x4* ×4)
// Fastcall mapping: ECX=this, EDX=unused, stack: mat1, mat2, mat3, mat4
// RET 0x10
// =============================================================================
const TRANSFORM_ADDR: u32 = 0x714260;
const TransformFn = fn (u32, u32, u32, u32, u32, u32) callconv(hook.cc.fastcall) void;
var transform_hook: hook.Detour(TransformFn) = .{};
fn transformDetour(this: u32, edx: u32, mat1: u32, mat2: u32, mat3: u32, mat4: u32) callconv(hook.cc.fastcall) void {
@@ -80,8 +89,7 @@ fn transformDetour(this: u32, edx: u32, mat1: u32, mat2: u32, mat3: u32, mat4: u
const start = rdtsc();
// Check sync gate — predict whether original will early-exit.
// Original code: if (this+0x10 == NULL || this+0x40 == *(*(this+0x30)+0x10)) return;
// Check sync gate — predict early exit
const model_data = hook.readMem(u32, this + 0x10);
var is_early = false;
var bone_count: u32 = 0;
@@ -94,43 +102,152 @@ fn transformDetour(this: u32, edx: u32, mat1: u32, mat2: u32, mat3: u32, mat4: u
const anim_sync = hook.readMem(u32, anim_ctx + 0x10);
if (sync_val == anim_sync) is_early = true;
}
// Read bone count from model header at this+0x2C -> +0x34
const model_hdr = hook.readMem(u32, this + 0x2C);
if (model_hdr != 0) {
bone_count = hook.readMem(u32, model_hdr + 0x34);
}
}
prof.depth += 1;
if (prof.depth > prof.max_depth) prof.max_depth = prof.depth;
t44_depth += 1;
if (t44_depth > prof.t44_max_depth) prof.t44_max_depth = t44_depth;
transform_hook.callOriginal(.{ this, edx, mat1, mat2, mat3, mat4 });
prof.depth -= 1;
t44_depth -= 1;
const elapsed = rdtsc() - start;
prof.cycles +|= elapsed;
prof.calls += 1;
if (is_early) prof.early_exits += 1;
prof.total_bones +|= bone_count;
if (bone_count > prof.max_bones) prof.max_bones = bone_count;
// Dump stats periodically (only at depth 0 to avoid spam during recursion)
if (prof.depth == 0 and prof.calls >= DUMP_INTERVAL) {
dumpStats();
}
prof.t44_cycles +|= elapsed;
prof.t44_calls += 1;
if (is_early) prof.t44_early += 1;
prof.t44_bones +|= bone_count;
if (bone_count > prof.t44_max_bones) prof.t44_max_bones = bone_count;
}
// =============================================================================
// Hook: renderFrame (0x707680)
// __thiscall(ECX=this, stack: float* cameraPosition)
// Fastcall mapping: ECX=this, EDX=unused, stack: cameraPos
// RET 0x4
// =============================================================================
const RenderFrameFn = fn (u32, u32, u32) callconv(hook.cc.fastcall) ?*anyopaque;
var render_frame_hook: hook.Detour(RenderFrameFn) = .{};
fn renderFrameDetour(this: u32, edx: u32, camera_pos: u32) callconv(hook.cc.fastcall) ?*anyopaque {
asm volatile ("" ::: .{ .esi = true, .edi = true, .ebx = true });
const start = rdtsc();
const ret = render_frame_hook.callOriginal(.{ this, edx, camera_pos });
prof.rf_cycles +|= rdtsc() - start;
prof.rf_calls += 1;
return ret;
}
// =============================================================================
// Hook: executeSceneRenderPass (0x708900)
// __thiscall(ECX=scene, stack: renderPassIndex) — Ghidra labels __stdcall but
// prologue does MOV ESI,ECX (saves this). RET 0x4.
// Fastcall mapping: ECX=this, EDX=unused, stack: passIndex
// =============================================================================
const ExecRenderPassFn = fn (u32, u32, u32) callconv(hook.cc.fastcall) ?*anyopaque;
var exec_render_pass_hook: hook.Detour(ExecRenderPassFn) = .{};
fn execRenderPassDetour(this: u32, edx: u32, pass_index: u32) callconv(hook.cc.fastcall) ?*anyopaque {
asm volatile ("" ::: .{ .esi = true, .edi = true, .ebx = true });
const start = rdtsc();
const ret = exec_render_pass_hook.callOriginal(.{ this, edx, pass_index });
prof.erp_cycles +|= rdtsc() - start;
prof.erp_calls += 1;
// Use render pass as frame boundary for dump trigger
prof.frames += 1;
if (prof.frames >= DUMP_FRAMES) {
dumpStats();
}
return ret;
}
// =============================================================================
// Hook: RenderTextureQuads (0x76FB00)
// __fastcall(ECX=RenderBatch*) — no stack params, RET
// RenderBatch+0xC = item_count
// =============================================================================
const RenderQuadsFn = fn (u32, u32) callconv(hook.cc.fastcall) void;
var render_quads_hook: hook.Detour(RenderQuadsFn) = .{};
fn renderQuadsDetour(batch: u32, edx: u32) callconv(hook.cc.fastcall) void {
asm volatile ("" ::: .{ .esi = true, .edi = true, .ebx = true });
// Read item count before call (batch+0xC)
const items: u32 = if (batch != 0) hook.readMem(u32, batch + 0xC) else 0;
const start = rdtsc();
render_quads_hook.callOriginal(.{ batch, edx });
prof.rtq_cycles +|= rdtsc() - start;
prof.rtq_calls += 1;
prof.rtq_items +|= items;
}
// =============================================================================
// Hook: CMovement::ProcessUnitMovementUpdate (0x616620)
// __thiscall(ECX=this, stack: timeNow(u32), lastUpdate(u32))
// RET 0x8 (2 stack params)
// Fastcall mapping: ECX=this, EDX=unused, stack: timeNow, lastUpdate
// =============================================================================
const MovementFn = fn (u32, u32, u32, u32) callconv(hook.cc.fastcall) void;
var movement_hook: hook.Detour(MovementFn) = .{};
fn movementDetour(this: u32, edx: u32, time_now: u32, last_update: u32) callconv(hook.cc.fastcall) void {
asm volatile ("" ::: .{ .esi = true, .edi = true, .ebx = true });
const start = rdtsc();
movement_hook.callOriginal(.{ this, edx, time_now, last_update });
prof.mov_cycles +|= rdtsc() - start;
prof.mov_calls += 1;
}
// =============================================================================
// Unified stats dump
// =============================================================================
fn dumpStats() void {
const real = prof.calls - prof.early_exits;
const avg_all = if (prof.calls > 0) prof.cycles / prof.calls else 0;
const avg_real = if (real > 0) prof.cycles / real else 0;
const avg_bones = if (real > 0) prof.total_bones / real else 0;
log.fmt("[prof] {d} calls ({d} work, {d} skip), {d}/{d} cyc avg/work, bones avg={d} max={d}, depth={d}\n", .{
prof.calls, real, prof.early_exits,
avg_all, avg_real, avg_bones,
prof.max_bones, prof.max_depth,
const f = prof.frames;
if (f == 0) return;
// transformMatrix4x4
const t44_real = prof.t44_calls - prof.t44_early;
const t44_avg = if (t44_real > 0) prof.t44_cycles / t44_real else 0;
const t44_avg_bones = if (t44_real > 0) prof.t44_bones / t44_real else 0;
// Per-frame averages
const rf_per_f = prof.rf_calls / f;
const t44_per_f = prof.t44_calls / f;
const rtq_per_f = prof.rtq_calls / f;
const mov_per_f = prof.mov_calls / f;
const erp_per_f = prof.erp_calls / f;
// Cycle totals per frame
const rf_cyc_f = prof.rf_cycles / f;
const t44_cyc_f = prof.t44_cycles / f;
const rtq_cyc_f = prof.rtq_cycles / f;
const mov_cyc_f = prof.mov_cycles / f;
const erp_cyc_f = prof.erp_cycles / f;
log.fmt(
\\[prof] {d} frames | per-frame: erp={d} rf={d} t44={d}({d}skip) rtq={d} mov={d}
\\ cycles/frame: erp={d}K rf={d}K t44={d}K rtq={d}K mov={d}K
\\ t44: {d}cyc/work bones_avg={d} max={d} depth={d} | rtq_items/frame={d}
\\
, .{
f,
erp_per_f, rf_per_f, t44_per_f, prof.t44_early / f, rtq_per_f, mov_per_f,
erp_cyc_f / 1000, rf_cyc_f / 1000, t44_cyc_f / 1000, rtq_cyc_f / 1000, mov_cyc_f / 1000,
t44_avg, t44_avg_bones, prof.t44_max_bones, prof.t44_max_depth,
prof.rtq_items / f,
});
// Reset but preserve depth (we're always at depth 0 here)
prof = ProfState{};
}
@@ -145,13 +262,21 @@ pub fn installHooks() void {
if (!g_is_hook_owner) return;
log = logging.Logger.open(module_name, .both);
_ = transform_hook.attach(TRANSFORM_ADDR, &transformDetour);
log.print("transform44: profiling hook installed at 0x714260\n");
_ = transform_hook.attach(0x714260, &transformDetour);
_ = render_frame_hook.attach(0x707680, &renderFrameDetour);
_ = exec_render_pass_hook.attach(0x708900, &execRenderPassDetour);
_ = render_quads_hook.attach(0x76FB00, &renderQuadsDetour);
_ = movement_hook.attach(0x616620, &movementDetour);
log.print("transform44: 5 profiling hooks installed\n");
}
pub fn removeHooks() void {
if (g_is_hook_owner) {
transform_hook.detach();
render_frame_hook.detach();
exec_render_pass_hook.detach();
render_quads_hook.detach();
movement_hook.detach();
log.close();
mod_mutex.release(&g_mutex);
}