dynamis-world 0.7.0

Host scene registry, step orchestration, and readback
Documentation
use crate::World;
use dynamis_abi::{Counters, FrameCounts, RowStreams, StepParamsRecord};
use dynamis_broadphase::BroadphaseDomain;
use dynamis_domain::StepFacts;
use dynamis_gpu::{ComputeRecorder, GpuContext};
use dynamis_pass::{Pass, PipelineBuilder, Schedule};
use dynamis_rigid::RigidDomain;
use dynamis_soft::SoftDomain;
use dynamis_state::StateDomain;
use wgpu::CommandEncoder;

dynamis_domain::domains! {
    state: StateDomain,
    broadphase: BroadphaseDomain,
    rigid: RigidDomain,
    soft: SoftDomain,
    [rigid <-> soft]
}

pub(crate) const ACTIVITY_LAG: u32 = dynamis_gpu::Readback::DEPTH as u32 + 2;

#[derive(Clone, Copy)]
pub(crate) struct Rest {
    quiet: u32,
}

impl Rest {
    pub(crate) const IDLE: Self = Self { quiet: 0 };

    pub(crate) fn gate(&mut self, simulating: bool) -> bool {
        if simulating {
            self.quiet = 0;
            return true;
        }
        self.quiet = self.quiet.saturating_add(1);
        self.quiet < ACTIVITY_LAG
    }
}

impl Planning {
    pub(crate) fn plan(&mut self, measured: &Counters, live: &Live, streams: &Streams) -> Plan {
        let (broadphase, idle) =
            self.broadphase
                .plan(measured, &live.broadphase, &streams.broadphase);
        let rigid = self.rigid.plan(
            measured,
            &live.rigid,
            idle,
            broadphase.pairs,
            &streams.rigid,
        );
        let state = dynamis_state::plan(&live.state, idle, &streams.state);
        let soft = dynamis_soft::plan(&live.soft, idle, state.bodies, &streams.soft);
        Plan {
            state,
            broadphase,
            rigid,
            soft,
        }
    }
}

pub(crate) struct StepPasses {
    schedule: Schedule,
    runtimes: PassRuntimes,
}

impl StepPasses {
    pub(crate) fn new(context: &GpuContext, streams: &Streams) -> Self {
        let mut builder = PipelineBuilder::new();
        PassIds::declare(&mut builder);
        let pipeline = builder.resolve();
        let ids = PassIds::resolve(&pipeline);
        Self {
            schedule: Schedule::new(
                context,
                pipeline,
                #[cfg(feature = "profile")]
                "dynamis step",
            ),
            runtimes: PassRuntimes::build(context, streams, ids),
        }
    }

    pub(crate) fn record(
        &mut self,
        encoder: &mut CommandEncoder,
        streams: &Streams,
        frames: &StepFrames,
    ) {
        self.schedule.begin_step();
        let Self { schedule, runtimes } = self;
        for index in 0..schedule.pipeline().len() as u32 {
            let pass = schedule.pass(index);
            runtimes.record(pass, index, schedule, encoder, streams, frames);
        }
    }

    pub(crate) fn encode_queries(
        &self,
        encoder: &mut CommandEncoder,
        streams: &Streams,
        frames: &StepFrames,
    ) {
        let mut commands =
            ComputeRecorder::begin(encoder, "query commands", self.schedule.per_row());
        self.runtimes
            .rigid
            .simulation
            .record_query_commands(&mut commands, streams, &frames.rigid);
        drop(commands);

        let mut sort = ComputeRecorder::begin(encoder, "query sort", self.schedule.per_row());
        self.runtimes.broadphase.sort_entries(&mut sort, streams);
        drop(sort);

        let mut flush = ComputeRecorder::begin(encoder, "query flush", self.schedule.per_row());
        self.runtimes
            .rigid
            .simulation
            .record_query_flush(&mut flush, streams, &frames.rigid);
        drop(flush);
    }

    pub(crate) fn declared(&self) -> &[Pass] {
        self.schedule.pipeline().passes()
    }

    #[cfg(feature = "profile")]
    pub(crate) fn capture_timings(
        &mut self,
        encoder: &mut dynamis_gpu::SubmissionEncoder,
    ) -> Option<Vec<dynamis_gpu::GpuPassTiming>> {
        self.schedule.capture_timings(encoder)
    }

