1use std::sync::atomic::{AtomicU8, Ordering};
2use std::sync::Arc;
3use wgpu::util::DeviceExt;
4
5use super::pipeline::{create_physics_pipelines, PhysicsPipelines};
6use super::types::*;
7
8pub struct GpuPhysicsSystem {
9 pub max_boxes: u32,
10 pub grid_size: u32,
11 pub boxes_buffer: wgpu::Buffer,
12 pub params_buffer: wgpu::Buffer,
13 pub grid_heads_buffer: wgpu::Buffer,
14 pub linked_nodes_buffer: wgpu::Buffer,
15 pub box_contacts_buffer: wgpu::Buffer,
16 pub colliders_buffer: wgpu::Buffer,
17 pub awake_flags_buffer: wgpu::Buffer,
18 pub joints_buffer: wgpu::Buffer,
19 pub joint_count: u32,
20 pub max_joints: u32,
21
22 pub pipelines: PhysicsPipelines,
23
24 pub box_vertex_buffer: wgpu::Buffer,
25 pub box_index_buffer: wgpu::Buffer,
26 pub index_count: u32,
27
28 pub readback_buffer: wgpu::Buffer,
29 pub readback_state: Arc<AtomicU8>,
31
32 pub indirect_buffer: wgpu::Buffer,
33 pub culled_boxes_buffer: wgpu::Buffer,
34
35 pub debug_enabled: bool,
37 pub debug_line_buffer: wgpu::Buffer,
38 pub debug_line_count_buffer: wgpu::Buffer,
39 pub debug_params_buffer: wgpu::Buffer,
40 pub debug_compute_bind_group: wgpu::BindGroup,
41 pub debug_compute_pipeline: wgpu::ComputePipeline,
42 pub debug_render_pipeline: wgpu::RenderPipeline,
43 pub debug_max_lines: u32,
44}
45
46impl GpuPhysicsSystem {
47 pub fn new(
48 device: &wgpu::Device,
49 max_boxes: u32,
50 global_bind_group_layout: &wgpu::BindGroupLayout,
51 output_format: wgpu::TextureFormat,
52 depth_format: wgpu::TextureFormat,
53 ) -> Self {
54 let mut initial_boxes = Vec::with_capacity(max_boxes as usize);
55 let grid_dim = (max_boxes as f32).powf(1.0 / 3.0).ceil() as u32;
56 let spacing = 2.1f32;
57 let offset = (grid_dim as f32 * spacing) / 2.0;
58
59 for i in 0..max_boxes {
60 let ix = i % grid_dim;
61 let iy = (i / grid_dim) % grid_dim;
62 let iz = i / (grid_dim * grid_dim);
63
64 let x = (ix as f32 * spacing) - offset;
65 let y = 30.0 + (iy as f32 * spacing); let z = (iz as f32 * spacing) - offset;
67
68 let color_r = ix as f32 / grid_dim as f32;
70 let color_g = iy as f32 / grid_dim as f32;
71 let color_b = iz as f32 / grid_dim as f32;
72
73 initial_boxes.push(GpuBox {
74 position: [x, y, z],
75 mass: 1.0,
76 velocity: [0.0, 0.0, 0.0],
77 state: 0,
78 rotation: [0.0, 0.0, 0.0, 1.0],
79 angular_velocity: [0.0, 0.0, 0.0],
80 sleep_counter: 0,
81 color: [color_r, color_g, color_b, 1.0],
82 half_extents: [1.0, 1.0, 1.0],
83 _pad: 0,
84 });
85 }
86
87 let boxes_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
88 label: Some("GPU Physics Buffer"),
89 contents: bytemuck::cast_slice(&initial_boxes),
90 usage: wgpu::BufferUsages::STORAGE
91 | wgpu::BufferUsages::VERTEX
92 | wgpu::BufferUsages::COPY_DST
93 | wgpu::BufferUsages::COPY_SRC,
94 });
95
96 let mut initial_colliders = Vec::new();
97 initial_colliders.push(GpuCollider {
99 shape_type: 1,
100 _pad1: [0; 3],
101 data1: [0.0, 1.0, 0.0, 0.0], data2: [0.0, 0.0, 0.0, 0.0], });
104
105 initial_colliders.push(GpuCollider {
107 shape_type: 0,
108 _pad1: [0; 3],
109 data1: [-40.0, 0.0, -40.0, 0.0], data2: [40.0, 20.0, 40.0, 0.0], });
112
113 initial_colliders.push(GpuCollider {
115 shape_type: 0,
116 _pad1: [0; 3],
117 data1: [45.0, 0.0, -40.0, 0.0], data2: [55.0, 40.0, 40.0, 0.0], });
120
121 let max_static_colliders = 100;
122 let num_initial = initial_colliders.len();
123 if num_initial < max_static_colliders {
124 let empty_col = GpuCollider {
125 shape_type: 0,
126 _pad1: [0; 3],
127 data1: [0.0; 4],
128 data2: [0.0; 4],
129 };
130 initial_colliders.resize(max_static_colliders, empty_col);
131 }
132
133 let colliders_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
134 label: Some("GPU Static Colliders Buffer"),
135 contents: bytemuck::cast_slice(&initial_colliders),
136 usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST,
137 });
138
139 let initial_awake_flags: Vec<u32> = vec![0; max_boxes as usize];
140 let awake_flags_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
141 label: Some("GPU Physics Awake Flags Buffer"),
142 contents: bytemuck::cast_slice(&initial_awake_flags),
143 usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST,
144 });
145
146 let params = PhysicsSimParams {
147 dt: 0.016,
148 _pad0: [0; 3],
149 _pad1: [0.0; 3],
150 _pad1b: 0,
151 gravity: [0.0, -9.81, 0.0],
152 damping: 0.99,
153 num_boxes: max_boxes,
154 num_colliders: initial_colliders.len() as u32,
155 num_joints: 0,
156 _pad2: 0,
157 };
158
159 let params_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
160 label: Some("GPU Physics Params Buffer"),
161 contents: bytemuck::cast_slice(&[params]),
162 usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
163 });
164
165 let grid_size = 262144u32;
166 let initial_heads = vec![-1i32; grid_size as usize];
167 let grid_heads_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
168 label: Some("GPU Physics Grid Heads Buffer"),
169 contents: bytemuck::cast_slice(&initial_heads),
170 usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST,
171 });
172
173 let initial_nodes = vec![-1i32; max_boxes as usize];
174 let linked_nodes_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
175 label: Some("GPU Physics Linked Nodes Buffer"),
176 contents: bytemuck::cast_slice(&initial_nodes),
177 usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST,
178 });
179
180 let (vertices, indices) = create_cube();
181
182 let box_vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
183 label: Some("Box Vertex Buffer"),
184 contents: bytemuck::cast_slice(&vertices),
185 usage: wgpu::BufferUsages::VERTEX,
186 });
187
188 let box_index_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
189 label: Some("Box Index Buffer"),
190 contents: bytemuck::cast_slice(&indices),
191 usage: wgpu::BufferUsages::INDEX,
192 });
193
194 let indirect_data: [u32; 5] = [
195 indices.len() as u32, 0, 0, 0, 0, ];
201
202 let indirect_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
203 label: Some("Culling Indirect Buffer"),
204 contents: bytemuck::cast_slice(&indirect_data),
205 usage: wgpu::BufferUsages::INDIRECT
206 | wgpu::BufferUsages::STORAGE
207 | wgpu::BufferUsages::COPY_DST,
208 });
209
210 let culled_boxes_buffer = device.create_buffer(&wgpu::BufferDescriptor {
211 label: Some("Culled Boxes Buffer"),
212 size: (max_boxes as wgpu::BufferAddress)
213 * std::mem::size_of::<GpuBox>() as wgpu::BufferAddress,
214 usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::VERTEX,
215 mapped_at_creation: false,
216 });
217
218 let max_joints = 16384u32;
220 let joints_buffer = device.create_buffer(&wgpu::BufferDescriptor {
221 label: Some("GPU Physics Joints Buffer"),
222 size: (max_joints as wgpu::BufferAddress)
223 * std::mem::size_of::<GpuJoint>() as wgpu::BufferAddress,
224 usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST,
225 mapped_at_creation: false,
226 });
227
228 let box_contacts_buffer = device.create_buffer(&wgpu::BufferDescriptor {
230 label: Some("GPU Physics Box Contacts Cache"),
231 size: (max_boxes as wgpu::BufferAddress) * 336,
232 usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST,
233 mapped_at_creation: false,
234 });
235
236 let pipelines = create_physics_pipelines(
237 device,
238 global_bind_group_layout,
239 output_format,
240 depth_format,
241 ¶ms_buffer,
242 &boxes_buffer,
243 &grid_heads_buffer,
244 &linked_nodes_buffer,
245 &colliders_buffer,
246 &awake_flags_buffer,
247 &joints_buffer,
248 &box_contacts_buffer,
249 &culled_boxes_buffer,
250 &indirect_buffer,
251 );
252
253 Self {
254 max_boxes,
255 grid_size,
256 boxes_buffer,
257 params_buffer,
258 grid_heads_buffer,
259 linked_nodes_buffer,
260 box_contacts_buffer,
261 colliders_buffer,
262 awake_flags_buffer,
263 joints_buffer,
264 joint_count: 0,
265 max_joints,
266 pipelines,
267 box_vertex_buffer,
268 box_index_buffer,
269 index_count: indices.len() as u32,
270
271 readback_buffer: device.create_buffer(&wgpu::BufferDescriptor {
272 label: Some("GPU Physics Readback Buffer"),
273 size: (max_boxes as wgpu::BufferAddress)
274 * std::mem::size_of::<GpuBox>() as wgpu::BufferAddress,
275 usage: wgpu::BufferUsages::MAP_READ | wgpu::BufferUsages::COPY_DST,
276 mapped_at_creation: false,
277 }),
278 readback_state: Arc::new(AtomicU8::new(0)),
279
280 indirect_buffer,
281 culled_boxes_buffer,
282
283 debug_enabled: false,
285 debug_line_buffer: device.create_buffer(&wgpu::BufferDescriptor {
286 label: Some("Debug Line Buffer"),
287 size: 32768 * 2 * std::mem::size_of::<DebugVertex>() as wgpu::BufferAddress,
288 usage: wgpu::BufferUsages::STORAGE
289 | wgpu::BufferUsages::VERTEX
290 | wgpu::BufferUsages::COPY_DST,
291 mapped_at_creation: false,
292 }),
293 debug_line_count_buffer: device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
294 label: Some("Debug Line Count"),
295 contents: bytemuck::cast_slice(&[0u32, 1u32, 0u32, 0u32]), usage: wgpu::BufferUsages::STORAGE
297 | wgpu::BufferUsages::INDIRECT
298 | wgpu::BufferUsages::COPY_DST,
299 }),
300 debug_params_buffer: {
301 let dp = DebugParams {
302 num_boxes: max_boxes,
303 num_joints: 0,
304 show_wireframes: 0,
305 _pad: 0,
306 };
307 device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
308 label: Some("Debug Params"),
309 contents: bytemuck::cast_slice(&[dp]),
310 usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
311 })
312 },
313 debug_compute_bind_group: device.create_bind_group(&wgpu::BindGroupDescriptor {
315 layout: &device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
316 entries: &[],
317 label: Some("empty_layout"),
318 }),
319 entries: &[],
320 label: Some("debug_placeholder"),
321 }),
322 debug_compute_pipeline: {
323 let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
324 label: Some("Physics Debug Compute Shader"),
325 source: wgpu::ShaderSource::Wgsl(
326 include_str!("../shaders/physics_debug.wgsl").into(),
327 ),
328 });
329 let layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
330 label: Some("Debug Compute Layout"),
331 bind_group_layouts: &[&device.create_bind_group_layout(
332 &wgpu::BindGroupLayoutDescriptor {
333 entries: &[
334 wgpu::BindGroupLayoutEntry {
335 binding: 0,
336 visibility: wgpu::ShaderStages::COMPUTE,
337 ty: wgpu::BindingType::Buffer {
338 ty: wgpu::BufferBindingType::Uniform,
339 has_dynamic_offset: false,
340 min_binding_size: None,
341 },
342 count: None,
343 },
344 wgpu::BindGroupLayoutEntry {
345 binding: 1,
346 visibility: wgpu::ShaderStages::COMPUTE,
347 ty: wgpu::BindingType::Buffer {
348 ty: wgpu::BufferBindingType::Storage { read_only: true },
349 has_dynamic_offset: false,
350 min_binding_size: None,
351 },
352 count: None,
353 },
354 wgpu::BindGroupLayoutEntry {
355 binding: 2,
356 visibility: wgpu::ShaderStages::COMPUTE,
357 ty: wgpu::BindingType::Buffer {
358 ty: wgpu::BufferBindingType::Storage { read_only: true },
359 has_dynamic_offset: false,
360 min_binding_size: None,
361 },
362 count: None,
363 },
364 wgpu::BindGroupLayoutEntry {
365 binding: 3,
366 visibility: wgpu::ShaderStages::COMPUTE,
367 ty: wgpu::BindingType::Buffer {
368 ty: wgpu::BufferBindingType::Storage { read_only: false },
369 has_dynamic_offset: false,
370 min_binding_size: None,
371 },
372 count: None,
373 },
374 wgpu::BindGroupLayoutEntry {
375 binding: 4,
376 visibility: wgpu::ShaderStages::COMPUTE,
377 ty: wgpu::BindingType::Buffer {
378 ty: wgpu::BufferBindingType::Storage { read_only: false },
379 has_dynamic_offset: false,
380 min_binding_size: None,
381 },
382 count: None,
383 },
384 ],
385 label: Some("debug_compute_layout_inner"),
386 },
387 )],
388 push_constant_ranges: &[],
389 });
390 device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor {
391 label: Some("Physics Debug Compute"),
392 layout: Some(&layout),
393 module: &shader,
394 entry_point: "generate_debug_lines",
395 compilation_options: Default::default(),
396 })
397 },
398 debug_render_pipeline: {
399 let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
400 label: Some("Physics Debug Shader"),
401 source: wgpu::ShaderSource::Wgsl(
402 include_str!("../shaders/physics_debug.wgsl").into(),
403 ),
404 });
405 let layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
406 label: Some("Debug Render Layout"),
407 bind_group_layouts: &[global_bind_group_layout],
408 push_constant_ranges: &[],
409 });
410 device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
411 label: Some("Physics Debug Lines"),
412 layout: Some(&layout),
413 vertex: wgpu::VertexState {
414 module: &shader,
415 entry_point: "vs_debug",
416 compilation_options: Default::default(),
417 buffers: &[DebugVertex::desc()],
418 },
419 fragment: Some(wgpu::FragmentState {
420 module: &shader,
421 entry_point: "fs_debug",
422 compilation_options: Default::default(),
423 targets: &[Some(wgpu::ColorTargetState {
424 format: output_format,
425 blend: Some(wgpu::BlendState::ALPHA_BLENDING),
426 write_mask: wgpu::ColorWrites::ALL,
427 })],
428 }),
429 primitive: wgpu::PrimitiveState {
430 topology: wgpu::PrimitiveTopology::LineList,
431 ..Default::default()
432 },
433 depth_stencil: Some(wgpu::DepthStencilState {
434 format: depth_format,
435 depth_write_enabled: false,
436 depth_compare: wgpu::CompareFunction::LessEqual,
437 stencil: wgpu::StencilState::default(),
438 bias: wgpu::DepthBiasState::default(),
439 }),
440 multisample: wgpu::MultisampleState::default(),
441 multiview: None,
442 })
443 },
444 debug_max_lines: 32768,
445 }
446 }
447
448 pub fn update_box(&self, queue: &wgpu::Queue, index: u32, box_struct: &GpuBox) {
449 if index < self.max_boxes {
450 let offset = (index as wgpu::BufferAddress)
451 * std::mem::size_of::<GpuBox>() as wgpu::BufferAddress;
452 queue.write_buffer(
453 &self.boxes_buffer,
454 offset,
455 bytemuck::cast_slice(&[*box_struct]),
456 );
457 }
458 }
459
460 pub fn update_collider(&self, queue: &wgpu::Queue, index: u32, collider: &GpuCollider) {
461 if index < 100 {
462 let offset = (index as wgpu::BufferAddress)
463 * std::mem::size_of::<GpuCollider>() as wgpu::BufferAddress;
464 queue.write_buffer(
465 &self.colliders_buffer,
466 offset,
467 bytemuck::cast_slice(&[*collider]),
468 );
469 }
470 }
471
472 pub fn add_joint(&mut self, queue: &wgpu::Queue, joint: GpuJoint) -> Option<u32> {
474 if self.joint_count >= self.max_joints {
475 return None;
476 }
477 let idx = self.joint_count;
478 let offset =
479 (idx as wgpu::BufferAddress) * std::mem::size_of::<GpuJoint>() as wgpu::BufferAddress;
480 queue.write_buffer(&self.joints_buffer, offset, bytemuck::cast_slice(&[joint]));
481 self.joint_count += 1;
482 Some(idx)
483 }
484
485 pub fn remove_joint(&self, queue: &wgpu::Queue, index: u32) {
487 if index < self.joint_count {
488 let mut empty = GpuJoint::ball(0, 0, [0.0; 3], [0.0; 3]);
489 empty.flags = 0; let offset = (index as wgpu::BufferAddress)
491 * std::mem::size_of::<GpuJoint>() as wgpu::BufferAddress;
492 queue.write_buffer(&self.joints_buffer, offset, bytemuck::cast_slice(&[empty]));
493 }
494 }
495
496 pub fn update_params(&self, queue: &wgpu::Queue, dt: f32, gravity: [f32; 3]) {
498 let params = PhysicsSimParams {
499 dt,
500 _pad0: [0; 3],
501 _pad1: [0.0; 3],
502 _pad1b: 0,
503 gravity,
504 damping: 0.99,
505 num_boxes: self.max_boxes,
506 num_colliders: 100, num_joints: self.joint_count,
508 _pad2: 0,
509 };
510 queue.write_buffer(&self.params_buffer, 0, bytemuck::cast_slice(&[params]));
511 }
512
513 pub fn enable_debug(&mut self, device: &wgpu::Device, _show_flags: u32) {
515 let layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
516 entries: &[
517 wgpu::BindGroupLayoutEntry {
518 binding: 0,
519 visibility: wgpu::ShaderStages::COMPUTE,
520 ty: wgpu::BindingType::Buffer {
521 ty: wgpu::BufferBindingType::Uniform,
522 has_dynamic_offset: false,
523 min_binding_size: None,
524 },
525 count: None,
526 },
527 wgpu::BindGroupLayoutEntry {
528 binding: 1,
529 visibility: wgpu::ShaderStages::COMPUTE,
530 ty: wgpu::BindingType::Buffer {
531 ty: wgpu::BufferBindingType::Storage { read_only: true },
532 has_dynamic_offset: false,
533 min_binding_size: None,
534 },
535 count: None,
536 },
537 wgpu::BindGroupLayoutEntry {
538 binding: 2,
539 visibility: wgpu::ShaderStages::COMPUTE,
540 ty: wgpu::BindingType::Buffer {
541 ty: wgpu::BufferBindingType::Storage { read_only: true },
542 has_dynamic_offset: false,
543 min_binding_size: None,
544 },
545 count: None,
546 },
547 wgpu::BindGroupLayoutEntry {
548 binding: 3,
549 visibility: wgpu::ShaderStages::COMPUTE,
550 ty: wgpu::BindingType::Buffer {
551 ty: wgpu::BufferBindingType::Storage { read_only: false },
552 has_dynamic_offset: false,
553 min_binding_size: None,
554 },
555 count: None,
556 },
557 wgpu::BindGroupLayoutEntry {
558 binding: 4,
559 visibility: wgpu::ShaderStages::COMPUTE,
560 ty: wgpu::BindingType::Buffer {
561 ty: wgpu::BufferBindingType::Storage { read_only: false },
562 has_dynamic_offset: false,
563 min_binding_size: None,
564 },
565 count: None,
566 },
567 ],
568 label: Some("debug_compute_layout"),
569 });
570
571 self.debug_compute_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
572 layout: &layout,
573 entries: &[
574 wgpu::BindGroupEntry {
575 binding: 0,
576 resource: self.debug_params_buffer.as_entire_binding(),
577 },
578 wgpu::BindGroupEntry {
579 binding: 1,
580 resource: self.boxes_buffer.as_entire_binding(),
581 },
582 wgpu::BindGroupEntry {
583 binding: 2,
584 resource: self.joints_buffer.as_entire_binding(),
585 },
586 wgpu::BindGroupEntry {
587 binding: 3,
588 resource: self.debug_line_buffer.as_entire_binding(),
589 },
590 wgpu::BindGroupEntry {
591 binding: 4,
592 resource: self.debug_line_count_buffer.as_entire_binding(),
593 },
594 ],
595 label: Some("debug_compute_bind_group"),
596 });
597
598 self.debug_enabled = true;
599 }
600
601 pub fn toggle_debug(&mut self) {
603 self.debug_enabled = !self.debug_enabled;
604 }
605
606 pub fn compute_pass(&self, encoder: &mut wgpu::CommandEncoder) {
607 let mut cpass = encoder.begin_compute_pass(&wgpu::ComputePassDescriptor {
608 label: Some("Physics Compute Pass"),
609 timestamp_writes: None,
610 });
611 cpass.set_bind_group(0, &self.pipelines.compute_bind_group, &[]);
612
613 cpass.set_pipeline(&self.pipelines.pipeline_clear);
616 cpass.dispatch_workgroups(self.grid_size.div_ceil(256), 1, 1);
617
618 cpass.set_pipeline(&self.pipelines.pipeline_build);
619 cpass.dispatch_workgroups(self.max_boxes.div_ceil(256), 1, 1);
620
621 cpass.set_pipeline(&self.pipelines.pipeline_narrowphase);
623 cpass.dispatch_workgroups(self.max_boxes.div_ceil(256), 1, 1);
624
625 let si_iterations = 6;
628 for _ in 0..si_iterations {
629 cpass.set_pipeline(&self.pipelines.pipeline_solve);
630 cpass.dispatch_workgroups(self.max_boxes.div_ceil(256), 1, 1);
631 }
632
633 cpass.set_pipeline(&self.pipelines.pipeline_integrate);
635 cpass.dispatch_workgroups(self.max_boxes.div_ceil(256), 1, 1);
636
637 if self.joint_count > 0 {
639 let joint_iterations = 4;
640 for _ in 0..joint_iterations {
641 cpass.set_pipeline(&self.pipelines.pipeline_solve_joints);
642 cpass.dispatch_workgroups(self.max_boxes.div_ceil(256), 1, 1);
643 }
644 }
645 }
646
647 pub fn cull_pass(
648 &self,
649 encoder: &mut wgpu::CommandEncoder,
650 global_bind_group: &wgpu::BindGroup,
651 ) {
652 encoder.clear_buffer(&self.indirect_buffer, 4, Some(4));
653
654 let mut cpass = encoder.begin_compute_pass(&wgpu::ComputePassDescriptor {
655 label: Some("Physics Culling Pass"),
656 timestamp_writes: None,
657 });
658 cpass.set_pipeline(&self.pipelines.pipeline_culling);
659 cpass.set_bind_group(0, global_bind_group, &[]);
660 cpass.set_bind_group(1, &self.pipelines.culling_bind_group, &[]);
661 cpass.dispatch_workgroups(self.max_boxes.div_ceil(256), 1, 1);
662 }
663
664 pub fn render_pass<'a>(
665 &'a self,
666 rpass: &mut wgpu::RenderPass<'a>,
667 global_bind_group: &'a wgpu::BindGroup,
668 ) {
669 rpass.set_pipeline(&self.pipelines.render_pipeline);
670 rpass.set_bind_group(0, global_bind_group, &[]);
671 rpass.set_vertex_buffer(0, self.box_vertex_buffer.slice(..));
672 rpass.set_vertex_buffer(1, self.culled_boxes_buffer.slice(..));
673 rpass.set_index_buffer(self.box_index_buffer.slice(..), wgpu::IndexFormat::Uint32);
674 rpass.draw_indexed_indirect(&self.indirect_buffer, 0);
675 }
676
677 pub fn debug_compute_pass(&self, encoder: &mut wgpu::CommandEncoder) {
678 if !self.debug_enabled {
679 return;
680 }
681
682 encoder.clear_buffer(&self.debug_line_count_buffer, 0, Some(4));
684
685 let mut cpass = encoder.begin_compute_pass(&wgpu::ComputePassDescriptor {
686 label: Some("Physics Debug Compute Pass"),
687 timestamp_writes: None,
688 });
689
690 cpass.set_pipeline(&self.debug_compute_pipeline);
691 cpass.set_bind_group(0, &self.debug_compute_bind_group, &[]);
692 cpass.dispatch_workgroups(
694 self.max_boxes
695 .div_ceil(256)
696 .max(self.max_joints.div_ceil(256)),
697 1,
698 1,
699 );
700 }
701
702 pub fn debug_render_pass<'a>(
703 &'a self,
704 rpass: &mut wgpu::RenderPass<'a>,
705 global_bind_group: &'a wgpu::BindGroup,
706 ) {
707 if !self.debug_enabled {
708 return;
709 }
710
711 rpass.set_pipeline(&self.debug_render_pipeline);
712 rpass.set_bind_group(0, global_bind_group, &[]);
713 rpass.set_vertex_buffer(0, self.debug_line_buffer.slice(..));
714 rpass.draw_indirect(&self.debug_line_count_buffer, 0);
715 }
716
717 pub fn request_readback(&self, encoder: &mut wgpu::CommandEncoder) {
718 if self
719 .readback_state
720 .compare_exchange(0, 1, Ordering::SeqCst, Ordering::SeqCst)
721 .is_ok()
722 {
723 let size = (self.max_boxes as wgpu::BufferAddress)
724 * std::mem::size_of::<GpuBox>() as wgpu::BufferAddress;
725 encoder.copy_buffer_to_buffer(&self.boxes_buffer, 0, &self.readback_buffer, 0, size);
726 }
727 }
728
729 pub fn poll_readback_data(&self, device: &wgpu::Device) -> Option<Vec<GpuBox>> {
730 if self
731 .readback_state
732 .compare_exchange(1, 2, Ordering::SeqCst, Ordering::SeqCst)
733 .is_ok()
734 {
735 let slice = self.readback_buffer.slice(..);
736 let state_clone = self.readback_state.clone();
737 slice.map_async(wgpu::MapMode::Read, move |result| {
738 if result.is_ok() {
739 state_clone.store(3, Ordering::SeqCst);
740 } else {
741 state_clone.store(0, Ordering::SeqCst);
742 }
743 });
744 }
745
746 device.poll(wgpu::Maintain::Poll);
747
748 if self.readback_state.load(Ordering::SeqCst) == 3 {
749 let slice = self.readback_buffer.slice(..);
750 let view = slice.get_mapped_range();
751
752 let data: &[GpuBox] = bytemuck::cast_slice(&view);
753 let vec_data = data.to_vec();
754
755 drop(view);
756 self.readback_buffer.unmap();
757
758 self.readback_state.store(0, Ordering::SeqCst);
759
760 return Some(vec_data);
761 }
762 None
763 }
764}
765
766fn create_cube() -> (Vec<crate::gpu_types::Vertex>, Vec<u32>) {
767 let s = 1.0f32;
768 let faces: [([f32; 3], [[f32; 3]; 4]); 6] = [
769 (
770 [0.0, 0.0, 1.0],
771 [[-s, -s, s], [s, -s, s], [s, s, s], [-s, s, s]],
772 ),
773 (
774 [0.0, 0.0, -1.0],
775 [[s, -s, -s], [-s, -s, -s], [-s, s, -s], [s, s, -s]],
776 ),
777 (
778 [1.0, 0.0, 0.0],
779 [[s, -s, s], [s, -s, -s], [s, s, -s], [s, s, s]],
780 ),
781 (
782 [-1.0, 0.0, 0.0],
783 [[-s, -s, -s], [-s, -s, s], [-s, s, s], [-s, s, -s]],
784 ),
785 (
786 [0.0, 1.0, 0.0],
787 [[-s, s, s], [s, s, s], [s, s, -s], [-s, s, -s]],
788 ),
789 (
790 [0.0, -1.0, 0.0],
791 [[-s, -s, -s], [s, -s, -s], [s, -s, s], [-s, -s, s]],
792 ),
793 ];
794
795 let mut vertices = Vec::with_capacity(24);
796 let mut indices = Vec::with_capacity(36);
797
798 for (normal, corners) in &faces {
799 let base = vertices.len() as u32;
800 for &p in corners {
801 vertices.push(crate::gpu_types::Vertex {
802 position: p,
803 color: [1.0, 1.0, 1.0],
804 normal: *normal,
805 tex_coords: [0.0, 0.0],
806 joint_indices: [0; 4],
807 joint_weights: [0.0; 4],
808 });
809 }
810 indices.extend_from_slice(&[base, base + 1, base + 2, base + 2, base + 3, base]);
811 }
812
813 (vertices, indices)
814}