Skip to main content

dynamis_world/world/
step.rs

1use 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}