dynamis_world/
query_pool.rs1use dynamis_abi::{MAX_HITS_PER_QUERY, QueryResultHeaderRecord, QueryResultRecord};
2use dynamis_model::{BodyHandle, SurfaceDesc};
3use std::collections::VecDeque;
4
5#[derive(Clone, Copy, Debug, PartialEq, Eq)]
6pub struct QueryHandle {
7 pub batch: u64,
8 pub index: u32,
9}
10
11#[derive(Clone, Copy, Debug, PartialEq)]
12pub struct QueryHit {
13 pub body: BodyHandle,
14 pub collider: u32,
15 pub distance: f32,
16 pub point: [f32; 3],
17 pub normal: [f32; 3],
18 pub triangle: Option<u32>,
19 pub surface: Option<SurfaceDesc>,
20 pub step: u64,
21}
22
23pub(crate) struct QueryOutcome {
24 pub hits: Vec<Vec<QueryHit>>,
25 pub overflow: Vec<bool>,
26}
27
28struct QueryBatch {
29 batch: u64,
30 step: u64,
31 width: usize,
32 outcome: Option<QueryOutcome>,
33}
34
35pub(crate) struct QueryPool {
36 batches: VecDeque<QueryBatch>,
37}
38
39impl QueryPool {
40 pub(crate) fn new() -> Self {
41 Self {
42 batches: VecDeque::new(),
43 }
44 }
45
46 pub(crate) fn submit(&mut self, batch: u64, step: u64, width: usize) {
47 self.batches.push_back(QueryBatch {
48 batch,
49 step,
50 width,
51 outcome: None,
52 });
53 }
54
55 pub(crate) fn hit(&self, handle: QueryHandle) -> Option<QueryHit> {
56 self.hits(handle).first().copied()
57 }
58
59 pub(crate) fn hits(&self, handle: QueryHandle) -> &[QueryHit] {
60 &self.outcome(handle).hits[handle.index as usize]
61 }
62
63 pub(crate) fn overflow(&self, handle: QueryHandle) -> bool {
64 self.outcome(handle).overflow[handle.index as usize]
65 }
66
67 fn outcome(&self, handle: QueryHandle) -> &QueryOutcome {
68 self.batch(handle)
69 .and_then(|batch| batch.outcome.as_ref())
70 .expect("query outcome read before it arrived")
71 }
72
73 pub(crate) fn is_current(&self, handle: QueryHandle) -> bool {
74 self.batch(handle).is_some()
75 }
76
77 pub(crate) fn is_ready(&self, handle: QueryHandle) -> bool {
78 self.batch(handle)
79 .is_some_and(|batch| batch.outcome.is_some())
80 }
81
82 fn batch(&self, handle: QueryHandle) -> Option<&QueryBatch> {
83 self.batches
84 .iter()
85 .find(|batch| batch.batch == handle.batch)
86 }
87
88 pub(crate) fn collect(
89 &mut self,
90 batch_id: u64,
91 bytes: &[u8],
92 surface: impl Fn(u32, u32) -> SurfaceDesc,
93 ) {
94 let batch = self
95 .batches
96 .iter_mut()
97 .find(|batch| batch.batch == batch_id)
98 .unwrap_or_else(|| panic!("no query batch is registered for batch {batch_id}"));
99 let step = batch.step;
100 let records = dynamis_abi::decode::<QueryResultRecord>(bytes);
101 let mut hits = vec![Vec::new(); batch.width];
102 let mut overflow = vec![false; batch.width];
103 for (index, result) in records.iter().take(batch.width).enumerate() {
104 let QueryResultHeaderRecord {
105 count,
106 overflow: spilled,
107 ..
108 } = result.header;
109 assert!(
110 count <= MAX_HITS_PER_QUERY,
111 "GPU query result exceeds the hit lane count"
112 );
113 hits[index] = result.hits[..count as usize]
114 .iter()
115 .map(|record| QueryHit {
116 body: BodyHandle {
117 id: record.body_id,
118 generation: record.body_generation,
119 },
120 collider: record.collider_index,
121 distance: record.distance,
122 point: record.point,
123 normal: record.normal,
124 triangle: (record.triangle != dynamis_abi::NO_TRIANGLE)
125 .then_some(record.triangle),
126 surface: (record.surface != dynamis_abi::NO_SURFACE)
127 .then(|| surface(record.collider_index, record.surface)),
128 step,
129 })
130 .collect();
131 overflow[index] = spilled != 0;
132 }
133 batch.outcome = Some(QueryOutcome { hits, overflow });
134 while self
135 .batches
136 .front()
137 .is_some_and(|oldest| oldest.outcome.is_some() && oldest.step < step)
138 {
139 self.batches.pop_front();
140 }
141 }
142}