dynamis_broadphase/
capacity.rs1use super::streams::{BroadphaseDemand, BroadphaseStreams};
2use dynamis_abi::{
3 COUNTER_ENTRIES, COUNTER_PAIRS, COUNTER_SPILLOVER_ENTRIES, COUNTER_SPILLOVER_PAIRS, Counters,
4 ENTRY_CELLS_PER_PARTICLE, MAX_CELLS_PER_COLLIDER,
5};
6use dynamis_domain::{MIN_SLOTS, STREAM_FLOOR, StreamWatch, product};
7
8const STREAM_DENSITY_PAIRS: u32 = 16;
9const PLAN_COOLDOWN: u32 = 10;
10
11#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
12pub struct BroadphaseCapacity {
13 pub entries: u32,
14 pub pairs: u32,
15}
16
17#[derive(Clone, Copy, Debug)]
18pub struct BroadphaseInputs {
19 pub colliders: u32,
20 pub particles: u32,
21 pub pending_commands: bool,
22}
23
24pub struct Capacity {
25 entries: StreamWatch,
26 pairs: StreamWatch,
27 cooldown: u32,
28}
29
30impl Default for Capacity {
31 fn default() -> Self {
32 Self::new()
33 }
34}
35
36impl Capacity {
37 pub const fn new() -> Self {
38 Self {
39 entries: StreamWatch::IDLE,
40 pairs: StreamWatch::IDLE,
41 cooldown: 0,
42 }
43 }
44
45 pub fn plan(
46 &mut self,
47 measured: &Counters,
48 inputs: &BroadphaseInputs,
49 current: &BroadphaseStreams,
50 ) -> (BroadphaseDemand, bool) {
51 let open = self.cooldown == 0;
52 self.pairs.observe(
53 measured[COUNTER_PAIRS],
54 measured[COUNTER_SPILLOVER_PAIRS] > 0,
55 current.pair_major.slots(),
56 );
57 self.entries.observe(
58 measured[COUNTER_ENTRIES],
59 measured[COUNTER_SPILLOVER_ENTRIES] > 0,
60 current.entry_keys.slots(),
61 );
62 let pressured = self.pairs.pressured() || self.entries.pressured();
63 let idle = open
64 && !pressured
65 && self.pairs.is_idle()
66 && self.entries.is_idle()
67 && !inputs.pending_commands;
68 if pressured {
69 self.entries.settle(false);
70 self.pairs.settle(false);
71 } else if idle {
72 self.entries.settle(true);
73 self.pairs.settle(true);
74 self.cooldown = PLAN_COOLDOWN;
75 } else {
76 self.cooldown = self.cooldown.saturating_sub(1);
77 }
78 let entry_budget = product(
79 inputs.colliders,
80 MAX_CELLS_PER_COLLIDER,
81 "grid collider entry",
82 )
83 .checked_add(product(
84 inputs.particles,
85 ENTRY_CELLS_PER_PARTICLE,
86 "grid particle entry",
87 ))
88 .unwrap_or_else(|| panic!("grid entry capacity exceeds the device index space"));
89 let pair_budget = product(inputs.colliders, STREAM_DENSITY_PAIRS, "pair");
90 let entries = if idle {
91 self.entries
92 .released(current.entry_keys.slots(), entry_budget)
93 } else {
94 self.entries
95 .widened(current.entry_keys.slots(), entry_budget)
96 };
97 let pairs = if idle {
98 self.pairs.released(current.pair_major.slots(), pair_budget)
99 } else {
100 self.pairs.widened(current.pair_major.slots(), pair_budget)
101 };
102 let sort = entries.max(pairs).max(MIN_SLOTS);
103 (
104 BroadphaseDemand {
105 entries,
106 pairs,
107 sort,
108 },
109 idle,
110 )
111 }
112
113 pub fn floor() -> BroadphaseDemand {
114 BroadphaseDemand {
115 entries: STREAM_FLOOR,
116 pairs: STREAM_FLOOR,
117 sort: STREAM_FLOOR,
118 }
119 }
120}