Skip to main content

dynamis_world/world/
query.rs

1use super::World;
2use crate::world::query_pool::{QueryHandle, QueryHit, QueryPool};
3use dynamis_layout::{MAX_HITS_PER_QUERY, QueryRecord};
4use dynamis_model::{QueryFilter, Shape};
5use std::mem::size_of;
6
7pub(crate) struct Queries {
8    pub(crate) pending: Vec<QueryRecord>,
9    pub(crate) next_batch: u64,
10    pub(crate) pool: QueryPool,
11}
12
13impl Queries {
14    pub(crate) fn new() -> Self {
15        Self {
16            pending: Vec::new(),
17            next_batch: 0,
18            pool: QueryPool::new(),
19        }
20    }
21}
22
23impl World {
24    pub fn ray_query(
25        &mut self,
26        origin: [f32; 3],
27        direction: [f32; 3],
28        max_t: f32,
29        filter: &QueryFilter,
30    ) -> QueryHandle {
31        assert!(max_t > 0.0, "raycast distance must be positive");
32        assert!(direction != [0.0; 3], "raycast direction must be non-zero");
33        self.submit_query(QueryRecord::ray(origin, direction, max_t, filter))
34    }
35
36    pub fn sphere_query(
37        &mut self,
38        center: [f32; 3],
39        radius: f32,
40        filter: &QueryFilter,
41    ) -> QueryHandle {
42        assert!(radius > 0.0, "sphere query radius must be positive");
43        self.submit_query(QueryRecord::sphere(center, radius, filter))
44    }
45
46    pub fn cuboid_query(
47        &mut self,
48        center: [f32; 3],
49        half_extents: [f32; 3],
50        filter: &QueryFilter,
51    ) -> QueryHandle {
52        assert!(
53            half_extents.iter().all(|extent| *extent > 0.0),
54            "cuboid query half extents must be strictly positive"
55        );
56        self.submit_query(QueryRecord::cuboid(center, half_extents, filter))
57    }
58
59    pub fn point_query(&mut self, origin: [f32; 3], filter: &QueryFilter) -> QueryHandle {
60        self.submit_query(QueryRecord::point(origin, filter))
61    }
62
63    pub fn overlap_query(
64        &mut self,
65        shape: &Shape,
66        orientation: [f32; 4],
67        position: [f32; 3],
68        filter: &QueryFilter,
69    ) -> QueryHandle {
70        self.assert_unit(orientation);
71        assert!(shape.is_convex(), "overlap queries require a convex shape");
72        self.submit_query(QueryRecord::convex(shape, orientation, position, filter))
73    }
74
75    pub fn sweep_query(
76        &mut self,
77        shape: &Shape,
78        orientation: [f32; 4],
79        start: [f32; 3],
80        direction: [f32; 3],
81        length: f32,
82        filter: &QueryFilter,
83    ) -> QueryHandle {
84        assert!(length > 0.0, "sweep length must be positive");
85        assert!(direction != [0.0; 3], "sweep direction must be non-zero");
86        self.assert_unit(orientation);
87        assert!(shape.is_convex(), "sweep queries require a convex shape");
88        self.submit_query(QueryRecord::sweep(
89            shape,
90            orientation,
91            start,
92            direction,
93            length,
94            filter,
95        ))
96    }
97
98    fn submit_query(&mut self, record: QueryRecord) -> QueryHandle {
99        assert!(
100            record.max_hits <= MAX_HITS_PER_QUERY,
101            "a query returns at most {MAX_HITS_PER_QUERY} hits"
102        );
103        let index = self.queries.pending.len() as u32;
104        self.queries.pending.push(record);
105        QueryHandle {
106            batch: self.queries.next_batch,
107            index,
108        }
109    }
110
111    pub fn query_hit(&self, handle: QueryHandle) -> Option<QueryHit> {
112        self.validate_query(handle);
113        self.queries.pool.hit(handle)
114    }
115
116    pub fn query_hits(&self, handle: QueryHandle) -> &[QueryHit] {
117        self.validate_query(handle);
118        self.queries.pool.hits(handle)
119    }
120
121    pub fn query_overflow(&self, handle: QueryHandle) -> bool {
122        self.validate_query(handle);
123        self.queries.pool.overflow(handle)
124    }
125
126    pub fn flush_queries(&mut self) {
127        if self.queries.pending.is_empty() {
128            self.apply_plan();
129            return;
130        }
131        self.backend.gpu.assert_alive();
132        self.collect_readbacks();
133        self.apply_plan();
134        self.flush_rows();
135        self.apply_pending_commands();
136        let step = self.clock.step;
137        let queue = self.backend.gpu.queue().clone();
138        let device = self.backend.gpu.device().clone();
139        self.backend
140            .buffers
141            .queries
142            .records
143            .write(&queue, bytemuck::cast_slice(&self.queries.pending));
144        let params = dynamis_layout::StepParamsRecord::new(
145            &self.config,
146            self.clock.sub_dt,
147            self.bodies.dynamic_count as u32,
148            self.bodies.alive.len() as u32,
149            self.constraints.alive.len() as u32,
150            dynamis_layout::RowStreams {
151                edit_runs: self.bodies.last_edits,
152                body_moves: self.bodies.last_moves,
153                constraint_moves: self.constraints.last_moves,
154            },
155            self.event_slot_of(step),
156        );
157        self.backend
158            .buffers
159            .params
160            .write(&queue, bytemuck::cast_slice(&[params]));
161        let count = self.queries.pending.len();
162        let batch = self.queries.next_batch;
163        self.queries.pool.submit(batch, step, count);
164        self.queries.next_batch += 1;
165        let frame = crate::dynamics::FrameParams {
166            dynamic_count: self.bodies.dynamic_count as u32,
167            body_count: self.bodies.alive.len() as u32,
168            solve_iterations: self.config.solve_iterations,
169            island_rounds: self.island_rounds(),
170            query_count: count as u32,
171            constraint_count: self.constraints.alive.len() as u32,
172            body_move_count: self.bodies.last_moves,
173            constraint_move_count: self.constraints.last_moves,
174            edit_run_count: self.bodies.last_edits,
175        };
176        let mut encoder = dynamis_gpu::SubmissionEncoder::new(&device, "dynamis query flush");
177        self.backend
178            .pipeline
179            .encode_queries(&mut encoder, &self.backend.buffers, &frame);
180        let bytes = count as u64 * size_of::<dynamis_layout::QueryResultRecord>() as u64;
181        let arrived = self.backend.buffers.readback.queries.enqueue(
182            &mut encoder,
183            self.backend.buffers.queries.results.buffer(),
184            0,
185            bytes,
186            batch,
187        );
188        encoder.submit(&queue);
189        if let Some((batch, bytes)) = arrived {
190            self.queries.pool.collect(batch, &bytes);
191        }
192        for (batch, bytes) in self.backend.buffers.readback.queries.drain() {
193            self.queries.pool.collect(batch, &bytes);
194        }
195        self.queries.pending.clear();
196        self.apply_plan();
197    }
198
199    fn validate_query(&self, handle: QueryHandle) {
200        assert!(
201            self.queries.pool.is_current(handle),
202            "query handle {handle:?} belongs to a batch that has been retired"
203        );
204        assert!(
205            self.queries.pool.is_ready(handle),
206            "query handle {handle:?} has no results yet; call poll() or wait()"
207        );
208    }
209}