pfx_physics/
fluid_inspect.rs1use super::{Fluid, FluidState, Particle};
2use std::collections::BTreeMap;
3
4impl Fluid {
5 pub fn inspect(&self, device: &wgpu::Device, queue: &wgpu::Queue) -> FluidState {
6 self.inspect_with_row(device, queue, None)
7 }
8
9 pub fn inspect_row(&self, device: &wgpu::Device, queue: &wgpu::Queue, row: u32) -> FluidState {
10 self.inspect_with_row(device, queue, Some(row))
11 }
12
13 fn inspect_with_row(
14 &self,
15 device: &wgpu::Device,
16 queue: &wgpu::Queue,
17 row: Option<u32>,
18 ) -> FluidState {
19 let particle_bytes = self.high as u64 * std::mem::size_of::<Particle>() as u64;
20 let padded = (self.width * 8).div_ceil(256) * 256;
21 let height_bytes = padded as u64 * self.height as u64;
22 let particles = device.create_buffer(&wgpu::BufferDescriptor {
23 label: Some("Fluid particle inspection"),
24 size: particle_bytes.max(4),
25 usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ,
26 mapped_at_creation: false,
27 });
28 let height = device.create_buffer(&wgpu::BufferDescriptor {
29 label: Some("Fluid height inspection"),
30 size: height_bytes,
31 usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ,
32 mapped_at_creation: false,
33 });
34 let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
35 label: Some("Fluid inspection"),
36 });
37 if particle_bytes > 0 {
38 encoder.copy_buffer_to_buffer(&self.particles, 0, &particles, 0, particle_bytes);
39 }
40 encoder.copy_texture_to_buffer(
41 self.height_texture.as_image_copy(),
42 wgpu::TexelCopyBufferInfo {
43 buffer: &height,
44 layout: wgpu::TexelCopyBufferLayout {
45 offset: 0,
46 bytes_per_row: Some(padded),
47 rows_per_image: Some(self.height),
48 },
49 },
50 self.height_texture.size(),
51 );
52 queue.submit(Some(encoder.finish()));
53 let particle_data = if particle_bytes > 0 {
54 mapped(device, &particles)
55 } else {
56 Vec::new()
57 };
58 let height_data = mapped(device, &height);
59 let mut state = FluidState {
60 live: 0,
61 asleep: self.asleep(),
62 bounds: None,
63 height_min: f32::INFINITY,
64 height_max: f32::NEG_INFINITY,
65 height_mean: 0.0,
66 channel_histogram: std::array::from_fn(|_| BTreeMap::new()),
67 resting: self.still > self.settle,
68 row: row
69 .filter(|r| *r < self.height)
70 .map(|_| Vec::with_capacity(self.width as usize)),
71 };
72 for particle in bytemuck::cast_slice::<u8, Particle>(&particle_data) {
73 if particle.born < 0.0 || !particle.pos.iter().all(|v| v.is_finite()) {
74 continue;
75 }
76 state.live += 1;
77 match &mut state.bounds {
78 Some((lo, hi)) => {
79 for axis in 0..2 {
80 lo[axis] = lo[axis].min(particle.pos[axis]);
81 hi[axis] = hi[axis].max(particle.pos[axis]);
82 }
83 }
84 None => state.bounds = Some((particle.pos, particle.pos)),
85 }
86 }
87 let mut sum = 0.0f64;
88 for y in 0..self.height as usize {
89 for x in 0..self.width as usize {
90 let at = y * padded as usize + x * 8;
91 let channels = std::array::from_fn::<_, 4, _>(|channel| {
92 half(u16::from_le_bytes([
93 height_data[at + channel * 2],
94 height_data[at + channel * 2 + 1],
95 ]))
96 });
97 let h = channels[0];
98 state.height_min = state.height_min.min(h);
99 state.height_max = state.height_max.max(h);
100 sum += f64::from(h);
101 if row == Some(y as u32)
102 && let Some(samples) = &mut state.row
103 {
104 samples.push(h);
105 }
106 if h > 0.01 {
107 for channel in 0..3 {
108 let value = (channels[channel + 1] / h * 16.0).round() as i32;
109 *state.channel_histogram[channel].entry(value).or_default() += 1;
110 }
111 }
112 }
113 }
114 state.height_mean = (sum / f64::from(self.width * self.height)) as f32;
115 state
116 }
117}
118
119pub(super) fn mapped(device: &wgpu::Device, buffer: &wgpu::Buffer) -> Vec<u8> {
120 let slice = buffer.slice(..);
121 let (send, recv) = std::sync::mpsc::channel();
122 slice.map_async(wgpu::MapMode::Read, move |result| {
123 let _ = send.send(result);
124 });
125 device
126 .poll(wgpu::PollType::wait_indefinitely())
127 .expect("fluid inspection poll");
128 recv.recv()
129 .expect("fluid inspection callback")
130 .expect("fluid inspection map");
131 slice.get_mapped_range().expect("a mapped range").to_vec()
132}
133
134fn half(h: u16) -> f32 {
135 let sign = ((h >> 15) & 1) as u32;
136 let exp = ((h >> 10) & 0x1f) as u32;
137 let man = (h & 0x3ff) as u32;
138 let bits = if exp == 0 {
139 if man == 0 {
140 sign << 31
141 } else {
142 let mut frac = man;
143 let mut exponent = 113u32;
144 while frac & 0x400 == 0 {
145 frac <<= 1;
146 exponent -= 1;
147 }
148 frac &= 0x3ff;
149 (sign << 31) | (exponent << 23) | (frac << 13)
150 }
151 } else if exp == 31 {
152 (sign << 31) | (255 << 23) | (man << 13)
153 } else {
154 (sign << 31) | ((exp + 112) << 23) | (man << 13)
155 };
156 f32::from_bits(bits)
157}