dynamis_world/world/
step.rs1use super::World;
2use crate::dynamics::FrameParams;
3use crate::world::commands::{CompiledBodyCommands, CompiledConstraintCommands};
4
5use dynamis_layout::{COUNTER_ACTIVE, QueryResultRecord, RowStreams, StepParamsRecord};
6use std::mem::size_of;
7
8impl World {
9 pub fn set_time_scale(&mut self, time_scale: f32) {
10 assert!(time_scale > 0.0, "time scale must be strictly positive");
11 self.clock.time_scale = time_scale;
12 }
13
14 pub fn update(&mut self, real_dt: f32, sub_dt: f32, max_substeps: u32) {
15 assert!(real_dt >= 0.0, "real dt must be non-negative");
16 assert!(sub_dt > 0.0, "sub dt must be strictly positive");
17 assert!(max_substeps > 0, "max substeps must be positive");
18 self.clock.sub_dt = sub_dt;
19 self.clock.accumulator += real_dt * self.clock.time_scale;
20 let mut steps = 0;
21 while self.clock.accumulator >= sub_dt && steps < max_substeps {
22 self.step(sub_dt);
23 self.clock.accumulator -= sub_dt;
24 steps += 1;
25 }
26 if steps == max_substeps {
27 self.clock.accumulator %= sub_dt;
28 }
29 }
30
31 pub fn interpolation_alpha(&self) -> f32 {
32 (self.clock.accumulator / self.clock.sub_dt).clamp(0.0, 1.0)
33 }
34
35 pub fn step(&mut self, dt: f32) {
36 assert!(dt > 0.0, "timestep must be strictly positive");
37 self.backend.gpu.assert_alive();
38 let step = self.clock.step;
39 self.collect_readbacks();
40 self.apply_plan();
41 self.flush_rows();
42 self.apply_pending_commands();
43 self.write_step_records(dt);
44 let query_count = self.queries.pending.len() as u32;
45 let batch = self.submit_queries(step, query_count);
46 let quiet = self.backend.measured_step.is_some_and(|measured| {
47 self.backend.measured[COUNTER_ACTIVE] == 0
48 && self
49 .backend
50 .commanded_step
51 .is_none_or(|commanded| commanded <= measured)
52 });
53 let idle = quiet && self.constraints.alive.is_empty();
54 let frame = FrameParams {
55 dynamic_count: self.bodies.dynamic_count as u32,
56 body_count: self.bodies.alive.len() as u32,
57 solve_iterations: self.config.solve_iterations,
58 island_rounds: self.island_rounds(),
59 query_count,
60 constraint_count: self.constraints.alive.len() as u32,
61 edit_run_count: self.bodies.last_edits,
62 body_move_count: self.bodies.last_moves,
63 constraint_move_count: self.constraints.last_moves,
64 };
65 self.encode_step(&frame, batch, step, idle);
66 self.bodies.device_count = frame.body_count;
67 self.queries.pending.clear();
68 self.clock.step += 1;
69 self.bodies.states_ready = false;
70 }
71
72 pub(crate) fn apply_pending_commands(&mut self) {
73 if self.bodies.commands.is_empty() && self.constraints.commands.is_empty() {
74 self.bodies.last_edits = 0;
75 self.bodies.last_moves = 0;
76 self.constraints.last_commands = 0;
77 self.constraints.last_moves = 0;
78 return;
79 }
80 self.backend.commanded_step = Some(self.clock.step);
81 let body_commands = self.compile_body_commands();
82 let constraint_commands = self.compile_constraint_commands();
83 self.upload_body_commands(&body_commands);
84 self.upload_constraint_commands(&constraint_commands);
85 self.bodies.last_edits = body_commands.runs.len() as u32;
86 self.constraints.last_commands = self.constraints.commands.len() as u32;
87 self.bodies.commands.clear();
88 self.constraints.commands.clear();
89 }
90
91 fn write_step_records(&self, dt: f32) {
92 let queue = self.backend.gpu.queue();
93 self.write_declared_counters();
94 let params = StepParamsRecord::new(
95 &self.config,
96 dt,
97 self.bodies.dynamic_count as u32,
98 self.bodies.alive.len() as u32,
99 self.constraints.alive.len() as u32,
100 RowStreams {
101 edit_runs: self.bodies.last_edits,
102 body_moves: self.bodies.last_moves,
103 constraint_moves: self.constraints.last_moves,
104 },
105 self.event_slot_of(self.clock.step),
106 );
107 self.backend
108 .buffers
109 .params
110 .write(queue, bytemuck::cast_slice(&[params]));
111 }
112
113 fn upload_body_commands(&mut self, compiled: &CompiledBodyCommands) {
114 let queue = self.backend.gpu.queue();
115 self.backend
116 .buffers
117 .bodies
118 .row_moves
119 .write(queue, bytemuck::cast_slice(&compiled.moves));
120 self.backend
121 .buffers
122 .bodies
123 .fresh_rows
124 .write(queue, bytemuck::cast_slice(&compiled.fresh));
125 self.backend
126 .buffers
127 .bodies
128 .edits
129 .write(queue, bytemuck::cast_slice(&compiled.edits));
130 self.backend
131 .buffers
132 .bodies
133 .edit_runs
134 .write(queue, bytemuck::cast_slice(&compiled.runs));
135 self.bodies.last_moves = compiled.moves.len() as u32;
136 }
137
138 fn upload_constraint_commands(&mut self, compiled: &CompiledConstraintCommands) {
139 let queue = self.backend.gpu.queue();
140 self.backend
141 .buffers
142 .constraints
143 .row_moves
144 .write(queue, bytemuck::cast_slice(&compiled.moves));
145 self.backend
146 .buffers
147 .constraints
148 .fresh_rows
149 .write(queue, bytemuck::cast_slice(&compiled.fresh));
150 self.constraints.last_moves = compiled.moves.len() as u32;
151 }
152
153 fn submit_queries(&mut self, step: u64, query_count: u32) -> Option<u64> {
154 if query_count == 0 {
155 return None;
156 }
157 self.backend.buffers.queries.records.write(
158 self.backend.gpu.queue(),
159 bytemuck::cast_slice(&self.queries.pending),
160 );
161 let batch = self.queries.next_batch;
162 self.queries.pool.submit(batch, step, query_count as usize);
163 self.queries.next_batch += 1;
164 Some(batch)
165 }
166
167 fn encode_step(&mut self, frame: &FrameParams, batch: Option<u64>, step: u64, idle: bool) {
168 let device = self.backend.gpu.device().clone();
169 let queue = self.backend.gpu.queue().clone();
170 let mut encoder = dynamis_gpu::SubmissionEncoder::new(&device, "dynamis step");
171
172 self.copy_events(&mut encoder);
173 self.backend
174 .pipeline
175 .encode(&mut encoder, &self.backend.buffers, frame, idle);
176 #[cfg(feature = "profile")]
177 let timings = self.backend.pipeline.capture_timings(&mut encoder, step);
178 let pack_bytes = self.pack_step(&mut encoder);
179 let pack = self.backend.buffers.readback.step.enqueue(
180 &mut encoder,
181 self.backend.buffers.readback.pack.buffer(),
182 0,
183 pack_bytes,
184 step,
185 );
186 let queries = match batch {
187 Some(batch) => self.backend.buffers.readback.queries.enqueue(
188 &mut encoder,
189 self.backend.buffers.queries.results.buffer(),
190 0,
191 self.queries.pending.len() as u64 * size_of::<QueryResultRecord>() as u64,
192 batch,
193 ),
194 None => None,
195 };
196 encoder.submit(&queue);
197 #[cfg(feature = "profile")]
198 if let Some((_step, timings)) = timings {
199 self.backend.pass_timings = timings;
200 }
201 if let Some((step, bytes)) = pack {
202 self.consume_pack(step, &bytes);
203 }
204 if let Some((batch, bytes)) = queries {
205 self.queries.pool.collect(batch, &bytes);
206 }
207 }
208
209 #[cfg(feature = "profile")]
210 pub fn gpu_pass_timings(&self) -> &[dynamis_gpu::GpuPassTiming] {
211 &self.backend.pass_timings
212 }
213
214 #[cfg(feature = "profile")]
215 pub fn gpu_timing_supported(&self) -> bool {
216 self.backend.gpu.supports_pass_timing()
217 }
218}