vk: add "persistent" speeds metrics for used memory, etc

Renames previous METRICS to COUNTERS. These are still reset to zero
every frame.

Adds new METRICS which are preserved, maintained externally to speeds,
and only sampled by speeds code once per frame.

Also adds new metrics:
- `studio.cached_submodels` -- number of submodels in cache
- `geom.used` -- memory used by long allocations
- `geom.{vertices,indices}` -- counts of vertices/indices for long
  allocations
- `geom.dyn_{vertices,indices}` -- counts of vertices/indices for
  single-frame dynamic allocations
This commit is contained in:
Ivan Avdeev 2023-06-14 11:23:09 -07:00
parent d13c0d4748
commit f691b4b4b0
13 changed files with 84 additions and 42 deletions

View File

@ -28,13 +28,13 @@ r_block_t R_BlockAllocLong(r_blocks_t *blocks, uint32_t size, uint32_t alignment
const alo_block_t ablock = aloPoolAllocate(blocks->long_pool, size, alignment);
if (ablock.offset == ALO_ALLOC_FAILED) {
/* gEngine.Con_Reportf("aloPoolAllocate failed\n"); */
gEngine.Con_Reportf(S_ERROR "aloPoolAllocate failed\n");
return ret;
}
const int metablock_index = allocMetablock(blocks);
if (metablock_index < 0) {
/* gEngine.Con_Reportf("allocMettablock failed\n"); */
gEngine.Con_Reportf(S_ERROR "allocMetablock failed\n");
aloPoolFree(blocks->long_pool, ablock.index);
return ret;
}
@ -50,6 +50,7 @@ r_block_t R_BlockAllocLong(r_blocks_t *blocks, uint32_t size, uint32_t alignment
/* gEngine.Con_Reportf("block alloc %dKiB => index=%d offset=%u\n", (int)size/1024, metablock_index, (int)ret.offset); */
blocks->allocated_long += size;
return ret;
}
@ -65,6 +66,7 @@ void R_BlocksCreate(r_blocks_t *blocks, uint32_t size, uint32_t once_size, int e
memset(blocks, 0, sizeof(*blocks));
blocks->size = size;
blocks->allocated_long = 0;
blocks->long_pool = aloPoolCreate(size - once_size, expected_allocs, 4);
aloIntPoolGrow(&blocks->blocks.freelist, expected_allocs);
@ -94,6 +96,7 @@ void R_BlockRelease(const r_block_t *block) {
aloPoolFree(blocks->long_pool, metablock->long_index);
aloIntPoolFree(&blocks->blocks.freelist, block->impl_.index);
blocks->allocated_long -= block->size;
}
void R_BlocksDestroy(r_blocks_t *blocks) {

View File

@ -30,6 +30,9 @@ typedef struct r_blocks_s {
alo_int_pool_t freelist;
struct r_blocks_block_s *storage;
} blocks;
// This is an estimate, it doesn't count alignment holes
int allocated_long;
} r_blocks_t;
r_block_t R_BlockAllocLong(r_blocks_t *blocks, uint32_t size, uint32_t alignment);

View File

@ -30,6 +30,7 @@ enum {
typedef struct {
int *p_value;
qboolean reset;
char name[64];
const char *var_name;
const char *src_file;
@ -213,7 +214,7 @@ static void drawCPUProfilerScopes(int draw, const aprof_event_t *events, uint64_
const uint64_t delta_ns = timestamp_ns - stack[depth].begin_ns;
if (!g_speeds.frame.scopes[scope_id].initialized) {
R_SpeedsRegisterMetric(&g_speeds.frame.scopes[scope_id].time_us, "scope", scope->name, kSpeedsMetricMicroseconds, scope->name, __FILE__, __LINE__);
R_SpeedsRegisterMetric(&g_speeds.frame.scopes[scope_id].time_us, "scope", scope->name, kSpeedsMetricMicroseconds, /*reset*/ true, scope->name, __FILE__, __LINE__);
g_speeds.frame.scopes[scope_id].initialized = 1;
}
@ -431,7 +432,7 @@ static void drawGPUProfilerScopes(qboolean draw, int y, uint64_t frame_begin_tim
const char *name = gpurofl->scopes[scope_index].name;
if (!g_speeds.frame.gpu_scopes[scope_index].initialized) {
R_SpeedsRegisterMetric(&g_speeds.frame.gpu_scopes[scope_index].time_us, "gpuscope", name, kSpeedsMetricMicroseconds, name, __FILE__, __LINE__);
R_SpeedsRegisterMetric(&g_speeds.frame.gpu_scopes[scope_index].time_us, "gpuscope", name, kSpeedsMetricMicroseconds, /*reset*/ true, name, __FILE__, __LINE__);
g_speeds.frame.gpu_scopes[scope_index].initialized = 1;
}
@ -523,10 +524,11 @@ static void printMetrics( void ) {
}
}
static void clearMetrics( void ) {
static void resetMetrics( void ) {
for (int i = 0; i < g_speeds.metrics_count; ++i) {
const r_speeds_metric_t *const metric = g_speeds.metrics + i;
*metric->p_value = 0;
if (metric->reset)
*metric->p_value = 0;
}
}
@ -817,10 +819,10 @@ void R_SpeedsInit( void ) {
gEngine.Cmd_AddCommand("r_speeds_list_metrics", listMetrics, "List all registered metrics");
gEngine.Cmd_AddCommand("r_speeds_graph", graphCmd, "Manipulate add/remove metrics graphs");
R_SPEEDS_METRIC(g_speeds.frame.frame_time_us, "frame", kSpeedsMetricMicroseconds);
R_SPEEDS_METRIC(g_speeds.frame.cpu_time_us, "cpu", kSpeedsMetricMicroseconds);
R_SPEEDS_METRIC(g_speeds.frame.cpu_wait_time_us, "cpu_wait", kSpeedsMetricMicroseconds);
R_SPEEDS_METRIC(g_speeds.frame.gpu_time_us, "gpu", kSpeedsMetricMicroseconds);
R_SPEEDS_COUNTER(g_speeds.frame.frame_time_us, "frame", kSpeedsMetricMicroseconds);
R_SPEEDS_COUNTER(g_speeds.frame.cpu_time_us, "cpu", kSpeedsMetricMicroseconds);
R_SPEEDS_COUNTER(g_speeds.frame.cpu_wait_time_us, "cpu_wait", kSpeedsMetricMicroseconds);
R_SPEEDS_COUNTER(g_speeds.frame.gpu_time_us, "gpu", kSpeedsMetricMicroseconds);
}
// grab r_speeds message
@ -841,11 +843,12 @@ qboolean R_SpeedsMessage( char *out, size_t size )
return true;
}
void R_SpeedsRegisterMetric(int* p_value, const char *module, const char *name, r_speeds_metric_type_t type, const char *var_name, const char *file, int line) {
void R_SpeedsRegisterMetric(int* p_value, const char *module, const char *name, r_speeds_metric_type_t type, qboolean reset, const char *var_name, const char *file, int line) {
ASSERT(g_speeds.metrics_count < MAX_SPEEDS_METRICS);
r_speeds_metric_t *metric = g_speeds.metrics + (g_speeds.metrics_count++);
metric->p_value = p_value;
metric->reset = reset;
Q_snprintf(metric->name, sizeof(metric->name), "%s.%s", module, name);
@ -927,7 +930,7 @@ void R_SpeedsDisplayMore(uint32_t prev_frame_index, const struct vk_combuf_scope
doListMetrics();
clearMetrics();
resetMetrics();
APROF_SCOPE_END(function);
}

View File

@ -16,7 +16,15 @@ typedef enum {
kSpeedsMetricMicroseconds,
} r_speeds_metric_type_t;
void R_SpeedsRegisterMetric(int* p_value, const char *module, const char *name, r_speeds_metric_type_t type, const char *var_name, const char *file, int line);
// TODO upper limit argument
void R_SpeedsRegisterMetric(int* p_value, const char *module, const char *name, r_speeds_metric_type_t type, qboolean reset, const char *var_name, const char *file, int line);
// A counter is a value accumulated during a single frame, and reset to zero between frames.
// Examples: drawn models count, scope times, etc.
#define R_SPEEDS_COUNTER(var, name, type) \
R_SpeedsRegisterMetric(&(var), MODULE_NAME, name, type, /*reset*/ true, #var, __FILE__, __LINE__)
// A metric is computed and preserved across frame boundaries.
// Examples: total allocated memory, cache sizes, etc.
#define R_SPEEDS_METRIC(var, name, type) \
R_SpeedsRegisterMetric(&(var), MODULE_NAME, name, type, #var, __FILE__, __LINE__)
R_SpeedsRegisterMetric(&(var), MODULE_NAME, name, type, /*reset*/ false, #var, __FILE__, __LINE__)

View File

@ -32,7 +32,7 @@ static struct {
} g_beam;
qboolean R_BeamInit(void) {
R_SPEEDS_METRIC(g_beam.stats.beams, "count", kSpeedsMetricCount);
R_SPEEDS_COUNTER(g_beam.stats.beams, "count", kSpeedsMetricCount);
return true;
}

View File

@ -95,9 +95,9 @@ qboolean VK_BrushInit( void )
{
VK_InitRandomTable ();
R_SPEEDS_METRIC(g_brush.stat.models_drawn, "drawn", kSpeedsMetricCount);
R_SPEEDS_METRIC(g_brush.stat.water_surfaces_drawn, "water.surfaces", kSpeedsMetricCount);
R_SPEEDS_METRIC(g_brush.stat.water_polys_drawn, "water.polys", kSpeedsMetricCount);
R_SPEEDS_COUNTER(g_brush.stat.models_drawn, "drawn", kSpeedsMetricCount);
R_SPEEDS_COUNTER(g_brush.stat.water_surfaces_drawn, "water.surfaces", kSpeedsMetricCount);
R_SPEEDS_COUNTER(g_brush.stat.water_polys_drawn, "water.polys", kSpeedsMetricCount);
return true;
}

View File

@ -1,7 +1,9 @@
#include "vk_geometry.h"
#include "vk_buffer.h"
#include "vk_staging.h"
#include "vk_framectl.h" // MAX_CONCURRENT_FRAMES
#include "r_speeds.h"
#define MODULE_NAME "geom"
#define MAX_BUFFER_VERTICES_STATIC (128 * 1024)
#define MAX_BUFFER_INDICES_STATIC (MAX_BUFFER_VERTICES_STATIC * 3)
@ -18,6 +20,11 @@
static struct {
vk_buffer_t buffer;
r_blocks_t alloc;
struct {
int vertices, indices;
int dyn_vertices, dyn_indices;
} stats;
} g_geom;
r_geometry_range_t R_GeometryRangeAlloc(int vertices, int indices) {
@ -38,11 +45,17 @@ r_geometry_range_t R_GeometryRangeAlloc(int vertices, int indices) {
ret.vertices.count = vertices;
ret.indices.count = indices;
g_geom.stats.indices += indices;
g_geom.stats.vertices += vertices;
return ret;
}
void R_GeometryRangeFree(const r_geometry_range_t* range) {
R_BlockRelease(&range->block_handle);
g_geom.stats.indices -= range->indices.count;
g_geom.stats.vertices -= range->vertices.count;
}
r_geometry_range_lock_t R_GeometryRangeLock(const r_geometry_range_t *range) {
@ -121,6 +134,9 @@ qboolean R_GeometryBufferAllocOnceAndLock(r_geometry_buffer_lock_t *lock, int ve
};
}
g_geom.stats.dyn_vertices += vertex_count;
g_geom.stats.dyn_indices += index_count;
return true;
}
@ -144,6 +160,12 @@ qboolean R_GeometryBuffer_Init(void) {
#define EXPECTED_ALLOCS 1024
R_BlocksCreate(&g_geom.alloc, GEOMETRY_BUFFER_SIZE, GEOMETRY_BUFFER_DYNAMIC_SIZE, EXPECTED_ALLOCS);
R_SPEEDS_METRIC(g_geom.alloc.allocated_long, "used", kSpeedsMetricBytes);
R_SPEEDS_METRIC(g_geom.stats.vertices, "vertices", kSpeedsMetricCount);
R_SPEEDS_METRIC(g_geom.stats.indices, "indices", kSpeedsMetricCount);
R_SPEEDS_COUNTER(g_geom.stats.dyn_vertices, "dyn_vertices", kSpeedsMetricCount);
R_SPEEDS_COUNTER(g_geom.stats.dyn_indices, "dyn_indices", kSpeedsMetricCount);
return true;
}

View File

@ -106,15 +106,15 @@ qboolean VK_LightsInit( void ) {
return false;
}
R_SPEEDS_METRIC(g_lights_.stats.dirty_cells, "dirty_cells", kSpeedsMetricCount);
R_SPEEDS_METRIC(g_lights_.stats.dirty_cells_size, "dirty_cells_size", kSpeedsMetricBytes);
R_SPEEDS_METRIC(g_lights_.stats.ranges_uploaded, "ranges_uploaded", kSpeedsMetricCount);
R_SPEEDS_METRIC(g_lights_.num_polygons, "polygons", kSpeedsMetricCount);
R_SPEEDS_METRIC(g_lights_.num_point_lights, "points", kSpeedsMetricCount);
R_SPEEDS_METRIC(g_lights_.stats.dynamic_polygons, "polygons_dynamic", kSpeedsMetricCount);
R_SPEEDS_METRIC(g_lights_.stats.dynamic_points, "points_dynamic", kSpeedsMetricCount);
R_SPEEDS_METRIC(g_lights_.stats.dlights, "dlights", kSpeedsMetricCount);
R_SPEEDS_METRIC(g_lights_.stats.elights, "elights", kSpeedsMetricCount);
R_SPEEDS_COUNTER(g_lights_.stats.dirty_cells, "dirty_cells", kSpeedsMetricCount);
R_SPEEDS_COUNTER(g_lights_.stats.dirty_cells_size, "dirty_cells_size", kSpeedsMetricBytes);
R_SPEEDS_COUNTER(g_lights_.stats.ranges_uploaded, "ranges_uploaded", kSpeedsMetricCount);
R_SPEEDS_COUNTER(g_lights_.num_polygons, "polygons", kSpeedsMetricCount);
R_SPEEDS_COUNTER(g_lights_.num_point_lights, "points", kSpeedsMetricCount);
R_SPEEDS_COUNTER(g_lights_.stats.dynamic_polygons, "polygons_dynamic", kSpeedsMetricCount);
R_SPEEDS_COUNTER(g_lights_.stats.dynamic_points, "points_dynamic", kSpeedsMetricCount);
R_SPEEDS_COUNTER(g_lights_.stats.dlights, "dlights", kSpeedsMetricCount);
R_SPEEDS_COUNTER(g_lights_.stats.elights, "elights", kSpeedsMetricCount);
return true;
}

View File

@ -390,8 +390,8 @@ qboolean RT_VkAccelInit(void) {
g_accel.accels_buffer_alloc = aloPoolCreate(MAX_ACCELS_BUFFER, MAX_INSTANCES, /* why */ 256);
R_SPEEDS_METRIC(g_accel.stats.instances_count, "instances", kSpeedsMetricCount);
R_SPEEDS_METRIC(g_accel.stats.accels_built, "built", kSpeedsMetricCount);
R_SPEEDS_COUNTER(g_accel.stats.instances_count, "instances", kSpeedsMetricCount);
R_SPEEDS_COUNTER(g_accel.stats.accels_built, "built", kSpeedsMetricCount);
return true;
}

View File

@ -338,8 +338,8 @@ qboolean VK_RenderInit( void ) {
if (!createPipelines())
return false;
R_SPEEDS_METRIC(g_render.stats.dynamic_model_count, "models_dynamic", kSpeedsMetricCount);
R_SPEEDS_METRIC(g_render.stats.models_count, "models", kSpeedsMetricCount);
R_SPEEDS_COUNTER(g_render.stats.dynamic_model_count, "models_dynamic", kSpeedsMetricCount);
R_SPEEDS_COUNTER(g_render.stats.models_count, "models", kSpeedsMetricCount);
return true;
}

View File

@ -128,7 +128,7 @@ static void destroyQuadModel(void) {
}
qboolean R_SpriteInit(void) {
R_SPEEDS_METRIC(g_sprite.stats.sprites, "count", kSpeedsMetricCount);
R_SPEEDS_COUNTER(g_sprite.stats.sprites, "count", kSpeedsMetricCount);
return true;
// TODO return createQuadModel();

View File

@ -64,12 +64,12 @@ qboolean R_VkStagingInit(void) {
R_FlippingBuffer_Init(&g_staging.buffer_alloc, DEFAULT_STAGING_SIZE);
R_SPEEDS_METRIC(g_staging.stats.total_size, "total_size", kSpeedsMetricBytes);
R_SPEEDS_METRIC(g_staging.stats.buffers_size, "buffers_size", kSpeedsMetricBytes);
R_SPEEDS_METRIC(g_staging.stats.images_size, "images_size", kSpeedsMetricBytes);
R_SPEEDS_COUNTER(g_staging.stats.total_size, "total_size", kSpeedsMetricBytes);
R_SPEEDS_COUNTER(g_staging.stats.buffers_size, "buffers_size", kSpeedsMetricBytes);
R_SPEEDS_COUNTER(g_staging.stats.images_size, "images_size", kSpeedsMetricBytes);
R_SPEEDS_METRIC(g_staging.stats.buffer_chunks, "buffer_chunks", kSpeedsMetricCount);
R_SPEEDS_METRIC(g_staging.stats.images, "images", kSpeedsMetricCount);
R_SPEEDS_COUNTER(g_staging.stats.buffer_chunks, "buffer_chunks", kSpeedsMetricCount);
R_SPEEDS_COUNTER(g_staging.stats.images, "images", kSpeedsMetricCount);
g_staging.buffer_upload_scope_id = R_VkGpuScope_Register("staging_buffers");
g_staging.image_upload_scope_id = R_VkGpuScope_Register("staging_images");

View File

@ -189,6 +189,7 @@ void R_StudioCacheClear( void ) {
}
static const r_studio_model_cache_entry_t *findSubModelInCacheForEntity(const mstudiomodel_t *submodel, const cl_entity_t *ent) {
// FIXME hash table, there are hundreds of entries
for (int i = 0; i < g_studio_cache.entries_count; ++i) {
const r_studio_model_cache_entry_t *const entry = g_studio_cache.entries + i;
if (entry->key_submodel == submodel && (entry->key_entity == NULL || entry->key_entity == ent))
@ -209,10 +210,12 @@ void R_StudioInit( void )
g_studio.framecount = 0;
m_fDoRemap = false;
R_SPEEDS_METRIC(g_studio_stats.models_count, "models", kSpeedsMetricCount);
R_SPEEDS_METRIC(g_studio_stats.submodels_total, "submodels_total", kSpeedsMetricCount);
R_SPEEDS_METRIC(g_studio_stats.submodels_static, "submodels_static", kSpeedsMetricCount);
R_SPEEDS_METRIC(g_studio_stats.submodels_dynamic, "submodels_dynamic", kSpeedsMetricCount);
R_SPEEDS_COUNTER(g_studio_stats.models_count, "models", kSpeedsMetricCount);
R_SPEEDS_COUNTER(g_studio_stats.submodels_total, "submodels_total", kSpeedsMetricCount);
R_SPEEDS_COUNTER(g_studio_stats.submodels_static, "submodels_static", kSpeedsMetricCount);
R_SPEEDS_COUNTER(g_studio_stats.submodels_dynamic, "submodels_dynamic", kSpeedsMetricCount);
R_SPEEDS_METRIC(g_studio_cache.entries_count, "cached_submodels", kSpeedsMetricCount);
}
/*