dynamis-broadphase 0.7.0

Spatial grid entries and coarse collision pair generation
Documentation
mod capacity;
mod domain;
mod streams;

pub use capacity::{BroadphaseCapacity, BroadphaseInputs, Capacity};
pub use domain::BroadphaseDomain;
pub use streams::{
    BroadphaseDemand, BroadphaseStream, BroadphaseStreams, entry_capacity, pair_capacity,
    sort_capacity,
};

use dynamis_abi::COUNTER_ENTRIES;
use dynamis_gpu::GpuContext;
use dynamis_gpu::Resources;
use dynamis_pass::{Schedule, Stage, domain_passes};
use dynamis_shader::{GRID_INDEX, stream};
use dynamis_sort::{RadixSort, SortChannels};
use dynamis_state::StateStream;

domain_passes!(BroadphasePasses, broadphase => &["entries", "soft_entries"]);

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct BroadphaseFrame {
    pub indexing: bool,
}

pub fn capacity(streams: &BroadphaseStreams) -> BroadphaseCapacity {
    BroadphaseCapacity {
        entries: streams.entry_keys.slots(),
        pairs: streams.pair_major.slots(),
    }
}

pub struct Broadphase {
    passes: BroadphasePasses,
    sort: RadixSort,
    cell_pairs: Stage,
    level_links: Stage,
}

impl Broadphase {
    pub fn new(context: &GpuContext, streams: &impl Resources, passes: BroadphasePasses) -> Self {
        Self {
            passes,
            sort: RadixSort::new(context, "grid sort", sort_capacity(streams)),
            cell_pairs: Stage::build(
                context,
                "cell_pairs",
                stream(
                    context,
                    include_str!("../shaders/cell_pairs.wgsl"),
                    GRID_INDEX,
                    "work",
                    BroadphaseStream::EntryKeys,
                ),
                streams,
                &[
                    ("pair_major", BroadphaseStream::PairMajor.whole()),
                    ("pair_minor", BroadphaseStream::PairMinor.whole()),
                    ("entry_keys", BroadphaseStream::EntryKeys.whole()),
                    ("entry_order", BroadphaseStream::EntryOrder.whole()),
                    ("entries", BroadphaseStream::Entries.whole()),
                    ("counters", StateStream::Counters.whole()),
                ],
                &[],
            ),
            level_links: Stage::build(
                context,
                "level_links",
                stream(
                    context,
                    include_str!("../shaders/level_links.wgsl"),
                    GRID_INDEX,
                    "work",
                    BroadphaseStream::EntryKeys,
                ),
                streams,
                &[
                    ("pair_major", BroadphaseStream::PairMajor.whole()),
                    ("pair_minor", BroadphaseStream::PairMinor.whole()),
                    ("entry_keys", BroadphaseStream::EntryKeys.whole()),
                    ("entry_order", BroadphaseStream::EntryOrder.whole()),
                    ("entries", BroadphaseStream::Entries.whole()),
                    ("counters", StateStream::Counters.whole()),
                ],
                &[],
            ),
        }
    }

    pub fn sort_entries(
        &self,
        recorder: &mut dynamis_gpu::ComputeRecorder,
        streams: &impl Resources,
    ) {
        let count = dynamis_state::counter(COUNTER_ENTRIES).resolve(streams);
        let channels = SortChannels {
            generation: streams.generation(),
            count,
            major: BroadphaseStream::EntryKeys.whole().resolve(streams),
            minor: BroadphaseStream::SortDummy.whole().resolve(streams),
            payload: BroadphaseStream::EntryOrder.whole().resolve(streams),
            scratch_major: BroadphaseStream::SortScratchMajor.whole().resolve(streams),
            scratch_minor: BroadphaseStream::SortScratchMinor.whole().resolve(streams),
            scratch_payload: BroadphaseStream::SortScratchPayload
                .whole()
                .resolve(streams),
        };
        self.sort.sort(recorder, &channels, 4, 0);
    }

    pub fn record(
        &self,
        pass: u32,
        schedule: &mut Schedule,
        encoder: &mut wgpu::CommandEncoder,
        streams: &impl Resources,
        frame: BroadphaseFrame,
    ) {
        if pass != self.passes.broadphase || !frame.indexing {
            return;
        }
        let mut index = schedule.open(encoder, pass);
        self.sort_entries(&mut index, streams);
        self.cell_pairs.record_stream(&mut index, streams);
        self.level_links.record_stream(&mut index, streams);
        drop(index);
    }
}