oxigdal_gpu/
workgroup_tuner.rs1use wgpu::Limits;
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq)]
20pub struct WorkgroupTuner {
21 pub raster_2d: (u32, u32),
23 pub reduction: u32,
25 pub fft: u32,
27}
28
29impl WorkgroupTuner {
30 pub fn derive_from_limits(limits: &Limits) -> Self {
44 let max_x = limits.max_compute_workgroup_size_x;
45 let max_y = limits.max_compute_workgroup_size_y;
46 let max_total = limits.max_compute_invocations_per_workgroup;
47
48 let raster_2d = if max_x >= 16 && max_y >= 16 && max_total >= 256 {
50 (16, 16)
51 } else if max_x >= 8 && max_y >= 8 && max_total >= 64 {
52 (8, 8)
53 } else {
54 (4, 4)
55 };
56
57 let reduction = 256u32.min(max_x).min(max_total);
59
60 let fft = 64u32.min(max_x).min(max_total);
62
63 Self {
64 raster_2d,
65 reduction,
66 fft,
67 }
68 }
69
70 pub fn unlimited() -> Self {
79 Self {
80 raster_2d: (16, 16),
81 reduction: 256,
82 fft: 64,
83 }
84 }
85}
86
87impl Default for WorkgroupTuner {
88 fn default() -> Self {
89 Self::unlimited()
90 }
91}
92
93#[cfg(test)]
94mod tests {
95 use super::*;
96
97 fn limits_with(max_x: u32, max_y: u32, max_total: u32) -> Limits {
98 Limits {
99 max_compute_workgroup_size_x: max_x,
100 max_compute_workgroup_size_y: max_y,
101 max_compute_invocations_per_workgroup: max_total,
102 ..Limits::default()
103 }
104 }
105
106 #[test]
107 fn test_tuner_picks_16x16_when_limits_unbounded() {
108 let tuner = WorkgroupTuner::derive_from_limits(&Limits::default());
109 assert_eq!(tuner.raster_2d, (16, 16));
110 assert_eq!(tuner.reduction, 256);
111 assert_eq!(tuner.fft, 64);
112 }
113
114 #[test]
115 fn test_tuner_falls_back_8x8_on_low_end_adapter() {
116 let tuner = WorkgroupTuner::derive_from_limits(&limits_with(8, 8, 64));
118 assert_eq!(tuner.raster_2d, (8, 8));
119 }
120
121 #[test]
122 fn test_tuner_falls_back_4x4_on_minimum_adapter() {
123 let tuner = WorkgroupTuner::derive_from_limits(&limits_with(4, 4, 16));
125 assert_eq!(tuner.raster_2d, (4, 4));
126 }
127
128 #[test]
129 fn test_tuner_respects_max_invocations_per_workgroup() {
130 let tuner = WorkgroupTuner::derive_from_limits(&limits_with(32, 32, 64));
132 assert_eq!(tuner.raster_2d, (8, 8));
133 }
134
135 #[test]
136 fn test_tuner_reduction_capped_at_256() {
137 let tuner = WorkgroupTuner::derive_from_limits(&Limits::default());
138 assert!(tuner.reduction <= 256);
139 let constrained = limits_with(128, 128, 128);
141 let tuner2 = WorkgroupTuner::derive_from_limits(&constrained);
142 assert!(tuner2.reduction <= 128);
143 }
144
145 #[test]
146 fn test_tuner_unlimited_returns_max_defaults() {
147 let tuner = WorkgroupTuner::unlimited();
148 assert_eq!(tuner.raster_2d, (16, 16));
149 assert_eq!(tuner.reduction, 256);
150 assert_eq!(tuner.fft, 64);
151 }
152
153 #[test]
154 fn test_tuner_default_equals_unlimited() {
155 let a = WorkgroupTuner::default();
156 let b = WorkgroupTuner::unlimited();
157 assert_eq!(a, b);
158 }
159
160 #[test]
161 fn test_tuner_reduction_respects_max_x_cap() {
162 let tuner = WorkgroupTuner::derive_from_limits(&limits_with(64, 256, 256));
164 assert!(tuner.reduction <= 64);
165 }
166
167 #[test]
168 fn test_tuner_fft_capped_at_64() {
169 let tuner = WorkgroupTuner::derive_from_limits(&Limits::default());
170 assert!(tuner.fft <= 64);
171 }
172
173 #[test]
174 fn test_tuner_fft_respects_low_adapter_limits() {
175 let tuner = WorkgroupTuner::derive_from_limits(&limits_with(32, 32, 32));
176 assert!(tuner.fft <= 32);
177 assert_eq!(tuner.fft, 32);
178 }
179
180 #[test]
181 fn test_tuner_clone_and_copy() {
182 let a = WorkgroupTuner::unlimited();
183 let b = a; let c = a; assert_eq!(b, c);
186 assert_eq!(a, b);
187 }
188
189 #[test]
190 fn test_tuner_debug_format() {
191 let tuner = WorkgroupTuner::unlimited();
192 let s = format!("{tuner:?}");
193 assert!(s.contains("WorkgroupTuner"));
194 assert!(s.contains("raster_2d"));
195 }
196}