vk: profiler: add cvar for selecting metrics to plot

also:
- invert graphs
- add text labels
- fix vertical gaps
This commit is contained in:
Ivan Avdeev 2023-03-27 11:49:15 -07:00 committed by Ivan Avdeev
parent af96609c04
commit 33aa4bc259
1 changed files with 104 additions and 70 deletions

View File

@ -44,6 +44,8 @@ typedef struct {
} r_speeds_graph_t;
static struct {
cvar_t *r_speeds_graphs;
aprof_event_t *paused_events;
int paused_events_count;
int pause_requested;
@ -234,10 +236,16 @@ static void handlePause( uint32_t prev_frame_index ) {
}
}
static int drawGraph( r_speeds_graph_t *const graph, const int frame_bar_y ) {
static int drawGraph( r_speeds_graph_t *const graph, int frame_bar_y ) {
const float width = (float)vk_frame.width / graph->data_count;
const float height_scale = (float)graph->height / graph->max_value;
rgba_t color = {255, 255, 255, 255}; // TODO = metric->color
const char *name = g_speeds.metrics[graph->source_metric].name;
gEngine.Con_DrawString(0, frame_bar_y, name, color);
frame_bar_y += graph->height;
// TODO
// 60fps
//CL_FillRGBA(0, frame_bar_y + frame_bar_y_scale * TARGET_FRAME_TIME, vk_frame.width, 1, 0, 255, 0, 50);
@ -246,7 +254,7 @@ static int drawGraph( r_speeds_graph_t *const graph, const int frame_bar_y ) {
int max_value = INT_MIN;
for (int i = 0; i < graph->data_count; ++i) {
int value = graph->data[(graph->data_write + i) % graph->data_count];
int value = Q_max(0, graph->data[(graph->data_write + i) % graph->data_count]);
max_value = Q_max(max_value, value);
value = Q_min(graph->max_value, value);
@ -255,13 +263,17 @@ static int drawGraph( r_speeds_graph_t *const graph, const int frame_bar_y ) {
//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 x0 = (float)i * width;
const int x1 = (float)(i+1) * width;
const int height = value * height_scale;
const int y = frame_bar_y - height;
const int red = 255, green = 255, blue = 255;
CL_FillRGBA(i * width, frame_bar_y, width, value * height_scale, red, green, blue, 127);
CL_FillRGBA(x0, y, x1-x0, height, red, green, blue, 127);
}
graph->max_value = max_value ? max_value : 1;
return frame_bar_y + graph->height; // frame_bar_y_scale * TARGET_FRAME_TIME * 2;
return frame_bar_y;
}
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 ) {
@ -351,50 +363,6 @@ static int drawGraphs( int y ) {
return y;
}
void R_ShowExtendedProfilingData(uint32_t prev_frame_index, uint64_t gpu_frame_begin_ns, uint64_t gpu_frame_end_ns) {
APROF_SCOPE_DECLARE_BEGIN(function, __FUNCTION__);
g_speeds.frame.message[0] = '\0';
const uint32_t speeds_bits = r_speeds->value;
if (speeds_bits)
speedsPrintf( "Renderer: ^1Vulkan%s^7\n", vk_frame.rtx_enabled ? " RT" : "" );
const uint32_t events = g_aprof.events_last_frame - prev_frame_index;
const uint64_t frame_begin_time = APROF_EVENT_TIMESTAMP(g_aprof.events[prev_frame_index]);
const unsigned long long delta_ns = APROF_EVENT_TIMESTAMP(g_aprof.events[g_aprof.events_last_frame]) - frame_begin_time;
g_speeds.frame.frame_time_us = delta_ns / 1000;
g_speeds.frame.gpu_time_us = (gpu_frame_end_ns - gpu_frame_begin_ns) / 1000;
handlePause( prev_frame_index );
{
getCurrentFontMetrics();
int y = 100;
const int draw = speeds_bits & SPEEDS_BIT_FRAME;
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) {
speedsPrintf("frame: %.03fms GPU: %.03fms\n", g_speeds.frame.frame_time_us * 1e-3f, g_speeds.frame.gpu_time_us * 1e-3);
speedsPrintf(" (ref) CPU: %.03fms wait: %.03fms\n", g_speeds.frame.cpu_time_us * 1e-3, g_speeds.frame.cpu_wait_time_us * 1e-3);
}
if (speeds_bits & SPEEDS_BIT_STATS) {
speedsPrintf("profiler events: %u, wraps: %d\n", events, g_aprof.current_frame_wraparounds);
printMetrics();
}
clearMetrics();
APROF_SCOPE_END(function);
}
static void togglePause( void ) {
if (g_speeds.paused_events) {
@ -405,39 +373,42 @@ static void togglePause( void ) {
} else {
g_speeds.pause_requested = 1;
}
}
static int findMetricIndexByName(const char *name) {
typedef struct {
const char *s;
int len;
} const_string_view_t;
static int findMetricIndexByName( const_string_view_t name) {
for (int i = 0; i < g_speeds.metrics_count; ++i) {
if (Q_strcmp(g_speeds.metrics[i].name, name) == 0)
if (Q_strncmp(g_speeds.metrics[i].name, name.s, name.len) == 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);
static void speedsGraphAddByMetricName( const_string_view_t name ) {
const int metric_index = findMetricIndexByName(name);
if (metric_index < 0) {
gEngine.Con_Printf(S_ERROR "Metric \"%s\" not found\n", args);
gEngine.Con_Printf(S_ERROR "Metric \"%.*s\" not found\n", name.len, name.s);
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);
gEngine.Con_Printf(S_WARN "Metric \"%.*s\" already has graph @%d\n", name.len, name.s, 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);
gEngine.Con_Printf(S_ERROR "Cannot add graph for metric \"%.*s\", no free graphs slots (max=%d)\n", name.len, name.s, MAX_GRAPHS);
return;
}
gEngine.Con_Printf("Adding profiler graph for metric %.*s(%d) at graph index %d\n", name.len, name.s, metric_index, g_speeds.graphs_count);
metric->graph_index = g_speeds.graphs_count++;
r_speeds_graph_t *const graph = g_speeds.graphs + metric->graph_index;
@ -447,16 +418,13 @@ static void speedsGraphAdd( void ) {
graph->max_value = 1; // Will be computed automatically on first frame
ASSERT(!graph->data);
graph->data = Mem_Malloc(vk_core.pool, graph->data_count * sizeof(float));
graph->data = Mem_Calloc(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
static void speedsGraphsRemoveAll( void ) {
gEngine.Con_Printf("Removing all %d profiler graphs\n", g_speeds.graphs_count);
for (int i = 0; i < g_speeds.graphs_count; ++i) {
r_speeds_graph_t *const graph = g_speeds.graphs + i;
ASSERT(graph->data);
@ -467,12 +435,30 @@ static void speedsGraphRemove( void ) {
g_speeds.graphs_count = 0;
}
void R_SpeedsInit( void ) {
gEngine.Cmd_AddCommand("r_speeds_toggle_pause", togglePause, "Toggle frame profiler pause");
static void processGraphCvar( void ) {
if (!(g_speeds.r_speeds_graphs->flags & FCVAR_CHANGED))
return;
// 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");
// TODO only remove graphs that are not present in the new list
speedsGraphsRemoveAll();
const char *p = g_speeds.r_speeds_graphs->string;
while (*p) {
const char *next = Q_strchrnul(p, ',');
const const_string_view_t name = {p, next - p};
speedsGraphAddByMetricName( name );
if (!*next)
break;
p = next + 1;
}
g_speeds.r_speeds_graphs->flags &= ~FCVAR_CHANGED;
}
void R_SpeedsInit( void ) {
g_speeds.r_speeds_graphs = gEngine.Cvar_Get("r_speeds_graphs", "", FCVAR_GLCONFIG, "List of metrics to plot as graphs, separated by commas");
gEngine.Cmd_AddCommand("r_speeds_toggle_pause", togglePause, "Toggle frame profiler pause");
R_SpeedsRegisterMetric(&g_speeds.frame.frame_time_us, "frame", kSpeedsMetricMicroseconds);
R_SpeedsRegisterMetric(&g_speeds.frame.cpu_time_us, "cpu", kSpeedsMetricMicroseconds);
@ -507,3 +493,51 @@ void R_SpeedsRegisterMetric(int* p_value, const char *name, r_speeds_metric_type
metric->type = type;
metric->graph_index = -1;
}
void R_ShowExtendedProfilingData(uint32_t prev_frame_index, uint64_t gpu_frame_begin_ns, uint64_t gpu_frame_end_ns) {
APROF_SCOPE_DECLARE_BEGIN(function, __FUNCTION__);
// Reads current font/DPI scale, many functions below use it
getCurrentFontMetrics();
processGraphCvar();
g_speeds.frame.message[0] = '\0';
const uint32_t speeds_bits = r_speeds->value;
if (speeds_bits)
speedsPrintf( "Renderer: ^1Vulkan%s^7\n", vk_frame.rtx_enabled ? " RT" : "" );
const uint32_t events = g_aprof.events_last_frame - prev_frame_index;
const uint64_t frame_begin_time = APROF_EVENT_TIMESTAMP(g_aprof.events[prev_frame_index]);
const unsigned long long delta_ns = APROF_EVENT_TIMESTAMP(g_aprof.events[g_aprof.events_last_frame]) - frame_begin_time;
g_speeds.frame.frame_time_us = delta_ns / 1000;
g_speeds.frame.gpu_time_us = (gpu_frame_end_ns - gpu_frame_begin_ns) / 1000;
handlePause( prev_frame_index );
{
int y = 100;
const int draw = speeds_bits & SPEEDS_BIT_FRAME;
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) {
speedsPrintf("frame: %.03fms GPU: %.03fms\n", g_speeds.frame.frame_time_us * 1e-3f, g_speeds.frame.gpu_time_us * 1e-3);
speedsPrintf(" (ref) CPU: %.03fms wait: %.03fms\n", g_speeds.frame.cpu_time_us * 1e-3, g_speeds.frame.cpu_wait_time_us * 1e-3);
}
if (speeds_bits & SPEEDS_BIT_STATS) {
speedsPrintf("profiler events: %u, wraps: %d\n", events, g_aprof.current_frame_wraparounds);
printMetrics();
}
clearMetrics();
APROF_SCOPE_END(function);
}