oxiphysics_gpu/kernels/
mod.rs1pub mod broadphase;
11pub mod md_force;
12pub mod rigid;
13pub mod sph;
14
15#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
19pub enum KernelFamily {
20 Sph,
22 Rigid,
24 Broadphase,
26 MdForce,
28 SdfCompute,
30 NeuralCompute,
32 GridReduce,
34}
35
36impl std::fmt::Display for KernelFamily {
37 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
38 let name = match self {
39 KernelFamily::Sph => "sph",
40 KernelFamily::Rigid => "rigid",
41 KernelFamily::Broadphase => "broadphase",
42 KernelFamily::MdForce => "md_force",
43 KernelFamily::SdfCompute => "sdf_compute",
44 KernelFamily::NeuralCompute => "neural_compute",
45 KernelFamily::GridReduce => "grid_reduce",
46 };
47 write!(f, "{name}")
48 }
49}
50
51#[derive(Debug, Clone, Copy, PartialEq, Eq)]
58pub struct DispatchDims {
59 pub x: u32,
61 pub y: u32,
63 pub z: u32,
65}
66
67impl DispatchDims {
68 pub fn linear(n: u32) -> Self {
70 Self { x: n, y: 1, z: 1 }
71 }
72
73 pub fn grid2d(x: u32, y: u32) -> Self {
75 Self { x, y, z: 1 }
76 }
77
78 pub fn grid3d(x: u32, y: u32, z: u32) -> Self {
80 Self { x, y, z }
81 }
82
83 pub fn total_groups(&self) -> u64 {
85 self.x as u64 * self.y as u64 * self.z as u64
86 }
87
88 pub fn total_threads(&self, threads_per_group: u32) -> u64 {
90 self.total_groups() * threads_per_group as u64
91 }
92}
93
94pub fn dispatch_size_1d(n: u32, group_size: u32) -> u32 {
97 if group_size == 0 {
98 return 0;
99 }
100 n.div_ceil(group_size)
101}
102
103#[derive(Debug, Clone, Default)]
107pub struct KernelPerfCounters {
108 pub dispatch_count: u64,
110 pub elements_processed: u64,
112 pub flop_count: u64,
114 pub bytes_read: u64,
116 pub bytes_written: u64,
118}
119
120impl KernelPerfCounters {
121 pub fn record_dispatch(&mut self, elements: u64, flops: u64, bytes_r: u64, bytes_w: u64) {
123 self.dispatch_count += 1;
124 self.elements_processed += elements;
125 self.flop_count += flops;
126 self.bytes_read += bytes_r;
127 self.bytes_written += bytes_w;
128 }
129
130 pub fn arithmetic_intensity(&self) -> f64 {
132 let bytes = self.bytes_read + self.bytes_written;
133 if bytes == 0 {
134 return 0.0;
135 }
136 self.flop_count as f64 / bytes as f64
137 }
138
139 pub fn reset(&mut self) {
141 *self = KernelPerfCounters::default();
142 }
143}
144
145pub fn smem_bytes_matmul<T>(tile: usize) -> usize {
150 2 * tile * tile * std::mem::size_of::<T>()
151}
152
153#[inline(always)]
158pub fn workgroup_barrier() {
159 std::sync::atomic::fence(std::sync::atomic::Ordering::SeqCst);
161}
162
163pub mod group_sizes {
167 pub const WG_64: u32 = 64;
169 pub const WG_128: u32 = 128;
171 pub const WG_256: u32 = 256;
173 pub const WG_512: u32 = 512;
175 pub const WG_1024: u32 = 1024;
177}
178
179#[cfg(test)]
182mod kernel_mod_tests {
183 use super::*;
184
185 #[test]
186 fn test_kernel_family_display() {
187 assert_eq!(KernelFamily::Sph.to_string(), "sph");
188 assert_eq!(KernelFamily::NeuralCompute.to_string(), "neural_compute");
189 assert_eq!(KernelFamily::GridReduce.to_string(), "grid_reduce");
190 }
191
192 #[test]
193 fn test_dispatch_dims_linear() {
194 let d = DispatchDims::linear(128);
195 assert_eq!(d.total_groups(), 128);
196 assert_eq!(d.total_threads(256), 128 * 256);
197 }
198
199 #[test]
200 fn test_dispatch_dims_grid3d() {
201 let d = DispatchDims::grid3d(4, 4, 4);
202 assert_eq!(d.total_groups(), 64);
203 }
204
205 #[test]
206 fn test_dispatch_size_1d_exact() {
207 assert_eq!(dispatch_size_1d(256, 64), 4);
208 }
209
210 #[test]
211 fn test_dispatch_size_1d_remainder() {
212 assert_eq!(dispatch_size_1d(257, 64), 5);
213 }
214
215 #[test]
216 fn test_dispatch_size_1d_zero_group() {
217 assert_eq!(dispatch_size_1d(100, 0), 0);
218 }
219
220 #[test]
221 fn test_perf_counters_arithmetic_intensity() {
222 let mut c = KernelPerfCounters::default();
223 c.record_dispatch(1024, 8192, 4096, 4096);
224 assert!((c.arithmetic_intensity() - 1.0).abs() < 1e-10);
226 }
227
228 #[test]
229 fn test_perf_counters_reset() {
230 let mut c = KernelPerfCounters::default();
231 c.record_dispatch(512, 1024, 512, 512);
232 c.reset();
233 assert_eq!(c.dispatch_count, 0);
234 assert_eq!(c.flop_count, 0);
235 }
236
237 #[test]
238 fn test_smem_bytes_matmul_f32() {
239 let bytes = smem_bytes_matmul::<f32>(16);
241 assert_eq!(bytes, 2048);
242 }
243
244 #[test]
245 fn test_smem_bytes_matmul_f64() {
246 let bytes = smem_bytes_matmul::<f64>(16);
248 assert_eq!(bytes, 4096);
249 }
250
251 #[test]
252 fn test_workgroup_barrier_no_panic() {
253 workgroup_barrier(); }
255
256 #[test]
257 fn test_group_sizes_constants() {
258 use group_sizes::*;
259 const _: () = assert!(WG_64 < WG_128);
260 const _: () = assert!(WG_128 < WG_256);
261 const _: () = assert!(WG_256 < WG_512);
262 const _: () = assert!(WG_512 < WG_1024);
263 }
264}