dynamis-world 0.7.0

Host scene registry, step orchestration, and readback
Documentation
use super::registry::Plan;
use dynamis_abi::{
    BodyStateRecord, BrokenConstraintRecord, COUNTER_DEVICE_COUNT, COUNTER_STRIDE,
    ContactEventRecord,
};
use dynamis_gpu::{GpuBuffer, Readback};
use dynamis_state::QUERY_RESULT_BYTES;
use std::mem::size_of;
use wgpu::Device;

const COUNTER_BYTES: u64 = COUNTER_STRIDE * COUNTER_DEVICE_COUNT as u64;

pub(crate) struct ReadbackBuffers {
    pub(crate) pack: GpuBuffer,
    pub(crate) step: Readback,
    pub(crate) events: Readback,
    pub(crate) breaks: Readback,
    pub(crate) queries: Readback,
    pub(crate) observations: Readback,
    pub(crate) states: Option<Readback>,
}

fn readback_sizes(plan: &Plan) -> (u64, u64, u64, u64, u64) {
    (
        COUNTER_BYTES,
        u64::from(plan.rigid.events) * size_of::<ContactEventRecord>() as u64,
        u64::from(plan.state.constraints) * size_of::<BrokenConstraintRecord>() as u64,
        u64::from(plan.state.queries) * QUERY_RESULT_BYTES,
        u64::from(plan.state.observed) * size_of::<BodyStateRecord>() as u64,
    )
}

impl ReadbackBuffers {
    pub(crate) fn new(device: &Device, plan: &Plan) -> Self {
        let (pack_bytes, event_bytes, break_bytes, query_bytes, observation_bytes) =
            readback_sizes(plan);
        Self {
            pack: GpuBuffer::new(device, "world readback pack", pack_bytes, dynamis_gpu::PACK),
            step: Readback::new(device, "world readback", pack_bytes, Readback::DEPTH),
            events: Readback::new(
                device,
                "world events readback",
                event_bytes,
                Readback::DEPTH,
            ),
            breaks: Readback::new(
                device,
                "constraint breaks readback",
                break_bytes,
                Readback::DEPTH,
            ),
            queries: Readback::new(
                device,
                "query results readback",
                query_bytes,
                Readback::DEPTH,
            ),
            observations: Readback::new(
                device,
                "body observation readback",
                observation_bytes,
                Readback::DEPTH,
            ),
            states: None,
        }
    }

    pub(crate) fn matches(&self, plan: &Plan) -> bool {
        let (pack_bytes, event_bytes, break_bytes, query_bytes, observation_bytes) =
            readback_sizes(plan);
        self.pack.size() == pack_bytes
            && self.step.size() == pack_bytes
            && self.events.size() == event_bytes
            && self.breaks.size() == break_bytes
            && self.queries.size() == query_bytes
            && self.observations.size() == observation_bytes
    }

    pub(crate) fn reserve(&mut self, device: &Device, plan: &Plan) -> bool {
        let (pack_bytes, event_bytes, break_bytes, query_bytes, observation_bytes) =
            readback_sizes(plan);
        if self.matches(plan) {
            return false;
        }
        assert!(
            self.step.is_idle()
                && self.events.is_idle()
                && self.breaks.is_idle()
                && self.queries.is_idle()
                && self.observations.is_idle(),
            "readback buffers require drained rings before they reallocate"
        );
        if self.pack.size() != pack_bytes {
            self.pack =
                GpuBuffer::new(device, "world readback pack", pack_bytes, dynamis_gpu::PACK);
            self.step = Readback::new(device, "world readback", pack_bytes, Readback::DEPTH);
        }
        if self.events.size() != event_bytes {
            self.events = Readback::new(
                device,
                "world events readback",
                event_bytes,
                Readback::DEPTH,
            );
        }
        if self.breaks.size() != break_bytes {
            self.breaks = Readback::new(
                device,
                "constraint breaks readback",
                break_bytes,
                Readback::DEPTH,
            );
        }
        if self.queries.size() != query_bytes {
            self.queries = Readback::new(
                device,
                "query results readback",
                query_bytes,
                Readback::DEPTH,
            );
        }
        if self.observations.size() != observation_bytes {
            self.observations = Readback::new(
                device,
                "body observation readback",
                observation_bytes,
                Readback::DEPTH,
            );
        }
        true
    }

    pub(crate) fn open_states(&mut self, device: &Device, slots: u32) {
        let bytes = u64::from(slots) * size_of::<BodyStateRecord>() as u64;
        if let Some(states) = &self.states {
            if states.size() >= bytes {
                return;
            }
            assert!(
                states.is_idle(),
                "the body state readback requires a drained ring before it reallocates"
            );
        }
        self.states = Some(Readback::new(
            device,
            "body state readback",
            bytes,
            Readback::DEPTH,
        ));
    }

    pub(crate) fn collect_states(&mut self) -> Vec<(u64, Vec<u8>)> {
        match &mut self.states {
            Some(states) => states.collect(),
            None => Vec::new(),
        }
    }

    pub(crate) fn drain_states(&mut self) -> Vec<(u64, Vec<u8>)> {
        match &mut self.states {
            Some(states) => states.drain(),
            None => Vec::new(),
        }
    }
}