Skip to main content

dynamis_world/
step.rs

1use super::World;
2use crate::backend::StepFrames;
3use crate::commands::{CompiledBodyCommands, CompiledConstraintCommands};
4use dynamis_abi::QueryResultRecord;
5use dynamis_abi::StepParamsRecord;
6use dynamis_rigid::RigidShape;
7use std::mem::size_of;
8
9impl World {
10    pub fn set_time_scale(&mut self, time_scale: f32) {
11        assert!(time_scale > 0.0, "time scale must be strictly positive");
12        self.clock.time_scale = time_scale;
13    }
14
15    pub fn update(&mut self, real_dt: f32, sub_dt: f32, max_substeps: u32) {
16        assert!(real_dt >= 0.0, "real dt must be non-negative");
17        assert!(sub_dt > 0.0, "sub dt must be strictly positive");
18        assert!(max_substeps > 0, "max substeps must be positive");
19        self.clock.sub_dt = sub_dt;
20        self.clock.accumulator += real_dt * self.clock.time_scale;
21        let mut steps = 0;
22        while self.clock.accumulator >= sub_dt && steps < max_substeps {
23            self.step(sub_dt);
24            self.clock.accumulator -= sub_dt;
25            steps += 1;
26        }
27        if steps == max_substeps {
28            self.clock.accumulator %= sub_dt;
29        }
30    }
31
32    pub fn interpolation_alpha(&self) -> f32 {
33        (self.clock.accumulator / self.clock.sub_dt).clamp(0.0, 1.0)
34    }
35
36    pub fn step(&mut self, dt: f32) {
37        assert!(dt > 0.0, "timestep must be strictly positive");
38        self.backend.gpu.assert_alive();
39        self.clock.sub_dt = dt;
40        let step = self.clock.step;
41        self.collect_readbacks();
42        let live = self.live();
43        self.apply_plan(&live);
44        self.flush_rows();
45        self.apply_pending_commands();
46        let work = self.host_work();
47        let query_count = work.state.queries;
48        self.backend.published = work.pending();
49        let params = self.step_params(dt);
50        let frames = self.frames(&live, &work, params);
51        self.shapes.uploaded = false;
52        self.soft.uploaded = false;
53        self.write_step_records(params);
54        self.declare_step(step);
55        let batch = self.submit_queries(step, query_count);
56        self.encode_step(&frames, batch, step);
57        self.bodies.device_count = params.body_count;
58        self.queries.pending.clear();
59        self.clock.step += 1;
60    }
61
62    pub(crate) fn apply_pending_commands(&mut self) {
63        if self.bodies.commands.is_empty() && self.constraints.commands.is_empty() {
64            self.bodies.last_edits = 0;
65            self.bodies.last_moves = 0;
66            self.constraints.last_commands = 0;
67            self.constraints.last_moves = 0;
68            return;
69        }
70        let body_commands = self.compile_body_commands();
71        let constraint_commands = self.compile_constraint_commands();
72        self.upload_body_commands(&body_commands);
73        self.upload_constraint_commands(&constraint_commands);
74        self.bodies.last_edits = body_commands.runs.len() as u32;
75        self.constraints.last_commands = self.constraints.commands.len() as u32;
76        self.bodies.commands.clear();
77        self.constraints.commands.clear();
78    }
79
80    pub fn rigid_shape(&self) -> RigidShape {
81        RigidShape::of(&self.frame_counts())
82    }
83
84    fn write_step_records(&self, params: StepParamsRecord) {
85        self.backend
86            .streams
87            .state
88            .params
89            .write(self.backend.gpu.queue(), bytemuck::cast_slice(&[params]));
90    }
91
92    fn upload_body_commands(&mut self, compiled: &CompiledBodyCommands) {
93        let queue = self.backend.gpu.queue();
94        self.backend
95            .streams
96            .state
97            .body_row_moves
98            .write(queue, bytemuck::cast_slice(&compiled.moves));
99        self.backend
100            .streams
101            .state
102            .body_fresh_rows
103            .write(queue, bytemuck::cast_slice(&compiled.fresh));
104        self.backend
105            .streams
106            .state
107            .body_edits
108            .write(queue, bytemuck::cast_slice(&compiled.edits));
109        self.backend
110            .streams
111            .state
112            .body_edit_runs
113            .write(queue, bytemuck::cast_slice(&compiled.runs));
114        self.bodies.last_moves = compiled.moves.len() as u32;
115    }
116
117    fn upload_constraint_commands(&mut self, compiled: &CompiledConstraintCommands) {
118        let queue = self.backend.gpu.queue();
119        self.backend
120            .streams
121            .state
122            .constraint_row_moves
123            .write(queue, bytemuck::cast_slice(&compiled.moves));
124        self.backend
125            .streams
126            .state
127            .constraint_fresh_rows
128            .write(queue, bytemuck::cast_slice(&compiled.fresh));
129        self.constraints.last_moves = compiled.moves.len() as u32;
130    }
131
132    fn submit_queries(&mut self, step: u64, query_count: u32) -> Option<u64> {
133        if query_count == 0 {
134            return None;
135        }
136        self.backend.streams.state.query_records.write(
137            self.backend.gpu.queue(),
138            bytemuck::cast_slice(&self.queries.pending),
139        );
140        let batch = self.queries.next_batch;
141        self.queries.pool.submit(batch, step, query_count as usize);
142        self.queries.next_batch += 1;
143        Some(batch)
144    }
145
146    fn encode_step(&mut self, frames: &StepFrames, batch: Option<u64>, step: u64) {
147        let device = self.backend.gpu.device().clone();
148        let mut encoder = dynamis_gpu::SubmissionEncoder::new(&device, "dynamis step");
149
150        self.copy_events(&mut encoder);
151        self.copy_breaks(&mut encoder);
152        self.backend
153            .passes
154            .record(&mut encoder, &self.backend.streams, frames);
155        #[cfg(feature = "profile")]
156        let timings = self.backend.passes.capture_timings(&mut encoder);
157        let pack_bytes = self.pack_step(&mut encoder);
158        self.copy_observations(&mut encoder, step);
159        let pack = self.backend.readback.step.enqueue(
160            &mut encoder,
161            self.backend.readback.pack.buffer(),
162            0,
163            pack_bytes,
164            step,
165        );
166        let queries = match batch {
167            Some(batch) => self.backend.readback.queries.enqueue(
168                &mut encoder,
169                self.backend.streams.state.query_results.buffer(),
170                0,
171                self.queries.pending.len() as u64 * size_of::<QueryResultRecord>() as u64,
172                batch,
173            ),
174            None => None,
175        };
176        self.submit(encoder);
177        #[cfg(feature = "profile")]
178        if let Some(timings) = timings {
179            self.backend.pass_timings = timings;
180        }
181        if let Some((step, bytes)) = pack {
182            self.consume_pack(step, &bytes);
183        }
184        if let Some((batch, bytes)) = queries {
185            self.collect_query_batch(batch, &bytes);
186        }
187    }
188
189    #[cfg(feature = "profile")]
190    pub fn gpu_pass_timings(&self) -> &[dynamis_gpu::GpuPassTiming] {
191        &self.backend.pass_timings
192    }
193
194    #[cfg(feature = "profile")]
195    pub fn gpu_timing_supported(&self) -> bool {
196        self.backend.gpu.supports_pass_timing()
197    }
198}