Skip to content
Flecs v4.1
sparse_query.hpp
Go to the documentation of this file.
1/** @file addons/cpp/mixins/query/sparse_query.hpp
2 * @brief Direct iteration of sparse component storages. */
3
4#pragma once
5
6namespace flecs {
7
8namespace _ {
9
10FLECS_ALWAYS_INLINE inline void* field_at_sparse(
11 const ecs_sparse_t *sparse, size_t size, uint64_t entity, bool checked)
12{
13 ecs_assert(sparse != nullptr, ECS_INVALID_PARAMETER, NULL);
14 ecs_assert(size == static_cast<size_t>(sparse->size),
16
17 int32_t page_index = FLECS_SPARSE_PAGE(entity);
18 if (checked && page_index >= sparse->pages.count) {
19 return nullptr;
20 }
21
22 ecs_sparse_page_t *page = &static_cast<ecs_sparse_page_t*>(
23 sparse->pages.array)[page_index];
24 int32_t offset = FLECS_SPARSE_OFFSET(entity);
25 if (checked) {
26 if (!page->sparse) {
27 return nullptr;
28 }
29
30 int32_t dense = page->sparse[offset];
31 if (!dense || dense >= sparse->count) {
32 return nullptr;
33 }
34 }
35
36 return ECS_OFFSET(page->data, size * static_cast<size_t>(offset));
37}
38
39}
40
41/** @defgroup cpp_queries Sparse queries
42 * @ingroup cpp_core
43 * Direct iteration of sparse component storages. @{ */
44
45/** Query that iterates sparse component storages directly.
46 * Returned by world::query() when all components have the dont_fragment
47 * trait and don't declare the on_instantiate::inherit policy. */
48template <typename ... Components>
49struct sparse_query {
50 static_assert(_::is_sparse_query<Components...>::value,
51 "all sparse_query components must have the dont_fragment trait and "
52 "must not declare the on_instantiate::inherit policy");
53
54 explicit sparse_query(flecs::world_t *world)
55 : world_(ECS_CONST_CAST(flecs::world_t*, ecs_get_world(world)))
56 , ids_{ _::type<Components>::id(world)... }
57 {
58 assert_policies();
59 }
60
61 /** Iterate the query.
62 * The function signature must match func(flecs::entity e, Components&...)
63 * or func(Components&...). */
64 template <typename Func>
65 void each(Func&& func) const {
66 each_impl(std::index_sequence_for<Components...>{}, func);
67 }
68
69 /** Return the number of entities matched by the query. */
70 int32_t count() const {
71 int32_t result = 0;
72 each([&](Components&...) { result ++; });
73 return result;
74 }
75
76 /** Convert to a regular flecs::query for the same components. */
77 operator flecs::query<Components...>() const;
78
79private:
80 template <size_t ... Is, typename Func>
81 void each_impl(std::index_sequence<Is...>, const Func& func) const {
82 constexpr size_t n = sizeof...(Components);
83
84 ecs_sparse_t *sparse[n] = { storage(ids_[Is])... };
85 for (size_t f = 0; f < n; f ++) {
86 if (!sparse[f]) {
87 return;
88 }
89 }
90
91 size_t lead = 0;
92 for (size_t f = 1; f < n; f ++) {
93 if (sparse[f]->count < sparse[lead]->count) {
94 lead = f;
95 }
96 }
97
98 each_impl_dispatch<0>(lead, sparse,
99 std::index_sequence<Is...>{}, func);
100 }
101
102 template <size_t Lead, size_t ... Is, typename Func>
103 void each_impl_dispatch(
104 size_t lead,
105 ecs_sparse_t *const *sparse,
106 std::index_sequence<Is...> seq,
107 const Func& func) const
108 {
109 if (lead == Lead) {
110 each_impl_lead<Lead>(sparse, seq, func);
111 return;
112 }
113
114 if constexpr (Lead + 1 < sizeof...(Components)) {
115 each_impl_dispatch<Lead + 1>(lead, sparse, seq, func);
116 }
117 }
118
119 template <size_t Lead, size_t ... Is, typename Func>
120 void each_impl_lead(
121 ecs_sparse_t *const *sparse,
122 std::index_sequence<Is...>,
123 const Func& func) const
124 {
125 const uint64_t *entities = static_cast<const uint64_t*>(
126 sparse[Lead]->dense.array) + 1;
127 int32_t count = sparse[Lead]->count - 1;
128
129 for (int32_t i = 0; i < count; i ++) {
130 uint64_t e = entities[i];
131 void *ptrs[sizeof...(Components)];
132 if (!(... && (ptrs[Is] = _::field_at_sparse(
133 sparse[Is], ECS_SIZEOF(remove_reference_t<Components>), e,
134 Is != Lead))))
135 {
136 continue;
137 }
138
139 const ecs_record_t *r = ecs_record_find(world_, e);
140 if (flecs_table_flags(r->table) &
141 (EcsTableNotQueryable|EcsTableIsPrefab|EcsTableIsDisabled))
142 {
143 continue;
144 }
145
146 if constexpr (std::is_invocable<
147 Func, flecs::entity, Components&...>::value)
148 {
149 func(flecs::entity(world_, e),
150 (*static_cast<remove_reference_t<Components>*>(
151 ptrs[Is]))...);
152 } else {
153 func((*static_cast<remove_reference_t<Components>*>(
154 ptrs[Is]))...);
155 }
156 }
157 }
158
159 ecs_sparse_t* storage(flecs::id_t id) const {
160 ecs_component_record_t *cr = flecs_components_get(world_, id);
161 return cr ? flecs_component_get_sparse(cr) : nullptr;
162 }
163
164 void assert_policies() const {
165#ifdef FLECS_PREFAB
166 for (flecs::id_t id : ids_) {
167 (void)id;
168 ecs_assert(ecs_get_target(world_, id, flecs::OnInstantiate, 0) !=
169 flecs::Inherit, ECS_INVALID_OPERATION,
170 "sparse_query component has the OnInstantiate Inherit trait, "
171 "which sparse queries cannot match; add the on_instantiate "
172 "trait at compile time instead");
173 }
174#endif
175 }
176
177 flecs::world_t *world_;
178 flecs::id_t ids_[sizeof...(Components)];
179};
180
181/** @} */
182
183}
#define ecs_assert(condition, error_code,...)
Assert.
Definition log.h:473
#define ECS_INVALID_OPERATION
Invalid operation error code.
Definition log.h:669
#define ECS_INVALID_PARAMETER
Invalid parameter error code.
Definition log.h:671
struct ecs_component_record_t ecs_component_record_t
Information about a (component) ID, such as type info and tables with the ID.
Definition flecs.h:507
struct ecs_record_t ecs_record_t
Information about an entity, like its table and row.
Definition flecs.h:504
ecs_id_t id_t
ID type.
Definition c_types.hpp:20
ecs_world_t world_t
World type.
Definition c_types.hpp:18
ecs_entity_t ecs_get_target(const ecs_world_t *world, ecs_entity_t entity, ecs_entity_t rel, int32_t index)
Get the target of a relationship.
const ecs_world_t * ecs_get_world(const ecs_poly_t *poly)
Get the world from a poly.
Int to enum.
Definition component.hpp:18
Typed query.
Definition impl.hpp:271
int32_t count() const
Return the number of entities matched by the query.
void each(Func &&func) const
Iterate the query.
Trait that marks a component as Sparse at compile time.
Definition utils.hpp:187
The world.
Definition world.hpp:129
flecs::entity entity(Args &&... args) const
Create an entity.