vk: profiler: start implementing universal metric graphs

This commit is contained in:
Ivan Avdeev 2023-03-25 12:49:31 -07:00 committed by Ivan Avdeev
parent 8afd23a2d4
commit 2b2e69da72
1 changed files with 120 additions and 26 deletions

View File

@ -9,9 +9,9 @@
#include "xash3d_mathlib.h" // Q_min
#define MAX_SPEEDS_MESSAGE 1024
#define MAX_FRAMES_HISTORY 256
#define MAX_SPEEDS_METRICS (APROF_MAX_SCOPES + 4)
#define TARGET_FRAME_TIME (1000.f / 60.f)
#define MAX_GRAPHS 8
// Valid bits for `r_speeds` argument:
enum {
@ -29,23 +29,35 @@ typedef struct {
const char *name;
r_speeds_metric_type_t type;
// int low_watermark, high_watermark;
int graph_index;
} r_speeds_metric_t;
static struct {
float frame_times[MAX_FRAMES_HISTORY];
uint32_t frame_num;
typedef struct {
float *data;
int data_count;
int data_write;
int source_metric;
int height;
int max_value;
} r_speeds_graph_t;
static struct {
aprof_event_t *paused_events;
int paused_events_count;
int pause_requested;
struct {
int glyph_width, glyph_height;
float scale;
} font_metrics;
r_speeds_metric_t metrics[MAX_SPEEDS_METRICS];
int metrics_count;
r_speeds_graph_t graphs[MAX_GRAPHS];
int graphs_count;
struct {
int frame_time_us, cpu_time_us, cpu_wait_time_us, gpu_time_us;
struct {
@ -221,26 +233,28 @@ static void handlePause( uint32_t prev_frame_index ) {
}
}
static int drawFrameTimeGraph( const int frame_bar_y, const float frame_bar_y_scale ) {
const float width = (float)vk_frame.width / MAX_FRAMES_HISTORY;
static int drawGraph( const r_speeds_graph_t *graph, const int frame_bar_y ) {
const float width = (float)vk_frame.width / graph->data_count;
const float height_scale = (float)graph->height / graph->max_value;
// TODO
// 60fps
CL_FillRGBA(0, frame_bar_y + frame_bar_y_scale * TARGET_FRAME_TIME, vk_frame.width, 1, 0, 255, 0, 50);
//CL_FillRGBA(0, frame_bar_y + frame_bar_y_scale * TARGET_FRAME_TIME, vk_frame.width, 1, 0, 255, 0, 50);
// 30fps
CL_FillRGBA(0, frame_bar_y + frame_bar_y_scale * TARGET_FRAME_TIME * 2, vk_frame.width, 1, 255, 0, 0, 50);
//CL_FillRGBA(0, frame_bar_y + frame_bar_y_scale * TARGET_FRAME_TIME * 2, vk_frame.width, 1, 255, 0, 0, 50);
for (int i = 0; i < MAX_FRAMES_HISTORY; ++i) {
const float frame_time = g_speeds.frame_times[(g_speeds.frame_num + i) % MAX_FRAMES_HISTORY];
for (int i = 0; i < graph->data_count; ++i) {
const float value = graph->data[(graph->data_write + i) % graph->data_count];
// > 60 fps => 0, 30..60 fps -> 1..0, <30fps => 1
const float time = linearstep(TARGET_FRAME_TIME, TARGET_FRAME_TIME*2.f, frame_time);
const int red = 255 * time;
const int green = 255 * (1 - time);
CL_FillRGBA(i * width, frame_bar_y, width, frame_time * frame_bar_y_scale, red, green, 0, 127);
//const float time = linearstep(TARGET_FRAME_TIME, TARGET_FRAME_TIME*2.f, value);
//const int red = 255 * time;
//const int green = 255 * (1 - time);
const int red = 255, green = 255, blue = 255;
CL_FillRGBA(i * width, frame_bar_y, width, value * height_scale, red, green, blue, 127);
}
return frame_bar_y + frame_bar_y_scale * TARGET_FRAME_TIME * 2;
return frame_bar_y + graph->height; // frame_bar_y_scale * TARGET_FRAME_TIME * 2;
}
static int drawFrames( int draw, uint32_t prev_frame_index, int y, const uint64_t gpu_frame_begin_ns, const uint64_t gpu_frame_end_ns ) {
@ -316,6 +330,18 @@ static void getCurrentFontMetrics(void) {
// we don't have any access to real font metrics from here, ref_api_t doesn't give us anything about fonts. ;_;
g_speeds.font_metrics.glyph_width = 8 * scale;
g_speeds.font_metrics.glyph_height = 20 * scale;
g_speeds.font_metrics.scale = scale;
}
static int drawGraphs( int y ) {
for (int i = 0; i < g_speeds.graphs_count; ++i) {
r_speeds_graph_t *const graph = g_speeds.graphs + i;
graph->data[graph->data_write] = *g_speeds.metrics[graph->source_metric].p_value;
graph->data_write = (graph->data_write + 1) % graph->data_count;
y = drawGraph(graph, y) + 10;
}
return y;
}
void R_ShowExtendedProfilingData(uint32_t prev_frame_index, uint64_t gpu_frame_begin_ns, uint64_t gpu_frame_end_ns) {
@ -335,23 +361,17 @@ void R_ShowExtendedProfilingData(uint32_t prev_frame_index, uint64_t gpu_frame_b
g_speeds.frame.frame_time_us = delta_ns / 1000;
g_speeds.frame.gpu_time_us = (gpu_frame_end_ns - gpu_frame_begin_ns) / 1000;
{
const float frame_time_ms = delta_ns * 1e-6;
g_speeds.frame_times[g_speeds.frame_num] = frame_time_ms;
g_speeds.frame_num = (g_speeds.frame_num + 1) % MAX_FRAMES_HISTORY;
}
handlePause( prev_frame_index );
{
getCurrentFontMetrics();
int y = 100;
const float frame_bar_y_scale = 2.f; // ms to pixels (somehow)
const int draw = speeds_bits & SPEEDS_BIT_FRAME;
if (draw)
y = drawFrameTimeGraph( y, frame_bar_y_scale ) + 20;
y = drawFrames( draw, prev_frame_index, y, gpu_frame_begin_ns, gpu_frame_end_ns );
if (draw)
y = drawGraphs(y + 10);
}
if (speeds_bits & SPEEDS_BIT_SIMPLE) {
@ -381,8 +401,81 @@ static void togglePause( void ) {
}
static int findMetricIndexByName(const char *name) {
for (int i = 0; i < g_speeds.metrics_count; ++i) {
if (Q_strcmp(g_speeds.metrics[i].name, name) == 0)
return i;
}
return -1;
}
static void speedsGraphAdd( void ) {
const char *args = gEngine.Cmd_Args();
gEngine.Con_Reportf("ARGS: %s\n", args);
const int metric_index = findMetricIndexByName(args);
if (metric_index < 0) {
gEngine.Con_Printf(S_ERROR "Metric \"%s\" not found\n", args);
return;
}
r_speeds_metric_t *const metric = g_speeds.metrics + metric_index;
if (metric->graph_index >= 0) {
gEngine.Con_Printf(S_WARN "Metric \"%s\" already has graph @%d\n", args, metric->graph_index);
return;
}
if (g_speeds.graphs_count == MAX_GRAPHS) {
gEngine.Con_Printf(S_ERROR "Cannot add graph for metric \"%s\", no free graphs slots (max=%d)\n", args, MAX_GRAPHS);
return;
}
metric->graph_index = g_speeds.graphs_count++;
r_speeds_graph_t *const graph = g_speeds.graphs + metric->graph_index;
// TODO make these customizable
graph->data_count = 60;
graph->height = 100 * g_speeds.font_metrics.scale;
switch (metric->type) {
case kSpeedsMetricCount:
graph->max_value = 20;
break;
case kSpeedsMetricBytes:
graph->max_value = 1024*1024;
break;
case kSpeedsMetricMicroseconds:
graph->max_value = TARGET_FRAME_TIME * 2 * 1000;
break;
}
ASSERT(!graph->data);
graph->data = Mem_Malloc(vk_core.pool, graph->data_count * sizeof(float));
graph->data_write = 0;
graph->source_metric = metric_index;
}
static void speedsGraphRemove( void ) {
const char *args = gEngine.Cmd_Args();
gEngine.Con_Reportf("ARGS: %s\n", args);
// FIXME remove everything only if there are no indexes specified
for (int i = 0; i < g_speeds.graphs_count; ++i) {
r_speeds_graph_t *const graph = g_speeds.graphs + i;
ASSERT(graph->data);
Mem_Free(graph->data);
graph->data = NULL;
}
g_speeds.graphs_count = 0;
}
void R_SpeedsInit( void ) {
gEngine.Cmd_AddCommand("r_slows_toggle_pause", togglePause, "Toggle frame profiler pause");
gEngine.Cmd_AddCommand("r_speeds_toggle_pause", togglePause, "Toggle frame profiler pause");
// TODO is there a better UI/UX for this
gEngine.Cmd_AddCommand("r_speeds_graph_add", speedsGraphAdd, "Add graph for metric");
gEngine.Cmd_AddCommand("r_speeds_graph_remove", speedsGraphRemove, "Remove graphs by their indexes");
R_SpeedsRegisterMetric(&g_speeds.frame.frame_time_us, "frame", kSpeedsMetricMicroseconds);
R_SpeedsRegisterMetric(&g_speeds.frame.cpu_time_us, "cpu", kSpeedsMetricMicroseconds);
@ -415,4 +508,5 @@ void R_SpeedsRegisterMetric(int* p_value, const char *name, r_speeds_metric_type
metric->p_value = p_value;
metric->name = name;
metric->type = type;
metric->graph_index = -1;
}