mirror of
https://github.com/valentderah/totk-pc-recomp.git
synced 2026-11-11 01:41:02 +00:00
The panel opens with the port version and the game update, so a screenshot in a bug report carries both. Under RAM, after a gap, two rows on the same grid: FPS with the 1% and 0.1% lows, and the frame times. perfmon.c keeps a histogram of frame times in 0.25 ms bins, one slice per second over the last ten seconds, and reads the lows from it twice a second. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
beb93276eb
commit
3422952e85
6 files changed
+82
-15
No files matched your search
+4
-2
@@ -910,10 +910,12 @@ static void probe_menu_text(void) {
|
||||
CHECK(has[0] && has[1] && has[2], "game language list lacks en-GB, fr-CA or es-419");
|
||||
}
|
||||
// the overlay formats keep their conversions
|
||||
CHECK(strstr(ui_text_in("ru", UI_MENU_OV_GAME_PCT), "%.0f%%") && strstr(ui_text_in("ru", UI_MENU_OV_MAX_MS), "%.1f"),
|
||||
CHECK(strstr(ui_text_in("ru", UI_MENU_OV_GAME_PCT), "%.0f%%") && strstr(ui_text_in("ru", UI_MENU_OV_MAX_MS), "%.1f") &&
|
||||
strstr(ui_text_in("ru", UI_MENU_OV_LOW1), "%.0f") && strstr(ui_text_in("ru", UI_MENU_OV_LOW01), "%.0f"),
|
||||
"overlay format lost its conversion");
|
||||
CHECK(strstr(ui_text_in("pt-BR", UI_MENU_OV_GAME_PCT), "%.0f%%") &&
|
||||
strstr(ui_text_in("pt-BR", UI_MENU_OV_MAX_MS), "%.1f"), "pt-BR overlay format lost its conversion");
|
||||
strstr(ui_text_in("pt-BR", UI_MENU_OV_MAX_MS), "%.1f") &&
|
||||
strstr(ui_text_in("pt-BR", UI_MENU_OV_LOW1), "%.0f") && strstr(ui_text_in("zh-Hans", UI_MENU_OV_LOW01), "%.0f"), "pt-BR overlay format lost its conversion");
|
||||
// a Brazilian game language gets the Brazilian keyboard row, Portugal's Portuguese keeps its own
|
||||
CHECK(!strcmp(ui_text_lang("pt-BR"), "pt-BR") && !strcmp(ui_text_lang("pt"), "pt") &&
|
||||
strcmp(ui_text_in("pt-BR", UI_SWKBD_PROMPT), ui_text_in("pt", UI_SWKBD_PROMPT)), "pt-BR keyboard row not used");
|
||||
|
||||
@@ -32,6 +32,14 @@ static float g_ring[PERF_HISTORY];
|
||||
static uint64_t g_ring_at[PERF_HISTORY]; // when each frame ended
|
||||
static int g_ring_n, g_ring_pos;
|
||||
|
||||
// The lows: a histogram of the frame times (perf.c's 0.25 ms bins) over the last LOW_SLICES seconds, kept as one slice
|
||||
// per second so the oldest second drops out whole. Per frame: one increment. The percentiles are read twice a second.
|
||||
#define LOW_BINS 1024
|
||||
#define LOW_SLICES 10
|
||||
static uint16_t g_low_hist[LOW_SLICES][LOW_BINS];
|
||||
static int g_low_cur;
|
||||
static uint64_t g_low_t0;
|
||||
|
||||
// The per-frame counters, summed since t0 (the overlay's lines are averages over half a second).
|
||||
static struct {
|
||||
uint64_t t0, frames, gpu0;
|
||||
@@ -47,6 +55,14 @@ void perfmon_frame(uint64_t now, uint64_t interval_ns, const uint64_t *wait_ns,
|
||||
g_ring_at[g_ring_pos] = now;
|
||||
g_ring_pos = (g_ring_pos + 1) % PERF_HISTORY;
|
||||
if (g_ring_n < PERF_HISTORY) g_ring_n++;
|
||||
if (now - g_low_t0 >= 1000000000ull) { // the next second: its slice starts empty
|
||||
g_low_cur = (g_low_cur + 1) % LOW_SLICES;
|
||||
memset(g_low_hist[g_low_cur], 0, sizeof g_low_hist[0]);
|
||||
g_low_t0 = now;
|
||||
}
|
||||
uint64_t bin = interval_ns / 250000ull;
|
||||
uint16_t *h = &g_low_hist[g_low_cur][bin < LOW_BINS ? bin : LOW_BINS - 1];
|
||||
if (*h < 65535) (*h)++;
|
||||
}
|
||||
g_win.frames++;
|
||||
g_win.replay += replay_ns / 1e6;
|
||||
@@ -76,9 +92,27 @@ static void frames_summary(PerfStats *s, uint64_t now) { // (lock held)
|
||||
}
|
||||
}
|
||||
|
||||
// The fps of the slowest 1% and 0.1% of the frames in the window (the bin's upper edge, as perf.c's summary does); -1
|
||||
// when the window holds too few frames. (lock held)
|
||||
static void lows_summary(PerfStats *s) {
|
||||
uint32_t total = 0;
|
||||
for (int k = 0; k < LOW_SLICES; k++)
|
||||
for (int b = 0; b < LOW_BINS; b++) total += g_low_hist[k][b];
|
||||
s->low1_fps = s->low01_fps = -1;
|
||||
if (total < 100) return;
|
||||
uint32_t cut1 = (total + 99) / 100, cut01 = (total + 999) / 1000, acc = 0;
|
||||
for (int b = LOW_BINS - 1; b >= 0 && s->low1_fps < 0; b--) {
|
||||
for (int k = 0; k < LOW_SLICES; k++) acc += g_low_hist[k][b];
|
||||
float fps = 1000.0f / ((b + 1) * 0.25f);
|
||||
if (s->low01_fps < 0 && acc >= cut01) s->low01_fps = fps;
|
||||
if (acc >= cut1) s->low1_fps = fps;
|
||||
}
|
||||
}
|
||||
|
||||
// Every half second: the window's sums as per-frame averages into g_stats, and a new window. (lock held)
|
||||
static void counters_summary(uint64_t now) {
|
||||
if (now - g_win.t0 < 500000000ull) return;
|
||||
lows_summary(&g_stats);
|
||||
uint64_t gpu = perf_gpu_busy_total();
|
||||
if (g_win.t0 && g_win.frames) {
|
||||
double n = (double)g_win.frames;
|
||||
@@ -342,6 +376,8 @@ void perfmon_mode(int mode) {
|
||||
}
|
||||
if (!mode) { // the next start begins a new graph and a new counter window
|
||||
g_ring_n = g_ring_pos = 0;
|
||||
memset(g_low_hist, 0, sizeof g_low_hist);
|
||||
g_low_t0 = 0;
|
||||
memset(&g_win, 0, sizeof g_win);
|
||||
}
|
||||
plat_mutex_unlock(&g_lock);
|
||||
|
||||
@@ -17,6 +17,8 @@ extern "C" {
|
||||
typedef struct {
|
||||
// the game's frames (presents), over the last second
|
||||
float fps, frame_ms_avg, frame_ms_max;
|
||||
// fps of the slowest 1% and 0.1% of the frames over the last ten seconds (-1: too few frames yet)
|
||||
float low1_fps, low01_fps;
|
||||
float frame_ms[PERF_HISTORY]; // oldest first
|
||||
// CPU: the whole machine and this process, percent of all logical processors
|
||||
char cpu_name[64];
|
||||
|
||||
+30
-13
@@ -1381,17 +1381,30 @@ static void perf_full_draw(const MenuTarget *t, float scale) {
|
||||
ImGui::SetWindowFontScale(0.72f);
|
||||
const ImVec4 gpu_c(0.55f, 0.83f, 0.31f, 1), cpu_c(0.18f, 0.59f, 0.80f, 1), ram_c(0.80f, 0.45f, 0.70f, 1),
|
||||
fps_c(0.95f, 0.95f, 0.95f, 1), wait_c(0.93f, 0.76f, 0.33f, 1), pic_c(0.62f, 0.70f, 0.95f, 1);
|
||||
// the port version and the game update first, so a screenshot in a bug report carries both; formatted once per
|
||||
// menu language
|
||||
static char ver[96];
|
||||
static const char *ver_fmt;
|
||||
if (ver_fmt != menu_text(UI_MENU_VERSION)) {
|
||||
ver_fmt = menu_text(UI_MENU_VERSION);
|
||||
char game[16];
|
||||
snprintf(game, sizeof game, "%d.%d.%d", rt_game_version / 100, rt_game_version / 10 % 10, rt_game_version % 10);
|
||||
snprintf(ver, sizeof ver, ver_fmt, rt_port_version, game);
|
||||
}
|
||||
ImGui::TextDisabled("%s", ver);
|
||||
if (ImGui::BeginTable("##perft", 4, ImGuiTableFlags_SizingFixedFit)) {
|
||||
static const UiTextId labels[] = {UI_MENU_OV_GPU, UI_MENU_OV_VRAM, UI_MENU_OV_CPU, UI_MENU_OV_RAM};
|
||||
static const PerfCol c0[] = {{UI_MENU_OV_FPS, 888}};
|
||||
static const PerfCol c1[] = {{UI_MENU_OV_FMT_PCT, 100}, {UI_MENU_OV_FMT_GB, 888.8f}, {UI_MENU_OV_FRAME_MS, 888.8f}};
|
||||
static const PerfCol c2[] = {{UI_MENU_OV_FMT_TEMP, 188}, {UI_MENU_OV_FMT_GB_OF, 888.8f}, {UI_MENU_OV_MAX_MS, 8888.8f}};
|
||||
static const UiTextId labels[] = {UI_MENU_OV_GPU, UI_MENU_OV_VRAM, UI_MENU_OV_CPU, UI_MENU_OV_RAM,
|
||||
UI_MENU_OV_FPS_LABEL, UI_MENU_OV_FRAME_LABEL};
|
||||
static const PerfCol c1[] = {{UI_MENU_OV_FMT_PCT, 100}, {UI_MENU_OV_FMT_GB, 888.8f}, {UI_MENU_OV_FMT_FPS, 888},
|
||||
{UI_MENU_OV_FRAME_MS, 888.8f}};
|
||||
static const PerfCol c2[] = {{UI_MENU_OV_FMT_TEMP, 188}, {UI_MENU_OV_FMT_GB_OF, 888.8f}, {UI_MENU_OV_LOW1, 888},
|
||||
{UI_MENU_OV_MAX_MS, 8888.8f}};
|
||||
static const PerfCol c3[] = {{UI_MENU_OV_FMT_POWER, 888}, {UI_MENU_OV_FMT_MHZ, 8888}, {UI_MENU_OV_GAME_PCT, 100},
|
||||
{UI_MENU_OV_GAME_GB, 888.8f}};
|
||||
perf_column("##c0", c0, 1, labels, 4);
|
||||
perf_column("##c1", c1, 3);
|
||||
perf_column("##c2", c2, 3);
|
||||
perf_column("##c3", c3, 4);
|
||||
{UI_MENU_OV_GAME_GB, 888.8f}, {UI_MENU_OV_LOW01, 888}};
|
||||
perf_column("##c0", nullptr, 0, labels, 6);
|
||||
perf_column("##c1", c1, 4);
|
||||
perf_column("##c2", c2, 4);
|
||||
perf_column("##c3", c3, 5);
|
||||
perf_label(UI_MENU_OV_GPU, gpu_c);
|
||||
perf_cell(UI_MENU_OV_FMT_PCT, s.gpu_pct);
|
||||
perf_cell(UI_MENU_OV_FMT_TEMP, s.gpu_temp_c);
|
||||
@@ -1408,12 +1421,16 @@ static void perf_full_draw(const MenuTarget *t, float scale) {
|
||||
perf_cell(UI_MENU_OV_FMT_GB, perf_gb(s.ram_used_mb));
|
||||
perf_cell(UI_MENU_OV_FMT_GB_OF, perf_gb(s.ram_total_mb));
|
||||
perf_cell(UI_MENU_OV_GAME_GB, perf_gb(s.ram_game_mb));
|
||||
ImGui::TableNextColumn(); // the frame row: no label, the rate in its place
|
||||
char b[64];
|
||||
snprintf(b, sizeof b, menu_text(UI_MENU_OV_FPS), s.fps);
|
||||
ImGui::TextColored(fps_c, "%s", b);
|
||||
ImGui::TableNextRow(ImGuiTableRowFlags_None, 6 * scale); // a gap between the machine and the frames
|
||||
// the frames: the rate over the last second with the lows over the last ten seconds, then the frame times
|
||||
perf_label(UI_MENU_OV_FPS_LABEL, fps_c);
|
||||
perf_cell(UI_MENU_OV_FMT_FPS, s.fps);
|
||||
perf_cell(UI_MENU_OV_LOW1, s.low1_fps);
|
||||
perf_cell(UI_MENU_OV_LOW01, s.low01_fps);
|
||||
perf_label(UI_MENU_OV_FRAME_LABEL, fps_c);
|
||||
perf_cell(UI_MENU_OV_FRAME_MS, s.frame_ms_avg);
|
||||
perf_cell(UI_MENU_OV_MAX_MS, s.frame_ms_max);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::EndTable();
|
||||
}
|
||||
float top = s.frame_ms_max > 50 ? s.frame_ms_max : 50;
|
||||
|
||||
@@ -408,6 +408,8 @@ static const char *const text_en[UI_TEXT_COUNT] = {
|
||||
|
||||
[UI_MENU_OV_FPS] = "%.0f FPS", [UI_MENU_OV_FRAME_MS] = "%.1f ms", [UI_MENU_OV_GAME_PCT] = "game %.0f%%",
|
||||
[UI_MENU_OV_MAX_MS] = "max %.1f ms",
|
||||
[UI_MENU_OV_FPS_LABEL] = "FPS", [UI_MENU_OV_FRAME_LABEL] = "Frame", [UI_MENU_OV_FMT_FPS] = "%.0f",
|
||||
[UI_MENU_OV_LOW1] = "1%% low %.0f", [UI_MENU_OV_LOW01] = "0.1%% low %.0f",
|
||||
[UI_MENU_OV_GPU] = "GPU", [UI_MENU_OV_CPU] = "CPU", [UI_MENU_OV_RAM] = "RAM", [UI_MENU_OV_VRAM] = "VRAM",
|
||||
[UI_MENU_OV_TEMP] = "Temperature", [UI_MENU_OV_POWER] = "Power", [UI_MENU_OV_CLOCK] = "Clock",
|
||||
[UI_MENU_OV_NONE] = "-",
|
||||
@@ -843,6 +845,8 @@ static const char *const text_ru[UI_TEXT_COUNT] = {
|
||||
|
||||
[UI_MENU_OV_FPS] = "%.0f FPS", [UI_MENU_OV_FRAME_MS] = "%.1f мс", [UI_MENU_OV_GAME_PCT] = "игра %.0f%%",
|
||||
[UI_MENU_OV_MAX_MS] = "макс. %.1f мс",
|
||||
[UI_MENU_OV_FPS_LABEL] = "FPS", [UI_MENU_OV_FRAME_LABEL] = "Кадр", [UI_MENU_OV_FMT_FPS] = "%.0f",
|
||||
[UI_MENU_OV_LOW1] = "1%% худших %.0f", [UI_MENU_OV_LOW01] = "0.1%% худших %.0f",
|
||||
[UI_MENU_OV_GPU] = "GPU", [UI_MENU_OV_CPU] = "ЦП", [UI_MENU_OV_RAM] = "ОЗУ", [UI_MENU_OV_VRAM] = "Видеопамять",
|
||||
[UI_MENU_OV_TEMP] = "Температура", [UI_MENU_OV_POWER] = "Мощность", [UI_MENU_OV_CLOCK] = "Частота",
|
||||
[UI_MENU_OV_NONE] = "-",
|
||||
@@ -1193,6 +1197,8 @@ static const char *const text_pt_br[UI_TEXT_COUNT] = {
|
||||
|
||||
[UI_MENU_OV_FPS] = "%.0f FPS", [UI_MENU_OV_FRAME_MS] = "%.1f ms", [UI_MENU_OV_GAME_PCT] = "jogo %.0f%%",
|
||||
[UI_MENU_OV_MAX_MS] = "máx. %.1f ms",
|
||||
[UI_MENU_OV_FPS_LABEL] = "FPS", [UI_MENU_OV_FRAME_LABEL] = "Quadro", [UI_MENU_OV_FMT_FPS] = "%.0f",
|
||||
[UI_MENU_OV_LOW1] = "1%% piores %.0f", [UI_MENU_OV_LOW01] = "0.1%% piores %.0f",
|
||||
[UI_MENU_OV_GPU] = "GPU", [UI_MENU_OV_CPU] = "CPU", [UI_MENU_OV_RAM] = "RAM", [UI_MENU_OV_VRAM] = "VRAM",
|
||||
[UI_MENU_OV_TEMP] = "Temperatura", [UI_MENU_OV_POWER] = "Consumo", [UI_MENU_OV_CLOCK] = "Frequência",
|
||||
[UI_MENU_OV_NONE] = "-",
|
||||
@@ -1785,6 +1791,8 @@ static const char *const text_zh_hans[UI_TEXT_COUNT] = {
|
||||
[UI_MENU_OV_FRAME_MS] = "%.1f ms",
|
||||
[UI_MENU_OV_GAME_PCT] = "游戏 %.0f%%",
|
||||
[UI_MENU_OV_MAX_MS] = "最长 %.1f ms",
|
||||
[UI_MENU_OV_FPS_LABEL] = "FPS", [UI_MENU_OV_FRAME_LABEL] = "帧时间", [UI_MENU_OV_FMT_FPS] = "%.0f",
|
||||
[UI_MENU_OV_LOW1] = "1%% 低 %.0f", [UI_MENU_OV_LOW01] = "0.1%% 低 %.0f",
|
||||
[UI_MENU_OV_GPU] = "GPU",
|
||||
[UI_MENU_OV_CPU] = "CPU",
|
||||
[UI_MENU_OV_RAM] = "内存",
|
||||
|
||||
@@ -204,6 +204,8 @@ typedef enum {
|
||||
|
||||
// Performance overlay
|
||||
UI_MENU_OV_FPS, UI_MENU_OV_FRAME_MS, UI_MENU_OV_GAME_PCT, UI_MENU_OV_MAX_MS,
|
||||
UI_MENU_OV_FPS_LABEL, UI_MENU_OV_FRAME_LABEL, UI_MENU_OV_FMT_FPS, // the frame rows: their labels, the rate
|
||||
UI_MENU_OV_LOW1, UI_MENU_OV_LOW01, // printf: the fps of the slowest 1% / 0.1% of the frames, no unit (the overlay column stays narrow)
|
||||
UI_MENU_OV_GPU, UI_MENU_OV_CPU, UI_MENU_OV_RAM, UI_MENU_OV_VRAM, UI_MENU_OV_TEMP, UI_MENU_OV_POWER,
|
||||
UI_MENU_OV_CLOCK, UI_MENU_OV_NONE,
|
||||
UI_MENU_OV_REPLAY, UI_MENU_OV_GPU_TIME, UI_MENU_OV_PIPE_WAIT, UI_MENU_OV_SHADER_WAIT, UI_MENU_OV_TEX_UPLOADS,
|
||||
|
||||
Reference in new issue
Block a user