1mod capacity;
2mod domain;
3mod streams;
4
5pub use capacity::{BroadphaseCapacity, BroadphaseInputs, Capacity};
6pub use domain::BroadphaseDomain;
7pub use streams::{
8 BroadphaseDemand, BroadphaseStream, BroadphaseStreams, entry_capacity, pair_capacity,
9 sort_capacity,
10};
11
12use dynamis_abi::COUNTER_ENTRIES;
13use dynamis_gpu::GpuContext;
14use dynamis_gpu::Resources;
15use dynamis_pass::{Schedule, Stage, domain_passes};
16use dynamis_shader::{GRID_INDEX, stream};
17use dynamis_sort::{RadixSort, SortChannels};
18use dynamis_state::StateStream;
19
20domain_passes!(BroadphasePasses, broadphase => &["entries", "soft_entries"]);
21
22#[derive(Clone, Copy, Debug, PartialEq, Eq)]
23pub struct BroadphaseFrame {
24 pub indexing: bool,
25}
26
27pub fn capacity(streams: &BroadphaseStreams) -> BroadphaseCapacity {
28 BroadphaseCapacity {
29 entries: streams.entry_keys.slots(),
30 pairs: streams.pair_major.slots(),
31 }
32}
33
34pub struct Broadphase {
35 passes: BroadphasePasses,
36 sort: RadixSort,
37 cell_pairs: Stage,
38 level_links: Stage,
39}
40
41impl Broadphase {
42 pub fn new(context: &GpuContext, streams: &impl Resources, passes: BroadphasePasses) -> Self {
43 Self {
44 passes,
45 sort: RadixSort::new(context, "grid sort", sort_capacity(streams)),
46 cell_pairs: Stage::build(
47 context,
48 "cell_pairs",
49 stream(
50 context,
51 include_str!("../shaders/cell_pairs.wgsl"),
52 GRID_INDEX,
53 "work",
54 BroadphaseStream::EntryKeys,
55 ),
56 streams,
57 &[
58 ("pair_major", BroadphaseStream::PairMajor.whole()),
59 ("pair_minor", BroadphaseStream::PairMinor.whole()),
60 ("entry_keys", BroadphaseStream::EntryKeys.whole()),
61 ("entry_order", BroadphaseStream::EntryOrder.whole()),
62 ("entries", BroadphaseStream::Entries.whole()),
63 ("counters", StateStream::Counters.whole()),
64 ],
65 &[],
66 ),
67 level_links: Stage::build(
68 context,
69 "level_links",
70 stream(
71 context,
72 include_str!("../shaders/level_links.wgsl"),
73 GRID_INDEX,
74 "work",
75 BroadphaseStream::EntryKeys,
76 ),
77 streams,
78 &[
79 ("pair_major", BroadphaseStream::PairMajor.whole()),
80 ("pair_minor", BroadphaseStream::PairMinor.whole()),
81 ("entry_keys", BroadphaseStream::EntryKeys.whole()),
82 ("entry_order", BroadphaseStream::EntryOrder.whole()),
83 ("entries", BroadphaseStream::Entries.whole()),
84 ("counters", StateStream::Counters.whole()),
85 ],
86 &[],
87 ),
88 }
89 }
90
91 pub fn sort_entries(
92 &self,
93 recorder: &mut dynamis_gpu::ComputeRecorder,
94 streams: &impl Resources,
95 ) {
96 let count = dynamis_state::counter(COUNTER_ENTRIES).resolve(streams);
97 let channels = SortChannels {
98 generation: streams.generation(),
99 count,
100 major: BroadphaseStream::EntryKeys.whole().resolve(streams),
101 minor: BroadphaseStream::SortDummy.whole().resolve(streams),
102 payload: BroadphaseStream::EntryOrder.whole().resolve(streams),
103 scratch_major: BroadphaseStream::SortScratchMajor.whole().resolve(streams),
104 scratch_minor: BroadphaseStream::SortScratchMinor.whole().resolve(streams),
105 scratch_payload: BroadphaseStream::SortScratchPayload
106 .whole()
107 .resolve(streams),
108 };
109 self.sort.sort(recorder, &channels, 4, 0);
110 }
111
112 pub fn record(
113 &self,
114 pass: u32,
115 schedule: &mut Schedule,
116 encoder: &mut wgpu::CommandEncoder,
117 streams: &impl Resources,
118 frame: BroadphaseFrame,
119 ) {
120 if pass != self.passes.broadphase || !frame.indexing {
121 return;
122 }
123 let mut index = schedule.open(encoder, pass);
124 self.sort_entries(&mut index, streams);
125 self.cell_pairs.record_stream(&mut index, streams);
126 self.level_links.record_stream(&mut index, streams);
127 drop(index);
128 }
129}