diff --git a/flecs.c b/flecs.c index 13dc467a5a..589e45e010 100644 --- a/flecs.c +++ b/flecs.c @@ -22524,12 +22524,15 @@ ECS_COMPONENT_DECLARE(EcsMetricSource); ECS_TAG_DECLARE(EcsMetric); ECS_TAG_DECLARE(EcsCounter); ECS_TAG_DECLARE(EcsCounterIncrement); +ECS_TAG_DECLARE(EcsCounterId); ECS_TAG_DECLARE(EcsGauge); /* Internal components */ ECS_COMPONENT_DECLARE(EcsMetricMember); ECS_COMPONENT_DECLARE(EcsMetricId); ECS_COMPONENT_DECLARE(EcsMetricOneOf); +ECS_COMPONENT_DECLARE(EcsMetricCountIds); +ECS_COMPONENT_DECLARE(EcsMetricCountTargets); ECS_COMPONENT_DECLARE(EcsMetricMemberInstance); ECS_COMPONENT_DECLARE(EcsMetricIdInstance); ECS_COMPONENT_DECLARE(EcsMetricOneOfInstance); @@ -22561,6 +22564,13 @@ typedef struct { ecs_map_t target_offset; /**< Pair target to metric type offset */ } ecs_oneof_metric_ctx_t; +/** Context for metric that monitors how many entities have a pair target */ +typedef struct { + ecs_metric_ctx_t metric; + ecs_id_record_t *idr; /**< Id record for monitored component */ + ecs_map_t targets; /**< Map of counters for each target */ +} ecs_count_targets_metric_ctx_t; + /** Stores context shared for all instances of member metric */ typedef struct { ecs_member_metric_ctx_t *ctx; @@ -22576,6 +22586,16 @@ typedef struct { ecs_oneof_metric_ctx_t *ctx; } EcsMetricOneOf; +/** Stores context shared for all instances of id counter metric */ +typedef struct { + ecs_id_t id; +} EcsMetricCountIds; + +/** Stores context shared for all instances of target counter metric */ +typedef struct { + ecs_count_targets_metric_ctx_t *ctx; +} EcsMetricCountTargets; + /** Instance of member metric */ typedef struct { ecs_ref_t ref; @@ -22626,6 +22646,18 @@ static ECS_MOVE(EcsMetricOneOf, dst, src, { src->ctx = NULL; }) +static ECS_DTOR(EcsMetricCountTargets, ptr, { + if (ptr->ctx) { + ecs_map_fini(&ptr->ctx->targets); + ecs_os_free(ptr->ctx); + } +}) + +static ECS_MOVE(EcsMetricCountTargets, dst, src, { + *dst = *src; + src->ctx = NULL; +}) + /** Observer used for creating new instances of member metric */ static void flecs_metrics_on_member_metric(ecs_iter_t *it) { ecs_world_t *world = it->world; @@ -22838,6 +22870,49 @@ static void UpdateCounterOneOfInstance(ecs_iter_t *it) { UpdateOneOfInstance(it, true); } +static void UpdateCountTargets(ecs_iter_t *it) { + ecs_world_t *world = it->real_world; + EcsMetricCountTargets *m = ecs_field(it, EcsMetricCountTargets, 1); + + int32_t i, count = it->count; + for (i = 0; i < count; i ++) { + ecs_count_targets_metric_ctx_t *ctx = m[i].ctx; + ecs_id_record_t *cur = ctx->idr; + while ((cur = cur->first.next)) { + ecs_id_t id = cur->id; + ecs_entity_t *mi = ecs_map_ensure(&ctx->targets, id); + if (!mi[0]) { + mi[0] = ecs_new_w_pair(world, EcsChildOf, ctx->metric.metric); + ecs_entity_t tgt = ecs_pair_second(world, cur->id); + const char *name = ecs_get_name(world, tgt); + if (name) { + ecs_set_name(world, mi[0], name); + } + + EcsMetricSource *source = ecs_get_mut( + world, mi[0], EcsMetricSource); + source->entity = tgt; + } + + EcsMetricValue *value = ecs_get_mut(world, mi[0], EcsMetricValue); + value->value += (double)ecs_count_id(world, cur->id) * + (double)it->delta_system_time; + } + } +} + +static void UpdateCountIds(ecs_iter_t *it) { + ecs_world_t *world = it->real_world; + EcsMetricCountIds *m = ecs_field(it, EcsMetricCountIds, 1); + EcsMetricValue *v = ecs_field(it, EcsMetricValue, 2); + + int32_t i, count = it->count; + for (i = 0; i < count; i ++) { + v[i].value += (double)ecs_count_id(world, m[i].id) * + (double)it->delta_system_time; + } +} + /** Initialize member metric */ static int flecs_member_metric_init( @@ -23037,6 +23112,39 @@ int flecs_oneof_metric_init( return -1; } +static +int flecs_count_id_targets_metric_init( + ecs_world_t *world, + ecs_entity_t metric, + const ecs_metric_desc_t *desc) +{ + ecs_count_targets_metric_ctx_t *ctx = ecs_os_calloc_t(ecs_count_targets_metric_ctx_t); + ctx->metric.metric = metric; + ctx->metric.kind = desc->kind; + ctx->idr = flecs_id_record_ensure(world, desc->id); + ecs_check(ctx->idr != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_map_init(&ctx->targets, NULL); + + ecs_set(world, metric, EcsMetricCountTargets, { .ctx = ctx }); + ecs_add_pair(world, metric, EcsMetric, desc->kind); + ecs_add_id(world, metric, EcsMetric); + + return 0; +error: + return -1; +} + +static +int flecs_count_ids_metric_init( + ecs_world_t *world, + ecs_entity_t metric, + const ecs_metric_desc_t *desc) +{ + ecs_set(world, metric, EcsMetricCountIds, { .id = desc->id }); + ecs_set(world, metric, EcsMetricValue, { .value = 0 }); + return 0; +} + ecs_entity_t ecs_metric_init( ecs_world_t *world, const ecs_metric_desc_t *desc) @@ -23055,8 +23163,12 @@ ecs_entity_t ecs_metric_init( goto error; } - if (kind != EcsCounter && kind != EcsGauge && kind != EcsCounterIncrement) { - ecs_err("invalid metric kind"); + if (kind != EcsGauge && + kind != EcsCounter && + kind != EcsCounterId && + kind != EcsCounterIncrement) + { + ecs_err("invalid metric kind %s", ecs_get_fullpath(world, kind)); goto error; } @@ -23065,6 +23177,11 @@ ecs_entity_t ecs_metric_init( goto error; } + if (kind == EcsCounterId && desc->member) { + ecs_err("CounterIncrement cannot be used in combination with member"); + goto error; + } + if (desc->brief) { #ifdef FLECS_DOC ecs_doc_set_brief(world, result, desc->brief); @@ -23098,20 +23215,32 @@ ecs_entity_t ecs_metric_init( goto error; } - ecs_entity_t first = ecs_pair_first(world, desc->id); - ecs_entity_t scope = flecs_get_oneof(world, first); - if (!scope) { - ecs_err("first element of pair must have OneOf with " - " targets enabled"); - goto error; - } + if (kind == EcsCounterId) { + if (flecs_count_id_targets_metric_init(world, result, desc)) { + goto error; + } + } else { + ecs_entity_t first = ecs_pair_first(world, desc->id); + ecs_entity_t scope = flecs_get_oneof(world, first); + if (!scope) { + ecs_err("first element of pair must have OneOf with " + " targets enabled"); + goto error; + } - if (flecs_oneof_metric_init(world, result, scope, desc)) { - goto error; + if (flecs_oneof_metric_init(world, result, scope, desc)) { + goto error; + } } } else { - if (flecs_id_metric_init(world, result, desc)) { - goto error; + if (kind == EcsCounterId) { + if (flecs_count_ids_metric_init(world, result, desc)) { + goto error; + } + } else { + if (flecs_id_metric_init(world, result, desc)) { + goto error; + } } } } else { @@ -23139,6 +23268,7 @@ void FlecsMetricsImport(ecs_world_t *world) { ecs_entity_t old_scope = ecs_set_scope(world, EcsMetric); ECS_TAG_DEFINE(world, EcsCounter); ECS_TAG_DEFINE(world, EcsCounterIncrement); + ECS_TAG_DEFINE(world, EcsCounterId); ECS_TAG_DEFINE(world, EcsGauge); ecs_set_scope(world, old_scope); @@ -23152,6 +23282,8 @@ void FlecsMetricsImport(ecs_world_t *world) { ECS_COMPONENT_DEFINE(world, EcsMetricMember); ECS_COMPONENT_DEFINE(world, EcsMetricId); ECS_COMPONENT_DEFINE(world, EcsMetricOneOf); + ECS_COMPONENT_DEFINE(world, EcsMetricCountIds); + ECS_COMPONENT_DEFINE(world, EcsMetricCountTargets); ecs_add_id(world, ecs_id(EcsMetricMemberInstance), EcsPrivate); ecs_add_id(world, ecs_id(EcsMetricIdInstance), EcsPrivate); @@ -23189,6 +23321,12 @@ void FlecsMetricsImport(ecs_world_t *world) { .move = ecs_move(EcsMetricOneOf) }); + ecs_set_hooks(world, EcsMetricCountTargets, { + .ctor = ecs_default_ctor, + .dtor = ecs_dtor(EcsMetricCountTargets), + .move = ecs_move(EcsMetricCountTargets) + }); + ecs_add_id(world, EcsMetric, EcsOneOf); ECS_SYSTEM(world, ClearMetricInstance, EcsPreStore, @@ -23228,6 +23366,12 @@ void FlecsMetricsImport(ecs_world_t *world) { [none] (_, Value), [in] OneOfInstance, [none] (Metric, Counter)); + + ECS_SYSTEM(world, UpdateCountIds, EcsPreStore, + [inout] CountIds, Value); + + ECS_SYSTEM(world, UpdateCountTargets, EcsPreStore, + [inout] CountTargets); } #endif diff --git a/flecs.h b/flecs.h index 3208bcbbb9..f5973b15bb 100644 --- a/flecs.h +++ b/flecs.h @@ -11325,6 +11325,9 @@ FLECS_API extern ECS_TAG_DECLARE(EcsCounter); /** Counter metric that is auto-incremented by source value */ FLECS_API extern ECS_TAG_DECLARE(EcsCounterIncrement); +/** Counter metric that counts the number of entities with an id */ +FLECS_API extern ECS_TAG_DECLARE(EcsCounterId); + /** Metric that represents current value */ FLECS_API extern ECS_TAG_DECLARE(EcsGauge); @@ -11350,7 +11353,7 @@ typedef struct ecs_metric_desc_t { ecs_entity_t entity; /* Entity associated with member that stores metric value. Must not be set - * at the same time as id. */ + * at the same time as id. Cannot be combined with EcsCounterId. */ ecs_entity_t member; /* Tracks whether entities have the specified component id. Must not be set @@ -11358,10 +11361,12 @@ typedef struct ecs_metric_desc_t { ecs_id_t id; /* If id is a (R, *) wildcard and relationship R has the OneOf property, the - * setting this value to true will track individual targets. */ + * setting this value to true will track individual targets. + * If the kind is EcsCountId and the id is a (R, *) wildcard, this value + * will create a metric per target. */ bool targets; - /* Must be either EcsGauge, EcsCounter or EcsCounterIncrement. */ + /* Must be EcsGauge, EcsCounter, EcsCounterIncrement or EcsCounterId */ ecs_entity_t kind; /* Description of metric. Will only be set if FLECS_DOC addon is enabled */ @@ -17397,6 +17402,7 @@ struct metrics { struct Metric { }; struct Counter { }; struct CounterIncrement { }; + struct CounterId { }; struct Gauge { }; metrics(flecs::world& world); @@ -26156,6 +26162,11 @@ struct filter_builder final : _::filter_builder_base { this->m_desc.entity = ecs_entity_init(world, &entity_desc); } } + + template + void each(Func&& func) { + this->build().each(FLECS_FWD(func)); + } }; } @@ -28591,6 +28602,7 @@ inline metrics::metrics(flecs::world& world) { world.entity("::flecs::metrics::Instance"); world.entity("::flecs::metrics::Metric"); world.entity("::flecs::metrics::Metric::Counter"); + world.entity("::flecs::metrics::Metric::CounterId"); world.entity("::flecs::metrics::Metric::CounterIncrement"); world.entity("::flecs::metrics::Metric::Gauge"); } diff --git a/include/flecs/addons/cpp/mixins/filter/builder.hpp b/include/flecs/addons/cpp/mixins/filter/builder.hpp index a80a188fc2..dda7ba2fe5 100644 --- a/include/flecs/addons/cpp/mixins/filter/builder.hpp +++ b/include/flecs/addons/cpp/mixins/filter/builder.hpp @@ -34,6 +34,11 @@ struct filter_builder final : _::filter_builder_base { this->m_desc.entity = ecs_entity_init(world, &entity_desc); } } + + template + void each(Func&& func) { + this->build().each(FLECS_FWD(func)); + } }; } diff --git a/include/flecs/addons/cpp/mixins/metrics/decl.hpp b/include/flecs/addons/cpp/mixins/metrics/decl.hpp index 0c184c64d2..bdfa1a7471 100644 --- a/include/flecs/addons/cpp/mixins/metrics/decl.hpp +++ b/include/flecs/addons/cpp/mixins/metrics/decl.hpp @@ -17,6 +17,7 @@ struct metrics { struct Metric { }; struct Counter { }; struct CounterIncrement { }; + struct CounterId { }; struct Gauge { }; metrics(flecs::world& world); diff --git a/include/flecs/addons/cpp/mixins/metrics/impl.hpp b/include/flecs/addons/cpp/mixins/metrics/impl.hpp index 9e22bd2cea..882d7f5e2b 100644 --- a/include/flecs/addons/cpp/mixins/metrics/impl.hpp +++ b/include/flecs/addons/cpp/mixins/metrics/impl.hpp @@ -16,6 +16,7 @@ inline metrics::metrics(flecs::world& world) { world.entity("::flecs::metrics::Instance"); world.entity("::flecs::metrics::Metric"); world.entity("::flecs::metrics::Metric::Counter"); + world.entity("::flecs::metrics::Metric::CounterId"); world.entity("::flecs::metrics::Metric::CounterIncrement"); world.entity("::flecs::metrics::Metric::Gauge"); } diff --git a/include/flecs/addons/metrics.h b/include/flecs/addons/metrics.h index 645b804d30..921a5f7096 100644 --- a/include/flecs/addons/metrics.h +++ b/include/flecs/addons/metrics.h @@ -47,6 +47,9 @@ FLECS_API extern ECS_TAG_DECLARE(EcsCounter); /** Counter metric that is auto-incremented by source value */ FLECS_API extern ECS_TAG_DECLARE(EcsCounterIncrement); +/** Counter metric that counts the number of entities with an id */ +FLECS_API extern ECS_TAG_DECLARE(EcsCounterId); + /** Metric that represents current value */ FLECS_API extern ECS_TAG_DECLARE(EcsGauge); @@ -72,7 +75,7 @@ typedef struct ecs_metric_desc_t { ecs_entity_t entity; /* Entity associated with member that stores metric value. Must not be set - * at the same time as id. */ + * at the same time as id. Cannot be combined with EcsCounterId. */ ecs_entity_t member; /* Tracks whether entities have the specified component id. Must not be set @@ -80,10 +83,12 @@ typedef struct ecs_metric_desc_t { ecs_id_t id; /* If id is a (R, *) wildcard and relationship R has the OneOf property, the - * setting this value to true will track individual targets. */ + * setting this value to true will track individual targets. + * If the kind is EcsCountId and the id is a (R, *) wildcard, this value + * will create a metric per target. */ bool targets; - /* Must be either EcsGauge, EcsCounter or EcsCounterIncrement. */ + /* Must be EcsGauge, EcsCounter, EcsCounterIncrement or EcsCounterId */ ecs_entity_t kind; /* Description of metric. Will only be set if FLECS_DOC addon is enabled */ diff --git a/src/addons/metrics.c b/src/addons/metrics.c index 938fd542fb..764798d2b6 100644 --- a/src/addons/metrics.c +++ b/src/addons/metrics.c @@ -15,12 +15,15 @@ ECS_COMPONENT_DECLARE(EcsMetricSource); ECS_TAG_DECLARE(EcsMetric); ECS_TAG_DECLARE(EcsCounter); ECS_TAG_DECLARE(EcsCounterIncrement); +ECS_TAG_DECLARE(EcsCounterId); ECS_TAG_DECLARE(EcsGauge); /* Internal components */ ECS_COMPONENT_DECLARE(EcsMetricMember); ECS_COMPONENT_DECLARE(EcsMetricId); ECS_COMPONENT_DECLARE(EcsMetricOneOf); +ECS_COMPONENT_DECLARE(EcsMetricCountIds); +ECS_COMPONENT_DECLARE(EcsMetricCountTargets); ECS_COMPONENT_DECLARE(EcsMetricMemberInstance); ECS_COMPONENT_DECLARE(EcsMetricIdInstance); ECS_COMPONENT_DECLARE(EcsMetricOneOfInstance); @@ -52,6 +55,13 @@ typedef struct { ecs_map_t target_offset; /**< Pair target to metric type offset */ } ecs_oneof_metric_ctx_t; +/** Context for metric that monitors how many entities have a pair target */ +typedef struct { + ecs_metric_ctx_t metric; + ecs_id_record_t *idr; /**< Id record for monitored component */ + ecs_map_t targets; /**< Map of counters for each target */ +} ecs_count_targets_metric_ctx_t; + /** Stores context shared for all instances of member metric */ typedef struct { ecs_member_metric_ctx_t *ctx; @@ -67,6 +77,16 @@ typedef struct { ecs_oneof_metric_ctx_t *ctx; } EcsMetricOneOf; +/** Stores context shared for all instances of id counter metric */ +typedef struct { + ecs_id_t id; +} EcsMetricCountIds; + +/** Stores context shared for all instances of target counter metric */ +typedef struct { + ecs_count_targets_metric_ctx_t *ctx; +} EcsMetricCountTargets; + /** Instance of member metric */ typedef struct { ecs_ref_t ref; @@ -117,6 +137,18 @@ static ECS_MOVE(EcsMetricOneOf, dst, src, { src->ctx = NULL; }) +static ECS_DTOR(EcsMetricCountTargets, ptr, { + if (ptr->ctx) { + ecs_map_fini(&ptr->ctx->targets); + ecs_os_free(ptr->ctx); + } +}) + +static ECS_MOVE(EcsMetricCountTargets, dst, src, { + *dst = *src; + src->ctx = NULL; +}) + /** Observer used for creating new instances of member metric */ static void flecs_metrics_on_member_metric(ecs_iter_t *it) { ecs_world_t *world = it->world; @@ -329,6 +361,49 @@ static void UpdateCounterOneOfInstance(ecs_iter_t *it) { UpdateOneOfInstance(it, true); } +static void UpdateCountTargets(ecs_iter_t *it) { + ecs_world_t *world = it->real_world; + EcsMetricCountTargets *m = ecs_field(it, EcsMetricCountTargets, 1); + + int32_t i, count = it->count; + for (i = 0; i < count; i ++) { + ecs_count_targets_metric_ctx_t *ctx = m[i].ctx; + ecs_id_record_t *cur = ctx->idr; + while ((cur = cur->first.next)) { + ecs_id_t id = cur->id; + ecs_entity_t *mi = ecs_map_ensure(&ctx->targets, id); + if (!mi[0]) { + mi[0] = ecs_new_w_pair(world, EcsChildOf, ctx->metric.metric); + ecs_entity_t tgt = ecs_pair_second(world, cur->id); + const char *name = ecs_get_name(world, tgt); + if (name) { + ecs_set_name(world, mi[0], name); + } + + EcsMetricSource *source = ecs_get_mut( + world, mi[0], EcsMetricSource); + source->entity = tgt; + } + + EcsMetricValue *value = ecs_get_mut(world, mi[0], EcsMetricValue); + value->value += (double)ecs_count_id(world, cur->id) * + (double)it->delta_system_time; + } + } +} + +static void UpdateCountIds(ecs_iter_t *it) { + ecs_world_t *world = it->real_world; + EcsMetricCountIds *m = ecs_field(it, EcsMetricCountIds, 1); + EcsMetricValue *v = ecs_field(it, EcsMetricValue, 2); + + int32_t i, count = it->count; + for (i = 0; i < count; i ++) { + v[i].value += (double)ecs_count_id(world, m[i].id) * + (double)it->delta_system_time; + } +} + /** Initialize member metric */ static int flecs_member_metric_init( @@ -528,6 +603,39 @@ int flecs_oneof_metric_init( return -1; } +static +int flecs_count_id_targets_metric_init( + ecs_world_t *world, + ecs_entity_t metric, + const ecs_metric_desc_t *desc) +{ + ecs_count_targets_metric_ctx_t *ctx = ecs_os_calloc_t(ecs_count_targets_metric_ctx_t); + ctx->metric.metric = metric; + ctx->metric.kind = desc->kind; + ctx->idr = flecs_id_record_ensure(world, desc->id); + ecs_check(ctx->idr != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_map_init(&ctx->targets, NULL); + + ecs_set(world, metric, EcsMetricCountTargets, { .ctx = ctx }); + ecs_add_pair(world, metric, EcsMetric, desc->kind); + ecs_add_id(world, metric, EcsMetric); + + return 0; +error: + return -1; +} + +static +int flecs_count_ids_metric_init( + ecs_world_t *world, + ecs_entity_t metric, + const ecs_metric_desc_t *desc) +{ + ecs_set(world, metric, EcsMetricCountIds, { .id = desc->id }); + ecs_set(world, metric, EcsMetricValue, { .value = 0 }); + return 0; +} + ecs_entity_t ecs_metric_init( ecs_world_t *world, const ecs_metric_desc_t *desc) @@ -546,8 +654,12 @@ ecs_entity_t ecs_metric_init( goto error; } - if (kind != EcsCounter && kind != EcsGauge && kind != EcsCounterIncrement) { - ecs_err("invalid metric kind"); + if (kind != EcsGauge && + kind != EcsCounter && + kind != EcsCounterId && + kind != EcsCounterIncrement) + { + ecs_err("invalid metric kind %s", ecs_get_fullpath(world, kind)); goto error; } @@ -556,6 +668,11 @@ ecs_entity_t ecs_metric_init( goto error; } + if (kind == EcsCounterId && desc->member) { + ecs_err("CounterIncrement cannot be used in combination with member"); + goto error; + } + if (desc->brief) { #ifdef FLECS_DOC ecs_doc_set_brief(world, result, desc->brief); @@ -589,20 +706,32 @@ ecs_entity_t ecs_metric_init( goto error; } - ecs_entity_t first = ecs_pair_first(world, desc->id); - ecs_entity_t scope = flecs_get_oneof(world, first); - if (!scope) { - ecs_err("first element of pair must have OneOf with " - " targets enabled"); - goto error; - } - - if (flecs_oneof_metric_init(world, result, scope, desc)) { - goto error; + if (kind == EcsCounterId) { + if (flecs_count_id_targets_metric_init(world, result, desc)) { + goto error; + } + } else { + ecs_entity_t first = ecs_pair_first(world, desc->id); + ecs_entity_t scope = flecs_get_oneof(world, first); + if (!scope) { + ecs_err("first element of pair must have OneOf with " + " targets enabled"); + goto error; + } + + if (flecs_oneof_metric_init(world, result, scope, desc)) { + goto error; + } } } else { - if (flecs_id_metric_init(world, result, desc)) { - goto error; + if (kind == EcsCounterId) { + if (flecs_count_ids_metric_init(world, result, desc)) { + goto error; + } + } else { + if (flecs_id_metric_init(world, result, desc)) { + goto error; + } } } } else { @@ -630,6 +759,7 @@ void FlecsMetricsImport(ecs_world_t *world) { ecs_entity_t old_scope = ecs_set_scope(world, EcsMetric); ECS_TAG_DEFINE(world, EcsCounter); ECS_TAG_DEFINE(world, EcsCounterIncrement); + ECS_TAG_DEFINE(world, EcsCounterId); ECS_TAG_DEFINE(world, EcsGauge); ecs_set_scope(world, old_scope); @@ -643,6 +773,8 @@ void FlecsMetricsImport(ecs_world_t *world) { ECS_COMPONENT_DEFINE(world, EcsMetricMember); ECS_COMPONENT_DEFINE(world, EcsMetricId); ECS_COMPONENT_DEFINE(world, EcsMetricOneOf); + ECS_COMPONENT_DEFINE(world, EcsMetricCountIds); + ECS_COMPONENT_DEFINE(world, EcsMetricCountTargets); ecs_add_id(world, ecs_id(EcsMetricMemberInstance), EcsPrivate); ecs_add_id(world, ecs_id(EcsMetricIdInstance), EcsPrivate); @@ -680,6 +812,12 @@ void FlecsMetricsImport(ecs_world_t *world) { .move = ecs_move(EcsMetricOneOf) }); + ecs_set_hooks(world, EcsMetricCountTargets, { + .ctor = ecs_default_ctor, + .dtor = ecs_dtor(EcsMetricCountTargets), + .move = ecs_move(EcsMetricCountTargets) + }); + ecs_add_id(world, EcsMetric, EcsOneOf); ECS_SYSTEM(world, ClearMetricInstance, EcsPreStore, @@ -719,6 +857,12 @@ void FlecsMetricsImport(ecs_world_t *world) { [none] (_, Value), [in] OneOfInstance, [none] (Metric, Counter)); + + ECS_SYSTEM(world, UpdateCountIds, EcsPreStore, + [inout] CountIds, Value); + + ECS_SYSTEM(world, UpdateCountTargets, EcsPreStore, + [inout] CountTargets); } #endif diff --git a/test/addons/project.json b/test/addons/project.json index d30421a15c..01f3325003 100644 --- a/test/addons/project.json +++ b/test/addons/project.json @@ -1431,7 +1431,9 @@ "member_auto_counter", "id_counter", "oneof_counter", - "metric_description" + "metric_description", + "id_count", + "id_target_count" ] }] } diff --git a/test/addons/src/Metrics.c b/test/addons/src/Metrics.c index 8872a7e352..9a152b552b 100644 --- a/test/addons/src/Metrics.c +++ b/test/addons/src/Metrics.c @@ -1670,3 +1670,96 @@ void Metrics_metric_description() { ecs_fini(world); } + +void Metrics_id_count() { + ecs_world_t *world = ecs_init(); + + ECS_IMPORT(world, FlecsMetrics); + + ECS_COMPONENT(world, Position); + + ecs_entity_t m = ecs_metric(world, { + .entity = ecs_entity(world, { .name = "metrics.position" }), + .id = ecs_id(Position), + .kind = EcsCounterId + }); + test_assert(m != 0); + + ecs_set(world, 0, Position, {10, 20}); + + ecs_progress(world, 1); + + { + const EcsMetricValue *v = ecs_get(world, m, EcsMetricValue); + test_assert(v != NULL); + test_int(v->value, 1); + } + + ecs_set(world, 0, Position, {10, 20}); + ecs_set(world, 0, Position, {10, 20}); + + ecs_progress(world, 1); + + { + const EcsMetricValue *v = ecs_get(world, m, EcsMetricValue); + test_assert(v != NULL); + test_int(v->value, 4); + } + + ecs_fini(world); +} + +void Metrics_id_target_count() { + ecs_world_t *world = ecs_init(); + + ECS_IMPORT(world, FlecsMetrics); + + ECS_TAG(world, Color); + ECS_TAG(world, Red); + ECS_TAG(world, Green); + ECS_TAG(world, Blue); + + ecs_entity_t m = ecs_metric(world, { + .entity = ecs_entity(world, { .name = "metrics.color" }), + .id = ecs_pair(Color, EcsWildcard), + .targets = true, + .kind = EcsCounterId + }); + test_assert(m != 0); + + ecs_entity_t e1 = ecs_new_id(world); + ecs_add_pair(world, e1, Color, Red); + ecs_entity_t e2 = ecs_new_id(world); + ecs_add_pair(world, e2, Color, Green); + ecs_add_pair(world, e2, Color, Blue); + ecs_entity_t e3 = ecs_new_id(world); + ecs_add_pair(world, e3, Color, Blue); + + ecs_progress(world, 1); + + { + ecs_entity_t red = ecs_lookup_fullpath(world, "metrics.color.Red"); + test_assert(red != 0); + const EcsMetricValue *v = ecs_get(world, red, EcsMetricValue); + test_assert(v != NULL); + test_int(v->value, 1); + } + + { + ecs_entity_t green = ecs_lookup_fullpath(world, "metrics.color.Green"); + test_assert(green != 0); + const EcsMetricValue *v = ecs_get(world, green, EcsMetricValue); + test_assert(v != NULL); + test_int(v->value, 1); + } + + { + ecs_entity_t blue = ecs_lookup_fullpath(world, "metrics.color.Blue"); + test_assert(blue != 0); + const EcsMetricValue *v = ecs_get(world, blue, EcsMetricValue); + test_assert(v != NULL); + test_int(v->value, 2); + } + + ecs_fini(world); +} diff --git a/test/addons/src/main.c b/test/addons/src/main.c index 9b079227e3..f9eca253f0 100644 --- a/test/addons/src/main.c +++ b/test/addons/src/main.c @@ -1367,6 +1367,8 @@ void Metrics_member_auto_counter(void); void Metrics_id_counter(void); void Metrics_oneof_counter(void); void Metrics_metric_description(void); +void Metrics_id_count(void); +void Metrics_id_target_count(void); bake_test_case Parser_testcases[] = { { @@ -6626,6 +6628,14 @@ bake_test_case Metrics_testcases[] = { { "metric_description", Metrics_metric_description + }, + { + "id_count", + Metrics_id_count + }, + { + "id_target_count", + Metrics_id_target_count } }; @@ -6844,7 +6854,7 @@ static bake_test_suite suites[] = { "Metrics", NULL, NULL, - 24, + 26, Metrics_testcases } }; diff --git a/test/cpp_api/project.json b/test/cpp_api/project.json index 54488945f9..c12d45d9e3 100644 --- a/test/cpp_api/project.json +++ b/test/cpp_api/project.json @@ -1181,7 +1181,9 @@ "component_mixin_member_metric_description", "member_metric_w_value_name", "member_metric_w_value_name_camel_case_type", - "member_metric_w_custom_name" + "member_metric_w_custom_name", + "counter_id_metric", + "counter_target_metric" ] }, { "id": "Meta", diff --git a/test/cpp_api/src/Misc.cpp b/test/cpp_api/src/Misc.cpp index 670e808a23..9a6d944d16 100644 --- a/test/cpp_api/src/Misc.cpp +++ b/test/cpp_api/src/Misc.cpp @@ -811,3 +811,77 @@ void Misc_member_metric_w_custom_name() { test_int(count, 2); } + +void Misc_counter_id_metric() { + flecs::world ecs; + + ecs.import(); + + flecs::entity m = ecs.metric("metrics.position") + .kind() + .id(); + + ecs.entity().set({10, 20}); + + ecs.progress(1.0); + + { + const flecs::metrics::Value *v = m.get(); + test_assert(v != nullptr); + test_int(v->value, 1); + } + + ecs.entity().set({10, 20}); + ecs.entity().set({10, 20}); + + ecs.progress(1.0); + + { + const flecs::metrics::Value *v = m.get(); + test_assert(v != nullptr); + test_int(v->value, 4); + } +} + +void Misc_counter_target_metric() { + flecs::world ecs; + + ecs.import(); + + flecs::entity m = ecs.metric("metrics::color") + .kind() + .id(flecs::Wildcard) + .targets(); + test_assert(m != 0); + + ecs.entity().add(Color::Red); + ecs.entity().add(Color::Green); + ecs.entity().add(Color::Blue); + ecs.entity().add(Color::Blue); + + ecs.progress(1.0); + + { + flecs::entity red = ecs.lookup("metrics::color::Red"); + test_assert(red != 0); + const flecs::metrics::Value *v = red.get(); + test_assert(v != nullptr); + test_int(v->value, 1); + } + + { + flecs::entity green = ecs.lookup("metrics::color::Green"); + test_assert(green != 0); + const flecs::metrics::Value *v = green.get(); + test_assert(v != nullptr); + test_int(v->value, 1); + } + + { + flecs::entity blue = ecs.lookup("metrics::color::Blue"); + test_assert(blue != 0); + const flecs::metrics::Value *v = blue.get(); + test_assert(v != nullptr); + test_int(v->value, 2); + } +} diff --git a/test/cpp_api/src/main.cpp b/test/cpp_api/src/main.cpp index 3e515c37b5..c645d0b0c6 100644 --- a/test/cpp_api/src/main.cpp +++ b/test/cpp_api/src/main.cpp @@ -1128,6 +1128,8 @@ void Misc_component_mixin_member_metric_description(void); void Misc_member_metric_w_value_name(void); void Misc_member_metric_w_value_name_camel_case_type(void); void Misc_member_metric_w_custom_name(void); +void Misc_counter_id_metric(void); +void Misc_counter_target_metric(void); // Testsuite 'Meta' void Meta_struct(void); @@ -5565,6 +5567,14 @@ bake_test_case Misc_testcases[] = { { "member_metric_w_custom_name", Misc_member_metric_w_custom_name + }, + { + "counter_id_metric", + Misc_counter_id_metric + }, + { + "counter_target_metric", + Misc_counter_target_metric } }; @@ -6062,7 +6072,7 @@ static bake_test_suite suites[] = { "Misc", Misc_setup, NULL, - 25, + 27, Misc_testcases }, {