khora_data/ecs/query_plan.rs
1// Copyright 2025 eraflo
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Defines the `QueryPlan`, which pre-calculates the most efficient way to execute an ECS query.
16
17use crate::ecs::SemanticDomain;
18use std::{any::TypeId, collections::HashSet};
19
20/// Defines whether a query can be executed "natively" or requires a transversal join.
21#[derive(Debug, Clone, Copy, PartialEq, Eq)]
22pub enum QueryMode {
23 /// Optimal path: all requested components are in the same domain.
24 /// Iteration happens linearly over matching `ComponentPage`s.
25 Native,
26 /// Join path: requested components span multiple domains.
27 /// Iteration is "driven" by one domain and peers are looked up via bitsets.
28 Transversal,
29}
30
31/// A pre-calculated execution strategy for a specific set of component types.
32///
33/// This plan is cached in the `World` to avoid re-analyzing query signatures
34/// on every frame.
35#[derive(Debug, Clone)]
36pub struct QueryPlan {
37 /// The execution mode (Native or Transversal).
38 pub mode: QueryMode,
39 /// If Transversal, this is the domain that drives the iteration.
40 pub driver_domain: Option<SemanticDomain>,
41 /// If Transversal, these are the peer domains that must be joined.
42 pub peer_domains: HashSet<SemanticDomain>,
43 /// The signature used to find matching pages (driver domain components).
44 pub driver_signature: Vec<TypeId>,
45}
46
47impl QueryPlan {
48 /// Creates a new `QueryPlan` for the given component types.
49 pub fn new(
50 is_transversal: bool,
51 driver_domain: Option<SemanticDomain>,
52 peer_domains: HashSet<SemanticDomain>,
53 driver_signature: Vec<TypeId>,
54 ) -> Self {
55 Self {
56 mode: if is_transversal {
57 QueryMode::Transversal
58 } else {
59 QueryMode::Native
60 },
61 driver_domain,
62 peer_domains,
63 driver_signature,
64 }
65 }
66}