dynamis-world 0.6.0

GPU-driven physics engine
Documentation
use super::{Shapes, World};
use crate::dynamics::Pipeline;
use crate::dynamics::buffers::WorldBuffers;
use crate::dynamics::capacity::{Capacity, Reservation, ShapeReservation};
use dynamis_gpu::GpuContext;
#[cfg(feature = "profile")]
use dynamis_gpu::GpuPassTiming;
use dynamis_layout::{
    COUNTER_ARCHIVED, COUNTER_COUNT, COUNTER_RESTING, COUNTER_RESTING_INDEX,
    COUNTER_RESTING_PENDING, COUNTER_STRIDE, Counters,
};
use dynamis_model::MAX_COLLIDERS_PER_BODY;

pub(crate) struct Backend {
    pub(crate) gpu: GpuContext,
    pub(crate) buffers: WorldBuffers,
    pub(crate) pipeline: Pipeline,
    pub(crate) reservation: Reservation,
    pub(crate) shape_reservation: ShapeReservation,
    pub(crate) capacity: Capacity,
    pub(crate) measured: Counters,
    pub(crate) measured_step: Option<u64>,
    pub(crate) commanded_step: Option<u64>,
    pub(crate) state_readback: Option<dynamis_gpu::BufferReadback>,
    #[cfg(feature = "profile")]
    pub(crate) pass_timings: Vec<GpuPassTiming>,
}

impl Backend {
    pub(crate) fn new(gpu: GpuContext, shapes: &Shapes) -> Self {
        let reservation = Reservation::initial();
        let shape_reservation =
            ShapeReservation::planned(&ShapeReservation::EMPTY, &shapes.pool.used());
        let buffers =
            WorldBuffers::new(gpu.device(), gpu.queue(), &reservation, &shape_reservation);
        let pipeline = Pipeline::new(&gpu, &buffers, &reservation);
        Self {
            gpu,
            buffers,
            pipeline,
            reservation,
            shape_reservation,
            capacity: Capacity::new(),
            measured: [0; COUNTER_COUNT],
            measured_step: None,
            commanded_step: None,
            state_readback: None,
            #[cfg(feature = "profile")]
            pass_timings: Vec::new(),
        }
    }
}

impl World {
    pub(crate) fn apply_plan(&mut self) {
        let demand = self
            .backend
            .capacity
            .observe(&self.backend.measured, &self.backend.reservation);
        let next = Reservation::planned(&self.backend.reservation, &self.live(), demand);
        let shapes =
            ShapeReservation::planned(&self.backend.shape_reservation, &self.shapes.pool.used());
        if next == self.backend.reservation && shapes == self.backend.shape_reservation {
            return;
        }
        self.backend.reservation = next;
        self.backend.shape_reservation = shapes;
        self.drain_readbacks();
        self.rebuild();
    }

    pub(crate) fn rebuild(&mut self) {
        let buffers = WorldBuffers::new(
            self.backend.gpu.device(),
            self.backend.gpu.queue(),
            &self.backend.reservation,
            &self.backend.shape_reservation,
        );

        self.transfer_device_state(&buffers);

        self.upload_host_state(&buffers);
        self.shapes.pool.reset_upload_cursor();
        self.shapes.pool.upload_pending(
            self.backend.gpu.queue(),
            &buffers.shapes.sources,
            &buffers.shapes.vertices,
            &buffers.shapes.triangles,
            &buffers.shapes.nodes,
        );

        self.backend.pipeline =
            Pipeline::new(&self.backend.gpu, &buffers, &self.backend.reservation);

        self.backend.buffers = buffers;
    }

