Skip to main content

dynamis_world/
view.rs

1use super::World;
2use dynamis_abi::{BodyStateRecord, NO_SLOT};
3use dynamis_gpu::SubmissionEncoder;
4use dynamis_model::{BodyHandle, BodyState};
5use std::mem::size_of;
6
7pub(crate) struct View {
8    observed: Vec<u32>,
9    slot_of: Vec<u32>,
10    dirty: bool,
11    epoch: Option<u64>,
12}
13
14impl View {
15    pub(crate) const fn new() -> Self {
16        Self {
17            observed: Vec::new(),
18            slot_of: Vec::new(),
19            dirty: false,
20            epoch: None,
21        }
22    }
23
24    pub(crate) fn len(&self) -> u32 {
25        self.observed.len() as u32
26    }
27
28    pub(crate) fn ids(&self) -> &[u32] {
29        &self.observed
30    }
31
32    pub(crate) fn observe(&mut self, id: u32) {
33        if self.slot_of.len() <= id as usize {
34            self.slot_of.resize(id as usize + 1, NO_SLOT);
35        }
36        if self.slot_of[id as usize] != NO_SLOT {
37            return;
38        }
39        self.slot_of[id as usize] = self.observed.len() as u32;
40        self.observed.push(id);
41        self.dirty = true;
42    }
43
44    pub(crate) fn forget(&mut self, id: u32) {
45        let Some(slot) = self
46            .slot_of
47            .get(id as usize)
48            .copied()
49            .filter(|slot| *slot != NO_SLOT)
50        else {
51            return;
52        };
53        self.slot_of[id as usize] = NO_SLOT;
54        self.observed.swap_remove(slot as usize);
55        if let Some(moved) = self.observed.get(slot as usize) {
56            self.slot_of[*moved as usize] = slot;
57        }
58        self.dirty = true;
59    }
60
61    fn take_dirty(&mut self) -> bool {
62        std::mem::take(&mut self.dirty)
63    }
64}
65
66impl World {
67    pub fn poll(&mut self) {
68        self.backend.gpu.assert_alive();
69        self.collect_readbacks();
70    }
71
72    pub fn wait(&mut self) {
73        self.backend.gpu.assert_alive();
74        self.resolve_queries();
75        loop {
76            self.drain_readbacks();
77            if self.states_current() {
78                return;
79            }
80            self.submit_states();
81        }
82    }
83
84    pub fn try_state(&mut self, handle: BodyHandle) -> Option<BodyState> {
85        self.validate(handle);
86        self.view.observe(handle.id);
87        self.view.epoch?;
88        self.bodies.states[handle.id as usize]
89    }
90
91    pub(crate) fn states_current(&self) -> bool {
92        self.bodies
93            .alive
94            .iter()
95            .all(|handle| self.body_current(handle.id))
96    }
97
98    pub(crate) fn body_current(&self, id: u32) -> bool {
99        self.bodies.covered[id as usize] == self.clock.step
100    }
101
102    fn completed_step(&self) -> Option<u64> {
103        self.clock.step.checked_sub(1)
104    }
105
106    pub(crate) fn flush_observed(&mut self) {
107        if !self.view.take_dirty() {
108            return;
109        }
110        let ids = self.view.ids();
111        if ids.is_empty() {
112            return;
113        }
114        self.backend
115            .streams
116            .state
117            .observed_ids
118            .write(self.backend.gpu.queue(), bytemuck::cast_slice(ids));
119    }
120
121    pub(crate) fn copy_observations(&mut self, encoder: &mut SubmissionEncoder, step: u64) {
122        let count = self.view.len();
123        if count == 0 {
124            return;
125        }
126        let stride = self.backend.streams.state.observed_states.stride();
127        let bytes = count as u64 * stride;
128        let displaced = self.backend.readback.observations.enqueue(
129            encoder,
130            self.backend.streams.state.observed_states.buffer(),
131            0,
132            bytes,
133            step,
134        );
135        if let Some((sequence, bytes)) = displaced {
136            self.consume_observations(sequence, &bytes);
137        }
138    }
139
140    pub(crate) fn consume_observations(&mut self, sequence: u64, bytes: &[u8]) {
141        let (records, remainder) = bytes.as_chunks::<{ size_of::<BodyStateRecord>() }>();
142        assert!(
143            remainder.is_empty(),
144            "an observation readback must be a whole number of records"
145        );
146        for chunk in records {
147            self.accept_body(sequence, bytemuck::pod_read_unaligned(chunk));
148        }
149        self.view.epoch = Some(sequence);
150    }
151
152    fn submit_states(&mut self) {
153        let step = self
154            .completed_step()
155            .expect("a body state snapshot requires a completed step");
156        let bytes = u64::from(self.bodies.device_count) * size_of::<BodyStateRecord>() as u64;
157        if bytes == 0 {
158            self.view.epoch = Some(step);
159            return;
160        }
161        let device = self.backend.gpu.device().clone();
162        self.backend
163            .readback
164            .open_states(&device, self.backend.streams.state.body_states.slots());
165        let mut encoder = SubmissionEncoder::new(&device, "dynamis body state readback");
166        let displaced = self
167            .backend
168            .readback
169            .states
170            .as_mut()
171            .expect("a body state snapshot opens its readback ring")
172            .enqueue(
173                &mut encoder,
174                self.backend.streams.state.body_states.buffer(),
175                0,
176                bytes,
177                step,
178            );
179        self.submit(encoder);
180        if let Some((sequence, bytes)) = displaced {
181            self.consume_states(sequence, &bytes);
182        }
183    }
184
185    pub(crate) fn consume_states(&mut self, sequence: u64, bytes: &[u8]) {
186        let (records, remainder) = bytes.as_chunks::<{ size_of::<BodyStateRecord>() }>();
187        assert!(
188            remainder.is_empty(),
189            "a body state readback must be a whole number of records"
190        );
191        for chunk in records {
192            self.accept_body(sequence, bytemuck::pod_read_unaligned(chunk));
193        }
194        self.view.epoch = Some(sequence);
195    }
196
197    fn accept_body(&mut self, sequence: u64, record: BodyStateRecord) {
198        let id = record.body_id as usize;
199        assert!(
200            id < self.bodies.ids.len(),
201            "a state readback returned an out-of-range body id"
202        );
203        if record.generation != self.bodies.ids.generation(record.body_id)
204            || self.bodies.index_of[id] == u32::MAX
205        {
206            return;
207        }
208        self.bodies.states[id] = Some(BodyState {
209            position: record.position,
210            prev_position: record.prev_position,
211            orientation: record.orientation,
212            velocity: record.velocity,
213            angular_velocity: record.angular_velocity,
214            inverse_mass: self.bodies.descriptors[id].inverse_mass,
215            com: self.bodies.descriptors[id].com,
216            sleeping: record.sleeping != 0,
217            step: sequence,
218        });
219        self.bodies.covered[id] = sequence + 1;
220    }
221}