mirror of
https://gitlab.com/ryandwyer/perfect-dark
synced 2026-07-01 03:00:09 -04:00
433 lines
14 KiB
C
433 lines
14 KiB
C
#include <ultra64.h>
|
|
#include "constants.h"
|
|
#include "bss.h"
|
|
#include "data.h"
|
|
#include "game/game_1531a0.h"
|
|
#include "lib/vi.h"
|
|
#include "types.h"
|
|
|
|
#define NUM_SAMPLES 32
|
|
|
|
#ifdef PROFILING
|
|
// [x][x][0] is the current ticks tally (for multiple start + stops within one frame)
|
|
// [x][x][1] is the start time
|
|
u32 g_ProfileMarkers[NUM_SAMPLES][NUM_PROFILEMARKERS][2];
|
|
s32 g_ProfileIndex = 0;
|
|
u32 g_ProfileAudStart;
|
|
u32 g_ProfileAudCycles;
|
|
u32 g_ProfileGfxCycles;
|
|
u32 g_ProfileGfxHistory[30];
|
|
u32 g_ProfileGfxIndex = 0;
|
|
struct profileslot *g_ProfileCurrentSlot;
|
|
|
|
struct profileslot {
|
|
char *file;
|
|
s32 line;
|
|
s32 ticks;
|
|
s32 startticks;
|
|
s32 numiterations;
|
|
struct profileslot *parent;
|
|
bool recursion;
|
|
};
|
|
|
|
struct profileslot g_ProfileSlots[30];
|
|
|
|
void profileReset(void)
|
|
{
|
|
s32 i;
|
|
|
|
g_ProfileIndex = (g_ProfileIndex + 1) % NUM_SAMPLES;
|
|
|
|
for (i = 0; i < NUM_PROFILEMARKERS; i++) {
|
|
g_ProfileMarkers[g_ProfileIndex][i][0] = 0;
|
|
g_ProfileMarkers[g_ProfileIndex][i][1] = 0;
|
|
}
|
|
|
|
for (i = 0; i < ARRAYCOUNT(g_ProfileSlots); i++) {
|
|
g_ProfileSlots[i].file = NULL;
|
|
g_ProfileSlots[i].parent = NULL;
|
|
}
|
|
|
|
g_ProfileCurrentSlot = NULL;
|
|
}
|
|
|
|
void profileStartDynamic(char *file, s32 line)
|
|
{
|
|
s32 i;
|
|
struct profileslot *emptyslot = NULL;
|
|
struct profileslot *slot = NULL;
|
|
|
|
for (i = 0; i < ARRAYCOUNT(g_ProfileSlots); i++) {
|
|
if (!emptyslot && g_ProfileSlots[i].file == NULL) {
|
|
emptyslot = &g_ProfileSlots[i];
|
|
} else if (g_ProfileSlots[i].file == file && g_ProfileSlots[i].line == line) {
|
|
slot = &g_ProfileSlots[i];
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (slot) {
|
|
// Another iteration on an existing slot
|
|
if (slot->startticks) {
|
|
slot->recursion = true;
|
|
} else {
|
|
slot->numiterations++;
|
|
slot->startticks = osGetCount();
|
|
}
|
|
|
|
g_ProfileCurrentSlot = slot;
|
|
} else if (emptyslot) {
|
|
// New slot
|
|
slot = emptyslot;
|
|
|
|
slot->file = file;
|
|
slot->line = line;
|
|
slot->parent = g_ProfileCurrentSlot;
|
|
slot->ticks = 0;
|
|
slot->numiterations = 1;
|
|
slot->recursion = false;
|
|
slot->startticks = osGetCount();
|
|
|
|
g_ProfileCurrentSlot = slot;
|
|
}
|
|
}
|
|
|
|
void profileEndDynamic(char *file, s32 line)
|
|
{
|
|
u32 count = osGetCount();
|
|
s32 i;
|
|
struct profileslot *slot = NULL;
|
|
|
|
for (i = 0; i < ARRAYCOUNT(g_ProfileSlots); i++) {
|
|
if (g_ProfileSlots[i].file == file && g_ProfileSlots[i].line == line) {
|
|
slot = &g_ProfileSlots[i];
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (slot) {
|
|
slot->ticks += count - slot->startticks;
|
|
slot->startticks = 0;
|
|
|
|
g_ProfileCurrentSlot = slot->parent;
|
|
}
|
|
}
|
|
|
|
void profileStart(s32 marker)
|
|
{
|
|
g_ProfileMarkers[g_ProfileIndex][marker][1] = osGetCount();
|
|
}
|
|
|
|
void profileEnd(s32 marker)
|
|
{
|
|
g_ProfileMarkers[g_ProfileIndex][marker][0] += osGetCount() - g_ProfileMarkers[g_ProfileIndex][marker][1];
|
|
}
|
|
|
|
u32 profileReadCounters(void)
|
|
{
|
|
u32 buf = IO_READ(DPC_BUFBUSY_REG);
|
|
u32 tmm = IO_READ(DPC_TMEM_REG);
|
|
u32 bus = IO_READ(DPC_PIPEBUSY_REG);
|
|
u32 max;
|
|
|
|
max = buf > tmm ? buf : tmm;
|
|
max = bus > max ? bus : max;
|
|
|
|
return max;
|
|
}
|
|
|
|
extern OSThread g_SchedThread;
|
|
|
|
void profileGetCounters(u32 counters[5])
|
|
{
|
|
s32 i;
|
|
u32 max = 0;
|
|
|
|
OSPri prevpri = osGetThreadPri(0);
|
|
osSetThreadPri(0, THREADPRI_SCHED + 1);
|
|
|
|
// RSP
|
|
counters[0] = g_ProfileAudCycles;
|
|
|
|
// RDP
|
|
for (i = 0; i < ARRAYCOUNT(g_ProfileGfxHistory); i++) {
|
|
if (g_ProfileGfxHistory[i] > max) {
|
|
max = g_ProfileGfxHistory[i];
|
|
}
|
|
}
|
|
|
|
counters[1] = g_ProfileGfxCycles;
|
|
|
|
// Audio thread
|
|
counters[2] = g_AudioManager.thread.cycles_saved;
|
|
g_AudioManager.thread.cycles_saved = 0;
|
|
|
|
// Main thread
|
|
counters[3] = g_MainThread.cycles_saved + (osGetCount() - g_MainThread.cycles_at_dispatch);
|
|
g_MainThread.cycles_saved = 0;
|
|
|
|
// Scheduler thread
|
|
counters[4] = g_SchedThread.cycles_saved;
|
|
g_SchedThread.cycles_saved = 0;
|
|
|
|
osSetThreadPri(0, prevpri);
|
|
}
|
|
|
|
void profileHandleRspEvent(s32 event)
|
|
{
|
|
switch (event) {
|
|
case RSPEVENT_AUD_START:
|
|
g_ProfileAudStart = osGetCount();
|
|
break;
|
|
case RSPEVENT_AUD_FINISH:
|
|
g_ProfileAudCycles = osGetCount() - g_ProfileAudStart;
|
|
break;
|
|
case RSPEVENT_GFX_START:
|
|
osDpSetStatus(DPC_CLR_CMD_CTR | DPC_CLR_PIPE_CTR | DPC_CLR_TMEM_CTR);
|
|
break;
|
|
case RSPEVENT_GFX_FINISH:
|
|
g_ProfileGfxCycles = profileReadCounters();
|
|
g_ProfileGfxHistory[g_ProfileGfxIndex] = g_ProfileGfxCycles;
|
|
g_ProfileGfxIndex = (g_ProfileGfxIndex + 1) % ARRAYCOUNT(g_ProfileGfxHistory);
|
|
break;
|
|
}
|
|
}
|
|
|
|
Gfx *profileRenderRdpLine(Gfx *gdl, s32 x, s32 *y, char *label, u32 *ticksarray)
|
|
{
|
|
char buffer[64];
|
|
s32 percent;
|
|
u32 colour;
|
|
u32 microseconds;
|
|
s32 textwidth;
|
|
s32 textheight;
|
|
s32 x2;
|
|
u32 ticks = 0;
|
|
s32 i;
|
|
|
|
for (i = 0; i < NUM_SAMPLES; i++) {
|
|
ticks += ticksarray[i];
|
|
}
|
|
|
|
ticks /= NUM_SAMPLES;
|
|
|
|
percent = ticks * 100 / (62500000 / 60);
|
|
microseconds = ticks * 10 / 625;
|
|
|
|
if (percent >= 100) {
|
|
colour = 0xff0000a0;
|
|
} else if (percent >= 80) {
|
|
colour = 0xffff00a0;
|
|
} else {
|
|
colour = 0x00ff00a0;
|
|
}
|
|
|
|
x2 = x;
|
|
gdl = textRender(gdl, &x2, y, label, g_CharsHandelGothicXs, g_FontHandelGothicXs, colour, 0x000000a0, viGetWidth(), viGetHeight(), 0, 0);
|
|
|
|
sprintf(buffer, "%d", microseconds);
|
|
textMeasure(&textheight, &textwidth, buffer, g_CharsHandelGothicXs, g_FontHandelGothicXs, 0);
|
|
|
|
x2 = x + 100 - textwidth;
|
|
gdl = textRender(gdl, &x2, y, buffer, g_CharsHandelGothicXs, g_FontHandelGothicXs, colour, 0x000000a0, viGetWidth(), viGetHeight(), 0, 0);
|
|
|
|
sprintf(buffer, "%d%%\n", percent);
|
|
textMeasure(&textheight, &textwidth, buffer, g_CharsHandelGothicXs, g_FontHandelGothicXs, 0);
|
|
|
|
x2 = x + 130 - textwidth;
|
|
gdl = textRender(gdl, &x2, y, buffer, g_CharsHandelGothicXs, g_FontHandelGothicXs, colour, 0x000000a0, viGetWidth(), viGetHeight(), 0, 0);
|
|
|
|
return gdl;
|
|
}
|
|
|
|
Gfx *profileRenderCpuLine(Gfx *gdl, s32 x, s32 *y, char *label, s32 marker)
|
|
{
|
|
char buffer[64];
|
|
u32 ticks = 0;
|
|
s32 percent;
|
|
u32 colour;
|
|
u32 microseconds;
|
|
s32 textwidth;
|
|
s32 textheight;
|
|
s32 x2;
|
|
s32 i;
|
|
|
|
for (i = 0; i < NUM_SAMPLES; i++) {
|
|
ticks += g_ProfileMarkers[i][marker][0];
|
|
}
|
|
|
|
ticks /= NUM_SAMPLES;
|
|
|
|
percent = 100 * ticks / (OS_CPU_COUNTER / 60);
|
|
microseconds = OS_CYCLES_TO_USEC(ticks);
|
|
|
|
if (percent >= 100) {
|
|
colour = 0xff0000a0;
|
|
} else if (percent >= 80) {
|
|
colour = 0xffff00a0;
|
|
} else {
|
|
colour = 0x00ff00a0;
|
|
}
|
|
|
|
x2 = x;
|
|
gdl = textRender(gdl, &x2, y, label, g_CharsHandelGothicXs, g_FontHandelGothicXs, colour, 0x000000a0, viGetWidth(), viGetHeight(), 0, 0);
|
|
|
|
sprintf(buffer, "%d", microseconds);
|
|
textMeasure(&textheight, &textwidth, buffer, g_CharsHandelGothicXs, g_FontHandelGothicXs, 0);
|
|
|
|
x2 = x + 100 - textwidth;
|
|
gdl = textRender(gdl, &x2, y, buffer, g_CharsHandelGothicXs, g_FontHandelGothicXs, colour, 0x000000a0, viGetWidth(), viGetHeight(), 0, 0);
|
|
|
|
sprintf(buffer, "%d%%\n", percent);
|
|
textMeasure(&textheight, &textwidth, buffer, g_CharsHandelGothicXs, g_FontHandelGothicXs, 0);
|
|
|
|
x2 = x + 130 - textwidth;
|
|
gdl = textRender(gdl, &x2, y, buffer, g_CharsHandelGothicXs, g_FontHandelGothicXs, colour, 0x000000a0, viGetWidth(), viGetHeight(), 0, 0);
|
|
|
|
return gdl;
|
|
}
|
|
|
|
Gfx *profileRender(Gfx *gdl)
|
|
{
|
|
if (g_FontHandelGothicXs) {
|
|
s32 x = 10;
|
|
s32 y = 10;
|
|
|
|
gdl = text0f153628(gdl);
|
|
gdl = profileRenderCpuLine(gdl, x, &y, "CPU", PROFILEMARKER_CPU);
|
|
gdl = profileRenderCpuLine(gdl, x, &y, " audio", PROFILEMARKER_AUDIO);
|
|
gdl = profileRenderCpuLine(gdl, x, &y, " lvTick", PROFILEMARKER_LVTICK);
|
|
gdl = profileRenderCpuLine(gdl, x, &y, " hudmsgs ", PROFILEMARKER_LVT_HUDMSGS );
|
|
gdl = profileRenderCpuLine(gdl, x, &y, " vtxstore ", PROFILEMARKER_LVT_VTXSTORE );
|
|
gdl = profileRenderCpuLine(gdl, x, &y, " casings ", PROFILEMARKER_LVT_CASINGS );
|
|
gdl = profileRenderCpuLine(gdl, x, &y, " shards ", PROFILEMARKER_LVT_SHARDS );
|
|
gdl = profileRenderCpuLine(gdl, x, &y, " sparks ", PROFILEMARKER_LVT_SPARKS );
|
|
gdl = profileRenderCpuLine(gdl, x, &y, " wallhits ", PROFILEMARKER_LVT_WALLHITS );
|
|
gdl = profileRenderCpuLine(gdl, x, &y, " splats ", PROFILEMARKER_LVT_SPLATS );
|
|
gdl = profileRenderCpuLine(gdl, x, &y, " weather ", PROFILEMARKER_LVT_WEATHER );
|
|
gdl = profileRenderCpuLine(gdl, x, &y, " nbombs ", PROFILEMARKER_LVT_NBOMBS );
|
|
gdl = profileRenderCpuLine(gdl, x, &y, " miscsfx ", PROFILEMARKER_LVT_MISCSFX );
|
|
gdl = profileRenderCpuLine(gdl, x, &y, " snd ", PROFILEMARKER_LVT_SND );
|
|
gdl = profileRenderCpuLine(gdl, x, &y, " pak ", PROFILEMARKER_LVT_PAK );
|
|
gdl = profileRenderCpuLine(gdl, x, &y, " lighting ", PROFILEMARKER_LVT_LIGHTING );
|
|
gdl = profileRenderCpuLine(gdl, x, &y, " modelmgr ", PROFILEMARKER_LVT_MODELMGR );
|
|
gdl = profileRenderCpuLine(gdl, x, &y, " boltbeams ", PROFILEMARKER_LVT_BOLTBEAMS );
|
|
gdl = profileRenderCpuLine(gdl, x, &y, " activemenu", PROFILEMARKER_LVT_ACTIVEMENU);
|
|
gdl = profileRenderCpuLine(gdl, x, &y, " menu ", PROFILEMARKER_LVT_MENU );
|
|
gdl = profileRenderCpuLine(gdl, x, &y, " scenario ", PROFILEMARKER_LVT_SCENARIO );
|
|
gdl = profileRenderCpuLine(gdl, x, &y, " props ", PROFILEMARKER_LVT_PROPS );
|
|
gdl = profileRenderCpuLine(gdl, x, &y, " music ", PROFILEMARKER_LVT_MUSIC );
|
|
gdl = profileRenderCpuLine(gdl, x, &y, " padeffects", PROFILEMARKER_LVT_PADEFFECTS);
|
|
gdl = profileRenderCpuLine(gdl, x, &y, " lvTickPlayers", PROFILEMARKER_LVTICKPLAYERS);
|
|
|
|
x = 170;
|
|
y = 10;
|
|
|
|
gdl = profileRenderCpuLine(gdl, x, &y, " lvRender", PROFILEMARKER_LVRENDER);
|
|
gdl = profileRenderCpuLine(gdl, x, &y, " prepare ", PROFILEMARKER_LVR_PREPARE );
|
|
gdl = profileRenderCpuLine(gdl, x, &y, " bondgun ", PROFILEMARKER_LVR_BONDGUN );
|
|
gdl = profileRenderCpuLine(gdl, x, &y, " sky1 ", PROFILEMARKER_LVR_SKY1 );
|
|
gdl = profileRenderCpuLine(gdl, x, &y, " bgtick ", PROFILEMARKER_LVR_BGTICK );
|
|
gdl = profileRenderCpuLine(gdl, x, &y, " lights ", PROFILEMARKER_LVR_LIGHTS );
|
|
gdl = profileRenderCpuLine(gdl, x, &y, " props ", PROFILEMARKER_LVR_PROPS );
|
|
gdl = profileRenderCpuLine(gdl, x, &y, " scenariochr ", PROFILEMARKER_LVR_SCENARIOCHR );
|
|
gdl = profileRenderCpuLine(gdl, x, &y, " propssort ", PROFILEMARKER_LVR_PROPSSORT );
|
|
gdl = profileRenderCpuLine(gdl, x, &y, " autoaim ", PROFILEMARKER_LVR_AUTOAIM );
|
|
gdl = profileRenderCpuLine(gdl, x, &y, " hands ", PROFILEMARKER_LVR_HANDS );
|
|
gdl = profileRenderCpuLine(gdl, x, &y, " lookingat ", PROFILEMARKER_LVR_LOOKINGAT );
|
|
gdl = profileRenderCpuLine(gdl, x, &y, " trackedprops", PROFILEMARKER_LVR_TRACKEDPROPS);
|
|
gdl = profileRenderCpuLine(gdl, x, &y, " pickup ", PROFILEMARKER_LVR_PICKUP );
|
|
gdl = profileRenderCpuLine(gdl, x, &y, " bg ", PROFILEMARKER_LVR_BG );
|
|
gdl = profileRenderCpuLine(gdl, x, &y, " beams ", PROFILEMARKER_LVR_BEAMS );
|
|
gdl = profileRenderCpuLine(gdl, x, &y, " shards ", PROFILEMARKER_LVR_SHARDS );
|
|
gdl = profileRenderCpuLine(gdl, x, &y, " sparks ", PROFILEMARKER_LVR_SPARKS );
|
|
gdl = profileRenderCpuLine(gdl, x, &y, " weather ", PROFILEMARKER_LVR_WEATHER );
|
|
gdl = profileRenderCpuLine(gdl, x, &y, " nbombs ", PROFILEMARKER_LVR_NBOMBS );
|
|
gdl = profileRenderCpuLine(gdl, x, &y, " hud ", PROFILEMARKER_LVR_HUD );
|
|
gdl = profileRenderCpuLine(gdl, x, &y, " scenario ", PROFILEMARKER_LVR_SCENARIO );
|
|
gdl = profileRenderCpuLine(gdl, x, &y, " fade ", PROFILEMARKER_LVR_FADE );
|
|
gdl = profileRenderCpuLine(gdl, x, &y, " sky2 ", PROFILEMARKER_LVR_SKY2 );
|
|
gdl = profileRenderCpuLine(gdl, x, &y, " activemenu ", PROFILEMARKER_LVR_ACTIVEMENU );
|
|
gdl = profileRenderCpuLine(gdl, x, &y, " menu ", PROFILEMARKER_LVR_MENU );
|
|
gdl = profileRenderCpuLine(gdl, x, &y, " artifacts ", PROFILEMARKER_LVR_ARTIFACTS );
|
|
gdl = profileRenderCpuLine(gdl, x, &y, "tmp", PROFILEMARKER_TMP);
|
|
gdl = text0f153780(gdl);
|
|
}
|
|
|
|
return gdl;
|
|
}
|
|
|
|
Gfx *profileRenderDynamicSlot(Gfx *gdl, s32 x, s32 *y, s32 index)
|
|
{
|
|
char buffer[64];
|
|
struct profileslot *slot = &g_ProfileSlots[index];
|
|
s32 childticks = 0;
|
|
s32 textwidth;
|
|
s32 textheight;
|
|
s32 x2 = x;
|
|
s32 i;
|
|
|
|
if (slot->recursion) {
|
|
sprintf(buffer, "%s:%d *RECURSION*\n", slot->file, slot->line);
|
|
gdl = textRender(gdl, &x2, y, buffer, g_CharsHandelGothicXs, g_FontHandelGothicXs, 0x00ff00a0, 0x000000a0, viGetWidth(), viGetHeight(), 0, 0);
|
|
} else {
|
|
// Render this slot
|
|
sprintf(buffer, "%s:%d", slot->file, slot->line);
|
|
gdl = textRender(gdl, &x2, y, buffer, g_CharsHandelGothicXs, g_FontHandelGothicXs, 0x00ff00a0, 0x000000a0, viGetWidth(), viGetHeight(), 0, 0);
|
|
|
|
sprintf(buffer, "%dus", (u32) OS_CYCLES_TO_USEC(slot->ticks));
|
|
textMeasure(&textheight, &textwidth, buffer, g_CharsHandelGothicXs, g_FontHandelGothicXs, 0);
|
|
|
|
x2 = 160 - textwidth;
|
|
gdl = textRender(gdl, &x2, y, buffer, g_CharsHandelGothicXs, g_FontHandelGothicXs, 0x00ff00a0, 0x000000a0, viGetWidth(), viGetHeight(), 0, 0);
|
|
|
|
sprintf(buffer, "%d\n", slot->numiterations);
|
|
x2 = 170;
|
|
gdl = textRender(gdl, &x2, y, buffer, g_CharsHandelGothicXs, g_FontHandelGothicXs, 0x00ff00a0, 0x000000a0, viGetWidth(), viGetHeight(), 0, 0);
|
|
|
|
// Render child slots
|
|
for (i = 0; i < ARRAYCOUNT(g_ProfileSlots); i++) {
|
|
if (g_ProfileSlots[i].file && g_ProfileSlots[i].parent == slot) {
|
|
gdl = profileRenderDynamicSlot(gdl, x + 5, y, i);
|
|
childticks += g_ProfileSlots[i].ticks;
|
|
}
|
|
}
|
|
|
|
// Render child "other" slot
|
|
if (childticks > 0) {
|
|
x2 = x + 5;
|
|
gdl = textRender(gdl, &x2, y, "other", g_CharsHandelGothicXs, g_FontHandelGothicXs, 0x00ff00a0, 0x000000a0, viGetWidth(), viGetHeight(), 0, 0);
|
|
|
|
sprintf(buffer, "%dus\n", (u32) OS_CYCLES_TO_USEC(slot->ticks - childticks));
|
|
textMeasure(&textheight, &textwidth, buffer, g_CharsHandelGothicXs, g_FontHandelGothicXs, 0);
|
|
|
|
x2 = 160 - textwidth;
|
|
gdl = textRender(gdl, &x2, y, buffer, g_CharsHandelGothicXs, g_FontHandelGothicXs, 0x00ff00a0, 0x000000a0, viGetWidth(), viGetHeight(), 0, 0);
|
|
}
|
|
}
|
|
|
|
return gdl;
|
|
}
|
|
|
|
Gfx *profileRenderDynamic(Gfx *gdl)
|
|
{
|
|
if (g_FontHandelGothicXs) {
|
|
s32 x = 10;
|
|
s32 y = 10;
|
|
s32 i;
|
|
|
|
gdl = text0f153628(gdl);
|
|
|
|
for (i = 0; i < ARRAYCOUNT(g_ProfileSlots); i++) {
|
|
if (g_ProfileSlots[i].file && g_ProfileSlots[i].parent == NULL) {
|
|
gdl = profileRenderDynamicSlot(gdl, x, &y, i);
|
|
}
|
|
}
|
|
|
|
gdl = text0f153780(gdl);
|
|
}
|
|
|
|
return gdl;
|
|
}
|
|
#endif
|