3#include "gaia/ecs/query_adapter_typed.inl"
9 inline TypedQueryExecState build_typed_query_exec_state(
10 World& world,
const QueryInfo& queryInfo,
const TypedQueryArgMeta* pMetas, uint32_t argCount) {
11 TypedQueryExecState state{};
12 QueryImpl::DirectChunkArgEvalDesc directChunkDescs[MAX_ITEMS_IN_QUERY]{};
13 bool hasSparseArgs =
false;
14 state.argCount = argCount;
16 state.argIds[i] = pMetas[i].termId;
17 state.writeFlags[i] = pMetas[i].isWrite;
18 if (pMetas[i].isWrite) {
19 state.hasWriteArgs =
true;
20 if (state.firstWriteArg == MAX_ITEMS_IN_QUERY)
21 state.firstWriteArg = (uint32_t)i;
23 state.needsInheritedArgIds = state.needsInheritedArgIds || !pMetas[i].isEntity;
24 hasSparseArgs = hasSparseArgs || pMetas[i].usesSparseStorage;
25 directChunkDescs[i] = {pMetas[i].termId, pMetas[i].isEntity, pMetas[i].isPair, pMetas[i].usesSparseStorage};
28 state.canUseDirectChunkEval =
29 QueryImpl::can_use_direct_chunk_term_eval_descs(world, queryInfo, directChunkDescs, argCount);
30 state.canUseSparseChunkEval = hasSparseArgs && QueryImpl::can_use_sparse_chunk_term_eval_descs(
31 world, queryInfo, directChunkDescs, argCount);
32 if (state.needsInheritedArgIds)
33 state.hasInheritedTerms = queryInfo.has_potential_inherited_id_terms();
37#if GAIA_ECS_TEST_HOOKS
38 template <
typename Func>
39 inline QueryImpl::QueryPlan QueryImpl::test_typed_plan(Func func) {
40 auto& queryInfo =
fetch();
43 using InputArgs =
decltype(core::func_args(&Func::operator()));
44 #if GAIA_ASSERT_ENABLED
45 GAIA_ASSERT(typed_query_args_match_query(queryInfo, InputArgs{}));
47 auto& world = *
const_cast<World*
>(queryInfo.world());
48 TypedQueryArgMeta metas[MAX_ITEMS_IN_QUERY]{};
49 const auto argCount = init_typed_query_arg_metas(metas, world, InputArgs{});
50 const auto state = build_typed_query_exec_state(world, queryInfo, metas, argCount);
55 inline QueryImpl::QueryPlan QueryImpl::test_iter_plan(Constraints constraints) {
56 auto& queryInfo =
fetch();
62 inline void finish_typed_chunk_state(
63 World& world, Chunk* pChunk, uint16_t from, uint16_t to,
const TypedQueryExecState& state);
65 inline void finish_typed_iter_state(QueryImpl& query, Iter& it,
const TypedQueryExecState& state);
67 inline void noop_row_done(uint32_t) {}
69 template <
typename Func,
typename ViewsTuple,
typename... T>
70 inline void invoke_typed_query_row_erased(
71 void* pFunc, Iter& it, ViewsTuple& dataPointerTuple, uint32_t row,
bool directLocalIndex);
73 template <
typename OnRowDone,
typename... T>
74 inline void run_typed_chunk_views(
75 const QueryInfo* pQueryInfo, Iter& it,
void* pFunc,
bool directViews, OnRowDone&& onRowDone,
76 void (*invokeDirectRow)(
77 void*, Iter&, std::tuple<
decltype(std::declval<Iter&>().
template sview_auto<T>())...>&, uint32_t,
bool),
78 void (*invokeMappedRow)(
79 void*, Iter&, std::tuple<
decltype(std::declval<Iter&>().
template view_auto_any<T>())...>&, uint32_t,
81 core::func_type_list<T...>);
83 template <
typename Func,
typename... T>
84 inline void run_typed_chunk_direct_iter_fast_cb(QueryImpl&, Iter& it,
void* pFunc,
const TypedQueryExecState&);
86 template <
typename Func,
typename... T>
88 run_typed_chunk_direct_iter_cb(QueryImpl& query, Iter& it,
void* pFunc,
const TypedQueryExecState& state);
90 template <
typename Func,
typename... T>
91 inline void run_typed_chunk_mapped_iter_cb(
92 QueryImpl& query,
const QueryInfo& queryInfo, Iter& it,
void* pFunc,
const TypedQueryExecState& state);
94 template <
typename Func,
typename... T>
95 inline void run_typed_sparse_chunk_rows(
96 Chunk* pChunk, uint16_t from, uint16_t to, Func& func,
const TypedQueryExecState& state,
97 core::func_type_list<T...> types);
99 template <
typename Func,
typename... T>
100 GAIA_NODISCARD
inline auto typed_run_direct_fast_chunk_ptr(core::func_type_list<T...>) {
101 return &run_typed_chunk_direct_iter_fast_cb<Func, T...>;
104 template <
typename Func,
typename... T>
105 GAIA_NODISCARD
inline auto typed_run_direct_chunk_ptr(core::func_type_list<T...>) {
106 return &run_typed_chunk_direct_iter_cb<Func, T...>;
109 template <
typename Func,
typename... T>
110 GAIA_NODISCARD
inline auto typed_run_mapped_chunk_ptr(core::func_type_list<T...>) {
111 return &run_typed_chunk_mapped_iter_cb<Func, T...>;
114 template <
typename Func,
typename... T>
115 inline void run_typed_sparse_plan_cb(
116 QueryImpl& query, QueryInfo& queryInfo,
const QueryImpl::QueryPlan& plan,
void* pFunc,
117 const TypedQueryExecState& state) {
118 auto& func = *
static_cast<Func*
>(pFunc);
120 query.run_query_on_sparse_entities_typed(queryInfo, state, func, core::func_type_list<T...>{});
126 query.run_query_on_chunks_sparse_typed<
true>(queryInfo, plan, state, func, core::func_type_list<T...>{});
128 query.run_query_on_chunks_sparse_typed<
false>(queryInfo, plan, state, func, core::func_type_list<T...>{});
131 template <
typename Func,
typename... T>
132 GAIA_NODISCARD
inline auto typed_run_sparse_plan_ptr(core::func_type_list<T...>) {
133 using RunSparsePlan =
134 void (*)(QueryImpl&, QueryInfo&,
const QueryImpl::QueryPlan&,
void*,
const TypedQueryExecState&);
135 if constexpr (typed_query_args_use_sparse_storage_v<T...>)
136 return RunSparsePlan(&run_typed_sparse_plan_cb<Func, T...>);
138 return RunSparsePlan(
nullptr);
141 template <
typename Func,
typename... T>
142 inline void run_typed_chunk_direct_iter_fast_cb(QueryImpl&, Iter& it,
void* pFunc,
const TypedQueryExecState&) {
143 run_typed_chunk_views(
144 nullptr, it, pFunc,
true, noop_row_done,
145 &invoke_typed_query_row_erased<
146 Func, std::tuple<
decltype(std::declval<Iter&>().
template sview_auto<T>())...>, T...>,
147 &invoke_typed_query_row_erased<
148 Func, std::tuple<
decltype(std::declval<Iter&>().template view_auto_any<T>())...>, T...>,
149 core::func_type_list<T...>{});
152 template <
typename Func,
typename... T>
154 run_typed_chunk_direct_iter_cb(QueryImpl& query, Iter& it,
void* pFunc,
const TypedQueryExecState& state) {
155 auto& func = *
static_cast<Func*
>(pFunc);
156 run_typed_chunk_direct_finish(query, it, func, state, core::func_type_list<T...>{});
159 template <
typename Func,
typename... T>
160 inline void run_typed_chunk_mapped_iter_cb(
161 QueryImpl& query,
const QueryInfo& queryInfo, Iter& it,
void* pFunc,
const TypedQueryExecState& state) {
162 auto& func = *
static_cast<Func*
>(pFunc);
163 if constexpr (typed_query_args_use_sparse_storage_v<T...>) {
164 if (state.canUseSparseChunkEval) {
165 auto boundState = state;
166 auto& world = *
const_cast<World*
>(queryInfo.world());
167 bind_typed_sparse_stores(boundState, world, core::func_type_list<T...>{});
168 auto* pChunk =
const_cast<Chunk*
>(it.chunk());
169 run_typed_sparse_chunk_rows(
170 pChunk, it.row_begin(), it.row_end(), func, boundState, core::func_type_list<T...>{});
171 if (boundState.hasWriteArgs)
172 finish_typed_chunk_state(world, pChunk, it.row_begin(), it.row_end(), boundState);
176 run_typed_chunk_mapped_finish(query, queryInfo, it, func, state, core::func_type_list<T...>{});
179 template <
typename Func,
typename... T>
180 inline void each_iter_dispatch(
181 QueryImpl& query,
const QueryInfo& queryInfo, Iter& it, Func& func,
const TypedQueryExecState& state,
182 core::func_type_list<T...>) {
183 if (state.canUseDirectChunkEval) {
184 run_typed_chunk_views(
185 nullptr, it, &func,
true, noop_row_done,
186 &invoke_typed_query_row_erased<
187 Func, std::tuple<
decltype(std::declval<Iter&>().
template sview_auto<T>())...>, T...>,
188 &invoke_typed_query_row_erased<
189 Func, std::tuple<
decltype(std::declval<Iter&>().template view_auto_any<T>())...>, T...>,
190 core::func_type_list<T...>{});
191 finish_typed_chunk_state(*it.world(),
const_cast<Chunk*
>(it.chunk()), it.row_begin(), it.row_end(), state);
193 run_typed_chunk_unmapped(query, queryInfo, it, func, state, core::func_type_list<T...>{});
196 template <
typename Func,
typename... T>
197 inline void run_typed_chunk_direct_finish(
198 QueryImpl& query, Iter& it, Func& func,
const TypedQueryExecState& state, core::func_type_list<T...>) {
199 run_typed_chunk_views(
200 nullptr, it, &func,
true, noop_row_done,
201 &invoke_typed_query_row_erased<
202 Func, std::tuple<
decltype(std::declval<Iter&>().
template sview_auto<T>())...>, T...>,
203 &invoke_typed_query_row_erased<
204 Func, std::tuple<
decltype(std::declval<Iter&>().template view_auto_any<T>())...>, T...>,
205 core::func_type_list<T...>{});
206 finish_typed_iter_state(query, it, state);
209 template <
typename Func,
typename... T>
210 inline void run_typed_chunk_mapped_finish(
211 QueryImpl& query,
const QueryInfo& queryInfo, Iter& it, Func& func,
const TypedQueryExecState& state,
212 core::func_type_list<T...>) {
213 run_typed_chunk_views(
214 &queryInfo, it, &func,
false, noop_row_done,
215 &invoke_typed_query_row_erased<
216 Func, std::tuple<
decltype(std::declval<Iter&>().
template sview_auto<T>())...>, T...>,
217 &invoke_typed_query_row_erased<
218 Func, std::tuple<
decltype(std::declval<Iter&>().template view_auto_any<T>())...>, T...>,
219 core::func_type_list<T...>{});
220 finish_typed_iter_state(query, it, state);
223 template <
typename Func,
typename... T>
224 inline void run_typed_chunk_direct_walk_cb(
225 QueryImpl& query,
const QueryInfo&, Iter& it,
void* pFunc,
const TypedQueryExecState& state) {
226 run_typed_chunk_direct_finish(query, it, *
static_cast<Func*
>(pFunc), state, core::func_type_list<T...>{});
229 template <
typename Func,
typename... T>
230 inline void run_typed_chunk_mapped_walk_cb(
231 QueryImpl& query,
const QueryInfo& queryInfo, Iter& it,
void* pFunc,
const TypedQueryExecState& state) {
232 run_typed_chunk_mapped_finish(
233 query, queryInfo, it, *
static_cast<Func*
>(pFunc), state, core::func_type_list<T...>{});
236 template <
typename Func,
typename... T>
237 inline void each_walk_dispatch_direct(
238 QueryImpl& query, QueryInfo& queryInfo, std::span<const Entity> entities, Constraints constraints, Func& func,
239 const TypedQueryExecState& state, core::func_type_list<T...>) {
240 query.each_walk_inter(
241 queryInfo, entities, constraints, &func, state, &run_typed_chunk_direct_walk_cb<Func, T...>);
244 template <
typename Func,
typename... T>
245 inline void each_walk_dispatch_mapped(
246 QueryImpl& query, QueryInfo& queryInfo, std::span<const Entity> entities, Constraints constraints, Func& func,
247 const TypedQueryExecState& state, core::func_type_list<T...>) {
248 query.each_walk_inter(
249 queryInfo, entities, constraints, &func, state, &run_typed_chunk_mapped_walk_cb<Func, T...>);
252 template <
typename Func,
typename... T>
253 inline void run_typed_chunk_unmapped(
254 QueryImpl& query,
const QueryInfo& queryInfo, Iter& it, Func& func,
const TypedQueryExecState& state,
255 core::func_type_list<T...> types) {
256 auto& world = *
const_cast<World*
>(queryInfo.world());
257 auto* pChunk =
const_cast<Chunk*
>(it.chunk());
258 const bool hasEntityFilters = queryInfo.has_entity_filter_terms();
260 if (!hasEntityFilters) {
261 run_typed_chunk_views(
262 &queryInfo, it, &func,
false, noop_row_done,
263 &invoke_typed_query_row_erased<
264 Func, std::tuple<
decltype(std::declval<Iter&>().
template sview_auto<T>())...>, T...>,
265 &invoke_typed_query_row_erased<
266 Func, std::tuple<
decltype(std::declval<Iter&>().template view_auto_any<T>())...>, T...>,
268 finish_typed_chunk_state(world, pChunk, it.row_begin(), it.row_end(), state);
270 run_typed_chunk_views(
271 &queryInfo, it, &func,
false,
273 finish_typed_chunk_state(
274 world, pChunk, (uint16_t)(it.row_begin() + row), (uint16_t)(it.row_begin() + row + 1), state);
276 &invoke_typed_query_row_erased<
277 Func, std::tuple<decltype(std::declval<Iter&>().template sview_auto<T>())...>, T...>,
278 &invoke_typed_query_row_erased<
279 Func, std::tuple<
decltype(std::declval<Iter&>().template view_auto_any<T>())...>, T...>,
283 it.clear_touched_writes();
286 template <
typename Func,
typename... T>
287 inline void run_typed_cached_seed_runs_basic(
288 QueryImpl& query, World& world, Constraints constraints, Func& func, std::span<const BfsChunkRun> runs,
289 const TypedQueryExecState& state) {
291 it.init_query_state(&world, constraints,
false);
292 const Archetype* pLastArchetype =
nullptr;
293 for (
const auto& run: runs) {
294 if (run.pArchetype != pLastArchetype) {
295 it.set_archetype(run.pArchetype);
296 pLastArchetype = run.pArchetype;
299 it.set_chunk(run.pChunk, run.from, run.to);
301 run_typed_chunk_direct_finish(query, it, func, state, core::func_type_list<T...>{});
305 template <
typename Func,
typename... T>
306 inline void run_typed_cached_seed_runs_entity_init(
307 QueryImpl& query, QueryInfo& queryInfo, World& world, Constraints constraints, Func& func,
308 std::span<const BfsChunkRun> runs,
const TypedQueryExecState& state) {
310 it.init_query_state(&world, constraints,
false);
311 const Archetype* pLastArchetype =
nullptr;
312 uint8_t indices[ChunkHeader::MAX_COMPONENTS];
313 Entity termIds[ChunkHeader::MAX_COMPONENTS];
314 for (
const auto& run: runs) {
315 const auto& ec = ::gaia::ecs::fetch(world, run.pChunk->entity_view()[run.from]);
316 query.init_direct_entity_iter(queryInfo, world, ec, it, indices, termIds, pLastArchetype);
317 it.set_chunk(run.pChunk, run.from, run.to);
319 run_typed_chunk_direct_finish(query, it, func, state, core::func_type_list<T...>{});
323 template <
typename Func,
typename... T>
324 inline void run_typed_cached_seed_runs(
325 QueryImpl& query, QueryInfo& queryInfo, World& world, Constraints constraints, Func& func,
326 std::span<const BfsChunkRun> runs,
bool canUseBasicInit,
const TypedQueryExecState& state) {
328 run_typed_cached_seed_runs_basic<Func, T...>(query, world, constraints, func, runs, state);
330 run_typed_cached_seed_runs_entity_init<Func, T...>(query, queryInfo, world, constraints, func, runs, state);
333 inline void QueryImpl::each_walk_inter(
334 QueryInfo& queryInfo, std::span<const Entity> entities, Constraints constraints,
void* pFunc,
335 const TypedQueryExecState& state,
336 void (*runChunk)(QueryImpl&,
const QueryInfo&, Iter&,
void*,
const TypedQueryExecState&)) {
337 auto& world = *queryInfo.world();
338 auto& walkData = ensure_each_walk_data();
340 it.init_query_state(&world, constraints,
false);
341 if (!walkData.cachedRuns.empty()) {
342 const auto& runs = walkData.cachedRuns;
343 const Archetype* pLastArchetype =
nullptr;
344 for (
const auto& run: runs) {
345 if (run.pArchetype != pLastArchetype) {
346 it.set_archetype(run.pArchetype);
347 pLastArchetype = run.pArchetype;
350 it.set_chunk(run.pChunk, run.from, run.to);
352 runChunk(*
this, queryInfo, it, pFunc, state);
357 const Archetype* pLastArchetype =
nullptr;
358 uint8_t indices[ChunkHeader::MAX_COMPONENTS];
359 Entity termIds[ChunkHeader::MAX_COMPONENTS];
360 for (
const auto entity: entities) {
361 const auto& ec = ::gaia::ecs::fetch(world, entity);
362 init_direct_entity_iter(queryInfo, world, ec, it, indices, termIds, pLastArchetype);
363 runChunk(*
this, queryInfo, it, pFunc, state);
367 inline void finish_typed_iter_state(QueryImpl& query, Iter& it,
const TypedQueryExecState& state) {
368 query.finish_typed_iter_writes_runtime(it, state.argIds, state.writeFlags, state.argCount, state.firstWriteArg);
369 it.clear_touched_writes();
372 inline void finish_typed_chunk_state(
373 World& world, Chunk* pChunk, uint16_t from, uint16_t to,
const TypedQueryExecState& state) {
374 QueryImpl::finish_typed_chunk_writes_runtime(
375 world, pChunk, from, to, state.argIds, state.writeFlags, state.argCount, state.firstWriteArg,
379 template <
typename InvokeRow,
typename OnRowDone>
380 inline void run_typed_query_rows_runtime(
381 const QueryInfo* pQueryInfo, Iter& it, InvokeRow&& invokeRow, OnRowDone&& onRowDone) {
382 const auto cnt = it.size();
383 const bool hasEntityFilters = pQueryInfo !=
nullptr && pQueryInfo->has_entity_filter_terms();
385 if (!hasEntityFilters) {
387 invokeRow((uint32_t)i);
388 onRowDone((uint32_t)i);
393 const auto entities = it.template view<Entity>();
395 if (!QueryImpl::match_entity_filters(*pQueryInfo->world(), entities[i], *pQueryInfo))
397 invokeRow((uint32_t)i);
398 onRowDone((uint32_t)i);
402 template <
typename Func,
typename ViewsTuple,
typename... T>
403 inline void invoke_typed_query_row(
404 Iter& it, Func& func, ViewsTuple& dataPointerTuple, uint32_t row,
bool directLocalIndex,
405 core::func_type_list<T...>) {
406 if constexpr (
sizeof...(T) > 0) {
407 if (directLocalIndex) {
409 [&](
auto&... views) {
415 [&](
auto&... views) {
416 func(views[it.template acc_index<T>(row)]...);
424 template <
typename Func,
typename ViewsTuple,
typename... T>
425 inline void invoke_typed_query_row_erased(
426 void* pFunc, Iter& it, ViewsTuple& dataPointerTuple, uint32_t row,
bool directLocalIndex) {
427 auto& func = *
static_cast<Func*
>(pFunc);
428 invoke_typed_query_row(it, func, dataPointerTuple, row, directLocalIndex, core::func_type_list<T...>{});
431 template <
typename OnRowDone,
typename ViewsTuple>
432 inline void run_typed_tuple_rows(
433 const QueryInfo* pQueryInfo, Iter& it,
void* pFunc, ViewsTuple& dataPointerTuple,
bool directLocalIndex,
434 void (*invokeRow)(
void*, Iter&, ViewsTuple&, uint32_t,
bool), OnRowDone&& onRowDone) {
435 run_typed_query_rows_runtime(
438 invokeRow(pFunc, it, dataPointerTuple, row, directLocalIndex);
440 GAIA_FWD(onRowDone));
443 template <
typename OnRowDone,
typename... T>
444 inline void run_typed_chunk_views(
445 const QueryInfo* pQueryInfo, Iter& it,
void* pFunc,
bool directViews, OnRowDone&& onRowDone,
446 void (*invokeDirectRow)(
447 void*, Iter&, std::tuple<
decltype(std::declval<Iter&>().
template sview_auto<T>())...>&, uint32_t,
bool),
448 void (*invokeMappedRow)(
449 void*, Iter&, std::tuple<
decltype(std::declval<Iter&>().
template view_auto_any<T>())...>&, uint32_t,
451 core::func_type_list<T...>) {
452 if constexpr (
sizeof...(T) > 0) {
454 auto dataPointerTuple = std::make_tuple(it.template sview_auto<T>()...);
455 run_typed_tuple_rows(pQueryInfo, it, pFunc, dataPointerTuple,
true, invokeDirectRow, GAIA_FWD(onRowDone));
457 auto dataPointerTuple = std::make_tuple(it.template view_auto_any<T>()...);
458 run_typed_tuple_rows(pQueryInfo, it, pFunc, dataPointerTuple,
false, invokeMappedRow, GAIA_FWD(onRowDone));
461 auto dataPointerTuple = std::tuple<>{};
462 run_typed_tuple_rows(
463 pQueryInfo, it, pFunc, dataPointerTuple, directViews, directViews ? invokeDirectRow : invokeMappedRow,
464 GAIA_FWD(onRowDone));
468 template <
typename ContainerOut>
469 inline void typed_arr_push(World& world, Entity entity, ContainerOut& outArray) {
470 using ContainerItemType =
typename ContainerOut::value_type;
471 if constexpr (std::is_same_v<ContainerItemType, Entity>)
472 outArray.push_back(entity);
474 auto tmp = world_direct_entity_arg<ContainerItemType>(world, entity);
475 outArray.push_back(tmp);
479 template <
bool UseFilters,
typename ContainerOut>
480 inline void run_typed_arr_rows(
481 QueryImpl& query,
const QueryInfo& queryInfo, Iter& it, ContainerOut& outArray, uint32_t changedWorldVersion,
482 uint32_t archetypeIdx,
const Archetype* pArchetype, Chunk* pChunk, uint16_t from, uint16_t to,
483 bool needsBarrierCache,
bool canUseDirectChunkEval) {
484 using ContainerItemType =
typename ContainerOut::value_type;
485 const bool barrierPasses = !needsBarrierCache || queryInfo.barrier_passes(archetypeIdx);
486 if GAIA_UNLIKELY (!query.can_process_archetype_inter(queryInfo, *pArchetype, it.constraints(), barrierPasses))
488 if GAIA_UNLIKELY (from == to)
491 GAIA_PROF_SCOPE(query::arr);
493 it.set_archetype(pArchetype);
494 it.set_chunk(pChunk, from, to);
497 const auto cnt = it.size();
501 if constexpr (UseFilters) {
506 const bool hasEntityFilters = queryInfo.has_entity_filter_terms();
507 if (canUseDirectChunkEval) {
508 const auto dataView = it.template sview_auto<ContainerItemType>();
510 auto tmp = dataView[i];
511 outArray.push_back(tmp);
516 const auto dataView = it.template view<ContainerItemType>();
517 if (!hasEntityFilters) {
519 const auto idx = it.template acc_index<ContainerItemType>(i);
520 auto tmp = dataView[idx];
521 outArray.push_back(tmp);
526 const auto entities = it.template view<Entity>();
528 if (!QueryImpl::match_entity_filters(*queryInfo.world(), entities[i], queryInfo))
530 const auto idx = it.template acc_index<ContainerItemType>(i);
531 auto tmp = dataView[idx];
532 outArray.push_back(tmp);
536 inline void QueryImpl::finish_typed_chunk_writes_runtime(
537 World& world, Chunk* pChunk, uint16_t from, uint16_t to,
const Entity* pArgIds,
const bool* pWriteFlags,
538 uint32_t argCnt, uint32_t firstWriteArg,
void*
const* pSparseStores) {
539 if (firstWriteArg >= argCnt || from >= to)
542 Entity seenTerms[ChunkHeader::MAX_COMPONENTS]{};
543 uint32_t seenCnt = 0;
544 const auto entities = pChunk->entity_view();
545 const auto finish_term = [&](Entity term,
bool sparseStoreBound) {
547 if (seenTerms[i] == term)
551 seenTerms[seenCnt++] = term;
552 if (sparseStoreBound) {
553 for (uint16_t row = from; row < to; ++row)
554 world_notify_on_set_entity(world, term, entities[row]);
557 if (!world_component_uses_sparse_storage(world, term)) {
558 const auto compIdx = core::get_index(pChunk->ids_view(), term);
559 if (compIdx != BadIndex) {
560 pChunk->finish_write(compIdx, from, to);
565 for (uint16_t row = from; row < to; ++row)
566 world_finish_write(world, term, entities[row]);
569 for (uint32_t i = firstWriteArg; i < argCnt; ++i) {
572 const auto term = pArgIds[i];
573 if (term != EntityBad)
574 finish_term(term, pSparseStores !=
nullptr && pSparseStores[i] !=
nullptr);
578 template <
typename... T>
579 inline void QueryImpl::finish_typed_chunk_writes(World& world, Chunk* pChunk, uint16_t from, uint16_t to) {
580 TypedQueryArgMeta metas[MAX_ITEMS_IN_QUERY]{};
581 const auto argCount = init_typed_query_arg_metas(metas, world, core::func_type_list<T...>{});
582 Entity argIds[MAX_ITEMS_IN_QUERY]{};
583 bool writeFlags[MAX_ITEMS_IN_QUERY]{};
584 uint32_t firstWriteArg = argCount;
586 argIds[i] = metas[i].termId;
587 writeFlags[i] = metas[i].isWrite;
588 if (metas[i].isWrite && firstWriteArg == argCount)
589 firstWriteArg = (uint32_t)i;
591 finish_typed_chunk_writes_runtime(world, pChunk, from, to, argIds, writeFlags, argCount, firstWriteArg);
594 inline void QueryImpl::finish_typed_iter_writes_runtime(
595 Iter& it,
const Entity* pArgIds,
const bool* pWriteFlags, uint32_t argCnt, uint32_t firstWriteArg) {
596 if (firstWriteArg >= argCnt)
599 auto* pChunk =
const_cast<Chunk*
>(it.chunk());
600 if (pChunk ==
nullptr || it.row_begin() >= it.row_end())
603 auto& world = *it.world();
604 auto entities = it.entity_rows();
605 Entity seenTerms[ChunkHeader::MAX_COMPONENTS]{};
606 uint32_t seenCnt = 0;
607 const auto finish_term = [&](Entity term, uint32_t fieldIdx) {
609 if (seenTerms[i] == term)
613 seenTerms[seenCnt++] = term;
614 const bool usesSparseStorage = world_component_uses_sparse_storage(world, term);
615 auto compIdx = uint8_t(0xFF);
616 if (it.comp_indices() !=
nullptr && fieldIdx < ChunkHeader::MAX_COMPONENTS)
617 compIdx = it.comp_indices()[fieldIdx];
618 else if (!usesSparseStorage)
619 compIdx = (uint8_t)pChunk->comp_idx(term);
621 if (compIdx != 0xFF && !usesSparseStorage) {
622 pChunk->finish_write(compIdx, it.row_begin(), it.row_end());
626 GAIA_FOR(entities.size()) {
627 world_finish_write(world, term, entities[i]);
631 for (uint32_t i = firstWriteArg; i < argCnt; ++i) {
635 Entity term = pArgIds[i];
636 if (it.term_ids() !=
nullptr && i < ChunkHeader::MAX_COMPONENTS && it.term_ids()[i] != EntityBad)
637 term = it.term_ids()[i];
638 if (term == EntityBad)
641 finish_term(term, i);
655 template <
typename T,
typename View>
656 GAIA_NODISCARD
inline decltype(
auto) typed_direct_chunk_arg_at(View& view, uint32_t row, uint16_t from) {
657 using U =
typename actual_type_t<T>::Type;
658 if constexpr (mem::is_soa_layout_v<U>)
659 return view[from + row];
674 template <
typename Func,
typename ViewsTuple,
typename... T,
size_t... I>
675 inline void invoke_typed_direct_chunk_row(
676 Func& func, ViewsTuple& views, uint32_t row, uint16_t from, core::func_type_list<T...>,
677 std::index_sequence<I...>) {
678 func(typed_direct_chunk_arg_at<T>(std::get<I>(views), row, from)...);
683 struct TypedDirectChunkRun {
685 Chunk* pChunk =
nullptr;
687 const uint8_t* pCompIndices =
nullptr;
689 const void*
const* pData =
nullptr;
704 template <
typename Func,
typename T>
705 inline void run_typed_direct_chunk_row_arg(
706 const TypedDirectChunkRun& chunkRun, Func& func,
const TypedQueryExecState& state, core::func_type_list<T>) {
707 Chunk* pChunk = chunkRun.pChunk;
708 const auto from = chunkRun.from;
709 const auto cnt = (uint32_t)(chunkRun.to - from);
710 using U =
typename actual_type_t<T>::Type;
711 if constexpr (std::is_same_v<U, Entity>) {
712 const auto data = pChunk->entity_view();
714 func(data[from + i]);
715 }
else if constexpr (!mem::is_soa_layout_v<U>) {
716 const auto* pData = chunkRun.pData !=
nullptr ? chunkRun.pData[0] :
nullptr;
717 if (pData ==
nullptr)
718 pData = pChunk->comp_rec_view()[pChunk->comp_idx(state.argIds[0])].pData;
719 auto* data = (U*)pData + from;
723 auto view = pChunk->template sview_auto<T>(from, chunkRun.to);
725 func(view[from + i]);
729 template <
typename T>
730 GAIA_NODISCARD
constexpr bool typed_direct_chunk_data_cacheable_arg() {
731 using U =
typename actual_type_t<T>::Type;
732 return !std::is_same_v<U, Entity> && !mem::is_soa_layout_v<U>;
735 template <
typename... T>
736 GAIA_NODISCARD
constexpr bool typed_direct_chunk_data_cacheable(core::func_type_list<T...>) {
737 if constexpr (
sizeof...(T) == 0)
740 return (typed_direct_chunk_data_cacheable_arg<T>() && ...);
743 template <
typename T>
744 GAIA_NODISCARD
inline decltype(
auto)
745 typed_direct_chunk_cached_arg(
const void*
const* pData, uint32_t argIdx, uint32_t row) {
746 using U =
typename actual_type_t<T>::Type;
747 auto* data = (U*)pData[argIdx];
751 template <
typename Func,
typename... T,
size_t... I>
752 inline void invoke_typed_direct_chunk_cached_row(
753 Func& func,
const void*
const* pData, uint32_t row, core::func_type_list<T...>, std::index_sequence<I...>) {
754 func(typed_direct_chunk_cached_arg<T>(pData, (uint32_t)I, row)...);
757 template <u
int32_t ArgCount>
758 GAIA_NODISCARD
inline bool typed_direct_chunk_cached_data_valid(
const void*
const* pData) {
759 if (pData ==
nullptr)
762 if (pData[i] ==
nullptr)
778 template <
typename Func,
typename... T>
779 inline void run_typed_direct_chunk_rows(
780 const TypedDirectChunkRun& chunkRun, Func& func,
const TypedQueryExecState& state,
781 core::func_type_list<T...>) {
782 Chunk* pChunk = chunkRun.pChunk;
783 const auto from = chunkRun.from;
784 const auto to = chunkRun.to;
785 if constexpr (
sizeof...(T) == 1) {
786 run_typed_direct_chunk_row_arg(chunkRun, func, state, core::func_type_list<T...>{});
787 }
else if constexpr (
sizeof...(T) > 0) {
788 const auto cnt = (uint32_t)(to - from);
789 if constexpr (typed_direct_chunk_data_cacheable(core::func_type_list<T...>{})) {
790 if (typed_direct_chunk_cached_data_valid<(uint32_t)
sizeof...(T)>(chunkRun.pData)) {
792 invoke_typed_direct_chunk_cached_row(
793 func, chunkRun.pData, (uint32_t)i, core::func_type_list<T...>{}, std::index_sequence_for<T...>{});
798 auto views = std::make_tuple(pChunk->template sview_auto<T>(from, to)...);
800 invoke_typed_direct_chunk_row(
801 func, views, (uint32_t)i, from, core::func_type_list<T...>{}, std::index_sequence_for<T...>{});
803 const auto cnt = (uint32_t)(to - from);
809 template <
typename T>
810 struct TypedSparseQueryView {
811 using U =
typename actual_type_t<T>::Type;
812 const Entity* pEntities =
nullptr;
813 void* pStore =
nullptr;
815 GAIA_NODISCARD
decltype(
auto)
operator[](
size_t idx)
const {
816 if constexpr (core::is_mut_v<typename actual_type_t<T>::TypeOriginal>)
817 return world_typed_sparse_store_mut<U>(pStore, pEntities[idx]);
819 return world_typed_sparse_store_get<U>(pStore, pEntities[idx]);
823 template <
typename T>
824 GAIA_NODISCARD
inline auto typed_sparse_chunk_view(
825 Chunk* pChunk, uint16_t from, uint16_t to,
const TypedQueryExecState& state, uint32_t argIdx) {
826 using U =
typename actual_type_t<T>::Type;
827 if constexpr (auto_storage_policy_v<U> == DataStorageType::Sparse)
828 return TypedSparseQueryView<T>{pChunk->entity_view().data() + from, state.sparseStores[argIdx]};
830 return pChunk->template sview_auto<T>(from, to);
833 template <
typename T,
typename View>
834 GAIA_NODISCARD
inline decltype(
auto) typed_sparse_chunk_arg_at(View& view, uint32_t row, uint16_t from) {
835 using U =
typename actual_type_t<T>::Type;
836 if constexpr (auto_storage_policy_v<U> == DataStorageType::Sparse)
839 return typed_direct_chunk_arg_at<T>(view, row, from);
842 template <
typename Func,
typename... T,
size_t... I>
843 inline void run_typed_sparse_chunk_rows_impl(
844 Chunk* pChunk, uint16_t from, uint16_t to, Func& func,
const TypedQueryExecState& state,
845 core::func_type_list<T...>, std::index_sequence<I...>) {
846 auto views = std::make_tuple(typed_sparse_chunk_view<T>(pChunk, from, to, state, (uint32_t)I)...);
847 const auto cnt = (uint32_t)(to - from);
849 func(typed_sparse_chunk_arg_at<T>(std::get<I>(views), (uint32_t)i, from)...);
852 template <
typename Func,
typename... T>
853 inline void run_typed_sparse_chunk_rows(
854 Chunk* pChunk, uint16_t from, uint16_t to, Func& func,
const TypedQueryExecState& state,
855 core::func_type_list<T...> types) {
856 run_typed_sparse_chunk_rows_impl(pChunk, from, to, func, state, types, std::index_sequence_for<T...>{});
859 template <
typename T>
860 GAIA_NODISCARD
inline decltype(
auto)
861 typed_sparse_entity_arg(World& world, Entity entity,
const TypedQueryExecState& state, uint32_t argIdx) {
862 using U =
typename actual_type_t<T>::Type;
863 if constexpr (std::is_same_v<U, Entity>)
865 else if constexpr (auto_storage_policy_v<U> == DataStorageType::Sparse) {
866 if (world_typed_sparse_store_has<U>(state.sparseStores[argIdx], entity)) {
867 if constexpr (core::is_mut_v<typename actual_type_t<T>::TypeOriginal>)
868 return world_typed_sparse_store_mut<U>(state.sparseStores[argIdx], entity);
870 return world_typed_sparse_store_get<U>(state.sparseStores[argIdx], entity);
873 if constexpr (core::is_mut_v<typename actual_type_t<T>::TypeOriginal>)
874 return world_query_entity_arg_by_id_raw<T>(world, entity, state.argIds[argIdx]);
876 return world_query_entity_arg_by_id<T>(world, entity, state.argIds[argIdx]);
879 template <
typename Func,
typename... T,
size_t... I>
880 inline void invoke_typed_sparse_entity(
881 World& world, Entity entity,
const TypedQueryExecState& state, Func& func, core::func_type_list<T...>,
882 std::index_sequence<I...>) {
883 func(typed_sparse_entity_arg<T>(world, entity, state, (uint32_t)I)...);
890 inline QueryImpl::QueryPlan
895 const bool hasFilters = queryInfo.has_filters();
896 const bool hasSortedPayload = queryInfo.has_sorted_payload();
901 if (queryInfo.has_entity_filter_terms())
903 if (queryInfo.has_inherited_data_payload())
905 if (queryInfo.has_grouped_payload())
907 if (hasSortedPayload || hasDepthOrderBarrier)
909 if (hasDepthOrderBarrier && !depthOrderBarrierPrunes)
911 if (depthOrderBarrierPrunes)
914 const bool canDirectEntitySeed = !hasFilters && can_use_direct_entity_seed_eval(queryInfo);
915 const bool canDirectChunks = !hasSortedPayload && (!hasDepthOrderBarrier || !depthOrderBarrierPrunes);
917 auto setDenseRange = [&]() ->
bool {
918 const auto cacheRange = selected_query_cache_range(queryInfo);
919 plan.idxFrom = cacheRange.idxFrom;
920 plan.idxTo = cacheRange.idxTo;
921 if (cacheRange.hasSelectedGroup) {
924 if (!cacheRange.valid)
927 return plan.idxFrom < plan.idxTo;
930 if ((state.canUseDirectChunkEval || state.canUseSparseChunkEval) && !canDirectEntitySeed && canDirectChunks) {
931 if (!setDenseRange()) {
942 if (canDirectEntitySeed) {
947 if (!setDenseRange()) {
954 if (hasSortedPayload) {
960 if (hasDepthOrderBarrier && depthOrderBarrierPrunes) {
972 if (hasDepthOrderBarrier) {
977 if (!canDirectChunks) {
982 if (!state.canUseDirectChunkEval) {
990 template <
bool HasFilters,
typename Func,
typename... T>
991 inline void QueryImpl::run_query_on_chunks_sparse_typed(
992 QueryInfo& queryInfo,
const QueryPlan& plan,
const TypedQueryExecState& state, Func& func,
993 core::func_type_list<T...> types) {
995 auto& world = *queryInfo.world();
996 auto boundState = state;
997 bind_typed_sparse_stores(boundState, world, types);
998 const auto cacheView = queryInfo.cache_archetype_view();
999 if (plan.idxFrom >= plan.idxTo)
1002 if (boundState.hasWriteArgs)
1003 ::gaia::ecs::update_version(*m_worldVersion);
1005 const bool canSkipProcessCheck =
1007 lock(*m_storage.world());
1008 for (uint32_t i = plan.idxFrom; i < plan.idxTo; ++i) {
1009 const auto* pArchetype = cacheView[i];
1010 if (canSkipProcessCheck) {
1011 if GAIA_UNLIKELY (pArchetype->is_req_del())
1016 std::span<const uint8_t> indicesView;
1017 if constexpr (HasFilters)
1018 indicesView = queryInfo.indices_mapping_view(i);
1020 for (auto* pChunk: pArchetype->chunks()) {
1023 if GAIA_UNLIKELY (from == to)
1025 if constexpr (HasFilters) {
1026 if GAIA_UNLIKELY (!
match_filters(*pChunk, queryInfo, m_changedWorldVersion, indicesView))
1030 GAIA_PROF_SCOPE(query_func);
1031 run_typed_sparse_chunk_rows(pChunk, from, to, func, boundState, types);
1032 if (boundState.hasWriteArgs)
1033 finish_typed_chunk_state(world, pChunk, from, to, boundState);
1037 unlock(*m_storage.world());
1038 commit_cmd_buffer_st(*m_storage.world());
1039 commit_cmd_buffer_mt(*m_storage.world());
1040 m_changedWorldVersion = *m_worldVersion;
1043 template <
typename Func,
typename... T>
1044 inline void QueryImpl::run_query_on_sparse_entities_typed(
1045 QueryInfo& queryInfo,
const TypedQueryExecState& state, Func& func, core::func_type_list<T...> types) {
1046 auto& world = *queryInfo.world();
1047 auto boundState = state;
1048 bind_typed_sparse_stores(boundState, world, types);
1049 if (boundState.hasWriteArgs)
1050 ::gaia::ecs::update_version(*m_worldVersion);
1052 auto execEntity = [&](Entity entity) {
1053 invoke_typed_sparse_entity(world, entity, boundState, func, types, std::index_sequence_for<T...>{});
1054 if (boundState.hasWriteArgs)
1055 finish_typed_query_args_by_id(world, entity, boundState);
1058 if (boundState.hasWriteArgs) {
1059 auto& scratch = direct_query_scratch();
1060 const auto seedInfo = build_direct_entity_seed(world, queryInfo, scratch.entities);
1061 for (
const auto entity: scratch.entities) {
1062 if (!match_direct_entity_constraints(world, queryInfo, entity, Constraints::EnabledOnly) ||
1063 !match_direct_entity_terms(world, entity, queryInfo, seedInfo))
1068 const auto seedPlan = direct_entity_seed_plan(world, queryInfo);
1069 if (seedPlan.preferOrSeed) {
1070 for_each_direct_or_union(world, queryInfo, Constraints::EnabledOnly, [&](Entity entity) {
1075 (void)for_each_direct_all_seed(world, queryInfo, seedPlan, Constraints::EnabledOnly, [&](Entity entity) {
1082 m_changedWorldVersion = *m_worldVersion;
1095 template <
bool HasFilters,
typename Func,
typename... T>
1096 inline void QueryImpl::run_query_on_chunks_direct_typed(
1097 QueryInfo& queryInfo,
const QueryPlan& plan,
const TypedQueryExecState& state, Func& func,
1098 core::func_type_list<T...> types) {
1099 auto& world = *queryInfo.world();
1100 if constexpr (HasFilters) {
1107 auto cacheView = queryInfo.cache_archetype_view();
1108 if (plan.idxFrom >= plan.idxTo)
1111 if (state.hasWriteArgs)
1112 ::gaia::ecs::update_version(*m_worldVersion);
1114 const bool canSkipProcessCheck =
1116 lock(*m_storage.world());
1117 if constexpr (!HasFilters) {
1118 if (!state.hasWriteArgs && canSkipProcessCheck && plan.idxTo - plan.idxFrom >= 1024) {
1119 const auto terms = queryInfo.ctx().data.terms_view();
1120 uint32_t dataFields[MAX_ITEMS_IN_QUERY]{};
1121 uint32_t dataFieldCount = 0;
1122 if constexpr (typed_direct_chunk_data_cacheable(types)) {
1123 dataFieldCount = state.argCount;
1124 GAIA_FOR(state.argCount) {
1125 const auto argId = state.argIds[i];
1126 uint32_t termIdx = UINT32_MAX;
1127 for (uint32_t j = 0; j < terms.size(); ++j) {
1128 if (terms[j].
id == argId) {
1129 termIdx = (uint32_t)j;
1133 if (termIdx == UINT32_MAX) {
1137 dataFields[i] = termIdx;
1140 const auto chunkView = queryInfo.direct_chunk_view(plan.idxFrom, plan.idxTo, dataFields, dataFieldCount);
1141 const auto dataView = queryInfo.direct_chunk_data_view();
1142 uint32_t chunkIdx = 0;
1143 for (
const auto& entry: chunkView) {
1144 if constexpr (
sizeof...(T) > 1 && typed_direct_chunk_data_cacheable(types)) {
1147 constexpr uint32_t PrefetchDistance = 8;
1148 const auto prefetchIdx = chunkIdx + PrefetchDistance;
1149 if (prefetchIdx < chunkView.size()) {
1150 const auto& prefetchEntry = chunkView[prefetchIdx];
1151 if (prefetchEntry.dataOffset != UINT32_MAX) {
1152 const auto* pPrefetchData = &dataView[prefetchEntry.dataOffset];
1153 GAIA_FOR_(
sizeof...(T), dataIdx) {
1154 if (pPrefetchData[dataIdx] !=
nullptr)
1155 gaia::prefetch(pPrefetchData[dataIdx], PrefetchHint::PREFETCH_HINT_T1);
1161 const void* pSingleData[1]{};
1162 const void*
const* pData =
nullptr;
1163 if (entry.pData !=
nullptr) {
1167 pSingleData[0] = entry.pData;
1168 pData = pSingleData;
1169 }
else if (entry.dataOffset != UINT32_MAX)
1170 pData = &dataView[entry.dataOffset];
1171 const TypedDirectChunkRun chunkRun{entry.pChunk, entry.pCompIndices, pData, entry.rowFrom, entry.rowTo};
1173 GAIA_PROF_SCOPE(query_func);
1174 run_typed_direct_chunk_rows(chunkRun, func, state, types);
1177 unlock(*m_storage.world());
1178 commit_cmd_buffer_st(*m_storage.world());
1179 commit_cmd_buffer_mt(*m_storage.world());
1180 m_changedWorldVersion = *m_worldVersion;
1185 for (uint32_t i = plan.idxFrom; i < plan.idxTo; ++i) {
1186 const auto* pArchetype = cacheView[i];
1187 if (canSkipProcessCheck) {
1188 if GAIA_UNLIKELY (pArchetype->is_req_del())
1193 std::span<const uint8_t> indicesView;
1194 if constexpr (HasFilters)
1195 indicesView = queryInfo.indices_mapping_view(i);
1197 const auto& chunks = pArchetype->chunks();
1198 for (auto* pChunk: chunks) {
1201 if GAIA_UNLIKELY (from == to)
1204 if constexpr (HasFilters) {
1205 if GAIA_UNLIKELY (!
match_filters(*pChunk, queryInfo, m_changedWorldVersion, indicesView))
1209 GAIA_PROF_SCOPE(query_func);
1210 run_typed_direct_chunk_rows({pChunk,
nullptr,
nullptr, from, to}, func, state, types);
1211 if (state.hasWriteArgs)
1212 finish_typed_chunk_state(world, pChunk, from, to, state);
1216 unlock(*m_storage.world());
1217 commit_cmd_buffer_st(*m_storage.world());
1218 commit_cmd_buffer_mt(*m_storage.world());
1219 m_changedWorldVersion = *m_worldVersion;
1222 inline void QueryImpl::run_query_on_chunks_direct_iter(
1223 QueryInfo& queryInfo,
const QueryPlan& plan,
const TypedQueryExecState& state,
void* pFunc,
1224 void (*runChunk)(QueryImpl&, Iter& it,
void*,
const TypedQueryExecState&)) {
1225 auto& world = *queryInfo.world();
1227 GAIA_ASSERT(!queryInfo.has_filters());
1228 auto cacheView = queryInfo.cache_archetype_view();
1229 if (plan.idxFrom >= plan.idxTo)
1232 if (state.hasWriteArgs)
1233 ::gaia::ecs::update_version(*m_worldVersion);
1235 lock(*m_storage.world());
1237 it.init_query_state(queryInfo.world(), Constraints::EnabledOnly,
false);
1238 const Archetype* pLastArchetype =
nullptr;
1239 for (uint32_t i = plan.idxFrom; i < plan.idxTo; ++i) {
1240 const auto* pArchetype = cacheView[i];
1244 const auto& chunks = pArchetype->chunks();
1245 for (
auto* pChunk: chunks) {
1248 if GAIA_UNLIKELY (from == to)
1251 GAIA_PROF_SCOPE(query_func);
1252 if (pArchetype != pLastArchetype) {
1253 it.set_archetype(pArchetype);
1254 pLastArchetype = pArchetype;
1256 it.set_chunk(pChunk, from, to);
1259 runChunk(*
this, it, pFunc, state);
1260 finish_typed_chunk_state(world, pChunk, from, to, state);
1264 unlock(*m_storage.world());
1265 commit_cmd_buffer_st(*m_storage.world());
1266 commit_cmd_buffer_mt(*m_storage.world());
1267 m_changedWorldVersion = *m_worldVersion;
1270 inline void QueryImpl::run_query_on_chunks_direct(
1271 QueryInfo& queryInfo,
const QueryPlan& plan,
const TypedQueryExecState& state,
void* pFunc,
1272 void (*runChunk)(QueryImpl&, Iter& it,
void*,
const TypedQueryExecState&)) {
1273 auto& world = *queryInfo.world();
1274 auto cacheView = queryInfo.cache_archetype_view();
1275 if (plan.idxFrom >= plan.idxTo)
1278 if (state.hasWriteArgs)
1279 ::gaia::ecs::update_version(*m_worldVersion);
1284 lock(*m_storage.world());
1286 it.init_query_state(queryInfo.world(), Constraints::EnabledOnly,
false);
1287 const Archetype* pLastArchetype =
nullptr;
1289 for (uint32_t i = plan.idxFrom; i < plan.idxTo; ++i) {
1290 const auto* pArchetype = cacheView[i];
1294 std::span<const uint8_t> indicesView;
1296 indicesView = queryInfo.indices_mapping_view(i);
1298 const auto& chunks = pArchetype->chunks();
1299 for (
auto* pChunk: chunks) {
1302 if GAIA_UNLIKELY (from == to)
1306 if GAIA_UNLIKELY (!
match_filters(*pChunk, queryInfo, m_changedWorldVersion, indicesView))
1310 GAIA_PROF_SCOPE(query_func);
1311 if (pArchetype != pLastArchetype) {
1312 it.set_archetype(pArchetype);
1313 pLastArchetype = pArchetype;
1315 it.set_chunk(pChunk, from, to);
1318 runChunk(*
this, it, pFunc, state);
1319 finish_typed_chunk_state(world, pChunk, from, to, state);
1323 unlock(*m_storage.world());
1324 commit_cmd_buffer_st(*m_storage.world());
1325 commit_cmd_buffer_mt(*m_storage.world());
1326 m_changedWorldVersion = *m_worldVersion;
1329 template <QueryExecType ExecType>
1330 inline void QueryImpl::each_inter(
1331 QueryInfo& queryInfo,
const QueryPlan& plan,
void* pFunc,
const TypedQueryExecState& state,
1332 const TypedQueryErasedOps& ops) {
1337 GAIA_PROF_SCOPE(query_func);
1339 queryInfo, Constraints::EnabledOnly, pFunc, state, ops.runDirectChunk, ops.needsInheritedArgIds,
1340 ops.invokeInherited);
1344 if (state.canUseDirectChunkEval) {
1345 if constexpr (ExecType == QueryExecType::Default) {
1347 run_query_on_chunks_direct(queryInfo, plan, state, pFunc, ops.runDirectFastChunk);
1351 TypedDirectChunkCallback cb{
this, pFunc, &state, ops.runDirectChunk};
1352 run_query_on_chunks<ExecType, IterModeEnabled>(queryInfo, cb);
1354 TypedMappedChunkCallback cb{
this, &queryInfo, pFunc, &state, ops.runMappedChunk};
1355 run_query_on_chunks<ExecType, IterModeEnabled>(queryInfo, cb);
1359 template <QueryExecType ExecType,
typename Func>
1360 inline void QueryImpl::each_typed_inter(QueryInfo& queryInfo, Func func) {
1361 using InputArgs =
decltype(core::func_args(&Func::operator()));
1363#if GAIA_ASSERT_ENABLED
1364 GAIA_ASSERT(typed_query_args_match_query(queryInfo, InputArgs{}));
1366 auto& world = *
const_cast<World*
>(queryInfo.world());
1367 TypedQueryArgMeta metas[MAX_ITEMS_IN_QUERY]{};
1368 const auto argCount = init_typed_query_arg_metas(metas, world, InputArgs{});
1369 const auto state = build_typed_query_exec_state(world, queryInfo, metas, argCount);
1371 if constexpr (ExecType == QueryExecType::Default) {
1372 if constexpr (typed_query_arg_list_uses_sparse_storage_v<InputArgs>) {
1374 run_query_on_sparse_entities_typed(queryInfo, state, func, InputArgs{});
1379 run_query_on_chunks_sparse_typed<true>(queryInfo, plan, state, func, InputArgs{});
1381 run_query_on_chunks_sparse_typed<false>(queryInfo, plan, state, func, InputArgs{});
1387 run_query_on_chunks_direct_typed<true>(queryInfo, plan, state, func, InputArgs{});
1389 run_query_on_chunks_direct_typed<false>(queryInfo, plan, state, func, InputArgs{});
1394 TypedQueryErasedOps ops;
1395 ops.runDirectFastChunk = typed_run_direct_fast_chunk_ptr<Func>(InputArgs{});
1396 ops.runDirectChunk = typed_run_direct_chunk_ptr<Func>(InputArgs{});
1397 ops.runMappedChunk = typed_run_mapped_chunk_ptr<Func>(InputArgs{});
1398 ops.invokeInherited = typed_invoke_inherited_ptr<Func>(InputArgs{});
1399 ops.needsInheritedArgIds = state.needsInheritedArgIds;
1400 each_inter<ExecType>(queryInfo, plan, &func, state, ops);
1403 inline void QueryImpl::each_typed_erased(
1404 QueryExecType execType,
void* pFunc,
const TypedQueryExecState& state,
const TypedQueryErasedOps& ops) {
1405 auto& queryInfo =
fetch();
1408 if (execType == QueryExecType::Default && ops.runSparsePlan !=
nullptr &&
1410 ops.runSparsePlan(*
this, queryInfo, plan, pFunc, state);
1415 case QueryExecType::Parallel:
1416 each_inter<QueryExecType::Parallel>(queryInfo, plan, pFunc, state, ops);
1418 case QueryExecType::ParallelPerf:
1419 each_inter<QueryExecType::ParallelPerf>(queryInfo, plan, pFunc, state, ops);
1421 case QueryExecType::ParallelEff:
1422 each_inter<QueryExecType::ParallelEff>(queryInfo, plan, pFunc, state, ops);
1425 each_inter<QueryExecType::Default>(queryInfo, plan, pFunc, state, ops);
1430 template <
typename Func, std::enable_if_t<!detail::is_query_iter_callback_v<Func>,
int>>
1432 each(func, QueryExecType::Default);
1435 template <
typename Func, std::enable_if_t<!detail::is_query_iter_callback_v<Func>,
int>>
1437 auto& queryInfo =
fetch();
1441 case QueryExecType::Parallel:
1442 each_typed_inter<QueryExecType::Parallel>(queryInfo, func);
1444 case QueryExecType::ParallelPerf:
1445 each_typed_inter<QueryExecType::ParallelPerf>(queryInfo, func);
1447 case QueryExecType::ParallelEff:
1448 each_typed_inter<QueryExecType::ParallelEff>(queryInfo, func);
1451 each_typed_inter<QueryExecType::Default>(queryInfo, func);
1456 template <QueryExecType ExecType>
1457 inline void QueryImpl::each_iter_inter_erased(
1458 QueryInfo& queryInfo,
const QueryPlan& plan,
void* pFunc,
const TypedQueryExecState& state,
1459 void (*runDirectFastChunk)(QueryImpl&, Iter&,
void*,
const TypedQueryExecState&),
1460 void (*runMappedChunk)(QueryImpl&,
const QueryInfo&, Iter&,
void*,
const TypedQueryExecState&)) {
1461 TypedIterErasedCallback cb{
this, pFunc, &state, runDirectFastChunk, runMappedChunk};
1467 each_direct_iter_inter(queryInfo, Constraints::EnabledOnly, cb);
1471 if constexpr (ExecType == QueryExecType::Default) {
1473 run_query_on_chunks_direct_iter(queryInfo, plan, state, pFunc, runDirectFastChunk);
1478 run_query_on_chunks<ExecType, IterModeEnabled>(queryInfo, cb);
1481 inline void QueryImpl::each_iter_erased(
1482 QueryExecType execType,
void* pFunc,
const TypedQueryExecState& state,
1483 void (*runDirectFastChunk)(QueryImpl&, Iter&,
void*,
const TypedQueryExecState&),
1484 void (*runMappedChunk)(QueryImpl&,
const QueryInfo&, Iter&,
void*,
const TypedQueryExecState&)) {
1485 auto& queryInfo =
fetch();
1490 case QueryExecType::Parallel:
1491 each_iter_inter_erased<QueryExecType::Parallel>(
1492 queryInfo, plan, pFunc, state, runDirectFastChunk, runMappedChunk);
1494 case QueryExecType::ParallelPerf:
1495 each_iter_inter_erased<QueryExecType::ParallelPerf>(
1496 queryInfo, plan, pFunc, state, runDirectFastChunk, runMappedChunk);
1498 case QueryExecType::ParallelEff:
1499 each_iter_inter_erased<QueryExecType::ParallelEff>(
1500 queryInfo, plan, pFunc, state, runDirectFastChunk, runMappedChunk);
1503 each_iter_inter_erased<QueryExecType::Default>(
1504 queryInfo, plan, pFunc, state, runDirectFastChunk, runMappedChunk);
1509 template <
typename Func>
1511 using InputArgs =
decltype(core::func_args(&Func::operator()));
1512 auto& queryInfo =
fetch();
1514#if GAIA_ASSERT_ENABLED
1515 GAIA_ASSERT(typed_query_args_match_query(queryInfo, InputArgs{}));
1517 TypedQueryArgMeta metas[MAX_ITEMS_IN_QUERY]{};
1518 const auto argCount = init_typed_query_arg_metas(metas, *it.world(), InputArgs{});
1519 const auto state = build_typed_query_exec_state(*it.world(), queryInfo, metas, argCount);
1521 each_iter_dispatch(*
this, queryInfo, it, func, state, InputArgs{});
1524 inline void QueryImpl::each_iter_erased(
1525 Iter& it,
void* pFunc,
const TypedQueryExecState& state,
1526 void (*runDirectFastChunk)(QueryImpl&, Iter&,
void*,
const TypedQueryExecState&),
1527 void (*runMappedChunk)(QueryImpl&,
const QueryInfo&, Iter&,
void*,
const TypedQueryExecState&)) {
1528 auto& queryInfo =
fetch();
1530 if (state.canUseDirectChunkEval) {
1531 runDirectFastChunk(*
this, it, pFunc, state);
1532 finish_typed_chunk_state(*it.world(),
const_cast<Chunk*
>(it.chunk()), it.row_begin(), it.row_end(), state);
1534 runMappedChunk(*
this, queryInfo, it, pFunc, state);
1538 QueryInfo& queryInfo, Constraints constraints,
void* pFunc,
const TypedQueryExecState& state,
1539 void (*runDirectChunk)(QueryImpl&, Iter&,
void*,
const TypedQueryExecState&),
bool needsInheritedArgIds,
1540 void (*invokeInherited)(World&, Entity,
const Entity*,
void*)) {
1541 auto& world = *queryInfo.world();
1542 const auto plan = direct_entity_seed_plan(world, queryInfo);
1543 const bool hasWriteTerms = queryInfo.ctx().data.readWriteMask != 0;
1545 auto exec_direct_entity = [&](Entity entity) {
1546 uint8_t indices[ChunkHeader::MAX_COMPONENTS];
1547 Entity termIds[ChunkHeader::MAX_COMPONENTS];
1549 it.set_constraints(constraints);
1550 init_direct_entity_iter(queryInfo, world, entity, it, indices, termIds);
1552 runDirectChunk(*
this, it, pFunc, state);
1555 auto exec_entity = [&](Entity entity) {
1556 if (needsInheritedArgIds && state.hasInheritedTerms) {
1557 invokeInherited(world, entity, state.argIds, pFunc);
1558 finish_typed_query_args_by_id(world, entity, state);
1562 exec_direct_entity(entity);
1565 if (!hasWriteTerms && !plan.preferOrSeed) {
1566 const auto* pSeedTerm = find_direct_all_seed_term(queryInfo, plan);
1567 if (pSeedTerm !=
nullptr && can_use_direct_seed_run_cache(world, queryInfo, *pSeedTerm)) {
1568 DirectEntitySeedInfo seedInfo{};
1569 seedInfo.seededAllTerm = pSeedTerm->id;
1570 seedInfo.seededAllMatchKind = pSeedTerm->matchKind;
1571 seedInfo.seededFromAll =
true;
1572 if (!state.hasInheritedTerms) {
1573 const auto runs = cached_direct_seed_runs(queryInfo, *pSeedTerm, seedInfo, constraints);
1574 if (state.canUseDirectChunkEval) {
1576 it.init_query_state(&world, constraints,
false);
1577 const Archetype* pLastArchetype =
nullptr;
1578 for (
const auto& run: runs) {
1579 if (run.pArchetype != pLastArchetype) {
1580 it.set_archetype(run.pArchetype);
1581 pLastArchetype = run.pArchetype;
1583 it.set_chunk(run.pChunk, run.from, run.to);
1586 runDirectChunk(*
this, it, pFunc, state);
1590 it.init_query_state(&world, constraints,
false);
1591 const Archetype* pLastArchetype =
nullptr;
1592 uint8_t indices[ChunkHeader::MAX_COMPONENTS];
1593 Entity termIds[ChunkHeader::MAX_COMPONENTS];
1594 for (
const auto& run: runs) {
1595 const auto& ec = ::gaia::ecs::fetch(world, run.pChunk->entity_view()[run.from]);
1596 init_direct_entity_iter(queryInfo, world, ec, it, indices, termIds, pLastArchetype);
1597 it.set_chunk(run.pChunk, run.from, run.to);
1600 runDirectChunk(*
this, it, pFunc, state);
1604 const auto entities = cached_direct_seed_chunk_entities(queryInfo, *pSeedTerm, seedInfo, constraints);
1605 for (
const auto entity: entities)
1606 exec_entity(entity);
1612 auto walk_entities = [&](
auto&& execEntity) {
1613 if (hasWriteTerms) {
1614 auto& scratch = direct_query_scratch();
1615 const auto seedInfo = build_direct_entity_seed(world, queryInfo, scratch.entities);
1616 for (
const auto entity: scratch.entities) {
1617 if (!match_direct_entity_constraints(world, queryInfo, entity, constraints))
1619 if (!match_direct_entity_terms(world, entity, queryInfo, seedInfo))
1626 if (plan.preferOrSeed) {
1627 for_each_direct_or_union(world, queryInfo, constraints, [&](Entity entity) {
1634 (void)for_each_direct_all_seed(world, queryInfo, plan, constraints, [&](Entity entity) {
1640 walk_entities(exec_entity);
1643 template <
typename Func, std::enable_if_t<!detail::is_query_walk_core_callback_v<Func>,
int>>
1644 inline void QueryImpl::each_walk(Func func, Entity relation, TravOrder order, Constraints constraints) {
1645 auto& queryInfo =
fetch();
1649 using InputArgs =
decltype(core::func_args(&Func::operator()));
1650 GAIA_ASSERT(typed_query_args_match_query(queryInfo, InputArgs{}));
1651 GAIA_ASSERT(can_use_direct_target_eval(queryInfo));
1652 if (!can_use_direct_target_eval(queryInfo))
1655 auto& world = *queryInfo.world();
1656 TypedQueryArgMeta metas[MAX_ITEMS_IN_QUERY]{};
1657 const auto argCount = init_typed_query_arg_metas(metas, world, InputArgs{});
1658 const auto state = build_typed_query_exec_state(world, queryInfo, metas, argCount);
1659 if (state.canUseDirectChunkEval) {
1660 each_walk_dispatch_direct(*
this, queryInfo, ordered, constraints, func, state, InputArgs{});
1662 each_walk_dispatch_mapped(*
this, queryInfo, ordered, constraints, func, state, InputArgs{});
1666 template <
bool UseFilters,
typename ContainerOut>
1667 inline void QueryImpl::arr_inter(QueryInfo& queryInfo, ContainerOut& outArray, Constraints constraints) {
1668 using ContainerItemType =
typename ContainerOut::value_type;
1669 if constexpr (!UseFilters) {
1670 if (can_use_direct_entity_seed_eval(queryInfo)) {
1671 auto& world = *queryInfo.world();
1672 const auto plan = direct_entity_seed_plan(world, queryInfo);
1673 if (plan.preferOrSeed) {
1674 for_each_direct_or_union(world, queryInfo, constraints, [&](Entity entity) {
1675 typed_arr_push(world, entity, outArray);
1680 (void)for_each_direct_all_seed(world, queryInfo, plan, constraints, [&](Entity entity) {
1681 typed_arr_push(world, entity, outArray);
1689 auto& world = *queryInfo.world();
1690 const auto meta = typed_query_arg_meta<ContainerItemType>(world);
1691 const DirectChunkArgEvalDesc desc{meta.termId, meta.isEntity, meta.isPair, meta.usesSparseStorage};
1693 it.init_query_state(queryInfo.world(), constraints,
false);
1694 const bool canUseDirectChunkEval = !UseFilters && !queryInfo.has_entity_filter_terms() &&
1695 can_use_direct_chunk_term_eval_descs(world, queryInfo, &desc, 1) &&
1696 can_use_direct_chunk_iteration_fastpath(queryInfo);
1697 const auto cacheView = queryInfo.cache_archetype_view();
1699 const bool hasSortedArrayPayload = queryInfo.has_sorted_payload() || needsBarrierCache;
1700 const auto sortView =
1701 hasSortedArrayPayload ? queryInfo.cache_sort_view() :
decltype(queryInfo.cache_sort_view()){};
1702 if (needsBarrierCache)
1703 queryInfo.ensure_depth_order_hierarchy_barrier_cache();
1704 const auto cacheRange = selected_query_cache_range(queryInfo);
1705 if (!cacheRange.valid)
1707 const auto idxFrom = cacheRange.idxFrom;
1708 const auto idxTo = cacheRange.idxTo;
1710 if (!sortView.empty()) {
1711 for (
const auto& view: sortView) {
1712 if (view.archetypeIdx < idxFrom || view.archetypeIdx >= idxTo)
1715 const bool barrierPasses = !needsBarrierCache || queryInfo.barrier_passes(view.archetypeIdx);
1717 queryInfo, *cacheView[view.archetypeIdx], constraints, barrierPasses))
1720 const auto viewFrom = view.startRow;
1721 const auto viewTo = (uint16_t)(view.startRow + view.count);
1722 uint16_t minStartRow = 0;
1723 uint16_t minEndRow = 0;
1724 chunk_effective_range(view.pChunk, constraints, needsBarrierCache, barrierPasses, minStartRow, minEndRow);
1725 const auto startRow = core::get_max(minStartRow, viewFrom);
1726 const auto endRow = core::get_min(minEndRow, viewTo);
1727 if (startRow == endRow)
1730 run_typed_arr_rows<UseFilters>(
1731 *
this, queryInfo, it, outArray, m_changedWorldVersion, view.archetypeIdx, cacheView[view.archetypeIdx],
1732 view.pChunk, startRow, endRow, needsBarrierCache, canUseDirectChunkEval);
1737 for (uint32_t i = idxFrom; i < idxTo; ++i) {
1738 auto* pArchetype = cacheView[i];
1739 const bool barrierPasses = !needsBarrierCache || queryInfo.barrier_passes(i);
1743 const auto& chunks = pArchetype->chunks();
1744 for (
auto* pChunk: chunks) {
1748 run_typed_arr_rows<UseFilters>(
1749 *
this, queryInfo, it, outArray, m_changedWorldVersion, i, pArchetype, pChunk, from, to,
1750 needsBarrierCache, canUseDirectChunkEval);
1755 template <
typename Container>
1756 inline void QueryImpl::arr(Container& outArray, Constraints constraints) {
1757 const auto entCnt =
count(constraints);
1761 outArray.reserve(entCnt);
1762 auto& queryInfo =
fetch();
1765 const bool hasFilters = queryInfo.has_filters();
1767 arr_inter<true>(queryInfo, outArray, constraints);
1769 arr_inter<false>(queryInfo, outArray, constraints);
static GAIA_NODISCARD uint16_t end_index(Chunk *pChunk) noexcept
Returns the end of the enabled row range in a chunk.
Definition chunk_iterator.h:1989
static GAIA_NODISCARD uint16_t start_index(Chunk *pChunk) noexcept
Returns the first enabled row in a chunk.
Definition chunk_iterator.h:1982
static void chunk_effective_range(Chunk *pChunk, Constraints constraints, bool needsBarrierCache, bool barrierPasses, uint16_t &from, uint16_t &to) noexcept
Calculates the row range of a chunk after applying row constraints and depth-order barrier state.
Definition query.h:1937
void arr(Container &outArray, Constraints constraints=Constraints::EnabledOnly)
Appends all components or entities matching the query to the output array.
GAIA_NODISCARD QueryPlan prepare_query_plan(const QueryInfo &queryInfo, Constraints constraints) const
Selects the prepared execution plan for public iterator callbacks.
Definition query.h:3649
static GAIA_NODISCARD bool needs_depth_order_hierarchy_barrier_cache(const QueryInfo &queryInfo, Constraints constraints)
Checks whether the current row constraints require the depth-order hierarchy barrier cache.
Definition query.h:1926
@ Grouped
Batches carry group ids but do not require sorted slices or inherited/barrier metadata.
@ NonTrivial
Batches require non-trivial side payload such as sorted slices, inherited data, or barriers.
@ QueryPlanFlag_Sorted
The plan may need sorted cache slices. Runners use them only with non-trivial payload.
Definition query.h:2158
@ QueryPlanFlag_Filtered
The query has per-chunk filters such as changed terms.
Definition query.h:2150
@ QueryPlanFlag_InheritedPayload
The query carries inherited component data into iterator payloads.
Definition query.h:2154
@ QueryPlanFlag_BarrierCache
The plan must use the depth-order hierarchy barrier cache when checking archetype/row ranges.
Definition query.h:2160
@ QueryPlanFlag_Grouped
The query uses grouped payload/ranges or grouped cache ordering.
Definition query.h:2156
@ QueryPlanFlag_EntityFilter
The query has entity-filter terms that require per-entity rechecks.
Definition query.h:2152
void each_iter(Iter &it, Func func)
Runs a typed callback against an already prepared iterator. This is used by higher-level adapters tha...
static GAIA_NODISCARD ExecPayloadKind exec_payload_kind(const QueryInfo &queryInfo, Constraints constraints)
Classifies the generic batch payload needed for a matched query under row constraints.
Definition query.h:2107
void each(Func func)
Iterates query matches using the default execution mode.
Definition query.h:6151
static GAIA_NODISCARD bool depth_order_hierarchy_barrier_prunes(const QueryInfo &queryInfo)
Checks whether cached depth-order barrier results can prune any matched archetype.
Definition query.h:1953
static GAIA_NODISCARD bool has_depth_order_hierarchy_enabled_barrier(const QueryInfo &queryInfo)
Checks whether depth-order grouping can prune disabled hierarchy subtrees.
Definition query.h:1915
void match_all(QueryInfo &queryInfo)
Matches the query against all relevant archetypes.
Definition query.h:953
GAIA_NODISCARD std::span< const Entity > ordered_entities_walk(QueryInfo &queryInfo, Entity relation, TravOrder order, Constraints constraints=Constraints::EnabledOnly)
Builds and caches relation traversal order for the current query result.
Definition query.h:6493
uint32_t count(Constraints constraints=Constraints::EnabledOnly)
Calculates the number of entities matching the query.
Definition query.h:6287
static GAIA_NODISCARD bool match_filters(const Chunk &chunk, const QueryInfo &queryInfo, uint32_t changedWorldVersion, std::span< const uint8_t > compIndices)
Returns whether a chunk passes the query's changed filters.
Definition query.h:1799
void each_walk(Func func, Entity relation, TravOrder order=TravOrder::Down, Constraints constraints=Constraints::EnabledOnly)
Iterates entities matching the query in a requested relation traversal order. For relation R this tre...
Definition query.h:6838
@ Sorted
Sorted payload execution that must preserve cache-provided chunk order.
@ SparseDense
Typed cached chunk iteration with compile-time sparse payload access.
@ DirectDense
Direct cached archetype/chunk iteration with query-term indices matching storage layout.
@ EntitySeed
Direct entity-seed evaluation over explicitly selected entities.
@ MappedDense
Typed dense cached archetype/chunk iteration using mapped component access. Public Iter callbacks use...
@ Empty
The selected group/range has no matching archetypes, so execution can return immediately.
@ Traversal
Traversal or inherited payload execution that requires the mapped generic path.
QueryInfo & fetch()
Fetches the QueryInfo object. Creates or refreshes the backing QueryInfo if needed.
Definition query.h:899
GAIA_NODISCARD bool can_process_archetype_inter(const QueryInfo &queryInfo, const Archetype &archetype, Constraints constraints, int8_t barrierPasses=-1) const
Checks whether a matched archetype can be processed for the current row constraints.
Definition query.h:1993
void each_direct_inter(QueryInfo &queryInfo, Constraints constraints, void *pFunc, const TypedQueryExecState &state, void(*runDirectChunk)(QueryImpl &, Iter &, void *, const TypedQueryExecState &), bool needsInheritedArgIds, void(*invokeInherited)(World &, Entity, const Entity *, void *))
Runs an erased typed callback over entities selected by a direct sparse or target-term seed.
ExecPayloadKind payloadKind
Payload layout required by generic chunk-batch runners independent of sorted-cache availability.
Definition query.h:2170