Files
perfect-dark/src/lib/profile.c
T

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