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}