dynamis-world 0.6.0

GPU-driven physics engine
Documentation
use super::World;
use crate::dynamics::buffers::EVENT_SLOTS;
use dynamis_layout::{COUNTER_EVENTS, ContactEventRecord};
use dynamis_model::{BodyHandle, ContactEvent, ContactEventKind};
use std::collections::VecDeque;
use std::mem::size_of;

pub(crate) struct Events {
    pub(crate) contact: Vec<ContactEvent>,
    pub(crate) due: VecDeque<(u64, u32)>,
    pub(crate) sink: Option<Box<dyn FnMut(ContactEvent)>>,
}

impl Events {
    pub(crate) fn new() -> Self {
        Self {
            contact: Vec::new(),
            due: VecDeque::new(),
            sink: None,
        }
    }
}

impl World {
    pub fn drain_events(&mut self) -> Vec<ContactEvent> {
        self.backend.gpu.assert_alive();
        self.collect_readbacks();
        self.sync_events();
        std::mem::take(&mut self.events.contact)
    }

    pub fn set_event_sink(&mut self, sink: Option<Box<dyn FnMut(ContactEvent)>>) {
        self.events.sink = sink;
    }

    pub(crate) fn note_events_due(&mut self, step: u64) {
        let count = self.backend.measured[COUNTER_EVENTS];
        if count > 0 {
            self.events.due.push_back((step, count));
        }
    }

    pub(crate) fn copy_events(&mut self, encoder: &mut dynamis_gpu::SubmissionEncoder) {
        while let Some((step, count)) = self.events.due.pop_front() {
            assert!(
                self.clock.step <= step + EVENT_SLOTS as u64,
                "event segment for step {step} was overwritten before step {} could copy it",
                self.clock.step
            );
            let segment = self.backend.buffers.events.size() / EVENT_SLOTS as u64;
            let offset = (step % EVENT_SLOTS as u64) * segment;
            let bytes = (count as u64 * size_of::<ContactEventRecord>() as u64).min(segment);
            let displaced = self.backend.buffers.readback.events.enqueue(
                encoder,
                self.backend.buffers.events.buffer(),
                offset,
                bytes,
                step,
            );
            if let Some((_, bytes)) = displaced {
                self.consume_events(&bytes);
            }
        }
    }

    pub(crate) fn sync_events(&mut self) {
        if self.events.due.is_empty() {
            return;
        }
        let device = self.backend.gpu.device().clone();
        let mut encoder = dynamis_gpu::SubmissionEncoder::new(&device, "dynamis event readback");
        self.copy_events(&mut encoder);
        encoder.submit(self.backend.gpu.queue());
        for (_, bytes) in self.backend.buffers.readback.events.drain() {
            self.consume_events(&bytes);
        }
    }

    pub(crate) fn consume_events(&mut self, bytes: &[u8]) {
        let records = dynamis_layout::decode::<ContactEventRecord>(bytes);
        let count = records.len();
        let mut fresh = Vec::with_capacity(count);
        for record in &records[..count] {
            let kind = match record.kind {
                dynamis_layout::EVENT_BEGIN => ContactEventKind::Begin,
                dynamis_layout::EVENT_END => ContactEventKind::End,
                dynamis_layout::EVENT_PERSIST => ContactEventKind::Persist,
                other => panic!("GPU event record has an invalid kind {other}"),
            };
            fresh.push(ContactEvent {
                kind,
                first: BodyHandle {
                    id: record.first_id,
                    generation: record.first_generation,
                },
                second: BodyHandle {
                    id: record.second_id,
                    generation: record.second_generation,
                },
                sensor: record.sensor == 1,
                point: record.point,
                normal: record.normal,
            });
        }
        if let Some(sink) = self.events.sink.as_mut() {
            for event in fresh.iter().copied() {
                sink(event);
            }
        }
        self.events.contact.extend(fresh);
    }
}