Revamp Djui Profilers (#381)

This commit is contained in:
Prince Frizzy 2024-10-20 06:27:09 -04:00 committed by GitHub
parent e99d600bf7
commit a0ae1cdc02
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 356 additions and 133 deletions

View file

@ -666,18 +666,5 @@ void render_hud(void) {
if (hudDisplayFlags & HUD_DISPLAY_FLAG_TIMER && showHud) {
render_hud_timer();
}
extern bool configLuaProfiler;
if (configLuaProfiler) {
extern void lua_profiler_update_counters();
lua_profiler_update_counters();
}
#ifdef DEVELOPMENT
extern bool configCtxProfiler;
if (configCtxProfiler) {
extern void ctx_profiler_update_counters();
ctx_profiler_update_counters();
}
#endif
}
}

View file

@ -2,8 +2,6 @@
#include "utils/misc.h"
#include "debug_context.h"
#include "debuglog.h"
#include "game/print.h"
#include "game/hud.h"
#include "gfx_dimensions.h"
static u32 sCtxDepth[CTX_MAX] = { 0 };
@ -16,20 +14,6 @@ static f64 sCtxTime[CTX_MAX] = { 0 };
static f64 sCtxStartTimeStack[MAX_TIME_STACK] = { 0 };
static u32 sCtxStackIndex = 0;
static char* sDebugContextNames[] = {
"NONE",
"FRAME",
"NET",
"INTERP",
"GAME",
"SMLUA",
"AUDIO",
"RENDER",
"LEVEL",
"HOOK",
"MAX",
};
#endif
void debug_context_begin(enum DebugContext ctx) {
@ -71,29 +55,16 @@ void debug_context_reset(void) {
}
bool debug_context_within(enum DebugContext ctx) {
if (ctx > CTX_MAX) { return false; }
if (ctx >= CTX_MAX) { return false; }
return sCtxDepth[ctx] > 0;
}
#ifdef DEVELOPMENT
void ctx_profiler_update_counters(void) {
s32 y = SCREEN_HEIGHT - 60;
for (s32 i = 1; i < CTX_MAX; i++) {
const char *name = sDebugContextNames[i];
s32 counterUs = (s32) (sCtxTime[i] * 1000000.0);
char text[256];
snprintf(text, 256, " %05d", counterUs);
memcpy(text, name, MIN(12, strlen(name)));
for (s32 j = 0; j != 12; ++j) {
char c = text[j];
if (c >= 'a' && c <= 'z') c -= ('a' - 'A');
if ((c < '0' || c > '9') && (c < 'A' || c > 'Z')) c = ' ';
text[j] = c;
}
print_text(GFX_DIMENSIONS_RECT_FROM_LEFT_EDGE(4), y, text);
y -= 18;
}
void debug_context_set_time(enum DebugContext ctx, f64 time) {
if (ctx >= CTX_MAX) { return; }
sCtxTime[ctx] = time;
}
#endif
f64 debug_context_get_time(enum DebugContext ctx) {
if (ctx >= CTX_MAX) { return 0.0; }
return sCtxTime[ctx];
}

View file

@ -1,15 +1,17 @@
#pragma once
#include <PR/ultratypes.h>
#include <stdbool.h>
#define CTX_BEGIN(_ctx) debug_context_begin(_ctx)
#define CTX_END(_ctx) debug_context_end(_ctx)
#define CTX_WITHIN(_ctx) debug_context_within(_ctx)
#define CTX_TIME(_ctx, time) debug_context_set_time(_ctx, time)
#define CTX_EXTENT(_ctx, _f) { CTX_BEGIN(_ctx); _f(); CTX_END(_ctx); }
enum DebugContext {
CTX_NONE,
CTX_FRAME,
CTX_TOTAL,
CTX_NETWORK,
CTX_INTERP,
CTX_GAME_LOOP,
@ -26,3 +28,5 @@ void debug_context_begin(enum DebugContext ctx);
void debug_context_end(enum DebugContext ctx);
void debug_context_reset(void);
bool debug_context_within(enum DebugContext ctx);
void debug_context_set_time(enum DebugContext ctx, f64 time);
f64 debug_context_get_time(enum DebugContext ctx);

View file

@ -5,7 +5,9 @@
#include "djui_panel_pause.h"
#include "djui_panel_join.h"
#include "djui_panel_join_message.h"
#include "djui_ctx_display.h"
#include "djui_fps_display.h"
#include "djui_lua_profiler.h"
#include "../debuglog.h"
#include "pc/cliopts.h"
#include "game/level_update.h"
@ -52,6 +54,8 @@ void djui_shutdown(void) {
}
djui_fps_display_destroy();
djui_ctx_display_destroy();
djui_lua_profiler_destroy();
gDjuiShuttingDown = false;
sDjuiInited = false;
@ -97,6 +101,8 @@ void djui_init(void) {
djui_console_create();
djui_fps_display_create();
djui_ctx_display_create();
djui_lua_profiler_create();
sDjuiInited = true;
}
@ -158,6 +164,8 @@ void djui_render(void) {
}
djui_fps_display_render();
djui_ctx_display_render();
djui_lua_profiler_render();
if (sDjuiLuaErrorTimeout > 0) {
sDjuiLuaErrorTimeout--;

View file

@ -0,0 +1,139 @@
#include "djui_ctx_display.h"
#include "djui.h"
#include "pc/pc_main.h"
#include "pc/debug_context.h"
#ifdef DEVELOPMENT
static char* sDebugContextNames[] = {
"NONE",
"TOTAL",
"NET",
"INTERP",
"GAME",
"SMLUA",
"AUDIO",
"RENDER",
"LEVEL",
"HOOK",
"OTHER",
"MAX",
};
#endif
struct DjuiCtxEntry {
struct DjuiText *name;
struct DjuiText *timing;
};
struct DjuiCtxDisplay {
struct DjuiCtxEntry topEntry;
struct DjuiCtxEntry entries[CTX_MAX];
struct DjuiBase base;
};
struct DjuiCtxDisplay *sCtxDisplay = NULL;
void djui_ctx_display_update(void) {
if (!configCtxProfiler || sCtxDisplay == NULL) { return; }
#ifdef DEVELOPMENT
// Time we have for a indivdual frame. If we exceed it. We are in the red.
f64 frameTime = 1.0 / 30.0;
s32 frameTimeMs = (s32)(frameTime * 1000000.0);
struct DjuiCtxEntry *topEntry = &sCtxDisplay->topEntry;
// If we've exceeded our available frame time. Make the top entry timing red - For dramatic effect.
// Otherwise. It's green!
if (debug_context_get_time(CTX_TOTAL) > frameTime) {
djui_base_set_color(&topEntry->timing->base, 255, 69, 0, 240);
} else {
djui_base_set_color(&topEntry->timing->base, 124, 252, 0, 240);
}
djui_text_set_text(topEntry->name, "FRAME");
char timing[32];
snprintf(timing, 32, "%05d", frameTimeMs);
djui_text_set_text(topEntry->timing, timing);
// Draw the counters.
for (s32 i = CTX_TOTAL; i < CTX_MAX; i++) {
struct DjuiCtxEntry *entry = &sCtxDisplay->entries[i];
const char *name = sDebugContextNames[i];
djui_text_set_text(entry->name, name);
// The timing is in microseconds.
s32 counterMs = (s32)(debug_context_get_time(i) * 1000000.0);
char timing[32];
snprintf(timing, 32, "%05d", counterMs);
djui_text_set_text(entry->timing, timing);
}
#endif
}
void djui_ctx_display_render(void) {
if (!configCtxProfiler || sCtxDisplay == NULL) { return; }
djui_rect_render(&sCtxDisplay->base);
djui_base_render(&sCtxDisplay->base);
}
void djui_ctx_display_on_destroy(UNUSED struct DjuiBase* base) {
free(sCtxDisplay);
}
void djui_ctx_display_initialize_entry(struct DjuiBase *base, struct DjuiCtxEntry *entry, f64 offset) {
struct DjuiText *name = djui_text_create(base, "");
djui_text_set_alignment(name, DJUI_HALIGN_LEFT, DJUI_VALIGN_TOP);
djui_base_set_size_type(&name->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE);
djui_base_set_size(&name->base, 1.0f, name->fontScale * 2);
djui_base_set_location(&name->base, 0, -name->fontScale / 3.0f + offset);
djui_base_set_color(&name->base, 255, 255, 255, 240);
struct DjuiText *timing = djui_text_create(base, "");
djui_text_set_alignment(timing, DJUI_HALIGN_RIGHT, DJUI_VALIGN_TOP);
djui_base_set_size_type(&timing->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE);
djui_base_set_size(&timing->base, 1.0f, timing->fontScale * 2);
djui_base_set_location(&timing->base, 0, -timing->fontScale / 3.0f + offset);
djui_base_set_color(&timing->base, 255, 255, 255, 240);
entry->name = name;
entry->timing = timing;
}
void djui_ctx_display_create(void) {
struct DjuiCtxDisplay *ctxDisplay = calloc(1, sizeof(struct DjuiCtxDisplay));
struct DjuiBase *base = &ctxDisplay->base;
djui_base_init(NULL, base, NULL, djui_ctx_display_on_destroy);
djui_base_set_size(base, 220.0f, 39.0f + ((CTX_MAX - 2) * 26.0f));
djui_base_set_color(base, 0, 0, 0, 240);
djui_base_set_border_color(base, 0, 0, 0, 200);
djui_base_set_border_width(base, 4);
djui_base_set_padding(base, 4, 4, 4, 4);
djui_base_set_location(base, 0, 52.0f);
{
f64 offset = 4.0;
djui_ctx_display_initialize_entry(base, &ctxDisplay->topEntry, offset);
offset += 35.0;
for (s32 i = CTX_TOTAL; i < CTX_MAX; i++) {
djui_ctx_display_initialize_entry(base, &ctxDisplay->entries[i], offset);
offset += 22.0;
}
}
sCtxDisplay = ctxDisplay;
}
void djui_ctx_display_destroy(void) {
if (sCtxDisplay) {
djui_base_destroy(&sCtxDisplay->base);
}
}

View file

@ -0,0 +1,7 @@
#pragma once
#include "djui.h"
void djui_ctx_display_update(void);
void djui_ctx_display_render(void);
void djui_ctx_display_create(void);
void djui_ctx_display_destroy(void);

View file

@ -0,0 +1,150 @@
#include "djui_lua_profiler.h"
#include "djui.h"
#include "pc/pc_main.h"
#include "pc/mods/mod.h"
#include "pc/mods/mods.h"
#define MAX_PROFILED_MODS 16
#define REFRESH_RATE 30
struct DjuiPrfCounter {
f64 start;
f64 end;
f64 sum;
f64 display;
};
struct DjuiPrfEntry {
struct DjuiText *name;
struct DjuiText *timing;
struct DjuiPrfCounter counter;
};
struct DjuiPrfDisplay {
struct DjuiPrfEntry entries[MAX_PROFILED_MODS];
struct DjuiBase base;
};
struct DjuiPrfDisplay *sPrfDisplay = NULL;
void lua_profiler_start_counter(UNUSED struct Mod *mod) {
if (!configLuaProfiler || sPrfDisplay == NULL) { return; }
#ifndef WAPI_DUMMY
for (s32 i = 0; i != MIN(MAX_PROFILED_MODS, gActiveMods.entryCount); ++i) {
if (gActiveMods.entries[i] == mod) {
f64 freq = SDL_GetPerformanceFrequency();
f64 curr = SDL_GetPerformanceCounter();
sPrfDisplay->entries[i].counter.start = curr / freq;
return;
}
}
#endif
}
void lua_profiler_stop_counter(UNUSED struct Mod *mod) {
if (!configLuaProfiler || sPrfDisplay == NULL) { return; }
#ifndef WAPI_DUMMY
for (s32 i = 0; i != MIN(MAX_PROFILED_MODS, gActiveMods.entryCount); ++i) {
if (gActiveMods.entries[i] == mod) {
f64 freq = SDL_GetPerformanceFrequency();
f64 curr = SDL_GetPerformanceCounter();
struct DjuiPrfCounter *counter = &sPrfDisplay->entries[i].counter;
counter->end = curr / freq;
counter->sum += counter->end - counter->start;
return;
}
}
#endif
}
void djui_lua_profiler_update(void) {
if (!configLuaProfiler || sPrfDisplay == NULL) { return; }
// Draw the counters.
for (s32 i = 0; i < MIN(MAX_PROFILED_MODS, gActiveMods.entryCount); i++) {
struct DjuiPrfEntry *entry = &sPrfDisplay->entries[i];
struct DjuiPrfCounter *counter = &entry->counter;
if (gGlobalTimer % REFRESH_RATE == 0) {
counter->display = counter->sum / (f64) REFRESH_RATE;
counter->sum = 0;
}
char name[256];
memset(name, 0, 256);
const char *modName = gActiveMods.entries[i]->relativePath;
memcpy(name, modName, MIN(16, strlen(modName) - (gActiveMods.entries[i]->isDirectory ? 0 : 4)));
for (s32 j = 0; j != 16; ++j) {
char c = name[j];
if (c >= 'a' && c <= 'z') c -= ('a' - 'A');
if ((c < '0' || c > '9') && (c < 'A' || c > 'Z')) c = ' ';
name[j] = c;
}
djui_text_set_text(entry->name, name);
// The timing is in microseconds.
s32 counterMs = (s32)(counter->display * 1000000.0);
char timing[32];
snprintf(timing, 32, "%05d", counterMs);
djui_text_set_text(entry->timing, timing);
}
}
void djui_lua_profiler_render(void) {
if (!configLuaProfiler || sPrfDisplay == NULL) { return; }
djui_rect_render(&sPrfDisplay->base);
djui_base_render(&sPrfDisplay->base);
}
void djui_lua_profiler_on_destroy(UNUSED struct DjuiBase* base) {
free(sPrfDisplay);
}
void djui_lua_profiler_initialize_entry(struct DjuiBase *base, struct DjuiPrfEntry *entry, f64 offset) {
struct DjuiText *name = djui_text_create(base, "");
djui_text_set_alignment(name, DJUI_HALIGN_LEFT, DJUI_VALIGN_TOP);
djui_base_set_size_type(&name->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE);
djui_base_set_size(&name->base, 1.0f, name->fontScale * 2);
djui_base_set_location(&name->base, 0, -name->fontScale / 3.0f + offset);
djui_base_set_color(&name->base, 255, 255, 255, 240);
struct DjuiText *timing = djui_text_create(base, "");
djui_text_set_alignment(timing, DJUI_HALIGN_RIGHT, DJUI_VALIGN_TOP);
djui_base_set_size_type(&timing->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE);
djui_base_set_size(&timing->base, 1.0f, timing->fontScale * 2);
djui_base_set_location(&timing->base, 0, -timing->fontScale / 3.0f + offset);
djui_base_set_color(&timing->base, 255, 255, 255, 240);
entry->name = name;
entry->timing = timing;
}
void djui_lua_profiler_create(void) {
struct DjuiPrfDisplay *prfDisplay = calloc(1, sizeof(struct DjuiPrfDisplay));
struct DjuiBase *base = &prfDisplay->base;
djui_base_init(NULL, base, NULL, djui_lua_profiler_on_destroy);
djui_base_set_size(base, 290.0f, MAX_PROFILED_MODS * 26.0f);
djui_base_set_color(base, 0, 0, 0, 240);
djui_base_set_border_color(base, 0, 0, 0, 200);
djui_base_set_border_width(base, 4);
djui_base_set_padding(base, 4, 4, 4, 4);
djui_base_set_location(base, 0, 300.0f);
f64 offset = 4.0;
for (s32 i = 0; i < MAX_PROFILED_MODS; ++i, offset += 22.0) {
djui_lua_profiler_initialize_entry(base, &prfDisplay->entries[i], offset);
}
sPrfDisplay = prfDisplay;
}
void djui_lua_profiler_destroy(void) {
if (sPrfDisplay) {
djui_base_destroy(&sPrfDisplay->base);
}
}

View file

@ -0,0 +1,11 @@
#pragma once
#include "djui.h"
#include "pc/mods/mod.h"
void lua_profiler_start_counter(UNUSED struct Mod *mod);
void lua_profiler_stop_counter(UNUSED struct Mod *mod);
void djui_lua_profiler_update(void);
void djui_lua_profiler_render(void);
void djui_lua_profiler_create(void);
void djui_lua_profiler_destroy(void);

View file

@ -15,6 +15,7 @@
#include "pc/network/socket/socket.h"
#include "pc/chat_commands.h"
#include "pc/pc_main.h"
#include "pc/djui/djui_lua_profiler.h"
#include "pc/djui/djui_panel.h"
#include "pc/configfile.h"
@ -22,66 +23,6 @@
#include "game/print.h"
#include "gfx_dimensions.h"
#define MAX_PROFILED_MODS 16
#define REFRESH_RATE 15
static struct {
f64 start;
f64 end;
f64 sum;
f64 disp;
} sLuaProfilerCounters[MAX_PROFILED_MODS];
static void lua_profiler_start_counter(UNUSED struct Mod *mod) {
#ifndef WAPI_DUMMY
for (s32 i = 0; i != MIN(MAX_PROFILED_MODS, gActiveMods.entryCount); ++i) {
if (gActiveMods.entries[i] == mod) {
f64 freq = SDL_GetPerformanceFrequency();
f64 curr = SDL_GetPerformanceCounter();
sLuaProfilerCounters[i].start = curr / freq;
return;
}
}
#endif
}
static void lua_profiler_stop_counter(UNUSED struct Mod *mod) {
#ifndef WAPI_DUMMY
for (s32 i = 0; i != MIN(MAX_PROFILED_MODS, gActiveMods.entryCount); ++i) {
if (gActiveMods.entries[i] == mod) {
f64 freq = SDL_GetPerformanceFrequency();
f64 curr = SDL_GetPerformanceCounter();
sLuaProfilerCounters[i].end = curr / freq;
sLuaProfilerCounters[i].sum += sLuaProfilerCounters[i].end - sLuaProfilerCounters[i].start;
return;
}
}
#endif
}
void lua_profiler_update_counters(void) {
if (gGlobalTimer % REFRESH_RATE == 0) {
for (s32 i = 0; i != MIN(MAX_PROFILED_MODS, gActiveMods.entryCount); ++i) {
sLuaProfilerCounters[i].disp = sLuaProfilerCounters[i].sum / (f64) REFRESH_RATE;
sLuaProfilerCounters[i].sum = 0;
}
}
for (s32 i = 0, y = SCREEN_HEIGHT - 60; i != MIN(MAX_PROFILED_MODS, gActiveMods.entryCount); ++i, y -= 18) {
const char *modName = gActiveMods.entries[i]->relativePath;
s32 modCounterUs = (s32) (sLuaProfilerCounters[i].disp * 1000000.0);
char text[256];
snprintf(text, 256, " %05d", modCounterUs);
memcpy(text, modName, MIN(12, strlen(modName) - (gActiveMods.entries[i]->isDirectory ? 0 : 4)));
for (s32 j = 0; j != 12; ++j) {
char c = text[j];
if (c >= 'a' && c <= 'z') c -= ('a' - 'A');
if ((c < '0' || c > '9') && (c < 'A' || c > 'Z')) c = ' ';
text[j] = c;
}
print_text(GFX_DIMENSIONS_RECT_FROM_LEFT_EDGE(4), y, text);
}
}
#define MAX_HOOKED_REFERENCES 64
#define LUA_BEHAVIOR_FLAG (1 << 15)
@ -102,18 +43,13 @@ int smlua_call_hook(lua_State* L, int nargs, int nresults, int errfunc, struct M
gLuaActiveMod = activeMod;
gLuaLastHookMod = activeMod;
extern bool configLuaProfiler;
if (configLuaProfiler) {
lua_profiler_start_counter(activeMod);
}
lua_profiler_start_counter(activeMod);
CTX_BEGIN(CTX_HOOK);
int rc = smlua_pcall(L, nargs, nresults, errfunc);
CTX_END(CTX_HOOK);
if (configLuaProfiler) {
lua_profiler_stop_counter(activeMod);
}
lua_profiler_stop_counter(activeMod);
gLuaActiveMod = prev;
return rc;

View file

@ -1,6 +1,7 @@
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <time.h>
#include <unistd.h>
@ -43,7 +44,9 @@
#include "pc/djui/djui_unicode.h"
#include "pc/djui/djui_panel.h"
#include "pc/djui/djui_panel_modlist.h"
#include "pc/djui/djui_ctx_display.h"
#include "pc/djui/djui_fps_display.h"
#include "pc/djui/djui_lua_profiler.h"
#include "pc/debuglog.h"
#include "pc/utils/misc.h"
@ -182,28 +185,30 @@ void produce_interpolation_frames_and_delay(void) {
// interpolate and render
while ((curTime = clock_elapsed_f64()) < sFrameTargetTime) {
gfx_start_frame();
f32 delta = ((!configUncappedFramerate && configFrameLimit == FRAMERATE)
? 1.0f
: MAX(MIN((curTime - sFrameTimeStart) / (sFrameTargetTime - sFrameTimeStart), 1.0f), 0.0f)
);
gRenderingDelta = delta;
gfx_start_frame();
if (!gSkipInterpolationTitleScreen) { patch_interpolations(delta); }
send_display_list(gGfxSPTask);
gfx_end_frame();
// delay
if (!configUncappedFramerate) {
f64 targetDelta = 1.0 / (f64) configFrameLimit;
f64 now = clock_elapsed_f64();
f64 actualDelta = now - curTime;
if (actualDelta < targetDelta) {
f64 delay = ((targetDelta - actualDelta) * 1000.0);
if (delay > 0.0f) { WAPI.delay((u32) delay); }
}
}
frames++;
if (configUncappedFramerate) { continue; }
// Delay if our framerate is capped.
f64 targetDelta = 1.0 / (f64) configFrameLimit;
f64 now = clock_elapsed_f64();
f64 actualDelta = now - curTime;
if (actualDelta >= targetDelta) { continue; }
f64 delay = ((targetDelta - actualDelta) * 1000.0) - 1.0;
if (delay > 0.0f) {
WAPI.delay((u32)delay);
}
}
static u64 sFramesSinceFpsUpdate = 0;
@ -465,7 +470,7 @@ int main(int argc, char *argv[]) {
// main loop
while (true) {
debug_context_reset();
CTX_BEGIN(CTX_FRAME);
CTX_BEGIN(CTX_TOTAL);
WAPI.main_loop(produce_one_frame);
#ifdef DISCORD_SDK
discord_update();
@ -475,7 +480,12 @@ int main(int argc, char *argv[]) {
fflush(stdout);
fflush(stderr);
#endif
CTX_END(CTX_FRAME);
CTX_END(CTX_TOTAL);
#ifdef DEVELOPMENT
djui_ctx_display_update();
#endif
djui_lua_profiler_update();
}
return 0;