    #[cfg(feature = "profile")]
    pub(crate) fn collect_timings(&mut self) -> Vec<Vec<dynamis_gpu::GpuPassTiming>> {
        self.schedule.collect_timings()
    }
}

impl World {
    pub(crate) fn live(&self) -> Live {
        let (particles, elements, attachments, adjacency) = self.soft.used();
        let bodies = self.bodies.alive.len() as u32;
        let colliders = self.colliders.live();
        let collider_pool = self.colliders.used();
        let constraints = self.constraints.alive.len() as u32;
        let body_commands = self.bodies.commands.len() as u32;
        let constraint_commands = self.constraints.commands.len() as u32;
        let queries = self.queries.pending.len() as u32;
        Live {
            state: dynamis_state::StateInputs {
                bodies,
                body_ids: self.bodies.ids.len() as u32,
                collider_pool,
                constraints,
                body_commands,
                constraint_commands,
                queries,
                shapes: self.shapes.pool.used(),
                observed: self.view.len(),
            },
            broadphase: dynamis_broadphase::BroadphaseInputs {
                colliders,
                particles,
                pending_commands: body_commands > 0 || constraint_commands > 0,
            },
            rigid: dynamis_rigid::RigidInputs {
                bodies,
                colliders,
                collider_pool,
                constraints,
                queries,
                observed: self.view.len(),
                ccd: self.ccd_active(),
            },
            soft: dynamis_soft::SoftInputs {
                particles,
                elements,
                attachments,
                adjacency,
                bodies: self.soft.ids_len() as u32,
                material: self.soft.carries_strength(),
            },
        }
    }

    pub(crate) fn host_work(&self) -> HostWork {
        HostWork {
            state: dynamis_state::StateWork {
                queries: self.queries.pending.len() as u32,
                shape_uploads: self.shapes.uploaded,
            },
            broadphase: (),
            rigid: dynamis_rigid::RigidWork {
                body_commands: self.bodies.last_edits + self.bodies.last_moves,
                constraint_commands: self.constraints.last_commands + self.constraints.last_moves,
            },
            soft: dynamis_soft::SoftWork {
                uploads: self.soft.uploaded,
            },
        }
    }

    pub(crate) fn frame_counts(&self) -> FrameCounts {
        FrameCounts {
            dynamic_bodies: self.bodies.dynamic_count as u32,
            bodies: self.bodies.alive.len() as u32,
            body_ids: self.bodies.ids.len() as u32,
            colliders: self.colliders.used(),
            constraints: self.constraints.alive.len() as u32,
            particles: self.soft.used().0,
            elements: self.soft.used().1,
            attachments: self.soft.used().2,
            soft_bodies: self.soft.ids_len() as u32,
        }
    }

    pub(crate) fn step_params(&self, dt: f32) -> StepParamsRecord {
        StepParamsRecord::new(
            &self.config,
            dt,
            self.frame_counts(),
            RowStreams {
                edit_runs: self.bodies.last_edits,
                body_moves: self.bodies.last_moves,
                constraint_moves: self.constraints.last_moves,
                observed: self.view.len(),
            },
            self.event_slot_of(self.clock.step),
        )
    }

    pub(crate) fn busy(&self, work: &HostWork) -> bool {
        self.observed(&self.live(), work).busy()
    }

    fn observed(&self, live: &Live, work: &HostWork) -> Activity {
        match self.backend.measured_step {
            None => Activity::BUSY,
            Some(_) => Activity::of(&self.backend.measured, live, work),
        }
    }

    fn gated(&mut self, live: &Live, work: &HostWork) -> Activity {
        if self.backend.measured_step.is_none() {
            return Activity::BUSY;
        }
        let liveness = Activity::of(&self.backend.measured, live, work);
        let hold = self.backend.rest.gate(liveness.simulating());
        liveness.held(hold)
    }

    pub(crate) fn frames(
        &mut self,
        live: &Live,
        work: &HostWork,
        params: StepParamsRecord,
    ) -> StepFrames {
        let liveness = self.gated(live, work);
        let facts = StepFacts {
            params,
            counts: self.frame_counts(),
        };
        StepFrames::of(&facts, live, liveness)
    }
}