Skip to main content

dynamis_world/
readback.rs

1use super::World;
2use dynamis_abi::COUNTER_RESTING;
3use dynamis_abi::{
4    COUNTER_CONTACTS, COUNTER_DEVICE_COUNT, COUNTER_STRIDE, ConstraintReactionRecord,
5    ConstraintRuntimeRecord, ContactRecord, Counters, DeclaredCounters, FEATURE_KIND_MASK,
6    FEATURE_TRIANGLE, FEATURE_TRIANGLE_MASK, JointStateRecord, NO_SURFACE, SHAPE_HEIGHTFIELD,
7    SHAPE_MESH,
8};
9use dynamis_model::{BodyHandle, ConstraintHandle, JointState, SurfaceDesc};
10use std::collections::HashSet;
11use std::mem::size_of;
12
13pub struct ContactPoint {
14    pub position: [f32; 3],
15    pub depth: f32,
16    pub normal_impulse: f32,
17    pub tangent_impulse: f32,
18    pub feature: u32,
19    pub triangle: Option<u32>,
20}
21
22pub struct ContactManifold {
23    pub first: BodyHandle,
24    pub second: BodyHandle,
25    pub sensor: bool,
26    pub normal: [f32; 3],
27    pub material: SurfaceDesc,
28    pub surface: Option<SurfaceDesc>,
29    pub points: Vec<ContactPoint>,
30    pub step: u64,
31}
32
33pub struct ConstraintForce {
34    pub constraint: ConstraintHandle,
35    pub first: BodyHandle,
36    pub second: BodyHandle,
37    pub force_on_second: [f32; 3],
38    pub torque_on_second: [f32; 3],
39}
40
41const COUNTER_BYTES: u64 = COUNTER_STRIDE * COUNTER_DEVICE_COUNT as u64;
42const CONTACT_BYTES: u64 = size_of::<ContactRecord>() as u64;
43const DECLARED_DEPTH: usize = 8;
44
45fn measured_counters(bytes: &[u8], counters: &mut Counters) {
46    let stride = COUNTER_STRIDE as usize;
47    for (slot, value) in counters.iter_mut().enumerate().take(COUNTER_DEVICE_COUNT) {
48        let at = slot * stride;
49        *value = u32::from_le_bytes(bytes[at..at + 4].try_into().expect("counter slot"));
50    }
51}
52
53impl World {
54    pub(crate) fn pack_step(&self, encoder: &mut wgpu::CommandEncoder) -> u64 {
55        encoder.copy_buffer_to_buffer(
56            self.backend.streams.state.counters.buffer(),
57            0,
58            self.backend.readback.pack.buffer(),
59            0,
60            COUNTER_BYTES,
61        );
62        COUNTER_BYTES
63    }
64
65    pub(crate) fn declare_step(&mut self, step: u64) {
66        let declared = DeclaredCounters {
67            bodies: self.bodies.alive.len() as u32,
68            colliders: self.colliders.used(),
69            constraints: self.constraints.alive.len() as u32,
70            body_edits: self.bodies.last_edits,
71            body_moves: self.bodies.last_moves,
72            constraint_commands: self.constraints.last_commands,
73            constraint_moves: self.constraints.last_moves,
74        };
75        assert!(
76            self.backend.declared.len() < DECLARED_DEPTH,
77            "a step declaration outlived its counter readback"
78        );
79        self.backend.declared.push_back((step, declared));
80    }
81
82    pub(crate) fn consume_pack(&mut self, step: u64, bytes: &[u8]) {
83        let (declared_step, declared) = self
84            .backend
85            .declared
86            .pop_front()
87            .expect("a counter readback retires a declared step");
88        assert_eq!(
89            declared_step, step,
90            "counter readbacks must retire in declaration order"
91        );
92        declared.write_into(&mut self.backend.measured);
93        measured_counters(bytes, &mut self.backend.measured);
94        self.accept_measured(step);
95    }
96
97    pub fn constraint_forces(&mut self) -> Vec<ConstraintForce> {
98        let count = self.constraints.alive.len();
99        if count == 0 {
100            return Vec::new();
101        }
102        self.wait();
103        let runtime = &self.backend.streams.state.constraint_runtime;
104        let bytes = count as u64 * runtime.stride();
105        let buffer = runtime.buffer().clone();
106        let read = self.read_regions("constraint force readback", &[(&buffer, 0, bytes)]);
107        dynamis_abi::decode::<ConstraintRuntimeRecord>(&read)
108            .into_iter()
109            .enumerate()
110            .map(|(row, runtime)| {
111                let constraint = self.constraints.alive[row];
112                let (first, second) = self.constraint_bodies(constraint);
113                self.constraint_force_of(constraint, first, second, runtime.reaction)
114            })
115            .collect()
116    }
117
118    pub fn constraint_force(&mut self, handle: ConstraintHandle) -> ConstraintForce {
119        self.validate_constraint(handle);
120        let row = self.constraints.index_of[handle.id as usize];
121        self.wait();
122        let runtime = &self.backend.streams.state.constraint_runtime;
123        let stride = runtime.stride();
124        let buffer = runtime.buffer().clone();
125        let read = self.read_regions(
126            "constraint force readback",
127            &[(&buffer, u64::from(row) * stride, stride)],
128        );
129        let record = dynamis_abi::decode::<ConstraintRuntimeRecord>(&read)
130            .first()
131            .copied()
132            .expect("a constraint force read covers exactly one record");
133        let (first, second) = self.constraint_bodies(handle);
134        self.constraint_force_of(handle, first, second, record.reaction)
135    }
136
137    pub fn joint_states(&mut self) -> Vec<(ConstraintHandle, JointState)> {
138        let count = self.constraints.alive.len();
139        if count == 0 {
140            return Vec::new();
141        }
142        self.wait();
143        let states = &self.backend.streams.rigid.joint_states;
144        let bytes = count as u64 * states.stride();
145        let buffer = states.buffer().clone();
146        let read = self.read_regions("joint state readback", &[(&buffer, 0, bytes)]);
147        dynamis_abi::decode::<JointStateRecord>(&read)
148            .into_iter()
149            .enumerate()
150            .map(|(row, state)| (self.constraints.alive[row], self.joint_state_of(row, state)))
151            .collect()
152    }
153
154    pub fn joint_state(&mut self, handle: ConstraintHandle) -> JointState {
155        self.validate_constraint(handle);
156        let row = self.constraints.index_of[handle.id as usize];
157        self.wait();
158        let states = &self.backend.streams.rigid.joint_states;
159        let stride = states.stride();
160        let buffer = states.buffer().clone();
161        let read = self.read_regions(
162            "joint state readback",
163            &[(&buffer, u64::from(row) * stride, stride)],
164        );
165        let state = dynamis_abi::decode::<JointStateRecord>(&read)
166            .first()
167            .copied()
168            .expect("a joint state read covers exactly one record");
169        self.joint_state_of(row as usize, state)
170    }
171
172    fn joint_state_of(&self, row: usize, state: JointStateRecord) -> JointState {
173        let kind = self.constraints.records[row].constraint_kind();
174        assert_eq!(
175            state.dof_count as usize,
176            kind.dofs().len(),
177            "the device and the host must agree on the {kind:?} dof layout"
178        );
179        JointState::new(kind, state.coordinates, state.rates, state.impulses)
180    }
181
182    fn constraint_force_of(
183        &self,
184        constraint: ConstraintHandle,
185        first: BodyHandle,
186        second: BodyHandle,
187        reaction: ConstraintReactionRecord,
188    ) -> ConstraintForce {
189        let step_dt = self.clock.sub_dt;
190        ConstraintForce {
191            constraint,
192            first,
193            second,
194            force_on_second: [
195                reaction.linear_second[0] / step_dt,
196                reaction.linear_second[1] / step_dt,
197                reaction.linear_second[2] / step_dt,
198            ],
199            torque_on_second: [
200                reaction.angular_second[0] / step_dt,
201                reaction.angular_second[1] / step_dt,
202                reaction.angular_second[2] / step_dt,
203            ],
204        }
205    }
206
207    pub fn contact_manifolds(&mut self) -> Vec<ContactManifold> {
208        self.wait();
209        let step = self.clock.step.saturating_sub(1);
210        let active = self.backend.measured[COUNTER_CONTACTS] as usize;
211        let capacity =
212            (self.backend.streams.rigid.resting_contacts.size() / CONTACT_BYTES) as usize;
213        let resting = (self.backend.measured[COUNTER_RESTING] as usize).min(capacity);
214        if active == 0 && resting == 0 {
215            return Vec::new();
216        }
217        let active_buffer = self.backend.streams.rigid.contacts.buffer().clone();
218        let resting_buffer = self.backend.streams.rigid.resting_contacts.buffer().clone();
219        let resting_live = self.backend.streams.rigid.resting_live.buffer().clone();
220        let mut regions = Vec::with_capacity(3);
221        if active > 0 {
222            regions.push((&active_buffer, 0, active as u64 * CONTACT_BYTES));
223        }
224        if resting > 0 {
225            regions.push((&resting_live, 0, resting as u64 * 4));
226            regions.push((&resting_buffer, 0, resting as u64 * CONTACT_BYTES));
227        }
228        let bytes = self.read_regions("world contact readback", &regions);
229        let mut manifolds = Vec::with_capacity(active + resting);
230        let mut seen = HashSet::new();
231        let active_bytes = active * size_of::<ContactRecord>();
232        for record in dynamis_abi::decode::<ContactRecord>(&bytes[..active_bytes]) {
233            if seen.insert((record.a, record.b)) {
234                manifolds.push(manifold_of(&record, step, self.contact_surface(&record)));
235            }
236        }
237        if resting > 0 {
238            let live = &bytes[active_bytes..active_bytes + resting * 4];
239            let resting_bytes = &bytes[active_bytes + resting * 4..];
240            for (index, record) in dynamis_abi::decode::<ContactRecord>(resting_bytes)
241                .into_iter()
242                .enumerate()
243            {
244                if live[index * 4..index * 4 + 4] == [0, 0, 0, 0] {
245                    continue;
246                }
247                if seen.insert((record.a, record.b)) {
248                    manifolds.push(manifold_of(&record, step, self.contact_surface(&record)));
249                }
250            }
251        }
252        manifolds
253    }
254
255    pub(crate) fn read_regions(
256        &mut self,
257        label: &str,
258        regions: &[(&wgpu::Buffer, u64, u64)],
259    ) -> Vec<u8> {
260        let bytes: u64 = regions.iter().map(|region| region.2).sum();
261        assert!(
262            bytes > 0 && bytes.is_multiple_of(4),
263            "an inspection read must cover a positive word aligned length"
264        );
265        let device = self.backend.gpu.device().clone();
266        let mut readback = match self.backend.inspect.take() {
267            Some(readback) if readback.size() >= bytes => readback,
268            _ => dynamis_gpu::Readback::new(&device, "world inspection readback", bytes, 1),
269        };
270        let mut encoder = dynamis_gpu::SubmissionEncoder::new(&device, label);
271        assert!(
272            readback.enqueue_regions(&mut encoder, regions, 0).is_none(),
273            "an inspection read requires an idle readback"
274        );
275        self.submit(encoder);
276        let entry = readback
277            .drain()
278            .pop()
279            .expect("an inspection read retires exactly once");
280        self.backend.inspect = Some(readback);
281        entry.1
282    }
283
284    pub(crate) fn collect_readbacks(&mut self) {
285        self.backend.gpu.poll();
286        for (step, bytes) in self.backend.readback.step.collect() {
287            self.consume_pack(step, &bytes);
288        }
289        for (_, bytes) in self.backend.readback.events.collect() {
290            self.consume_events(&bytes);
291        }
292        for (_, bytes) in self.backend.readback.breaks.collect() {
293            self.consume_breaks(&bytes);
294        }
295        for (batch, bytes) in self.backend.readback.queries.collect() {
296            self.collect_query_batch(batch, &bytes);
297        }
298        for (sequence, bytes) in self.backend.readback.observations.collect() {
299            self.consume_observations(sequence, &bytes);
300        }
301        for (sequence, bytes) in self.backend.readback.collect_states() {
302            self.consume_states(sequence, &bytes);
303        }
304        #[cfg(feature = "profile")]
305        for timings in self.backend.passes.collect_timings() {
306            self.backend.pass_timings = timings;
307        }
308    }
309
310    pub(crate) fn drain_readbacks(&mut self) {
311        for (step, bytes) in self.backend.readback.step.drain() {
312            self.consume_pack(step, &bytes);
313        }
314        for (_, bytes) in self.backend.readback.events.drain() {
315            self.consume_events(&bytes);
316        }
317        for (_, bytes) in self.backend.readback.breaks.drain() {
318            self.consume_breaks(&bytes);
319        }
320        for (batch, bytes) in self.backend.readback.queries.drain() {
321            self.collect_query_batch(batch, &bytes);
322        }
323        for (sequence, bytes) in self.backend.readback.observations.drain() {
324            self.consume_observations(sequence, &bytes);
325        }
326        for (sequence, bytes) in self.backend.readback.drain_states() {
327            self.consume_states(sequence, &bytes);
328        }
329        #[cfg(feature = "profile")]
330        for timings in self.backend.passes.collect_timings() {
331            self.backend.pass_timings = timings;
332        }
333    }
334
335    pub(crate) fn accept_measured(&mut self, step: u64) {
336        self.backend.measured_step = Some(step);
337        self.note_events_due(step);
338        self.note_breaks_due(step);
339    }
340
341    pub fn measured(&self) -> &Counters {
342        &self.backend.measured
343    }
344
345    pub(crate) fn contact_surface(&self, record: &ContactRecord) -> Option<SurfaceDesc> {
346        if record.surface == NO_SURFACE {
347            return None;
348        }
349        for slot in [record.a, record.b] {
350            let collider = self.colliders.records()[slot as usize];
351            if collider.kind == SHAPE_MESH || collider.kind == SHAPE_HEIGHTFIELD {
352                return Some(
353                    self.shapes
354                        .pool
355                        .source_surface(collider.source, record.surface),
356                );
357            }
358        }
359        panic!("a contact surface must belong to its scene geometry");
360    }
361
362    pub(crate) fn accept_constraint_break(&mut self, constraint_id: u32, generation: u32) {
363        let id = constraint_id as usize;
364        if id >= self.constraints.ids.len() {
365            return;
366        }
367        if self.constraints.ids.generation(constraint_id) != generation
368            || self.constraints.index_of[id] == u32::MAX
369        {
370            return;
371        }
372        let handle = ConstraintHandle {
373            id: constraint_id,
374            generation,
375        };
376        self.constraints.broken.push(handle);
377        self.remove_constraint(handle);
378    }
379}
380
381fn feature_triangle(feature: u32) -> Option<u32> {
382    ((feature & FEATURE_KIND_MASK) == FEATURE_TRIANGLE).then_some(feature & FEATURE_TRIANGLE_MASK)
383}
384
385fn manifold_of(record: &ContactRecord, step: u64, surface: Option<SurfaceDesc>) -> ContactManifold {
386    ContactManifold {
387        first: BodyHandle {
388            id: record.first_body_id,
389            generation: record.first_generation,
390        },
391        second: BodyHandle {
392            id: record.second_body_id,
393            generation: record.second_generation,
394        },
395        sensor: record.sensor == 1,
396        normal: record.normal,
397        material: SurfaceDesc {
398            friction: record.friction,
399            restitution: record.restitution,
400            rolling_friction: record.rolling_friction,
401            spin_friction: record.spin_friction,
402        },
403        surface,
404        points: record.points[..record.point_count as usize]
405            .iter()
406            .map(|point| ContactPoint {
407                position: point.position,
408                depth: point.depth,
409                normal_impulse: point.accumulated_normal,
410                tangent_impulse: (point.accumulated_tangent_1 * point.accumulated_tangent_1
411                    + point.accumulated_tangent_2 * point.accumulated_tangent_2)
412                    .sqrt(),
413                feature: point.feature,
414                triangle: feature_triangle(point.feature),
415            })
416            .collect(),
417        step,
418    }
419}