Files
PixelDefense/engine/libs/flecs/src/addons/stats.c

865 lines
31 KiB
C

/**
* @file addons/stats.c
* @brief Stats addon.
*/
#include "../private_api.h"
#ifdef FLECS_SYSTEM
#include "../addons/system/system.h"
#endif
#ifdef FLECS_PIPELINE
#include "../addons/pipeline/pipeline.h"
#endif
#ifdef FLECS_STATS
#define ECS_GAUGE_RECORD(m, t, value)\
flecs_gauge_record(m, t, (ecs_float_t)(value))
#define ECS_COUNTER_RECORD(m, t, value)\
flecs_counter_record(m, t, (double)(value))
#define ECS_METRIC_FIRST(stats)\
ECS_CAST(ecs_metric_t*, ECS_OFFSET(&stats->first_, ECS_SIZEOF(int64_t)))
#define ECS_METRIC_LAST(stats)\
ECS_CAST(ecs_metric_t*, ECS_OFFSET(&stats->last_, -ECS_SIZEOF(ecs_metric_t)))
static
int32_t t_next(
int32_t t)
{
return (t + 1) % ECS_STAT_WINDOW;
}
static
int32_t t_prev(
int32_t t)
{
return (t - 1 + ECS_STAT_WINDOW) % ECS_STAT_WINDOW;
}
static
void flecs_gauge_record(
ecs_metric_t *m,
int32_t t,
ecs_float_t value)
{
m->gauge.avg[t] = value;
m->gauge.min[t] = value;
m->gauge.max[t] = value;
}
static
double flecs_counter_record(
ecs_metric_t *m,
int32_t t,
double value)
{
int32_t tp = t_prev(t);
double prev = m->counter.value[tp];
m->counter.value[t] = value;
double gauge_value = value - prev;
if (gauge_value < 0) {
gauge_value = 0; /* Counters are monotonically increasing */
}
flecs_gauge_record(m, t, (ecs_float_t)gauge_value);
return gauge_value;
}
static
void flecs_metric_print(
const char *name,
ecs_float_t value)
{
ecs_size_t len = ecs_os_strlen(name);
ecs_trace("%s: %*s %.2f", name, 32 - len, "", (double)value);
}
static
void flecs_gauge_print(
const char *name,
int32_t t,
const ecs_metric_t *m)
{
flecs_metric_print(name, m->gauge.avg[t]);
}
static
void flecs_counter_print(
const char *name,
int32_t t,
const ecs_metric_t *m)
{
flecs_metric_print(name, m->counter.rate.avg[t]);
}
void ecs_metric_reduce(
ecs_metric_t *dst,
const ecs_metric_t *src,
int32_t t_dst,
int32_t t_src)
{
ecs_check(dst != NULL, ECS_INVALID_PARAMETER, NULL);
ecs_check(src != NULL, ECS_INVALID_PARAMETER, NULL);
bool min_set = false;
dst->gauge.avg[t_dst] = 0;
dst->gauge.min[t_dst] = 0;
dst->gauge.max[t_dst] = 0;
ecs_float_t fwindow = (ecs_float_t)ECS_STAT_WINDOW;
int32_t i;
for (i = 0; i < ECS_STAT_WINDOW; i ++) {
int32_t t = (t_src + i) % ECS_STAT_WINDOW;
dst->gauge.avg[t_dst] += src->gauge.avg[t] / fwindow;
if (!min_set || (src->gauge.min[t] < dst->gauge.min[t_dst])) {
dst->gauge.min[t_dst] = src->gauge.min[t];
min_set = true;
}
if ((src->gauge.max[t] > dst->gauge.max[t_dst])) {
dst->gauge.max[t_dst] = src->gauge.max[t];
}
}
dst->counter.value[t_dst] = src->counter.value[t_src];
error:
return;
}
void ecs_metric_reduce_last(
ecs_metric_t *m,
int32_t prev,
int32_t count)
{
ecs_check(m != NULL, ECS_INVALID_PARAMETER, NULL);
int32_t t = t_next(prev);
if (m->gauge.min[t] < m->gauge.min[prev]) {
m->gauge.min[prev] = m->gauge.min[t];
}
if (m->gauge.max[t] > m->gauge.max[prev]) {
m->gauge.max[prev] = m->gauge.max[t];
}
ecs_float_t fcount = (ecs_float_t)(count + 1);
ecs_float_t cur = m->gauge.avg[prev];
ecs_float_t next = m->gauge.avg[t];
cur *= ((fcount - 1) / fcount);
next *= 1 / fcount;
m->gauge.avg[prev] = cur + next;
m->counter.value[prev] = m->counter.value[t];
error:
return;
}
void ecs_metric_copy(
ecs_metric_t *m,
int32_t dst,
int32_t src)
{
ecs_check(m != NULL, ECS_INVALID_PARAMETER, NULL);
ecs_check(dst != src, ECS_INVALID_PARAMETER, NULL);
m->gauge.avg[dst] = m->gauge.avg[src];
m->gauge.min[dst] = m->gauge.min[src];
m->gauge.max[dst] = m->gauge.max[src];
m->counter.value[dst] = m->counter.value[src];
error:
return;
}
static
void flecs_stats_reduce(
ecs_metric_t *dst_cur,
ecs_metric_t *dst_last,
ecs_metric_t *src_cur,
int32_t t_dst,
int32_t t_src)
{
for (; dst_cur <= dst_last; dst_cur ++, src_cur ++) {
ecs_metric_reduce(dst_cur, src_cur, t_dst, t_src);
}
}
static
void flecs_stats_reduce_last(
ecs_metric_t *dst_cur,
ecs_metric_t *dst_last,
ecs_metric_t *src_cur,
int32_t t_dst,
int32_t t_src,
int32_t count)
{
int32_t t_dst_next = t_next(t_dst);
for (; dst_cur <= dst_last; dst_cur ++, src_cur ++) {
/* Reduce into previous value */
ecs_metric_reduce_last(dst_cur, t_dst, count);
/* Restore old value */
dst_cur->gauge.avg[t_dst_next] = src_cur->gauge.avg[t_src];
dst_cur->gauge.min[t_dst_next] = src_cur->gauge.min[t_src];
dst_cur->gauge.max[t_dst_next] = src_cur->gauge.max[t_src];
dst_cur->counter.value[t_dst_next] = src_cur->counter.value[t_src];
}
}
static
void flecs_stats_repeat_last(
ecs_metric_t *cur,
ecs_metric_t *last,
int32_t t)
{
int32_t prev = t_prev(t);
for (; cur <= last; cur ++) {
ecs_metric_copy(cur, t, prev);
}
}
static
void flecs_stats_copy_last(
ecs_metric_t *dst_cur,
ecs_metric_t *dst_last,
ecs_metric_t *src_cur,
int32_t t_dst,
int32_t t_src)
{
for (; dst_cur <= dst_last; dst_cur ++, src_cur ++) {
dst_cur->gauge.avg[t_dst] = src_cur->gauge.avg[t_src];
dst_cur->gauge.min[t_dst] = src_cur->gauge.min[t_src];
dst_cur->gauge.max[t_dst] = src_cur->gauge.max[t_src];
dst_cur->counter.value[t_dst] = src_cur->counter.value[t_src];
}
}
void ecs_world_stats_get(
const ecs_world_t *world,
ecs_world_stats_t *s)
{
ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL);
ecs_check(s != NULL, ECS_INVALID_PARAMETER, NULL);
world = ecs_get_world(world);
int32_t t = s->t = t_next(s->t);
double delta_frame_count =
ECS_COUNTER_RECORD(&s->frame.frame_count, t, world->info.frame_count_total);
ECS_COUNTER_RECORD(&s->frame.merge_count, t, world->info.merge_count_total);
ECS_COUNTER_RECORD(&s->frame.rematch_count, t, world->info.rematch_count_total);
ECS_COUNTER_RECORD(&s->frame.pipeline_build_count, t, world->info.pipeline_build_count_total);
ECS_COUNTER_RECORD(&s->frame.systems_ran, t, world->info.systems_ran_frame);
ECS_COUNTER_RECORD(&s->frame.observers_ran, t, world->info.observers_ran_frame);
ECS_COUNTER_RECORD(&s->frame.event_emit_count, t, world->event_id);
double delta_world_time =
ECS_COUNTER_RECORD(&s->performance.world_time_raw, t, world->info.world_time_total_raw);
ECS_COUNTER_RECORD(&s->performance.world_time, t, world->info.world_time_total);
ECS_COUNTER_RECORD(&s->performance.frame_time, t, world->info.frame_time_total);
ECS_COUNTER_RECORD(&s->performance.system_time, t, world->info.system_time_total);
ECS_COUNTER_RECORD(&s->performance.emit_time, t, world->info.emit_time_total);
ECS_COUNTER_RECORD(&s->performance.merge_time, t, world->info.merge_time_total);
ECS_COUNTER_RECORD(&s->performance.rematch_time, t, world->info.rematch_time_total);
ECS_GAUGE_RECORD(&s->performance.delta_time, t, delta_world_time);
if (ECS_NEQZERO(delta_world_time) && ECS_NEQZERO(delta_frame_count)) {
ECS_GAUGE_RECORD(&s->performance.fps, t, (double)1 / (delta_world_time / (double)delta_frame_count));
} else {
ECS_GAUGE_RECORD(&s->performance.fps, t, 0);
}
ECS_GAUGE_RECORD(&s->entities.count, t, flecs_entities_count(world));
ECS_GAUGE_RECORD(&s->entities.not_alive_count, t, flecs_entities_not_alive_count(world));
ECS_GAUGE_RECORD(&s->ids.count, t, world->info.id_count);
ECS_GAUGE_RECORD(&s->ids.tag_count, t, world->info.tag_id_count);
ECS_GAUGE_RECORD(&s->ids.component_count, t, world->info.component_id_count);
ECS_GAUGE_RECORD(&s->ids.pair_count, t, world->info.pair_id_count);
ECS_GAUGE_RECORD(&s->ids.wildcard_count, t, world->info.wildcard_id_count);
ECS_GAUGE_RECORD(&s->ids.type_count, t, ecs_sparse_count(&world->type_info));
ECS_COUNTER_RECORD(&s->ids.create_count, t, world->info.id_create_total);
ECS_COUNTER_RECORD(&s->ids.delete_count, t, world->info.id_delete_total);
ECS_GAUGE_RECORD(&s->queries.query_count, t, ecs_count_id(world, EcsQuery));
ECS_GAUGE_RECORD(&s->queries.observer_count, t, ecs_count_id(world, EcsObserver));
if (ecs_is_alive(world, EcsSystem)) {
ECS_GAUGE_RECORD(&s->queries.system_count, t, ecs_count_id(world, EcsSystem));
}
ECS_COUNTER_RECORD(&s->tables.create_count, t, world->info.table_create_total);
ECS_COUNTER_RECORD(&s->tables.delete_count, t, world->info.table_delete_total);
ECS_GAUGE_RECORD(&s->tables.count, t, world->info.table_count);
ECS_GAUGE_RECORD(&s->tables.empty_count, t, world->info.empty_table_count);
ECS_GAUGE_RECORD(&s->tables.tag_only_count, t, world->info.tag_table_count);
ECS_GAUGE_RECORD(&s->tables.trivial_only_count, t, world->info.trivial_table_count);
ECS_GAUGE_RECORD(&s->tables.storage_count, t, world->info.table_storage_count);
ECS_GAUGE_RECORD(&s->tables.record_count, t, world->info.table_record_count);
ECS_COUNTER_RECORD(&s->commands.add_count, t, world->info.cmd.add_count);
ECS_COUNTER_RECORD(&s->commands.remove_count, t, world->info.cmd.remove_count);
ECS_COUNTER_RECORD(&s->commands.delete_count, t, world->info.cmd.delete_count);
ECS_COUNTER_RECORD(&s->commands.clear_count, t, world->info.cmd.clear_count);
ECS_COUNTER_RECORD(&s->commands.set_count, t, world->info.cmd.set_count);
ECS_COUNTER_RECORD(&s->commands.get_mut_count, t, world->info.cmd.get_mut_count);
ECS_COUNTER_RECORD(&s->commands.modified_count, t, world->info.cmd.modified_count);
ECS_COUNTER_RECORD(&s->commands.other_count, t, world->info.cmd.other_count);
ECS_COUNTER_RECORD(&s->commands.discard_count, t, world->info.cmd.discard_count);
ECS_COUNTER_RECORD(&s->commands.batched_entity_count, t, world->info.cmd.batched_entity_count);
ECS_COUNTER_RECORD(&s->commands.batched_count, t, world->info.cmd.batched_command_count);
int64_t outstanding_allocs = ecs_os_api_malloc_count +
ecs_os_api_calloc_count - ecs_os_api_free_count;
ECS_COUNTER_RECORD(&s->memory.alloc_count, t, ecs_os_api_malloc_count + ecs_os_api_calloc_count);
ECS_COUNTER_RECORD(&s->memory.realloc_count, t, ecs_os_api_realloc_count);
ECS_COUNTER_RECORD(&s->memory.free_count, t, ecs_os_api_free_count);
ECS_GAUGE_RECORD(&s->memory.outstanding_alloc_count, t, outstanding_allocs);
outstanding_allocs = ecs_block_allocator_alloc_count - ecs_block_allocator_free_count;
ECS_COUNTER_RECORD(&s->memory.block_alloc_count, t, ecs_block_allocator_alloc_count);
ECS_COUNTER_RECORD(&s->memory.block_free_count, t, ecs_block_allocator_free_count);
ECS_GAUGE_RECORD(&s->memory.block_outstanding_alloc_count, t, outstanding_allocs);
outstanding_allocs = ecs_stack_allocator_alloc_count - ecs_stack_allocator_free_count;
ECS_COUNTER_RECORD(&s->memory.stack_alloc_count, t, ecs_stack_allocator_alloc_count);
ECS_COUNTER_RECORD(&s->memory.stack_free_count, t, ecs_stack_allocator_free_count);
ECS_GAUGE_RECORD(&s->memory.stack_outstanding_alloc_count, t, outstanding_allocs);
#ifdef FLECS_REST
ECS_COUNTER_RECORD(&s->rest.request_count, t, ecs_rest_request_count);
ECS_COUNTER_RECORD(&s->rest.entity_count, t, ecs_rest_entity_count);
ECS_COUNTER_RECORD(&s->rest.entity_error_count, t, ecs_rest_entity_error_count);
ECS_COUNTER_RECORD(&s->rest.query_count, t, ecs_rest_query_count);
ECS_COUNTER_RECORD(&s->rest.query_error_count, t, ecs_rest_query_error_count);
ECS_COUNTER_RECORD(&s->rest.query_name_count, t, ecs_rest_query_name_count);
ECS_COUNTER_RECORD(&s->rest.query_name_error_count, t, ecs_rest_query_name_error_count);
ECS_COUNTER_RECORD(&s->rest.query_name_from_cache_count, t, ecs_rest_query_name_from_cache_count);
ECS_COUNTER_RECORD(&s->rest.enable_count, t, ecs_rest_enable_count);
ECS_COUNTER_RECORD(&s->rest.enable_error_count, t, ecs_rest_enable_error_count);
ECS_COUNTER_RECORD(&s->rest.world_stats_count, t, ecs_rest_world_stats_count);
ECS_COUNTER_RECORD(&s->rest.pipeline_stats_count, t, ecs_rest_pipeline_stats_count);
ECS_COUNTER_RECORD(&s->rest.stats_error_count, t, ecs_rest_stats_error_count);
#endif
#ifdef FLECS_HTTP
ECS_COUNTER_RECORD(&s->http.request_received_count, t, ecs_http_request_received_count);
ECS_COUNTER_RECORD(&s->http.request_invalid_count, t, ecs_http_request_invalid_count);
ECS_COUNTER_RECORD(&s->http.request_handled_ok_count, t, ecs_http_request_handled_ok_count);
ECS_COUNTER_RECORD(&s->http.request_handled_error_count, t, ecs_http_request_handled_error_count);
ECS_COUNTER_RECORD(&s->http.request_not_handled_count, t, ecs_http_request_not_handled_count);
ECS_COUNTER_RECORD(&s->http.request_preflight_count, t, ecs_http_request_preflight_count);
ECS_COUNTER_RECORD(&s->http.send_ok_count, t, ecs_http_send_ok_count);
ECS_COUNTER_RECORD(&s->http.send_error_count, t, ecs_http_send_error_count);
ECS_COUNTER_RECORD(&s->http.busy_count, t, ecs_http_busy_count);
#endif
error:
return;
}
void ecs_world_stats_reduce(
ecs_world_stats_t *dst,
const ecs_world_stats_t *src)
{
flecs_stats_reduce(ECS_METRIC_FIRST(dst), ECS_METRIC_LAST(dst),
ECS_METRIC_FIRST(src), (dst->t = t_next(dst->t)), src->t);
}
void ecs_world_stats_reduce_last(
ecs_world_stats_t *dst,
const ecs_world_stats_t *src,
int32_t count)
{
flecs_stats_reduce_last(ECS_METRIC_FIRST(dst), ECS_METRIC_LAST(dst),
ECS_METRIC_FIRST(src), (dst->t = t_prev(dst->t)), src->t, count);
}
void ecs_world_stats_repeat_last(
ecs_world_stats_t *stats)
{
flecs_stats_repeat_last(ECS_METRIC_FIRST(stats), ECS_METRIC_LAST(stats),
(stats->t = t_next(stats->t)));
}
void ecs_world_stats_copy_last(
ecs_world_stats_t *dst,
const ecs_world_stats_t *src)
{
flecs_stats_copy_last(ECS_METRIC_FIRST(dst), ECS_METRIC_LAST(dst),
ECS_METRIC_FIRST(src), dst->t, t_next(src->t));
}
void ecs_query_stats_get(
const ecs_world_t *world,
const ecs_query_t *query,
ecs_query_stats_t *s)
{
ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL);
ecs_check(query != NULL, ECS_INVALID_PARAMETER, NULL);
ecs_check(s != NULL, ECS_INVALID_PARAMETER, NULL);
(void)world;
int32_t t = s->t = t_next(s->t);
if (query->filter.flags & EcsFilterMatchThis) {
ECS_GAUGE_RECORD(&s->matched_entity_count, t,
ecs_query_entity_count(query));
ECS_GAUGE_RECORD(&s->matched_table_count, t,
ecs_query_table_count(query));
ECS_GAUGE_RECORD(&s->matched_empty_table_count, t,
ecs_query_empty_table_count(query));
} else {
ECS_GAUGE_RECORD(&s->matched_entity_count, t, 0);
ECS_GAUGE_RECORD(&s->matched_table_count, t, 0);
ECS_GAUGE_RECORD(&s->matched_empty_table_count, t, 0);
}
error:
return;
}
void ecs_query_stats_reduce(
ecs_query_stats_t *dst,
const ecs_query_stats_t *src)
{
flecs_stats_reduce(ECS_METRIC_FIRST(dst), ECS_METRIC_LAST(dst),
ECS_METRIC_FIRST(src), (dst->t = t_next(dst->t)), src->t);
}
void ecs_query_stats_reduce_last(
ecs_query_stats_t *dst,
const ecs_query_stats_t *src,
int32_t count)
{
flecs_stats_reduce_last(ECS_METRIC_FIRST(dst), ECS_METRIC_LAST(dst),
ECS_METRIC_FIRST(src), (dst->t = t_prev(dst->t)), src->t, count);
}
void ecs_query_stats_repeat_last(
ecs_query_stats_t *stats)
{
flecs_stats_repeat_last(ECS_METRIC_FIRST(stats), ECS_METRIC_LAST(stats),
(stats->t = t_next(stats->t)));
}
void ecs_query_stats_copy_last(
ecs_query_stats_t *dst,
const ecs_query_stats_t *src)
{
flecs_stats_copy_last(ECS_METRIC_FIRST(dst), ECS_METRIC_LAST(dst),
ECS_METRIC_FIRST(src), dst->t, t_next(src->t));
}
#ifdef FLECS_SYSTEM
bool ecs_system_stats_get(
const ecs_world_t *world,
ecs_entity_t system,
ecs_system_stats_t *s)
{
ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL);
ecs_check(s != NULL, ECS_INVALID_PARAMETER, NULL);
ecs_check(system != 0, ECS_INVALID_PARAMETER, NULL);
world = ecs_get_world(world);
const ecs_system_t *ptr = ecs_poly_get(world, system, ecs_system_t);
if (!ptr) {
return false;
}
ecs_query_stats_get(world, ptr->query, &s->query);
int32_t t = s->query.t;
ECS_COUNTER_RECORD(&s->time_spent, t, ptr->time_spent);
ECS_COUNTER_RECORD(&s->invoke_count, t, ptr->invoke_count);
s->task = !(ptr->query->filter.flags & EcsFilterMatchThis);
return true;
error:
return false;
}
void ecs_system_stats_reduce(
ecs_system_stats_t *dst,
const ecs_system_stats_t *src)
{
ecs_query_stats_reduce(&dst->query, &src->query);
dst->task = src->task;
flecs_stats_reduce(ECS_METRIC_FIRST(dst), ECS_METRIC_LAST(dst),
ECS_METRIC_FIRST(src), dst->query.t, src->query.t);
}
void ecs_system_stats_reduce_last(
ecs_system_stats_t *dst,
const ecs_system_stats_t *src,
int32_t count)
{
ecs_query_stats_reduce_last(&dst->query, &src->query, count);
dst->task = src->task;
flecs_stats_reduce_last(ECS_METRIC_FIRST(dst), ECS_METRIC_LAST(dst),
ECS_METRIC_FIRST(src), dst->query.t, src->query.t, count);
}
void ecs_system_stats_repeat_last(
ecs_system_stats_t *stats)
{
ecs_query_stats_repeat_last(&stats->query);
flecs_stats_repeat_last(ECS_METRIC_FIRST(stats), ECS_METRIC_LAST(stats),
(stats->query.t));
}
void ecs_system_stats_copy_last(
ecs_system_stats_t *dst,
const ecs_system_stats_t *src)
{
ecs_query_stats_copy_last(&dst->query, &src->query);
dst->task = src->task;
flecs_stats_copy_last(ECS_METRIC_FIRST(dst), ECS_METRIC_LAST(dst),
ECS_METRIC_FIRST(src), dst->query.t, t_next(src->query.t));
}
#endif
#ifdef FLECS_PIPELINE
bool ecs_pipeline_stats_get(
ecs_world_t *stage,
ecs_entity_t pipeline,
ecs_pipeline_stats_t *s)
{
ecs_check(stage != NULL, ECS_INVALID_PARAMETER, NULL);
ecs_check(s != NULL, ECS_INVALID_PARAMETER, NULL);
ecs_check(pipeline != 0, ECS_INVALID_PARAMETER, NULL);
const ecs_world_t *world = ecs_get_world(stage);
const EcsPipeline *pqc = ecs_get(world, pipeline, EcsPipeline);
if (!pqc) {
return false;
}
ecs_pipeline_state_t *pq = pqc->state;
ecs_assert(pq != NULL, ECS_INTERNAL_ERROR, NULL);
int32_t sys_count = 0, active_sys_count = 0;
/* Count number of active systems */
ecs_iter_t it = ecs_query_iter(stage, pq->query);
while (ecs_query_next(&it)) {
if (flecs_id_record_get_table(pq->idr_inactive, it.table) != NULL) {
continue;
}
active_sys_count += it.count;
}
/* Count total number of systems in pipeline */
it = ecs_query_iter(stage, pq->query);
while (ecs_query_next(&it)) {
sys_count += it.count;
}
/* Also count synchronization points */
ecs_vec_t *ops = &pq->ops;
ecs_pipeline_op_t *op = ecs_vec_first_t(ops, ecs_pipeline_op_t);
ecs_pipeline_op_t *op_last = ecs_vec_last_t(ops, ecs_pipeline_op_t);
int32_t pip_count = active_sys_count + ecs_vec_count(ops);
if (!sys_count) {
return false;
}
if (ecs_map_is_init(&s->system_stats) && !sys_count) {
ecs_map_fini(&s->system_stats);
}
ecs_map_init_if(&s->system_stats, NULL);
if (op) {
ecs_entity_t *systems = NULL;
if (pip_count) {
ecs_vec_init_if_t(&s->systems, ecs_entity_t);
ecs_vec_set_count_t(NULL, &s->systems, ecs_entity_t, pip_count);
systems = ecs_vec_first_t(&s->systems, ecs_entity_t);
/* Populate systems vector, keep track of sync points */
it = ecs_query_iter(stage, pq->query);
int32_t i, i_system = 0, ran_since_merge = 0;
while (ecs_query_next(&it)) {
if (flecs_id_record_get_table(pq->idr_inactive, it.table) != NULL) {
continue;
}
for (i = 0; i < it.count; i ++) {
systems[i_system ++] = it.entities[i];
ran_since_merge ++;
if (op != op_last && ran_since_merge == op->count) {
ran_since_merge = 0;
op++;
systems[i_system ++] = 0; /* 0 indicates a merge point */
}
}
}
systems[i_system ++] = 0; /* Last merge */
ecs_assert(pip_count == i_system, ECS_INTERNAL_ERROR, NULL);
} else {
ecs_vec_fini_t(NULL, &s->systems, ecs_entity_t);
}
/* Get sync point statistics */
int32_t i, count = ecs_vec_count(ops);
if (count) {
ecs_vec_init_if_t(&s->sync_points, ecs_sync_stats_t);
ecs_vec_set_min_count_zeromem_t(NULL, &s->sync_points, ecs_sync_stats_t, count);
op = ecs_vec_first_t(ops, ecs_pipeline_op_t);
for (i = 0; i < count; i ++) {
ecs_pipeline_op_t *cur = &op[i];
ecs_sync_stats_t *el = ecs_vec_get_t(&s->sync_points,
ecs_sync_stats_t, i);
ECS_COUNTER_RECORD(&el->time_spent, s->t, cur->time_spent);
ECS_COUNTER_RECORD(&el->commands_enqueued, s->t,
cur->commands_enqueued);
el->system_count = cur->count;
el->multi_threaded = cur->multi_threaded;
el->no_readonly = cur->no_readonly;
}
}
}
/* Separately populate system stats map from build query, which includes
* systems that aren't currently active */
it = ecs_query_iter(stage, pq->query);
while (ecs_query_next(&it)) {
int32_t i;
for (i = 0; i < it.count; i ++) {
ecs_system_stats_t *stats = ecs_map_ensure_alloc_t(&s->system_stats,
ecs_system_stats_t, it.entities[i]);
stats->query.t = s->t;
ecs_system_stats_get(world, it.entities[i], stats);
}
}
s->t = t_next(s->t);
return true;
error:
return false;
}
void ecs_pipeline_stats_fini(
ecs_pipeline_stats_t *stats)
{
ecs_map_iter_t it = ecs_map_iter(&stats->system_stats);
while (ecs_map_next(&it)) {
ecs_system_stats_t *elem = ecs_map_ptr(&it);
ecs_os_free(elem);
}
ecs_map_fini(&stats->system_stats);
ecs_vec_fini_t(NULL, &stats->systems, ecs_entity_t);
ecs_vec_fini_t(NULL, &stats->sync_points, ecs_sync_stats_t);
}
void ecs_pipeline_stats_reduce(
ecs_pipeline_stats_t *dst,
const ecs_pipeline_stats_t *src)
{
int32_t system_count = ecs_vec_count(&src->systems);
ecs_vec_init_if_t(&dst->systems, ecs_entity_t);
ecs_vec_set_count_t(NULL, &dst->systems, ecs_entity_t, system_count);
ecs_entity_t *dst_systems = ecs_vec_first_t(&dst->systems, ecs_entity_t);
ecs_entity_t *src_systems = ecs_vec_first_t(&src->systems, ecs_entity_t);
ecs_os_memcpy_n(dst_systems, src_systems, ecs_entity_t, system_count);
int32_t i, sync_count = ecs_vec_count(&src->sync_points);
ecs_vec_init_if_t(&dst->sync_points, ecs_sync_stats_t);
ecs_vec_set_min_count_zeromem_t(NULL, &dst->sync_points, ecs_sync_stats_t, sync_count);
ecs_sync_stats_t *dst_syncs = ecs_vec_first_t(&dst->sync_points, ecs_sync_stats_t);
ecs_sync_stats_t *src_syncs = ecs_vec_first_t(&src->sync_points, ecs_sync_stats_t);
for (i = 0; i < sync_count; i ++) {
ecs_sync_stats_t *dst_el = &dst_syncs[i];
ecs_sync_stats_t *src_el = &src_syncs[i];
flecs_stats_reduce(ECS_METRIC_FIRST(dst_el), ECS_METRIC_LAST(dst_el),
ECS_METRIC_FIRST(src_el), dst->t, src->t);
dst_el->system_count = src_el->system_count;
dst_el->multi_threaded = src_el->multi_threaded;
dst_el->no_readonly = src_el->no_readonly;
}
ecs_map_init_if(&dst->system_stats, NULL);
ecs_map_iter_t it = ecs_map_iter(&src->system_stats);
while (ecs_map_next(&it)) {
ecs_system_stats_t *sys_src = ecs_map_ptr(&it);
ecs_system_stats_t *sys_dst = ecs_map_ensure_alloc_t(&dst->system_stats,
ecs_system_stats_t, ecs_map_key(&it));
sys_dst->query.t = dst->t;
ecs_system_stats_reduce(sys_dst, sys_src);
}
dst->t = t_next(dst->t);
}
void ecs_pipeline_stats_reduce_last(
ecs_pipeline_stats_t *dst,
const ecs_pipeline_stats_t *src,
int32_t count)
{
int32_t i, sync_count = ecs_vec_count(&src->sync_points);
ecs_sync_stats_t *dst_syncs = ecs_vec_first_t(&dst->sync_points, ecs_sync_stats_t);
ecs_sync_stats_t *src_syncs = ecs_vec_first_t(&src->sync_points, ecs_sync_stats_t);
for (i = 0; i < sync_count; i ++) {
ecs_sync_stats_t *dst_el = &dst_syncs[i];
ecs_sync_stats_t *src_el = &src_syncs[i];
flecs_stats_reduce_last(ECS_METRIC_FIRST(dst_el), ECS_METRIC_LAST(dst_el),
ECS_METRIC_FIRST(src_el), dst->t, src->t, count);
dst_el->system_count = src_el->system_count;
dst_el->multi_threaded = src_el->multi_threaded;
dst_el->no_readonly = src_el->no_readonly;
}
ecs_map_init_if(&dst->system_stats, NULL);
ecs_map_iter_t it = ecs_map_iter(&src->system_stats);
while (ecs_map_next(&it)) {
ecs_system_stats_t *sys_src = ecs_map_ptr(&it);
ecs_system_stats_t *sys_dst = ecs_map_ensure_alloc_t(&dst->system_stats,
ecs_system_stats_t, ecs_map_key(&it));
sys_dst->query.t = dst->t;
ecs_system_stats_reduce_last(sys_dst, sys_src, count);
}
dst->t = t_prev(dst->t);
}
void ecs_pipeline_stats_repeat_last(
ecs_pipeline_stats_t *stats)
{
int32_t i, sync_count = ecs_vec_count(&stats->sync_points);
ecs_sync_stats_t *syncs = ecs_vec_first_t(&stats->sync_points, ecs_sync_stats_t);
for (i = 0; i < sync_count; i ++) {
ecs_sync_stats_t *el = &syncs[i];
flecs_stats_repeat_last(ECS_METRIC_FIRST(el), ECS_METRIC_LAST(el),
(stats->t));
}
ecs_map_iter_t it = ecs_map_iter(&stats->system_stats);
while (ecs_map_next(&it)) {
ecs_system_stats_t *sys = ecs_map_ptr(&it);
sys->query.t = stats->t;
ecs_system_stats_repeat_last(sys);
}
stats->t = t_next(stats->t);
}
void ecs_pipeline_stats_copy_last(
ecs_pipeline_stats_t *dst,
const ecs_pipeline_stats_t *src)
{
int32_t i, sync_count = ecs_vec_count(&src->sync_points);
ecs_vec_init_if_t(&dst->sync_points, ecs_sync_stats_t);
ecs_vec_set_min_count_zeromem_t(NULL, &dst->sync_points, ecs_sync_stats_t, sync_count);
ecs_sync_stats_t *dst_syncs = ecs_vec_first_t(&dst->sync_points, ecs_sync_stats_t);
ecs_sync_stats_t *src_syncs = ecs_vec_first_t(&src->sync_points, ecs_sync_stats_t);
for (i = 0; i < sync_count; i ++) {
ecs_sync_stats_t *dst_el = &dst_syncs[i];
ecs_sync_stats_t *src_el = &src_syncs[i];
flecs_stats_copy_last(ECS_METRIC_FIRST(dst_el), ECS_METRIC_LAST(dst_el),
ECS_METRIC_FIRST(src_el), dst->t, t_next(src->t));
dst_el->system_count = src_el->system_count;
dst_el->multi_threaded = src_el->multi_threaded;
dst_el->no_readonly = src_el->no_readonly;
}
ecs_map_init_if(&dst->system_stats, NULL);
ecs_map_iter_t it = ecs_map_iter(&src->system_stats);
while (ecs_map_next(&it)) {
ecs_system_stats_t *sys_src = ecs_map_ptr(&it);
ecs_system_stats_t *sys_dst = ecs_map_ensure_alloc_t(&dst->system_stats,
ecs_system_stats_t, ecs_map_key(&it));
sys_dst->query.t = dst->t;
ecs_system_stats_copy_last(sys_dst, sys_src);
}
}
#endif
void ecs_world_stats_log(
const ecs_world_t *world,
const ecs_world_stats_t *s)
{
int32_t t = s->t;
ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL);
ecs_check(s != NULL, ECS_INVALID_PARAMETER, NULL);
world = ecs_get_world(world);
flecs_counter_print("Frame", t, &s->frame.frame_count);
ecs_trace("-------------------------------------");
flecs_counter_print("pipeline rebuilds", t, &s->frame.pipeline_build_count);
flecs_counter_print("systems ran", t, &s->frame.systems_ran);
ecs_trace("");
flecs_metric_print("target FPS", world->info.target_fps);
flecs_metric_print("time scale", world->info.time_scale);
ecs_trace("");
flecs_gauge_print("actual FPS", t, &s->performance.fps);
flecs_counter_print("frame time", t, &s->performance.frame_time);
flecs_counter_print("system time", t, &s->performance.system_time);
flecs_counter_print("merge time", t, &s->performance.merge_time);
flecs_counter_print("simulation time elapsed", t, &s->performance.world_time);
ecs_trace("");
flecs_gauge_print("id count", t, &s->ids.count);
flecs_gauge_print("tag id count", t, &s->ids.tag_count);
flecs_gauge_print("component id count", t, &s->ids.component_count);
flecs_gauge_print("pair id count", t, &s->ids.pair_count);
flecs_gauge_print("wildcard id count", t, &s->ids.wildcard_count);
flecs_gauge_print("type count", t, &s->ids.type_count);
flecs_counter_print("id create count", t, &s->ids.create_count);
flecs_counter_print("id delete count", t, &s->ids.delete_count);
ecs_trace("");
flecs_gauge_print("alive entity count", t, &s->entities.count);
flecs_gauge_print("not alive entity count", t, &s->entities.not_alive_count);
ecs_trace("");
flecs_gauge_print("query count", t, &s->queries.query_count);
flecs_gauge_print("observer count", t, &s->queries.observer_count);
flecs_gauge_print("system count", t, &s->queries.system_count);
ecs_trace("");
flecs_gauge_print("table count", t, &s->tables.count);
flecs_gauge_print("empty table count", t, &s->tables.empty_count);
flecs_gauge_print("tag table count", t, &s->tables.tag_only_count);
flecs_gauge_print("trivial table count", t, &s->tables.trivial_only_count);
flecs_gauge_print("table storage count", t, &s->tables.storage_count);
flecs_gauge_print("table cache record count", t, &s->tables.record_count);
flecs_counter_print("table create count", t, &s->tables.create_count);
flecs_counter_print("table delete count", t, &s->tables.delete_count);
ecs_trace("");
flecs_counter_print("add commands", t, &s->commands.add_count);
flecs_counter_print("remove commands", t, &s->commands.remove_count);
flecs_counter_print("delete commands", t, &s->commands.delete_count);
flecs_counter_print("clear commands", t, &s->commands.clear_count);
flecs_counter_print("set commands", t, &s->commands.set_count);
flecs_counter_print("get_mut commands", t, &s->commands.get_mut_count);
flecs_counter_print("modified commands", t, &s->commands.modified_count);
flecs_counter_print("other commands", t, &s->commands.other_count);
flecs_counter_print("discarded commands", t, &s->commands.discard_count);
flecs_counter_print("batched entities", t, &s->commands.batched_entity_count);
flecs_counter_print("batched commands", t, &s->commands.batched_count);
ecs_trace("");
error:
return;
}
#endif