    fn transfer_device_state(&self, next: &WorldBuffers) {
        let previous = &self.backend.buffers;
        let bodies = previous.bodies.states.size().min(next.bodies.states.size());
        let aabbs = previous.bodies.aabbs.size().min(next.bodies.aabbs.size());
        let rows = previous.bodies.rows.size().min(next.bodies.rows.size());
        let contacts = previous
            .contacts
            .manifolds
            .size()
            .min(next.contacts.manifolds.size());
        let island = previous
            .islands
            .parents
            .size()
            .min(next.islands.parents.size());
        let wake_flags = previous
            .islands
            .wake_flags
            .size()
            .min(next.islands.wake_flags.size());
        let constraints = previous
            .constraints
            .runtime
            .size()
            .min(next.constraints.runtime.size());
        let resting = previous
            .contacts
            .resting
            .size()
            .min(next.contacts.resting.size());
        let resting_index = previous
            .contacts
            .resting_index
            .major
            .size()
            .min(next.contacts.resting_index.major.size());
        let resting_live = previous
            .contacts
            .resting_live
            .size()
            .min(next.contacts.resting_live.size());
        let resting_next = previous
            .contacts
            .resting_next
            .size()
            .min(next.contacts.resting_next.size());
        let resting_free = previous
            .contacts
            .resting_free
            .size()
            .min(next.contacts.resting_free.size());
        let mut encoder =
            self.backend
                .gpu
                .device()
                .create_command_encoder(&wgpu::CommandEncoderDescriptor {
                    label: Some("dynamis reallocate"),
                });
        let mut copy =
            |source: &dynamis_gpu::GpuBuffer, target: &dynamis_gpu::GpuBuffer, bytes: u64| {
                encoder.copy_buffer_to_buffer(source.buffer(), 0, target.buffer(), 0, bytes);
            };
        copy(&previous.bodies.states, &next.bodies.states, bodies);
        copy(&previous.bodies.rows, &next.bodies.rows, rows);
        copy(&previous.bodies.aabbs, &next.bodies.aabbs, aabbs);
        copy(&previous.contacts.archive, &next.contacts.archive, contacts);
        copy(
            &previous.islands.wake_flags,
            &next.islands.wake_flags,
            wake_flags,
        );
        copy(&previous.islands.parents, &next.islands.parents, island);
        copy(&previous.islands.state, &next.islands.state, island);
        copy(
            &previous.constraints.runtime,
            &next.constraints.runtime,
            constraints,
        );
        copy(&previous.contacts.resting, &next.contacts.resting, resting);
        copy(
            &previous.contacts.resting_index.major,
            &next.contacts.resting_index.major,
            resting_index,
        );
        copy(
            &previous.contacts.resting_index.minor,
            &next.contacts.resting_index.minor,
            resting_index,
        );
        copy(
            &previous.contacts.resting_index.payload,
            &next.contacts.resting_index.payload,
            resting_index,
        );
        copy(
            &previous.contacts.resting_live,
            &next.contacts.resting_live,
            resting_live,
        );
        copy(
            &previous.contacts.resting_next,
            &next.contacts.resting_next,
            resting_next,
        );
        copy(
            &previous.contacts.resting_free,
            &next.contacts.resting_free,
            resting_free,
        );
        for slot in [
            COUNTER_ARCHIVED,
            COUNTER_RESTING,
            COUNTER_RESTING_INDEX,
            COUNTER_RESTING_PENDING,
        ] {
            let offset = slot as u64 * COUNTER_STRIDE;
            encoder.copy_buffer_to_buffer(
                previous.counters.buffer(),
                offset,
                next.counters.buffer(),
                offset,
                4,
            );
        }
        self.backend.gpu.queue().submit([encoder.finish()]);
    }

    fn upload_host_state(&self, next: &WorldBuffers) {
        let queue = self.backend.gpu.queue();
        let mut descriptors = Vec::with_capacity(self.bodies.alive.len());
        let mut colliders = Vec::with_capacity(self.bodies.alive.len() * MAX_COLLIDERS_PER_BODY);
        for handle in &self.bodies.alive {
            descriptors.push(self.bodies.descriptors[handle.id as usize]);
            colliders.extend_from_slice(&self.collider_block_of(handle.id as usize));
        }
        next.bodies
            .descriptors
            .write(queue, bytemuck::cast_slice(&descriptors));
        next.bodies
            .colliders
            .write(queue, bytemuck::cast_slice(&colliders));
        next.constraints.descriptors.write(
            queue,
            bytemuck::cast_slice(&self.constraints.records[..self.constraints.alive.len()]),
        );
    }
}