Gaia-ECS v0.9.3
A simple and powerful entity component system
Loading...
Searching...
No Matches
query_cache.h
1#pragma once
2#include "gaia/config/config.h"
3
4#include "gaia/cnt/darray.h"
5#include "gaia/cnt/map.h"
6#include "gaia/core/utility.h"
7#include "gaia/ecs/component.h"
8#include "gaia/ecs/id.h"
9#include "gaia/ecs/query_common.h"
10#include "gaia/ecs/query_info.h"
11
12namespace gaia {
13 namespace ecs {
15 QueryLookupHash m_hash;
16 const QueryCtx* m_pCtx;
17
18 public:
19 static constexpr bool IsDirectHashKey = true;
20
21 QueryLookupKey(): m_hash({0}), m_pCtx(nullptr) {}
22 explicit QueryLookupKey(QueryLookupHash hash, const QueryCtx* pCtx): m_hash(hash), m_pCtx(pCtx) {}
23
24 size_t hash() const {
25 return (size_t)m_hash.hash;
26 }
27
28 bool operator==(const QueryLookupKey& other) const {
29 // Hash doesn't match we don't have a match.
30 // Hash collisions are expected to be very unlikely so optimize for this case.
31 if GAIA_LIKELY (m_hash != other.m_hash)
32 return false;
33
34 const auto id = m_pCtx->q.handle.id();
35
36 // Temporary key is given. Do full context comparison.
37 if (id == QueryIdBad)
38 return *m_pCtx == *other.m_pCtx;
39
40 // Real key is given. Compare context pointer.
41 // Normally we'd compare query IDs but because we do not allow query copies and all queries are
42 // unique it's guaranteed that if pointers are the same we have a match.
43 return m_pCtx == other.m_pCtx;
44 }
45 };
46
47 class QueryCache {
49 // TODO: Make m_queryArr allocate data in pages.
50 // Currently ilist always uses a darray internally which keeps growing, making
51 // it not suitable for this particular use case.
52 // QueryInfo is quite big and we do not want to copy a lot of data every time
53 // resizing is necessary.
55
58
59 public:
60 QueryCache() {
61 m_queryArr.reserve(256);
62 }
63
64 ~QueryCache() = default;
65
66 QueryCache(QueryCache&&) = delete;
67 QueryCache(const QueryCache&) = delete;
68 QueryCache& operator=(QueryCache&&) = delete;
69 QueryCache& operator=(const QueryCache&) = delete;
70
71 GAIA_NODISCARD bool valid(QueryHandle handle) const {
72 if (handle.id() == QueryIdBad)
73 return false;
74
75 // Entity ID has to fit inside the entity array
76 if (handle.id() >= m_queryArr.size())
77 return false;
78
79 const auto& h = m_queryArr[handle.id()];
80 return h.idx == handle.id() && h.data.gen == handle.gen();
81 }
82
83 void clear() {
84 m_queryCache.clear();
85 m_queryArr.clear();
86 m_entityToQuery.clear();
87 }
88
93 if (!valid(handle))
94 return nullptr;
95
96 auto& info = m_queryArr[handle.id()];
97 GAIA_ASSERT(info.idx == handle.id());
98 GAIA_ASSERT(info.data.gen == handle.gen());
99 return &info;
100 }
101
106 GAIA_ASSERT(valid(handle));
107
108 auto& info = m_queryArr[handle.id()];
109 GAIA_ASSERT(info.idx == handle.id());
110 GAIA_ASSERT(info.data.gen == handle.gen());
111 return info;
112 }
113
119 QueryInfo&
120 add(QueryCtx&& ctx, //
121 const EntityToArchetypeMap& entityToArchetypeMap, //
122 const ArchetypeDArray& allArchetypes) {
123 GAIA_ASSERT(ctx.hashLookup.hash != 0);
124
125 // First check if the query cache record exists
126 auto ret = m_queryCache.try_emplace(QueryLookupKey(ctx.hashLookup, &ctx));
127 if (!ret.second) {
128 const auto idx = ret.first->second;
129 auto& info = m_queryArr[idx];
130 GAIA_ASSERT(idx == info.idx);
131 info.add_ref();
132 return info;
133 }
134
135 // No record exists, let us create a new one
136 QueryInfoCreationCtx creationCtx{};
137 creationCtx.pQueryCtx = &ctx;
138 creationCtx.pEntityToArchetypeMap = &entityToArchetypeMap;
139 creationCtx.pAllArchetypes = &allArchetypes;
140 auto handle = m_queryArr.alloc(&creationCtx);
141
142 // We are moving the rvalue to "ctx". As a result, the pointer stored in m_queryCache.emplace above is no longer
143 // going to be valid. Therefore we swap the map key with a one with a valid pointer.
144 auto& info = get(handle);
145 info.add_ref();
146 auto new_p = robin_hood::pair(std::make_pair(QueryLookupKey(ctx.hashLookup, &info.ctx()), info.idx));
147 ret.first->swap(new_p);
148
149 // Add the entity->query pair
150 add_entity_to_query_pairs(info.ctx().data.ids_view(), handle);
151
152 return info;
153 }
154
158 bool del(QueryHandle handle) {
159 auto* pInfo = try_get(handle);
160 if (pInfo == nullptr)
161 return false;
162
163 pInfo->dec_ref();
164 if (pInfo->refs() != 0)
165 return false;
166
167 // If this was the last reference to the query, we can safely remove it
168 auto it = m_queryCache.find(QueryLookupKey(pInfo->ctx().hashLookup, &pInfo->ctx()));
169 GAIA_ASSERT(it != m_queryCache.end());
170 m_queryCache.erase(it);
171 m_queryArr.free(handle);
172
173 // Remove the entity->query pair
174 del_entity_to_query_pairs(pInfo->ctx().data.ids_view(), handle);
175
176 return true;
177 }
178
179 cnt::darray<QueryInfo>::iterator begin() {
180 return m_queryArr.begin();
181 }
182
183 cnt::darray<QueryInfo>::iterator end() {
184 return m_queryArr.end();
185 }
186
194 auto it = m_entityToQuery.find(entityKey);
195 if (it == m_entityToQuery.end())
196 return;
197
198 const auto& handles = it->second;
199 for (auto& handle: handles) {
200 auto& info = get(handle);
201 info.reset();
202 info.ctx().refresh();
203 }
204 }
205
206 private:
210 void add_entity_query_pair(Entity entity, QueryHandle handle) {
211 EntityLookupKey entityKey(entity);
212 const auto it = m_entityToQuery.find(entityKey);
213 if (it == m_entityToQuery.end()) {
214 m_entityToQuery.try_emplace(entityKey, cnt::darray<QueryHandle>{handle});
215 return;
216 }
217
218 auto& handles = it->second;
219 if (!core::has(handles, handle))
220 handles.push_back(handle);
221 }
222
226 void del_entity_archetype_pair(Entity entity, QueryHandle handle) {
227 auto it = m_entityToQuery.find(EntityLookupKey(entity));
228 if (it == m_entityToQuery.end())
229 return;
230
231 auto& handles = it->second;
232 const auto idx = core::get_index_unsafe(handles, handle);
233 core::swap_erase_unsafe(handles, idx);
234
235 // Remove the mapping if there are no more matches
236 if (handles.empty())
237 m_entityToQuery.erase(it);
238 }
239
242 void add_entity_to_query_pairs(EntitySpan entities, QueryHandle handle) {
243 for (auto entity: entities) {
244 add_entity_query_pair(entity, handle);
245 }
246 }
247
250 void del_entity_to_query_pairs(EntitySpan entities, QueryHandle handle) {
251 for (auto entity: entities) {
252 add_entity_query_pair(entity, handle);
253 }
254 }
255 };
256 } // namespace ecs
257} // namespace gaia
Array with variable size of elements of type.
Definition darray_impl.h:25
Definition query_cache.h:47
QueryInfo & add(QueryCtx &&ctx, const EntityToArchetypeMap &entityToArchetypeMap, const ArchetypeDArray &allArchetypes)
Registers the provided query lookup context ctx. If it already exists it is returned.
Definition query_cache.h:120
QueryInfo & get(QueryHandle handle)
Returns a QueryInfo object associated with handle.
Definition query_cache.h:105
bool del(QueryHandle handle)
Deletes an existing QueryInfo object given the provided query handle.
Definition query_cache.h:158
QueryInfo * try_get(QueryHandle handle)
Returns a QueryInfo object associated with handle.
Definition query_cache.h:92
void invalidate_queries_for_entity(EntityLookupKey entityKey)
Invalidates all cached queries that work with the given entity This covers the following kinds of que...
Definition query_cache.h:193
Definition query_info.h:37
Definition query_cache.h:14
Definition robin_hood.h:720
Checks if endianess was detected correctly at compile-time.
Definition bitset.h:9
Implicit list. Rather than with pointers, items.
Definition ilist.h:76
TListItem & free(TItemHandle handle)
Invalidates handle. Every time an item is deallocated its generation is increased by one.
Definition ilist.h:235
GAIA_NODISCARD TItemHandle alloc(void *ctx)
Allocates a new item in the list.
Definition ilist.h:175
Hashmap lookup structure used for Entity.
Definition id.h:336
Definition id.h:225
Definition query_common.h:197
QueryIdentity q
Query identity.
Definition query_common.h:205
Definition query_common.h:50
QueryHandle handle
Query id.
Definition query_common.h:185
Definition query_info.h:31
Definition robin_hood.h:418