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#if GAIA_ASSERT_ENABLED
17 GAIA_ASSERT(ChunkIterImpl::allows_term_access(queryInfo.ctx().data, pMetas[i].termId,
false));
19 state.argIds[i] = pMetas[i].termId;
20 state.writeFlags[i] = pMetas[i].isWrite;
21 if (pMetas[i].isWrite) {
22 state.hasWriteArgs =
true;
23 if (state.firstWriteArg == MAX_ITEMS_IN_QUERY)
24 state.firstWriteArg = (uint32_t)i;
26 state.needsInheritedArgIds = state.needsInheritedArgIds || !pMetas[i].isEntity;
27 hasSparseArgs = hasSparseArgs || pMetas[i].usesSparseStorage;
28 directChunkDescs[i] = {pMetas[i].termId, pMetas[i].isEntity, pMetas[i].isPair, pMetas[i].usesSparseStorage};
31 state.canUseDirectChunkEval =
32 QueryImpl::can_use_direct_chunk_term_eval_descs(world, queryInfo, directChunkDescs, argCount);
33 state.canUseSparseChunkEval = hasSparseArgs && QueryImpl::can_use_sparse_chunk_term_eval_descs(
34 world, queryInfo, directChunkDescs, argCount);
35 if (state.needsInheritedArgIds)
36 state.hasInheritedTerms = queryInfo.has_potential_inherited_id_terms();
40#if GAIA_ECS_TEST_HOOKS
41 template <
typename Func>
42 inline QueryImpl::QueryPlan QueryImpl::test_typed_plan(Func func) {
43 auto& queryInfo =
fetch();
46 using InputArgs =
decltype(core::func_args(&Func::operator()));
47 #if GAIA_ASSERT_ENABLED
48 GAIA_ASSERT(typed_query_args_match_query(queryInfo, InputArgs{}));
50 auto& world = *
const_cast<World*
>(queryInfo.world());
51 TypedQueryArgMeta metas[MAX_ITEMS_IN_QUERY]{};
52 const auto argCount = init_typed_query_arg_metas(metas, world, InputArgs{});
53 const auto state = build_typed_query_exec_state(world, queryInfo, metas, argCount);
58 inline QueryImpl::QueryPlan QueryImpl::test_iter_plan(Constraints constraints) {
59 auto& queryInfo =
fetch();
65 inline void finish_typed_chunk_state(
66 World& world, Chunk* pChunk, uint16_t from, uint16_t to,
const TypedQueryExecState& state);
68 inline void finish_typed_iter_state(QueryImpl& query, Iter& it,
const TypedQueryExecState& state);
70 inline void noop_row_done(uint32_t) {}
72 template <
typename Func,
typename ViewsTuple,
typename... T>
73 inline void invoke_typed_query_row_erased(
74 void* pFunc, Iter& it, ViewsTuple& dataPointerTuple, uint32_t row,
bool directLocalIndex);
76 template <
typename OnRowDone,
typename... T>
77 inline void run_typed_chunk_views(
78 const QueryInfo* pQueryInfo, Iter& it,
void* pFunc,
bool directViews, OnRowDone&& onRowDone,
79 void (*invokeDirectRow)(
80 void*, Iter&, std::tuple<
decltype(std::declval<Iter&>().
template sview_auto<T>())...>&, uint32_t,
bool),
81 void (*invokeMappedRow)(
82 void*, Iter&, std::tuple<
decltype(std::declval<Iter&>().
template view_auto_any<T>())...>&, uint32_t,
84 core::func_type_list<T...>);
86 template <
typename Func,
typename... T>
87 inline void run_typed_chunk_direct_iter_fast_cb(QueryImpl&, Iter& it,
void* pFunc,
const TypedQueryExecState&);
89 template <
typename Func,
typename... T>
91 run_typed_chunk_direct_iter_cb(QueryImpl& query, Iter& it,
void* pFunc,
const TypedQueryExecState& state);
93 template <
typename Func,
typename... T>
94 inline void run_typed_chunk_mapped_iter_cb(
95 QueryImpl& query,
const QueryInfo& queryInfo, Iter& it,
void* pFunc,
const TypedQueryExecState& state);
97 template <
typename Func,
typename... T>
98 inline void run_typed_sparse_chunk_rows(
99 Chunk* pChunk, uint16_t from, uint16_t to, Func& func,
const TypedQueryExecState& state,
100 core::func_type_list<T...> types);
102 template <
typename Func,
typename... T>
103 GAIA_NODISCARD
inline auto typed_run_direct_fast_chunk_ptr(core::func_type_list<T...>) {
104 return &run_typed_chunk_direct_iter_fast_cb<Func, T...>;
107 template <
typename Func,
typename... T>
108 GAIA_NODISCARD
inline auto typed_run_direct_chunk_ptr(core::func_type_list<T...>) {
109 return &run_typed_chunk_direct_iter_cb<Func, T...>;
112 template <
typename Func,
typename... T>
113 GAIA_NODISCARD
inline auto typed_run_mapped_chunk_ptr(core::func_type_list<T...>) {
114 return &run_typed_chunk_mapped_iter_cb<Func, T...>;
117 template <
typename Func,
typename... T>
118 inline void run_typed_sparse_plan_cb(
119 QueryImpl& query, QueryInfo& queryInfo,
const QueryImpl::QueryPlan& plan,
void* pFunc,
120 const TypedQueryExecState& state) {
121 auto& func = *
static_cast<Func*
>(pFunc);
123 query.run_query_on_sparse_entities_typed(queryInfo, state, func, core::func_type_list<T...>{});
129 query.run_query_on_chunks_sparse_typed<
true>(queryInfo, plan, state, func, core::func_type_list<T...>{});
131 query.run_query_on_chunks_sparse_typed<
false>(queryInfo, plan, state, func, core::func_type_list<T...>{});
134 template <
typename Func,
typename... T>
135 GAIA_NODISCARD
inline auto typed_run_sparse_plan_ptr(core::func_type_list<T...>) {
136 using RunSparsePlan =
137 void (*)(QueryImpl&, QueryInfo&,
const QueryImpl::QueryPlan&,
void*,
const TypedQueryExecState&);
138 if constexpr (typed_query_args_use_sparse_storage_v<T...>)
139 return RunSparsePlan(&run_typed_sparse_plan_cb<Func, T...>);
141 return RunSparsePlan(
nullptr);
144 template <
typename Func,
typename... T>
145 inline void run_typed_chunk_direct_iter_fast_cb(QueryImpl&, Iter& it,
void* pFunc,
const TypedQueryExecState&) {
146 run_typed_chunk_views(
147 nullptr, it, pFunc,
true, noop_row_done,
148 &invoke_typed_query_row_erased<
149 Func, std::tuple<
decltype(std::declval<Iter&>().
template sview_auto<T>())...>, T...>,
150 &invoke_typed_query_row_erased<
151 Func, std::tuple<
decltype(std::declval<Iter&>().template view_auto_any<T>())...>, T...>,
152 core::func_type_list<T...>{});
155 template <
typename Func,
typename... T>
157 run_typed_chunk_direct_iter_cb(QueryImpl& query, Iter& it,
void* pFunc,
const TypedQueryExecState& state) {
158 auto& func = *
static_cast<Func*
>(pFunc);
159 run_typed_chunk_direct_finish(query, it, func, state, core::func_type_list<T...>{});
162 template <
typename Func,
typename... T>
163 inline void run_typed_chunk_mapped_iter_cb(
164 QueryImpl& query,
const QueryInfo& queryInfo, Iter& it,
void* pFunc,
const TypedQueryExecState& state) {
165 auto& func = *
static_cast<Func*
>(pFunc);
166 if constexpr (typed_query_args_use_sparse_storage_v<T...>) {
167 if (state.canUseSparseChunkEval) {
168 auto boundState = state;
169 auto& world = *
const_cast<World*
>(queryInfo.world());
170 bind_typed_sparse_stores(boundState, world, core::func_type_list<T...>{});
171 auto* pChunk =
const_cast<Chunk*
>(it.chunk());
172 run_typed_sparse_chunk_rows(
173 pChunk, it.row_begin(), it.row_end(), func, boundState, core::func_type_list<T...>{});
174 if (boundState.hasWriteArgs)
175 finish_typed_chunk_state(world, pChunk, it.row_begin(), it.row_end(), boundState);
179 run_typed_chunk_mapped_finish(query, queryInfo, it, func, state, core::func_type_list<T...>{});
182 template <
typename Func,
typename... T>
183 inline void each_iter_dispatch(
184 const QueryInfo& queryInfo, Iter& it, Func& func,
const TypedQueryExecState& state,
185 core::func_type_list<T...>) {
186 if (state.canUseDirectChunkEval) {
187 run_typed_chunk_views(
188 nullptr, it, &func,
true, noop_row_done,
189 &invoke_typed_query_row_erased<
190 Func, std::tuple<
decltype(std::declval<Iter&>().
template sview_auto<T>())...>, T...>,
191 &invoke_typed_query_row_erased<
192 Func, std::tuple<
decltype(std::declval<Iter&>().template view_auto_any<T>())...>, T...>,
193 core::func_type_list<T...>{});
194 finish_typed_chunk_state(*it.world(),
const_cast<Chunk*
>(it.chunk()), it.row_begin(), it.row_end(), state);
196 run_typed_chunk_unmapped(queryInfo, it, func, state, core::func_type_list<T...>{});
199 template <
typename Func,
typename... T>
200 inline void run_typed_chunk_direct_finish(
201 QueryImpl& query, Iter& it, Func& func,
const TypedQueryExecState& state, core::func_type_list<T...>) {
202 run_typed_chunk_views(
203 nullptr, it, &func,
true, noop_row_done,
204 &invoke_typed_query_row_erased<
205 Func, std::tuple<
decltype(std::declval<Iter&>().
template sview_auto<T>())...>, T...>,
206 &invoke_typed_query_row_erased<
207 Func, std::tuple<
decltype(std::declval<Iter&>().template view_auto_any<T>())...>, T...>,
208 core::func_type_list<T...>{});
209 finish_typed_iter_state(query, it, state);
212 template <
typename Func,
typename... T>
213 inline void run_typed_chunk_mapped_finish(
214 QueryImpl& query,
const QueryInfo& queryInfo, Iter& it, Func& func,
const TypedQueryExecState& state,
215 core::func_type_list<T...>) {
216 run_typed_chunk_views(
217 &queryInfo, it, &func,
false, noop_row_done,
218 &invoke_typed_query_row_erased<
219 Func, std::tuple<
decltype(std::declval<Iter&>().
template sview_auto<T>())...>, T...>,
220 &invoke_typed_query_row_erased<
221 Func, std::tuple<
decltype(std::declval<Iter&>().template view_auto_any<T>())...>, T...>,
222 core::func_type_list<T...>{});
223 finish_typed_iter_state(query, it, state);
226 template <
typename Func,
typename... T>
227 inline void run_typed_chunk_direct_walk_cb(
228 QueryImpl& query,
const QueryInfo&, Iter& it,
void* pFunc,
const TypedQueryExecState& state) {
229 run_typed_chunk_direct_finish(query, it, *
static_cast<Func*
>(pFunc), state, core::func_type_list<T...>{});
232 template <
typename Func,
typename... T>
233 inline void run_typed_chunk_mapped_walk_cb(
234 QueryImpl& query,
const QueryInfo& queryInfo, Iter& it,
void* pFunc,
const TypedQueryExecState& state) {
235 run_typed_chunk_mapped_finish(
236 query, queryInfo, it, *
static_cast<Func*
>(pFunc), state, core::func_type_list<T...>{});
239 template <
typename Func,
typename... T>
240 inline void each_walk_dispatch_direct(
241 QueryImpl& query, QueryInfo& queryInfo, std::span<const Entity> entities, Constraints constraints, Func& func,
242 const TypedQueryExecState& state, core::func_type_list<T...>) {
243 query.each_walk_inter(
244 queryInfo, entities, constraints, &func, state, &run_typed_chunk_direct_walk_cb<Func, T...>);
247 template <
typename Func,
typename... T>
248 inline void each_walk_dispatch_mapped(
249 QueryImpl& query, QueryInfo& queryInfo, std::span<const Entity> entities, Constraints constraints, Func& func,
250 const TypedQueryExecState& state, core::func_type_list<T...>) {
251 query.each_walk_inter(
252 queryInfo, entities, constraints, &func, state, &run_typed_chunk_mapped_walk_cb<Func, T...>);
255 template <
typename Func,
typename... T>
256 inline void run_typed_chunk_unmapped(
257 const QueryInfo& queryInfo, Iter& it, Func& func,
const TypedQueryExecState& state,
258 core::func_type_list<T...> types) {
259 auto& world = *
const_cast<World*
>(queryInfo.world());
260 auto* pChunk =
const_cast<Chunk*
>(it.chunk());
261 const bool hasEntityFilters = queryInfo.has_entity_filter_terms();
263 if (!hasEntityFilters) {
264 run_typed_chunk_views(
265 &queryInfo, it, &func,
false, noop_row_done,
266 &invoke_typed_query_row_erased<
267 Func, std::tuple<
decltype(std::declval<Iter&>().
template sview_auto<T>())...>, T...>,
268 &invoke_typed_query_row_erased<
269 Func, std::tuple<
decltype(std::declval<Iter&>().template view_auto_any<T>())...>, T...>,
271 finish_typed_chunk_state(world, pChunk, it.row_begin(), it.row_end(), state);
273 run_typed_chunk_views(
274 &queryInfo, it, &func,
false,
276 finish_typed_chunk_state(
277 world, pChunk, (uint16_t)(it.row_begin() + row), (uint16_t)(it.row_begin() + row + 1), state);
279 &invoke_typed_query_row_erased<
280 Func, std::tuple<decltype(std::declval<Iter&>().template sview_auto<T>())...>, T...>,
281 &invoke_typed_query_row_erased<
282 Func, std::tuple<
decltype(std::declval<Iter&>().template view_auto_any<T>())...>, T...>,
286 it.clear_touched_writes();
289 template <
typename Func,
typename... T>
290 inline void run_typed_cached_seed_runs_basic(
291 QueryImpl& query, World& world, Constraints constraints, Func& func, std::span<const BfsChunkRun> runs,
292 const TypedQueryExecState& state) {
294 it.init_query_state(&world, constraints,
false);
295 const Archetype* pLastArchetype =
nullptr;
296 for (
const auto& run: runs) {
297 if (run.pArchetype != pLastArchetype) {
298 it.set_archetype(run.pArchetype);
299 pLastArchetype = run.pArchetype;
302 it.set_chunk(run.pChunk, run.from, run.to);
304 run_typed_chunk_direct_finish(query, it, func, state, core::func_type_list<T...>{});
308 template <
typename Func,
typename... T>
309 inline void run_typed_cached_seed_runs_entity_init(
310 QueryImpl& query, QueryInfo& queryInfo, World& world, Constraints constraints, Func& func,
311 std::span<const BfsChunkRun> runs,
const TypedQueryExecState& state) {
313 it.init_query_state(&world, constraints,
false);
314#if GAIA_ASSERT_ENABLED
315 it.set_query_access(&queryInfo.ctx().data);
317 const Archetype* pLastArchetype =
nullptr;
318 uint8_t indices[ChunkHeader::MAX_COMPONENTS];
319 Entity termIds[ChunkHeader::MAX_COMPONENTS];
320 for (
const auto& run: runs) {
321 const auto& ec = ::gaia::ecs::fetch(world, run.pChunk->entity_view()[run.from]);
322 query.init_direct_entity_iter(queryInfo, world, ec, it, indices, termIds, pLastArchetype);
323 it.set_chunk(run.pChunk, run.from, run.to);
325 run_typed_chunk_direct_finish(query, it, func, state, core::func_type_list<T...>{});
329 template <
typename Func,
typename... T>
330 inline void run_typed_cached_seed_runs(
331 QueryImpl& query, QueryInfo& queryInfo, World& world, Constraints constraints, Func& func,
332 std::span<const BfsChunkRun> runs,
bool canUseBasicInit,
const TypedQueryExecState& state) {
334 run_typed_cached_seed_runs_basic<Func, T...>(query, world, constraints, func, runs, state);
336 run_typed_cached_seed_runs_entity_init<Func, T...>(query, queryInfo, world, constraints, func, runs, state);
339 inline void QueryImpl::each_walk_inter(
340 QueryInfo& queryInfo, std::span<const Entity> entities, Constraints constraints,
void* pFunc,
341 const TypedQueryExecState& state,
342 void (*runChunk)(QueryImpl&,
const QueryInfo&, Iter&,
void*,
const TypedQueryExecState&)) {
343 auto& world = *queryInfo.world();
344 auto& walkData = ensure_each_walk_data();
346 it.init_query_state(&world, constraints,
false);
347#if GAIA_ASSERT_ENABLED
348 it.set_query_access(&queryInfo.ctx().data);
350 if (!walkData.cachedRuns.empty()) {
351 const auto& runs = walkData.cachedRuns;
352 const Archetype* pLastArchetype =
nullptr;
353 for (
const auto& run: runs) {
354 if (run.pArchetype != pLastArchetype) {
355 it.set_archetype(run.pArchetype);
356 pLastArchetype = run.pArchetype;
359 it.set_chunk(run.pChunk, run.from, run.to);
361 runChunk(*
this, queryInfo, it, pFunc, state);
366 const Archetype* pLastArchetype =
nullptr;
367 uint8_t indices[ChunkHeader::MAX_COMPONENTS];
368 Entity termIds[ChunkHeader::MAX_COMPONENTS];
369 for (
const auto entity: entities) {
370 const auto& ec = ::gaia::ecs::fetch(world, entity);
371 init_direct_entity_iter(queryInfo, world, ec, it, indices, termIds, pLastArchetype);
372 runChunk(*
this, queryInfo, it, pFunc, state);
376 inline void finish_typed_iter_state(QueryImpl& query, Iter& it,
const TypedQueryExecState& state) {
377 query.finish_typed_iter_writes_runtime(it, state.argIds, state.writeFlags, state.argCount, state.firstWriteArg);
378 it.clear_touched_writes();
381 inline void finish_typed_chunk_state(
382 World& world, Chunk* pChunk, uint16_t from, uint16_t to,
const TypedQueryExecState& state) {
383 QueryImpl::finish_typed_chunk_writes_runtime(
384 world, pChunk, from, to, state.argIds, state.writeFlags, state.argCount, state.firstWriteArg,
388 template <
typename InvokeRow,
typename OnRowDone>
389 inline void run_typed_query_rows_runtime(
390 const QueryInfo* pQueryInfo, Iter& it, InvokeRow&& invokeRow, OnRowDone&& onRowDone) {
391 const auto cnt = it.size();
392 const bool hasEntityFilters = pQueryInfo !=
nullptr && pQueryInfo->has_entity_filter_terms();
394 if (!hasEntityFilters) {
396 invokeRow((uint32_t)i);
397 onRowDone((uint32_t)i);
402 const auto entities = it.template view<Entity>();
404 if (!QueryImpl::match_entity_filters(*pQueryInfo->world(), entities[i], *pQueryInfo))
406 invokeRow((uint32_t)i);
407 onRowDone((uint32_t)i);
411 template <
typename Func,
typename ViewsTuple,
typename... T>
412 inline void invoke_typed_query_row(
413 Iter& it, Func& func, ViewsTuple& dataPointerTuple, uint32_t row,
bool directLocalIndex,
414 core::func_type_list<T...>) {
415 if constexpr (
sizeof...(T) > 0) {
416 if (directLocalIndex) {
418 [&](
auto&... views) {
424 [&](
auto&... views) {
425 func(views[it.template acc_index<T>(row)]...);
433 template <
typename Func,
typename ViewsTuple,
typename... T>
434 inline void invoke_typed_query_row_erased(
435 void* pFunc, Iter& it, ViewsTuple& dataPointerTuple, uint32_t row,
bool directLocalIndex) {
436 auto& func = *
static_cast<Func*
>(pFunc);
437 invoke_typed_query_row(it, func, dataPointerTuple, row, directLocalIndex, core::func_type_list<T...>{});
440 template <
typename OnRowDone,
typename ViewsTuple>
441 inline void run_typed_tuple_rows(
442 const QueryInfo* pQueryInfo, Iter& it,
void* pFunc, ViewsTuple& dataPointerTuple,
bool directLocalIndex,
443 void (*invokeRow)(
void*, Iter&, ViewsTuple&, uint32_t,
bool), OnRowDone&& onRowDone) {
444 run_typed_query_rows_runtime(
447 invokeRow(pFunc, it, dataPointerTuple, row, directLocalIndex);
449 GAIA_FWD(onRowDone));
452 template <
typename OnRowDone,
typename... T>
453 inline void run_typed_chunk_views(
454 const QueryInfo* pQueryInfo, Iter& it,
void* pFunc,
bool directViews, OnRowDone&& onRowDone,
455 void (*invokeDirectRow)(
456 void*, Iter&, std::tuple<
decltype(std::declval<Iter&>().
template sview_auto<T>())...>&, uint32_t,
bool),
457 void (*invokeMappedRow)(
458 void*, Iter&, std::tuple<
decltype(std::declval<Iter&>().
template view_auto_any<T>())...>&, uint32_t,
460 core::func_type_list<T...>) {
461 if constexpr (
sizeof...(T) > 0) {
463 auto dataPointerTuple = std::make_tuple(it.template sview_auto<T>()...);
464 run_typed_tuple_rows(pQueryInfo, it, pFunc, dataPointerTuple,
true, invokeDirectRow, GAIA_FWD(onRowDone));
466 auto dataPointerTuple = std::make_tuple(it.template view_auto_any<T>()...);
467 run_typed_tuple_rows(pQueryInfo, it, pFunc, dataPointerTuple,
false, invokeMappedRow, GAIA_FWD(onRowDone));
470 auto dataPointerTuple = std::tuple<>{};
471 run_typed_tuple_rows(
472 pQueryInfo, it, pFunc, dataPointerTuple, directViews, directViews ? invokeDirectRow : invokeMappedRow,
473 GAIA_FWD(onRowDone));
477 template <
typename ContainerOut>
478 inline void typed_arr_push(World& world, Entity entity, ContainerOut& outArray) {
479 using ContainerItemType =
typename ContainerOut::value_type;
480 if constexpr (std::is_same_v<ContainerItemType, Entity>)
481 outArray.push_back(entity);
483 auto tmp = world_direct_entity_arg<ContainerItemType>(world, entity);
484 outArray.push_back(tmp);
488 template <
bool UseFilters,
typename ContainerOut>
489 inline void run_typed_arr_rows(
490 QueryImpl& query,
const QueryInfo& queryInfo, Iter& it, ContainerOut& outArray, uint32_t changedWorldVersion,
491 uint32_t archetypeIdx,
const Archetype* pArchetype, Chunk* pChunk, uint16_t from, uint16_t to,
492 bool needsBarrierCache,
bool canUseDirectChunkEval) {
493 using ContainerItemType =
typename ContainerOut::value_type;
494 const bool barrierPasses = !needsBarrierCache || queryInfo.barrier_passes(archetypeIdx);
495 if GAIA_UNLIKELY (!query.can_process_archetype_inter(queryInfo, *pArchetype, it.constraints(), barrierPasses))
497 if GAIA_UNLIKELY (from == to)
500 GAIA_PROF_SCOPE(query::arr);
502 it.set_archetype(pArchetype);
503 it.set_chunk(pChunk, from, to);
506 const auto cnt = it.size();
510 if constexpr (UseFilters) {
515 const bool hasEntityFilters = queryInfo.has_entity_filter_terms();
516 if (canUseDirectChunkEval) {
517 const auto dataView = it.template sview_auto<ContainerItemType>();
519 auto tmp = dataView[i];
520 outArray.push_back(tmp);
525 const auto dataView = it.template view<ContainerItemType>();
526 if (!hasEntityFilters) {
528 const auto idx = it.template acc_index<ContainerItemType>(i);
529 auto tmp = dataView[idx];
530 outArray.push_back(tmp);
535 const auto entities = it.template view<Entity>();
537 if (!QueryImpl::match_entity_filters(*queryInfo.world(), entities[i], queryInfo))
539 const auto idx = it.template acc_index<ContainerItemType>(i);
540 auto tmp = dataView[idx];
541 outArray.push_back(tmp);
545 inline void QueryImpl::finish_typed_chunk_writes_runtime(
546 World& world, Chunk* pChunk, uint16_t from, uint16_t to,
const Entity* pArgIds,
const bool* pWriteFlags,
547 uint32_t argCnt, uint32_t firstWriteArg,
void*
const* pSparseStores) {
548 if (firstWriteArg >= argCnt || from >= to)
551 Entity seenTerms[ChunkHeader::MAX_COMPONENTS]{};
552 uint32_t seenCnt = 0;
553 const auto entities = pChunk->entity_view();
554 const auto finish_term = [&](Entity term,
bool sparseStoreBound) {
556 if (seenTerms[i] == term)
560 seenTerms[seenCnt++] = term;
561 if (sparseStoreBound) {
562 for (uint16_t row = from; row < to; ++row)
563 world_notify_on_set_entity(world, term, entities[row]);
566 if (!world_component_uses_sparse_storage(world, term)) {
567 const auto compIdx = core::get_index(pChunk->ids_view(), term);
568 if (compIdx != BadIndex) {
569 pChunk->finish_write(compIdx, from, to);
574 for (uint16_t row = from; row < to; ++row)
575 world_finish_write(world, term, entities[row]);
578 for (uint32_t i = firstWriteArg; i < argCnt; ++i) {
581 const auto term = pArgIds[i];
582 if (term != EntityBad)
583 finish_term(term, pSparseStores !=
nullptr && pSparseStores[i] !=
nullptr);
587 template <
typename... T>
588 inline void QueryImpl::finish_typed_chunk_writes(World& world, Chunk* pChunk, uint16_t from, uint16_t to) {
589 TypedQueryArgMeta metas[MAX_ITEMS_IN_QUERY]{};
590 const auto argCount = init_typed_query_arg_metas(metas, world, core::func_type_list<T...>{});
591 Entity argIds[MAX_ITEMS_IN_QUERY]{};
592 bool writeFlags[MAX_ITEMS_IN_QUERY]{};
593 uint32_t firstWriteArg = argCount;
595 argIds[i] = metas[i].termId;
596 writeFlags[i] = metas[i].isWrite;
597 if (metas[i].isWrite && firstWriteArg == argCount)
598 firstWriteArg = (uint32_t)i;
600 finish_typed_chunk_writes_runtime(world, pChunk, from, to, argIds, writeFlags, argCount, firstWriteArg);
603 inline void QueryImpl::finish_typed_iter_writes_runtime(
604 Iter& it,
const Entity* pArgIds,
const bool* pWriteFlags, uint32_t argCnt, uint32_t firstWriteArg) {
605 if (firstWriteArg >= argCnt)
608 auto* pChunk =
const_cast<Chunk*
>(it.chunk());
609 if (pChunk ==
nullptr || it.row_begin() >= it.row_end())
612 auto& world = *it.world();
613 auto entities = it.entity_rows();
614 Entity seenTerms[ChunkHeader::MAX_COMPONENTS]{};
615 uint32_t seenCnt = 0;
616 const auto finish_term = [&](Entity term, uint32_t fieldIdx) {
618 if (seenTerms[i] == term)
622 seenTerms[seenCnt++] = term;
623 const bool usesSparseStorage = world_component_uses_sparse_storage(world, term);
624 auto compIdx = uint8_t(0xFF);
625 if (it.comp_indices() !=
nullptr && fieldIdx < ChunkHeader::MAX_COMPONENTS)
626 compIdx = it.comp_indices()[fieldIdx];
627 else if (!usesSparseStorage)
628 compIdx = (uint8_t)pChunk->comp_idx(term);
630 if (compIdx != 0xFF && !usesSparseStorage) {
631 pChunk->finish_write(compIdx, it.row_begin(), it.row_end());
635 GAIA_FOR(entities.size()) {
636 world_finish_write(world, term, entities[i]);
640 for (uint32_t i = firstWriteArg; i < argCnt; ++i) {
644 Entity term = pArgIds[i];
645 if (it.term_ids() !=
nullptr && i < ChunkHeader::MAX_COMPONENTS && it.term_ids()[i] != EntityBad)
646 term = it.term_ids()[i];
647 if (term == EntityBad)
650 finish_term(term, i);
664 template <
typename T,
typename View>
665 GAIA_NODISCARD
inline decltype(
auto) typed_direct_chunk_arg_at(View& view, uint32_t row, uint16_t from) {
666 using U =
typename actual_type_t<T>::Type;
667 if constexpr (mem::is_soa_layout_v<U>)
668 return view[from + row];
683 template <
typename Func,
typename ViewsTuple,
typename... T,
size_t... I>
684 inline void invoke_typed_direct_chunk_row(
685 Func& func, ViewsTuple& views, uint32_t row, uint16_t from, core::func_type_list<T...>,
686 std::index_sequence<I...>) {
687 func(typed_direct_chunk_arg_at<T>(std::get<I>(views), row, from)...);
692 struct TypedDirectChunkRun {
694 Chunk* pChunk =
nullptr;
696 const uint8_t* pCompIndices =
nullptr;
698 const void*
const* pData =
nullptr;
713 template <
typename Func,
typename T>
714 inline void run_typed_direct_chunk_row_arg(
715 const TypedDirectChunkRun& chunkRun, Func& func,
const TypedQueryExecState& state, core::func_type_list<T>) {
716 Chunk* pChunk = chunkRun.pChunk;
717 const auto from = chunkRun.from;
718 const auto cnt = (uint32_t)(chunkRun.to - from);
719 using U =
typename actual_type_t<T>::Type;
720 if constexpr (std::is_same_v<U, Entity>) {
721 const auto data = pChunk->entity_view();
723 func(data[from + i]);
724 }
else if constexpr (!mem::is_soa_layout_v<U>) {
725 const auto* pData = chunkRun.pData !=
nullptr ? chunkRun.pData[0] :
nullptr;
726 if (pData ==
nullptr)
727 pData = pChunk->comp_rec_view()[pChunk->comp_idx(state.argIds[0])].pData;
728 auto* data = (U*)pData + from;
732 auto view = pChunk->template sview_auto<T>(from, chunkRun.to);
734 func(view[from + i]);
738 template <
typename T>
739 GAIA_NODISCARD
constexpr bool typed_direct_chunk_data_cacheable_arg() {
740 using U =
typename actual_type_t<T>::Type;
741 return !std::is_same_v<U, Entity> && !mem::is_soa_layout_v<U>;
744 template <
typename... T>
745 GAIA_NODISCARD
constexpr bool typed_direct_chunk_data_cacheable(core::func_type_list<T...>) {
746 if constexpr (
sizeof...(T) == 0)
749 return (typed_direct_chunk_data_cacheable_arg<T>() && ...);
752 template <
typename T>
753 GAIA_NODISCARD
inline decltype(
auto)
754 typed_direct_chunk_cached_arg(
const void*
const* pData, uint32_t argIdx, uint32_t row) {
755 using U =
typename actual_type_t<T>::Type;
756 auto* data = (U*)pData[argIdx];
760 template <
typename Func,
typename... T,
size_t... I>
761 inline void invoke_typed_direct_chunk_cached_row(
762 Func& func,
const void*
const* pData, uint32_t row, core::func_type_list<T...>, std::index_sequence<I...>) {
763 func(typed_direct_chunk_cached_arg<T>(pData, (uint32_t)I, row)...);
766 template <u
int32_t ArgCount>
767 GAIA_NODISCARD
inline bool typed_direct_chunk_cached_data_valid(
const void*
const* pData) {
768 if (pData ==
nullptr)
771 if (pData[i] ==
nullptr)
787 template <
typename Func,
typename... T>
788 inline void run_typed_direct_chunk_rows(
789 const TypedDirectChunkRun& chunkRun, Func& func,
const TypedQueryExecState& state,
790 core::func_type_list<T...>) {
791 Chunk* pChunk = chunkRun.pChunk;
792 const auto from = chunkRun.from;
793 const auto to = chunkRun.to;
794 if constexpr (
sizeof...(T) == 1) {
795 run_typed_direct_chunk_row_arg(chunkRun, func, state, core::func_type_list<T...>{});
796 }
else if constexpr (
sizeof...(T) > 0) {
797 const auto cnt = (uint32_t)(to - from);
798 if constexpr (typed_direct_chunk_data_cacheable(core::func_type_list<T...>{})) {
799 if (typed_direct_chunk_cached_data_valid<(uint32_t)
sizeof...(T)>(chunkRun.pData)) {
801 invoke_typed_direct_chunk_cached_row(
802 func, chunkRun.pData, (uint32_t)i, core::func_type_list<T...>{}, std::index_sequence_for<T...>{});
807 auto views = std::make_tuple(pChunk->template sview_auto<T>(from, to)...);
809 invoke_typed_direct_chunk_row(
810 func, views, (uint32_t)i, from, core::func_type_list<T...>{}, std::index_sequence_for<T...>{});
812 const auto cnt = (uint32_t)(to - from);
818 template <
typename T>
819 struct TypedSparseQueryView {
820 using U =
typename actual_type_t<T>::Type;
821 using UOriginal =
typename actual_type_t<T>::TypeOriginal;
822 const Entity* pEntities =
nullptr;
823 void* pStore =
nullptr;
825 GAIA_NODISCARD
decltype(
auto)
operator[](
size_t idx)
const {
826 if constexpr (core::is_mut_v<UOriginal>)
827 return world_typed_sparse_store_mut<U>(pStore, pEntities[idx]);
829 return world_typed_sparse_store_get<U>(pStore, pEntities[idx]);
833 template <
typename T>
834 GAIA_NODISCARD
inline auto typed_sparse_chunk_view(
835 Chunk* pChunk, uint16_t from, uint16_t to,
const TypedQueryExecState& state, uint32_t argIdx) {
836 if constexpr (uses_ct_sparse_storage_v<T>)
837 return TypedSparseQueryView<T>{pChunk->entity_view().data() + from, state.sparseStores[argIdx]};
839 return pChunk->template sview_auto<T>(from, to);
842 template <
typename T,
typename View>
843 GAIA_NODISCARD
inline decltype(
auto) typed_sparse_chunk_arg_at(View& view, uint32_t row, uint16_t from) {
844 if constexpr (uses_ct_sparse_storage_v<T>)
847 return typed_direct_chunk_arg_at<T>(view, row, from);
850 template <
typename Func,
typename... T,
size_t... I>
851 inline void run_typed_sparse_chunk_rows_impl(
852 Chunk* pChunk, uint16_t from, uint16_t to, Func& func,
const TypedQueryExecState& state,
853 core::func_type_list<T...>, std::index_sequence<I...>) {
854 auto views = std::make_tuple(typed_sparse_chunk_view<T>(pChunk, from, to, state, (uint32_t)I)...);
855 const auto cnt = (uint32_t)(to - from);
857 func(typed_sparse_chunk_arg_at<T>(std::get<I>(views), (uint32_t)i, from)...);
860 template <
typename Func,
typename... T>
861 inline void run_typed_sparse_chunk_rows(
862 Chunk* pChunk, uint16_t from, uint16_t to, Func& func,
const TypedQueryExecState& state,
863 core::func_type_list<T...> types) {
864 run_typed_sparse_chunk_rows_impl(pChunk, from, to, func, state, types, std::index_sequence_for<T...>{});
867 template <
typename T>
868 GAIA_NODISCARD
inline decltype(
auto)
869 typed_sparse_entity_arg(World& world, Entity entity,
const TypedQueryExecState& state, uint32_t argIdx) {
870 using U =
typename actual_type_t<T>::Type;
871 using UOriginal =
typename actual_type_t<T>::TypeOriginal;
872 if constexpr (std::is_same_v<U, Entity>)
874 else if constexpr (uses_ct_sparse_storage_v<T>) {
875 if constexpr (core::is_mut_v<UOriginal>) {
876 if (
auto* pValue = world_typed_sparse_store_try_mut<U>(state.sparseStores[argIdx], entity);
880 if (
const auto* pValue = world_typed_sparse_store_try_get<U>(state.sparseStores[argIdx], entity);
885 if constexpr (core::is_mut_v<UOriginal>)
886 return world_query_entity_arg_by_id_raw<T>(world, entity, state.argIds[argIdx]);
888 return world_query_entity_arg_by_id<T>(world, entity, state.argIds[argIdx]);
891 template <
typename Func,
typename... T,
size_t... I>
892 inline void invoke_typed_sparse_entity(
893 World& world, Entity entity,
const TypedQueryExecState& state, Func& func, core::func_type_list<T...>,
894 std::index_sequence<I...>) {
895 func(typed_sparse_entity_arg<T>(world, entity, state, (uint32_t)I)...);
902 inline QueryImpl::QueryPlan
907 const bool hasFilters = queryInfo.has_filters();
908 const bool hasSortedPayload = queryInfo.has_sorted_payload();
913 if (queryInfo.has_entity_filter_terms())
915 if (queryInfo.has_inherited_data_payload())
917 if (queryInfo.has_grouped_payload())
919 if (hasSortedPayload || hasDepthOrderBarrier)
921 if (hasDepthOrderBarrier && !depthOrderBarrierPrunes)
923 if (depthOrderBarrierPrunes)
926 const bool canDirectEntitySeed = !hasFilters && can_use_direct_entity_seed_eval(queryInfo);
927 const bool canDirectChunks = !hasSortedPayload && (!hasDepthOrderBarrier || !depthOrderBarrierPrunes);
929 auto setDenseRange = [&]() ->
bool {
930 const auto cacheRange = selected_query_cache_range(queryInfo);
931 plan.idxFrom = cacheRange.idxFrom;
932 plan.idxTo = cacheRange.idxTo;
933 if (cacheRange.hasSelectedGroup) {
936 if (!cacheRange.valid)
939 return plan.idxFrom < plan.idxTo;
942 if ((state.canUseDirectChunkEval || state.canUseSparseChunkEval) && !canDirectEntitySeed && canDirectChunks) {
943 if (!setDenseRange()) {
954 if (canDirectEntitySeed) {
959 if (!setDenseRange()) {
966 if (hasSortedPayload) {
972 if (hasDepthOrderBarrier && depthOrderBarrierPrunes) {
984 if (hasDepthOrderBarrier) {
989 if (!canDirectChunks) {
994 if (!state.canUseDirectChunkEval) {
1002 template <
bool HasFilters,
typename Func,
typename... T>
1003 inline void QueryImpl::run_query_on_chunks_sparse_typed(
1004 QueryInfo& queryInfo,
const QueryPlan& plan,
const TypedQueryExecState& state, Func& func,
1005 core::func_type_list<T...> types) {
1007 auto& world = *queryInfo.world();
1008 auto boundState = state;
1009 bind_typed_sparse_stores(boundState, world, types);
1010 const auto cacheView = queryInfo.cache_archetype_view();
1011 if (plan.idxFrom >= plan.idxTo)
1014 if (boundState.hasWriteArgs)
1015 ::gaia::ecs::update_version(*m_worldVersion);
1017 const bool canSkipProcessCheck =
1019 lock(*m_storage.world());
1020 for (uint32_t i = plan.idxFrom; i < plan.idxTo; ++i) {
1021 const auto* pArchetype = cacheView[i];
1022 if (canSkipProcessCheck) {
1023 if GAIA_UNLIKELY (pArchetype->is_req_del())
1028 std::span<const uint8_t> indicesView;
1029 if constexpr (HasFilters)
1030 indicesView = queryInfo.indices_mapping_view(i);
1032 for (auto* pChunk: pArchetype->chunks()) {
1035 if GAIA_UNLIKELY (from == to)
1037 if constexpr (HasFilters) {
1038 if GAIA_UNLIKELY (!
match_filters(*pChunk, queryInfo, m_changedWorldVersion, indicesView))
1042 GAIA_PROF_SCOPE(query_func);
1043 run_typed_sparse_chunk_rows(pChunk, from, to, func, boundState, types);
1044 if (boundState.hasWriteArgs)
1045 finish_typed_chunk_state(world, pChunk, from, to, boundState);
1049 unlock(*m_storage.world());
1050 commit_cmd_buffer_st(*m_storage.world());
1051 commit_cmd_buffer_mt(*m_storage.world());
1052 m_changedWorldVersion = *m_worldVersion;
1055 template <
typename Func,
typename... T>
1056 inline void QueryImpl::run_query_on_sparse_entities_typed(
1057 QueryInfo& queryInfo,
const TypedQueryExecState& state, Func& func, core::func_type_list<T...> types) {
1058 auto& world = *queryInfo.world();
1059 auto boundState = state;
1060 bind_typed_sparse_stores(boundState, world, types);
1061 if (boundState.hasWriteArgs)
1062 ::gaia::ecs::update_version(*m_worldVersion);
1064 auto execEntity = [&](Entity entity) {
1065 invoke_typed_sparse_entity(world, entity, boundState, func, types, std::index_sequence_for<T...>{});
1066 if (boundState.hasWriteArgs)
1067 finish_typed_query_args_by_id(world, entity, boundState);
1070 if (boundState.hasWriteArgs) {
1071 DirectQueryScratchScope scratchScope;
1072 auto& scratch = scratchScope.scratch;
1073 const auto seedInfo = build_direct_entity_seed(world, queryInfo, scratch);
1074 for (
const auto entity: scratch.entities) {
1075 if (!match_direct_entity_constraints(world, queryInfo, entity, Constraints::EnabledOnly) ||
1076 !match_direct_entity_terms(world, entity, queryInfo, seedInfo))
1081 const auto seedPlan = direct_entity_seed_plan(world, queryInfo);
1082 if (seedPlan.preferOrSeed) {
1083 each_direct_or_union(world, queryInfo, Constraints::EnabledOnly, [&](Entity entity) {
1088 (void)each_direct_all_seed(world, queryInfo, seedPlan, Constraints::EnabledOnly, [&](Entity entity) {
1095 finish_unlocked_each();
1108 template <
bool HasFilters,
typename Func,
typename... T>
1109 inline void QueryImpl::run_query_on_chunks_direct_typed(
1110 QueryInfo& queryInfo,
const QueryPlan& plan,
const TypedQueryExecState& state, Func& func,
1111 core::func_type_list<T...> types) {
1112 auto& world = *queryInfo.world();
1113 if constexpr (HasFilters) {
1120 auto cacheView = queryInfo.cache_archetype_view();
1121 if (plan.idxFrom >= plan.idxTo)
1124 if (state.hasWriteArgs)
1125 ::gaia::ecs::update_version(*m_worldVersion);
1127 const bool canSkipProcessCheck =
1129 lock(*m_storage.world());
1130 if constexpr (!HasFilters) {
1131 if (!state.hasWriteArgs && canSkipProcessCheck && plan.idxTo - plan.idxFrom >= 1024) {
1132 const auto terms = queryInfo.ctx().data.terms_view();
1133 uint32_t dataFields[MAX_ITEMS_IN_QUERY]{};
1134 uint32_t dataFieldCount = 0;
1135 if constexpr (typed_direct_chunk_data_cacheable(types)) {
1136 dataFieldCount = state.argCount;
1137 GAIA_FOR(state.argCount) {
1138 const auto argId = state.argIds[i];
1139 uint32_t termIdx = UINT32_MAX;
1140 for (uint32_t j = 0; j < terms.size(); ++j) {
1141 if (terms[j].
id == argId) {
1142 termIdx = (uint32_t)j;
1146 if (termIdx == UINT32_MAX) {
1150 dataFields[i] = termIdx;
1153 const auto chunkView = queryInfo.direct_chunk_view(plan.idxFrom, plan.idxTo, dataFields, dataFieldCount);
1154 const auto dataView = queryInfo.direct_chunk_data_view();
1155 uint32_t chunkIdx = 0;
1156 for (
const auto& entry: chunkView) {
1157 if constexpr (
sizeof...(T) > 1 && typed_direct_chunk_data_cacheable(types)) {
1160 constexpr uint32_t PrefetchDistance = 8;
1161 const auto prefetchIdx = chunkIdx + PrefetchDistance;
1162 if (prefetchIdx < chunkView.size()) {
1163 const auto& prefetchEntry = chunkView[prefetchIdx];
1164 if (prefetchEntry.dataOffset != UINT32_MAX) {
1165 const auto* pPrefetchData = &dataView[prefetchEntry.dataOffset];
1166 GAIA_FOR_(
sizeof...(T), dataIdx) {
1167 if (pPrefetchData[dataIdx] !=
nullptr)
1168 gaia::prefetch(pPrefetchData[dataIdx], PrefetchHint::PREFETCH_HINT_T1);
1174 const void* pSingleData[1]{};
1175 const void*
const* pData =
nullptr;
1176 if (entry.pData !=
nullptr) {
1180 pSingleData[0] = entry.pData;
1181 pData = pSingleData;
1182 }
else if (entry.dataOffset != UINT32_MAX)
1183 pData = &dataView[entry.dataOffset];
1184 const TypedDirectChunkRun chunkRun{entry.pChunk, entry.pCompIndices, pData, entry.rowFrom, entry.rowTo};
1186 GAIA_PROF_SCOPE(query_func);
1187 run_typed_direct_chunk_rows(chunkRun, func, state, types);
1190 unlock(*m_storage.world());
1191 commit_cmd_buffer_st(*m_storage.world());
1192 commit_cmd_buffer_mt(*m_storage.world());
1193 m_changedWorldVersion = *m_worldVersion;
1198 for (uint32_t i = plan.idxFrom; i < plan.idxTo; ++i) {
1199 const auto* pArchetype = cacheView[i];
1200 if (canSkipProcessCheck) {
1201 if GAIA_UNLIKELY (pArchetype->is_req_del())
1206 std::span<const uint8_t> indicesView;
1207 if constexpr (HasFilters)
1208 indicesView = queryInfo.indices_mapping_view(i);
1210 const auto& chunks = pArchetype->chunks();
1211 for (auto* pChunk: chunks) {
1214 if GAIA_UNLIKELY (from == to)
1217 if constexpr (HasFilters) {
1218 if GAIA_UNLIKELY (!
match_filters(*pChunk, queryInfo, m_changedWorldVersion, indicesView))
1222 GAIA_PROF_SCOPE(query_func);
1223 run_typed_direct_chunk_rows({pChunk,
nullptr,
nullptr, from, to}, func, state, types);
1224 if (state.hasWriteArgs)
1225 finish_typed_chunk_state(world, pChunk, from, to, state);
1229 unlock(*m_storage.world());
1230 commit_cmd_buffer_st(*m_storage.world());
1231 commit_cmd_buffer_mt(*m_storage.world());
1232 m_changedWorldVersion = *m_worldVersion;
1235 inline void QueryImpl::run_query_on_chunks_direct_iter(
1236 QueryInfo& queryInfo,
const QueryPlan& plan,
const TypedQueryExecState& state,
void* pFunc,
1237 void (*runChunk)(QueryImpl&, Iter& it,
void*,
const TypedQueryExecState&)) {
1238 auto& world = *queryInfo.world();
1240 GAIA_ASSERT(!queryInfo.has_filters());
1241 auto cacheView = queryInfo.cache_archetype_view();
1242 if (plan.idxFrom >= plan.idxTo)
1245 if (state.hasWriteArgs)
1246 ::gaia::ecs::update_version(*m_worldVersion);
1248 lock(*m_storage.world());
1250 it.init_query_state(queryInfo.world(), Constraints::EnabledOnly,
false);
1251#if GAIA_ASSERT_ENABLED
1252 it.set_query_access(&queryInfo.ctx().data);
1254 const Archetype* pLastArchetype =
nullptr;
1255 for (uint32_t i = plan.idxFrom; i < plan.idxTo; ++i) {
1256 const auto* pArchetype = cacheView[i];
1260 const auto& chunks = pArchetype->chunks();
1261 for (
auto* pChunk: chunks) {
1264 if GAIA_UNLIKELY (from == to)
1267 GAIA_PROF_SCOPE(query_func);
1268 if (pArchetype != pLastArchetype) {
1269 it.set_archetype(pArchetype);
1270 pLastArchetype = pArchetype;
1272 it.set_chunk(pChunk, from, to);
1275 runChunk(*
this, it, pFunc, state);
1276 finish_typed_chunk_state(world, pChunk, from, to, state);
1280 unlock(*m_storage.world());
1281 commit_cmd_buffer_st(*m_storage.world());
1282 commit_cmd_buffer_mt(*m_storage.world());
1283 m_changedWorldVersion = *m_worldVersion;
1286 inline void QueryImpl::run_query_on_chunks_direct(
1287 QueryInfo& queryInfo,
const QueryPlan& plan,
const TypedQueryExecState& state,
void* pFunc,
1288 void (*runChunk)(QueryImpl&, Iter& it,
void*,
const TypedQueryExecState&)) {
1289 auto& world = *queryInfo.world();
1290 auto cacheView = queryInfo.cache_archetype_view();
1291 if (plan.idxFrom >= plan.idxTo)
1294 if (state.hasWriteArgs)
1295 ::gaia::ecs::update_version(*m_worldVersion);
1300 lock(*m_storage.world());
1302 it.init_query_state(queryInfo.world(), Constraints::EnabledOnly,
false);
1303#if GAIA_ASSERT_ENABLED
1304 it.set_query_access(&queryInfo.ctx().data);
1306 const Archetype* pLastArchetype =
nullptr;
1308 for (uint32_t i = plan.idxFrom; i < plan.idxTo; ++i) {
1309 const auto* pArchetype = cacheView[i];
1313 std::span<const uint8_t> indicesView;
1315 indicesView = queryInfo.indices_mapping_view(i);
1317 const auto& chunks = pArchetype->chunks();
1318 for (
auto* pChunk: chunks) {
1321 if GAIA_UNLIKELY (from == to)
1325 if GAIA_UNLIKELY (!
match_filters(*pChunk, queryInfo, m_changedWorldVersion, indicesView))
1329 GAIA_PROF_SCOPE(query_func);
1330 if (pArchetype != pLastArchetype) {
1331 it.set_archetype(pArchetype);
1332 pLastArchetype = pArchetype;
1334 it.set_chunk(pChunk, from, to);
1337 runChunk(*
this, it, pFunc, state);
1338 finish_typed_chunk_state(world, pChunk, from, to, state);
1342 unlock(*m_storage.world());
1343 commit_cmd_buffer_st(*m_storage.world());
1344 commit_cmd_buffer_mt(*m_storage.world());
1345 m_changedWorldVersion = *m_worldVersion;
1348 template <QueryExecType ExecType>
1349 inline void QueryImpl::each_inter(
1350 QueryInfo& queryInfo,
const QueryPlan& plan,
void* pFunc,
const TypedQueryExecState& state,
1351 const TypedQueryErasedOps& ops) {
1356 GAIA_PROF_SCOPE(query_func);
1358 queryInfo, Constraints::EnabledOnly, pFunc, state, ops.runDirectChunk, ops.needsInheritedArgIds,
1359 ops.invokeInherited);
1363 if (state.canUseDirectChunkEval) {
1364 if constexpr (ExecType == QueryExecType::Default) {
1366 run_query_on_chunks_direct(queryInfo, plan, state, pFunc, ops.runDirectFastChunk);
1370 TypedDirectChunkCallback cb{
this, pFunc, &state, ops.runDirectChunk};
1371 run_query_on_chunks<ExecType, IterModeEnabled>(queryInfo, cb);
1373 TypedMappedChunkCallback cb{
this, &queryInfo, pFunc, &state, ops.runMappedChunk};
1374 run_query_on_chunks<ExecType, IterModeEnabled>(queryInfo, cb);
1378#if GAIA_ASSERT_ENABLED
1379 template <
typename Func>
1380 inline void QueryImpl::validate_typed_access() {
1381 using InputArgs =
decltype(core::func_args(&Func::operator()));
1383 auto& queryInfo =
fetch();
1384 TypedQueryArgMeta metas[MAX_ITEMS_IN_QUERY]{};
1385 const auto argCount = init_typed_query_arg_metas(metas, *queryInfo.world(), InputArgs{});
1386 GAIA_FOR(argCount) {
1387 GAIA_ASSERT(ChunkIterImpl::allows_term_access(queryInfo.ctx().data, metas[i].termId,
false));
1392 template <QueryExecType ExecType,
typename Func>
1393 inline void QueryImpl::each_typed_inter(QueryInfo& queryInfo, Func func) {
1394 using InputArgs =
decltype(core::func_args(&Func::operator()));
1396#if GAIA_ASSERT_ENABLED
1397 GAIA_ASSERT(typed_query_args_match_query(queryInfo, InputArgs{}));
1399 auto& world = *
const_cast<World*
>(queryInfo.world());
1400 TypedQueryArgMeta metas[MAX_ITEMS_IN_QUERY]{};
1401 const auto argCount = init_typed_query_arg_metas(metas, world, InputArgs{});
1402 const auto state = build_typed_query_exec_state(world, queryInfo, metas, argCount);
1404 if constexpr (ExecType == QueryExecType::Default) {
1405 if constexpr (typed_query_arg_list_uses_sparse_storage_v<InputArgs>) {
1407 run_query_on_sparse_entities_typed(queryInfo, state, func, InputArgs{});
1412 run_query_on_chunks_sparse_typed<true>(queryInfo, plan, state, func, InputArgs{});
1414 run_query_on_chunks_sparse_typed<false>(queryInfo, plan, state, func, InputArgs{});
1420 run_query_on_chunks_direct_typed<true>(queryInfo, plan, state, func, InputArgs{});
1422 run_query_on_chunks_direct_typed<false>(queryInfo, plan, state, func, InputArgs{});
1427 TypedQueryErasedOps ops;
1428 ops.runDirectFastChunk = typed_run_direct_fast_chunk_ptr<Func>(InputArgs{});
1429 ops.runDirectChunk = typed_run_direct_chunk_ptr<Func>(InputArgs{});
1430 ops.runMappedChunk = typed_run_mapped_chunk_ptr<Func>(InputArgs{});
1431 ops.invokeInherited = typed_invoke_inherited_ptr<Func>(InputArgs{});
1432 ops.needsInheritedArgIds = state.needsInheritedArgIds;
1433 each_inter<ExecType>(queryInfo, plan, &func, state, ops);
1436 inline void QueryImpl::each_typed_erased(
1437 QueryExecType execType,
void* pFunc,
const TypedQueryExecState& state,
const TypedQueryErasedOps& ops) {
1438 auto& queryInfo =
fetch();
1441 if (execType == QueryExecType::Default && ops.runSparsePlan !=
nullptr &&
1443 ops.runSparsePlan(*
this, queryInfo, plan, pFunc, state);
1448 case QueryExecType::Parallel:
1449 each_inter<QueryExecType::Parallel>(queryInfo, plan, pFunc, state, ops);
1451 case QueryExecType::ParallelPerf:
1452 each_inter<QueryExecType::ParallelPerf>(queryInfo, plan, pFunc, state, ops);
1454 case QueryExecType::ParallelEff:
1455 each_inter<QueryExecType::ParallelEff>(queryInfo, plan, pFunc, state, ops);
1458 each_inter<QueryExecType::Default>(queryInfo, plan, pFunc, state, ops);
1463 template <
typename Func, std::enable_if_t<!detail::is_query_iter_callback_v<Func>,
int>>
1465 each(func, QueryExecType::Default);
1468 template <
typename Func, std::enable_if_t<!detail::is_query_iter_callback_v<Func>,
int>>
1470 auto& queryInfo =
fetch();
1474 case QueryExecType::Parallel:
1475 each_typed_inter<QueryExecType::Parallel>(queryInfo, func);
1477 case QueryExecType::ParallelPerf:
1478 each_typed_inter<QueryExecType::ParallelPerf>(queryInfo, func);
1480 case QueryExecType::ParallelEff:
1481 each_typed_inter<QueryExecType::ParallelEff>(queryInfo, func);
1484 each_typed_inter<QueryExecType::Default>(queryInfo, func);
1489 template <QueryExecType ExecType>
1490 inline void QueryImpl::each_iter_inter_erased(
1491 QueryInfo& queryInfo,
const QueryPlan& plan,
void* pFunc,
const TypedQueryExecState& state,
1492 void (*runDirectFastChunk)(QueryImpl&, Iter&,
void*,
const TypedQueryExecState&),
1493 void (*runMappedChunk)(QueryImpl&,
const QueryInfo&, Iter&,
void*,
const TypedQueryExecState&)) {
1494 TypedIterErasedCallback cb{
this, pFunc, &state, runDirectFastChunk, runMappedChunk};
1500 each_direct_iter_inter(queryInfo, Constraints::EnabledOnly, cb);
1504 if constexpr (ExecType == QueryExecType::Default) {
1506 run_query_on_chunks_direct_iter(queryInfo, plan, state, pFunc, runDirectFastChunk);
1511 run_query_on_chunks<ExecType, IterModeEnabled>(queryInfo, cb);
1514 inline void QueryImpl::each_iter_erased(
1515 QueryExecType execType,
void* pFunc,
const TypedQueryExecState& state,
1516 void (*runDirectFastChunk)(QueryImpl&, Iter&,
void*,
const TypedQueryExecState&),
1517 void (*runMappedChunk)(QueryImpl&,
const QueryInfo&, Iter&,
void*,
const TypedQueryExecState&)) {
1518 auto& queryInfo =
fetch();
1523 case QueryExecType::Parallel:
1524 each_iter_inter_erased<QueryExecType::Parallel>(
1525 queryInfo, plan, pFunc, state, runDirectFastChunk, runMappedChunk);
1527 case QueryExecType::ParallelPerf:
1528 each_iter_inter_erased<QueryExecType::ParallelPerf>(
1529 queryInfo, plan, pFunc, state, runDirectFastChunk, runMappedChunk);
1531 case QueryExecType::ParallelEff:
1532 each_iter_inter_erased<QueryExecType::ParallelEff>(
1533 queryInfo, plan, pFunc, state, runDirectFastChunk, runMappedChunk);
1536 each_iter_inter_erased<QueryExecType::Default>(
1537 queryInfo, plan, pFunc, state, runDirectFastChunk, runMappedChunk);
1542 template <
typename Func>
1544 using InputArgs =
decltype(core::func_args(&Func::operator()));
1545 auto& queryInfo =
fetch();
1547#if GAIA_ASSERT_ENABLED
1548 GAIA_ASSERT(typed_query_args_match_query(queryInfo, InputArgs{}));
1550 TypedQueryArgMeta metas[MAX_ITEMS_IN_QUERY]{};
1551 const auto argCount = init_typed_query_arg_metas(metas, *it.world(), InputArgs{});
1552 const auto state = build_typed_query_exec_state(*it.world(), queryInfo, metas, argCount);
1554 each_iter_dispatch(queryInfo, it, func, state, InputArgs{});
1557 inline void QueryImpl::each_iter_erased(
1558 Iter& it,
void* pFunc,
const TypedQueryExecState& state,
1559 void (*runDirectFastChunk)(QueryImpl&, Iter&,
void*,
const TypedQueryExecState&),
1560 void (*runMappedChunk)(QueryImpl&,
const QueryInfo&, Iter&,
void*,
const TypedQueryExecState&)) {
1561 auto& queryInfo =
fetch();
1563 if (state.canUseDirectChunkEval) {
1564 runDirectFastChunk(*
this, it, pFunc, state);
1565 finish_typed_chunk_state(*it.world(),
const_cast<Chunk*
>(it.chunk()), it.row_begin(), it.row_end(), state);
1567 runMappedChunk(*
this, queryInfo, it, pFunc, state);
1571 QueryInfo& queryInfo, Constraints constraints,
void* pFunc,
const TypedQueryExecState& state,
1572 void (*runDirectChunk)(QueryImpl&, Iter&,
void*,
const TypedQueryExecState&),
bool needsInheritedArgIds,
1573 void (*invokeInherited)(World&, Entity,
const Entity*,
void*)) {
1574 auto& world = *queryInfo.world();
1575 const auto plan = direct_entity_seed_plan(world, queryInfo);
1576 const bool hasWriteTerms = queryInfo.ctx().data.readWriteMask != 0;
1578 auto exec_direct_entity = [&](Entity entity) {
1579 uint8_t indices[ChunkHeader::MAX_COMPONENTS];
1580 Entity termIds[ChunkHeader::MAX_COMPONENTS];
1582 it.set_constraints(constraints);
1583 init_direct_entity_iter(queryInfo, world, entity, it, indices, termIds);
1585 runDirectChunk(*
this, it, pFunc, state);
1588 auto exec_entity = [&](Entity entity) {
1589 if (needsInheritedArgIds && state.hasInheritedTerms) {
1590 invokeInherited(world, entity, state.argIds, pFunc);
1591 finish_typed_query_args_by_id(world, entity, state);
1595 exec_direct_entity(entity);
1598 if (!hasWriteTerms && !plan.preferOrSeed) {
1599 const auto* pSeedTerm = find_direct_all_seed_term(queryInfo, plan);
1600 if (pSeedTerm !=
nullptr && can_use_direct_seed_run_cache(world, queryInfo, *pSeedTerm)) {
1601 DirectEntitySeedInfo seedInfo{};
1602 seedInfo.seededAllTerm = pSeedTerm->id;
1603 seedInfo.seededAllMatchKind = pSeedTerm->matchKind;
1604 seedInfo.seededFromAll =
true;
1605 if (!state.hasInheritedTerms) {
1606 const auto runs = cached_direct_seed_runs(queryInfo, *pSeedTerm, seedInfo, constraints);
1607 if (state.canUseDirectChunkEval) {
1609 it.init_query_state(&world, constraints,
false);
1610#if GAIA_ASSERT_ENABLED
1611 it.set_query_access(&queryInfo.ctx().data);
1613 const Archetype* pLastArchetype =
nullptr;
1614 for (
const auto& run: runs) {
1615 if (run.pArchetype != pLastArchetype) {
1616 it.set_archetype(run.pArchetype);
1617 pLastArchetype = run.pArchetype;
1619 it.set_chunk(run.pChunk, run.from, run.to);
1622 runDirectChunk(*
this, it, pFunc, state);
1626 it.init_query_state(&world, constraints,
false);
1627#if GAIA_ASSERT_ENABLED
1628 it.set_query_access(&queryInfo.ctx().data);
1630 const Archetype* pLastArchetype =
nullptr;
1631 uint8_t indices[ChunkHeader::MAX_COMPONENTS];
1632 Entity termIds[ChunkHeader::MAX_COMPONENTS];
1633 for (
const auto& run: runs) {
1634 const auto& ec = ::gaia::ecs::fetch(world, run.pChunk->entity_view()[run.from]);
1635 init_direct_entity_iter(queryInfo, world, ec, it, indices, termIds, pLastArchetype);
1636 it.set_chunk(run.pChunk, run.from, run.to);
1639 runDirectChunk(*
this, it, pFunc, state);
1643 const auto entities = cached_direct_seed_chunk_entities(queryInfo, *pSeedTerm, seedInfo, constraints);
1644 for (
const auto entity: entities)
1645 exec_entity(entity);
1647 finish_unlocked_each();
1652 auto walk_entities = [&](
auto&& execEntity) {
1653 if (hasWriteTerms) {
1654 DirectQueryScratchScope scratchScope;
1655 auto& scratch = scratchScope.scratch;
1656 const auto seedInfo = build_direct_entity_seed(world, queryInfo, scratch);
1657 for (
const auto entity: scratch.entities) {
1658 if (!match_direct_entity_constraints(world, queryInfo, entity, constraints))
1660 if (!match_direct_entity_terms(world, entity, queryInfo, seedInfo))
1667 if (plan.preferOrSeed) {
1668 each_direct_or_union(world, queryInfo, constraints, [&](Entity entity) {
1675 (void)each_direct_all_seed(world, queryInfo, plan, constraints, [&](Entity entity) {
1681 walk_entities(exec_entity);
1682 finish_unlocked_each();
1685 template <
typename Func, std::enable_if_t<!detail::is_query_walk_core_callback_v<Func>,
int>>
1686 inline void QueryImpl::each_walk(Func func, Entity relation, TravOrder order, Constraints constraints) {
1687 auto& queryInfo =
fetch();
1691 using InputArgs =
decltype(core::func_args(&Func::operator()));
1692 GAIA_ASSERT(typed_query_args_match_query(queryInfo, InputArgs{}));
1693 GAIA_ASSERT(can_use_direct_target_eval(queryInfo));
1694 if (!can_use_direct_target_eval(queryInfo))
1697 auto& world = *queryInfo.world();
1698 TypedQueryArgMeta metas[MAX_ITEMS_IN_QUERY]{};
1699 const auto argCount = init_typed_query_arg_metas(metas, world, InputArgs{});
1700 const auto state = build_typed_query_exec_state(world, queryInfo, metas, argCount);
1701 if (state.canUseDirectChunkEval) {
1702 each_walk_dispatch_direct(*
this, queryInfo, ordered, constraints, func, state, InputArgs{});
1704 each_walk_dispatch_mapped(*
this, queryInfo, ordered, constraints, func, state, InputArgs{});
1708 template <
bool UseFilters,
typename ContainerOut>
1709 inline void QueryImpl::arr_inter(QueryInfo& queryInfo, ContainerOut& outArray, Constraints constraints) {
1710 using ContainerItemType =
typename ContainerOut::value_type;
1711 if constexpr (!UseFilters) {
1712 if (can_use_direct_entity_seed_eval(queryInfo)) {
1713 auto& world = *queryInfo.world();
1714 const auto plan = direct_entity_seed_plan(world, queryInfo);
1715 if (plan.preferOrSeed) {
1716 each_direct_or_union(world, queryInfo, constraints, [&](Entity entity) {
1717 typed_arr_push(world, entity, outArray);
1722 (void)each_direct_all_seed(world, queryInfo, plan, constraints, [&](Entity entity) {
1723 typed_arr_push(world, entity, outArray);
1731 auto& world = *queryInfo.world();
1732 const auto meta = typed_query_arg_meta<ContainerItemType>(world);
1733 const DirectChunkArgEvalDesc desc{meta.termId, meta.isEntity, meta.isPair, meta.usesSparseStorage};
1735 it.init_query_state(queryInfo.world(), constraints,
false);
1736#if GAIA_ASSERT_ENABLED
1737 it.set_query_access(&queryInfo.ctx().data);
1739 const bool canUseDirectChunkEval = !UseFilters && !queryInfo.has_entity_filter_terms() &&
1740 can_use_direct_chunk_term_eval_descs(world, queryInfo, &desc, 1) &&
1741 can_use_direct_chunk_iteration_fastpath(queryInfo);
1742 const auto cacheView = queryInfo.cache_archetype_view();
1744 const bool hasSortedArrayPayload = queryInfo.has_sorted_payload() || needsBarrierCache;
1745 const auto sortView =
1746 hasSortedArrayPayload ? queryInfo.cache_sort_view() :
decltype(queryInfo.cache_sort_view()){};
1747 if (needsBarrierCache)
1748 queryInfo.ensure_depth_order_hierarchy_barrier_cache();
1749 const auto cacheRange = selected_query_cache_range(queryInfo);
1750 if (!cacheRange.valid)
1752 const auto idxFrom = cacheRange.idxFrom;
1753 const auto idxTo = cacheRange.idxTo;
1755 if (!sortView.empty()) {
1756 for (
const auto& view: sortView) {
1757 if (view.archetypeIdx < idxFrom || view.archetypeIdx >= idxTo)
1760 const bool barrierPasses = !needsBarrierCache || queryInfo.barrier_passes(view.archetypeIdx);
1762 queryInfo, *cacheView[view.archetypeIdx], constraints, barrierPasses))
1765 const auto viewFrom = view.startRow;
1766 const auto viewTo = (uint16_t)(view.startRow + view.count);
1767 uint16_t minStartRow = 0;
1768 uint16_t minEndRow = 0;
1769 chunk_effective_range(view.pChunk, constraints, needsBarrierCache, barrierPasses, minStartRow, minEndRow);
1770 const auto startRow = core::get_max(minStartRow, viewFrom);
1771 const auto endRow = core::get_min(minEndRow, viewTo);
1772 if (startRow == endRow)
1775 run_typed_arr_rows<UseFilters>(
1776 *
this, queryInfo, it, outArray, m_changedWorldVersion, view.archetypeIdx, cacheView[view.archetypeIdx],
1777 view.pChunk, startRow, endRow, needsBarrierCache, canUseDirectChunkEval);
1782 for (uint32_t i = idxFrom; i < idxTo; ++i) {
1783 auto* pArchetype = cacheView[i];
1784 const bool barrierPasses = !needsBarrierCache || queryInfo.barrier_passes(i);
1788 const auto& chunks = pArchetype->chunks();
1789 for (
auto* pChunk: chunks) {
1793 run_typed_arr_rows<UseFilters>(
1794 *
this, queryInfo, it, outArray, m_changedWorldVersion, i, pArchetype, pChunk, from, to,
1795 needsBarrierCache, canUseDirectChunkEval);
1800 template <
typename Container>
1801 inline void QueryImpl::arr(Container& outArray, Constraints constraints) {
1802#if GAIA_ASSERT_ENABLED
1804 auto& queryInfo =
fetch();
1806 ChunkIterImpl::allows_term_access(
1807 queryInfo.ctx().data, typed_query_arg_meta<typename Container::value_type>(*queryInfo.world()).termId,
1811 const auto entCnt =
count(constraints);
1815 outArray.reserve(entCnt);
1816 auto& queryInfo =
fetch();
1819 const bool hasFilters = queryInfo.has_filters();
1821 arr_inter<true>(queryInfo, outArray, constraints);
1823 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:2116
static GAIA_NODISCARD uint16_t start_index(Chunk *pChunk) noexcept
Returns the first enabled row in a chunk.
Definition chunk_iterator.h:2109
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:2099
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:3836
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:2088
@ 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:2320
@ QueryPlanFlag_Filtered
The query has per-chunk filters such as changed terms.
Definition query.h:2312
@ QueryPlanFlag_InheritedPayload
The query carries inherited component data into iterator payloads.
Definition query.h:2316
@ QueryPlanFlag_BarrierCache
The plan must use the depth-order hierarchy barrier cache when checking archetype/row ranges.
Definition query.h:2322
@ QueryPlanFlag_Grouped
The query uses grouped payload/ranges or grouped cache ordering.
Definition query.h:2318
@ QueryPlanFlag_EntityFilter
The query has entity-filter terms that require per-entity rechecks.
Definition query.h:2314
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:2269
void each(Func func)
Iterates query matches using the default execution mode. Each callback receives a contiguous chunk wi...
Definition query.h:6581
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:2115
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:2077
void match_all(QueryInfo &queryInfo)
Matches the query against all relevant archetypes.
Definition query.h:1094
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:6926
uint32_t count(Constraints constraints=Constraints::EnabledOnly)
Calculates the number of entities matching the query.
Definition query.h:6720
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:1961
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:7267
@ 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:1039
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:2155
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:2332