astrelis_render/
capability.rs1use crate::features::GpuFeatures;
22
23pub trait RenderCapability {
30 fn requirements() -> GpuRequirements;
32
33 fn name() -> &'static str {
35 std::any::type_name::<Self>()
36 }
37}
38
39#[derive(Debug, Clone)]
44pub struct GpuRequirements {
45 pub required_features: GpuFeatures,
47 pub requested_features: GpuFeatures,
49 pub additional_wgpu_features: wgpu::Features,
51 pub min_limits: wgpu::Limits,
53}
54
55impl GpuRequirements {
56 pub fn none() -> Self {
58 Self {
59 required_features: GpuFeatures::empty(),
60 requested_features: GpuFeatures::empty(),
61 additional_wgpu_features: wgpu::Features::empty(),
62 min_limits: wgpu::Limits::default(),
63 }
64 }
65
66 pub fn new() -> Self {
68 Self::none()
69 }
70
71 pub fn require_features(mut self, features: GpuFeatures) -> Self {
73 self.required_features |= features;
74 self
75 }
76
77 pub fn request_features(mut self, features: GpuFeatures) -> Self {
79 self.requested_features |= features;
80 self
81 }
82
83 pub fn with_wgpu_features(mut self, features: wgpu::Features) -> Self {
85 self.additional_wgpu_features |= features;
86 self
87 }
88
89 pub fn with_min_limits(mut self, f: impl FnOnce(&mut wgpu::Limits)) -> Self {
99 f(&mut self.min_limits);
100 self
101 }
102
103 pub fn merge(&mut self, other: &GpuRequirements) {
107 self.required_features |= other.required_features;
108 self.requested_features |= other.requested_features;
109 self.additional_wgpu_features |= other.additional_wgpu_features;
110 merge_limits_max(&mut self.min_limits, &other.min_limits);
111 }
112}
113
114impl Default for GpuRequirements {
115 fn default() -> Self {
116 Self::none()
117 }
118}
119
120pub fn merge_limits_max(target: &mut wgpu::Limits, other: &wgpu::Limits) {
125 target.max_texture_dimension_1d = target
127 .max_texture_dimension_1d
128 .max(other.max_texture_dimension_1d);
129 target.max_texture_dimension_2d = target
130 .max_texture_dimension_2d
131 .max(other.max_texture_dimension_2d);
132 target.max_texture_dimension_3d = target
133 .max_texture_dimension_3d
134 .max(other.max_texture_dimension_3d);
135 target.max_texture_array_layers = target
136 .max_texture_array_layers
137 .max(other.max_texture_array_layers);
138 target.max_bind_groups = target.max_bind_groups.max(other.max_bind_groups);
139 target.max_bindings_per_bind_group = target
140 .max_bindings_per_bind_group
141 .max(other.max_bindings_per_bind_group);
142 target.max_dynamic_uniform_buffers_per_pipeline_layout = target
143 .max_dynamic_uniform_buffers_per_pipeline_layout
144 .max(other.max_dynamic_uniform_buffers_per_pipeline_layout);
145 target.max_dynamic_storage_buffers_per_pipeline_layout = target
146 .max_dynamic_storage_buffers_per_pipeline_layout
147 .max(other.max_dynamic_storage_buffers_per_pipeline_layout);
148 target.max_sampled_textures_per_shader_stage = target
149 .max_sampled_textures_per_shader_stage
150 .max(other.max_sampled_textures_per_shader_stage);
151 target.max_samplers_per_shader_stage = target
152 .max_samplers_per_shader_stage
153 .max(other.max_samplers_per_shader_stage);
154 target.max_storage_buffers_per_shader_stage = target
155 .max_storage_buffers_per_shader_stage
156 .max(other.max_storage_buffers_per_shader_stage);
157 target.max_storage_textures_per_shader_stage = target
158 .max_storage_textures_per_shader_stage
159 .max(other.max_storage_textures_per_shader_stage);
160 target.max_uniform_buffers_per_shader_stage = target
161 .max_uniform_buffers_per_shader_stage
162 .max(other.max_uniform_buffers_per_shader_stage);
163 target.max_uniform_buffer_binding_size = target
164 .max_uniform_buffer_binding_size
165 .max(other.max_uniform_buffer_binding_size);
166 target.max_storage_buffer_binding_size = target
167 .max_storage_buffer_binding_size
168 .max(other.max_storage_buffer_binding_size);
169 target.max_vertex_buffers = target.max_vertex_buffers.max(other.max_vertex_buffers);
170 target.max_buffer_size = target.max_buffer_size.max(other.max_buffer_size);
171 target.max_vertex_attributes = target
172 .max_vertex_attributes
173 .max(other.max_vertex_attributes);
174 target.max_vertex_buffer_array_stride = target
175 .max_vertex_buffer_array_stride
176 .max(other.max_vertex_buffer_array_stride);
177 target.max_push_constant_size = target
178 .max_push_constant_size
179 .max(other.max_push_constant_size);
180 target.max_compute_workgroup_storage_size = target
181 .max_compute_workgroup_storage_size
182 .max(other.max_compute_workgroup_storage_size);
183 target.max_compute_invocations_per_workgroup = target
184 .max_compute_invocations_per_workgroup
185 .max(other.max_compute_invocations_per_workgroup);
186 target.max_compute_workgroup_size_x = target
187 .max_compute_workgroup_size_x
188 .max(other.max_compute_workgroup_size_x);
189 target.max_compute_workgroup_size_y = target
190 .max_compute_workgroup_size_y
191 .max(other.max_compute_workgroup_size_y);
192 target.max_compute_workgroup_size_z = target
193 .max_compute_workgroup_size_z
194 .max(other.max_compute_workgroup_size_z);
195 target.max_compute_workgroups_per_dimension = target
196 .max_compute_workgroups_per_dimension
197 .max(other.max_compute_workgroups_per_dimension);
198 target.max_binding_array_elements_per_shader_stage = target
199 .max_binding_array_elements_per_shader_stage
200 .max(other.max_binding_array_elements_per_shader_stage);
201
202 target.min_uniform_buffer_offset_alignment = target
204 .min_uniform_buffer_offset_alignment
205 .min(other.min_uniform_buffer_offset_alignment);
206 target.min_storage_buffer_offset_alignment = target
207 .min_storage_buffer_offset_alignment
208 .min(other.min_storage_buffer_offset_alignment);
209}
210
211pub fn clamp_limits_to_adapter(requested: &wgpu::Limits, adapter: &wgpu::Limits) -> wgpu::Limits {
217 let mut result = requested.clone();
218
219 result.max_texture_dimension_1d = result
221 .max_texture_dimension_1d
222 .min(adapter.max_texture_dimension_1d);
223 result.max_texture_dimension_2d = result
224 .max_texture_dimension_2d
225 .min(adapter.max_texture_dimension_2d);
226 result.max_texture_dimension_3d = result
227 .max_texture_dimension_3d
228 .min(adapter.max_texture_dimension_3d);
229 result.max_texture_array_layers = result
230 .max_texture_array_layers
231 .min(adapter.max_texture_array_layers);
232 result.max_bind_groups = result.max_bind_groups.min(adapter.max_bind_groups);
233 result.max_bindings_per_bind_group = result
234 .max_bindings_per_bind_group
235 .min(adapter.max_bindings_per_bind_group);
236 result.max_dynamic_uniform_buffers_per_pipeline_layout = result
237 .max_dynamic_uniform_buffers_per_pipeline_layout
238 .min(adapter.max_dynamic_uniform_buffers_per_pipeline_layout);
239 result.max_dynamic_storage_buffers_per_pipeline_layout = result
240 .max_dynamic_storage_buffers_per_pipeline_layout
241 .min(adapter.max_dynamic_storage_buffers_per_pipeline_layout);
242 result.max_sampled_textures_per_shader_stage = result
243 .max_sampled_textures_per_shader_stage
244 .min(adapter.max_sampled_textures_per_shader_stage);
245 result.max_samplers_per_shader_stage = result
246 .max_samplers_per_shader_stage
247 .min(adapter.max_samplers_per_shader_stage);
248 result.max_storage_buffers_per_shader_stage = result
249 .max_storage_buffers_per_shader_stage
250 .min(adapter.max_storage_buffers_per_shader_stage);
251 result.max_storage_textures_per_shader_stage = result
252 .max_storage_textures_per_shader_stage
253 .min(adapter.max_storage_textures_per_shader_stage);
254 result.max_uniform_buffers_per_shader_stage = result
255 .max_uniform_buffers_per_shader_stage
256 .min(adapter.max_uniform_buffers_per_shader_stage);
257 result.max_uniform_buffer_binding_size = result
258 .max_uniform_buffer_binding_size
259 .min(adapter.max_uniform_buffer_binding_size);
260 result.max_storage_buffer_binding_size = result
261 .max_storage_buffer_binding_size
262 .min(adapter.max_storage_buffer_binding_size);
263 result.max_vertex_buffers = result.max_vertex_buffers.min(adapter.max_vertex_buffers);
264 result.max_buffer_size = result.max_buffer_size.min(adapter.max_buffer_size);
265 result.max_vertex_attributes = result
266 .max_vertex_attributes
267 .min(adapter.max_vertex_attributes);
268 result.max_vertex_buffer_array_stride = result
269 .max_vertex_buffer_array_stride
270 .min(adapter.max_vertex_buffer_array_stride);
271 result.max_push_constant_size = result
272 .max_push_constant_size
273 .min(adapter.max_push_constant_size);
274 result.max_compute_workgroup_storage_size = result
275 .max_compute_workgroup_storage_size
276 .min(adapter.max_compute_workgroup_storage_size);
277 result.max_compute_invocations_per_workgroup = result
278 .max_compute_invocations_per_workgroup
279 .min(adapter.max_compute_invocations_per_workgroup);
280 result.max_compute_workgroup_size_x = result
281 .max_compute_workgroup_size_x
282 .min(adapter.max_compute_workgroup_size_x);
283 result.max_compute_workgroup_size_y = result
284 .max_compute_workgroup_size_y
285 .min(adapter.max_compute_workgroup_size_y);
286 result.max_compute_workgroup_size_z = result
287 .max_compute_workgroup_size_z
288 .min(adapter.max_compute_workgroup_size_z);
289 result.max_compute_workgroups_per_dimension = result
290 .max_compute_workgroups_per_dimension
291 .min(adapter.max_compute_workgroups_per_dimension);
292 result.max_binding_array_elements_per_shader_stage = result
293 .max_binding_array_elements_per_shader_stage
294 .min(adapter.max_binding_array_elements_per_shader_stage);
295 result.max_color_attachments = result
296 .max_color_attachments
297 .min(adapter.max_color_attachments);
298 result.max_color_attachment_bytes_per_sample = result
299 .max_color_attachment_bytes_per_sample
300 .min(adapter.max_color_attachment_bytes_per_sample);
301 result.max_inter_stage_shader_components = result
302 .max_inter_stage_shader_components
303 .min(adapter.max_inter_stage_shader_components);
304 result.max_non_sampler_bindings = result
305 .max_non_sampler_bindings
306 .min(adapter.max_non_sampler_bindings);
307
308 result.min_uniform_buffer_offset_alignment = result
310 .min_uniform_buffer_offset_alignment
311 .max(adapter.min_uniform_buffer_offset_alignment);
312 result.min_storage_buffer_offset_alignment = result
313 .min_storage_buffer_offset_alignment
314 .max(adapter.min_storage_buffer_offset_alignment);
315
316 result.min_subgroup_size = adapter.min_subgroup_size;
318 result.max_subgroup_size = adapter.max_subgroup_size;
319
320 result
321}
322
323#[cfg(test)]
324mod tests {
325 use super::*;
326
327 #[test]
328 fn test_gpu_requirements_none() {
329 let req = GpuRequirements::none();
330 assert!(req.required_features.is_empty());
331 assert!(req.requested_features.is_empty());
332 assert!(req.additional_wgpu_features.is_empty());
333 }
334
335 #[test]
336 fn test_gpu_requirements_builder() {
337 let req = GpuRequirements::new()
338 .require_features(GpuFeatures::INDIRECT_FIRST_INSTANCE)
339 .request_features(GpuFeatures::TIMESTAMP_QUERY)
340 .with_min_limits(|l| {
341 l.max_binding_array_elements_per_shader_stage = 256;
342 });
343
344 assert!(
345 req.required_features
346 .contains(GpuFeatures::INDIRECT_FIRST_INSTANCE)
347 );
348 assert!(
349 req.requested_features
350 .contains(GpuFeatures::TIMESTAMP_QUERY)
351 );
352 assert_eq!(
353 req.min_limits.max_binding_array_elements_per_shader_stage,
354 256
355 );
356 }
357
358 #[test]
359 fn test_gpu_requirements_merge() {
360 let mut a = GpuRequirements::new()
361 .require_features(GpuFeatures::INDIRECT_FIRST_INSTANCE)
362 .with_min_limits(|l| {
363 l.max_binding_array_elements_per_shader_stage = 128;
364 });
365
366 let b = GpuRequirements::new()
367 .require_features(GpuFeatures::TEXTURE_BINDING_ARRAY)
368 .request_features(GpuFeatures::TIMESTAMP_QUERY)
369 .with_min_limits(|l| {
370 l.max_binding_array_elements_per_shader_stage = 256;
371 });
372
373 a.merge(&b);
374
375 assert!(
376 a.required_features
377 .contains(GpuFeatures::INDIRECT_FIRST_INSTANCE)
378 );
379 assert!(
380 a.required_features
381 .contains(GpuFeatures::TEXTURE_BINDING_ARRAY)
382 );
383 assert!(a.requested_features.contains(GpuFeatures::TIMESTAMP_QUERY));
384 assert_eq!(
385 a.min_limits.max_binding_array_elements_per_shader_stage,
386 256
387 );
388 }
389
390 #[test]
391 fn test_merge_limits_max() {
392 let mut a = wgpu::Limits::default();
393 let mut b = wgpu::Limits::default();
394
395 a.max_texture_dimension_2d = 4096;
396 b.max_texture_dimension_2d = 8192;
397 b.max_bind_groups = 8;
398
399 merge_limits_max(&mut a, &b);
400
401 assert_eq!(a.max_texture_dimension_2d, 8192);
402 assert_eq!(a.max_bind_groups, 8);
403 }
404
405 #[test]
406 fn test_clamp_limits_to_adapter() {
407 let mut requested = wgpu::Limits::default();
408 let mut adapter = wgpu::Limits::default();
409
410 requested.max_binding_array_elements_per_shader_stage = 1024;
412 adapter.max_binding_array_elements_per_shader_stage = 256;
413
414 requested.max_texture_dimension_2d = 4096;
416 adapter.max_texture_dimension_2d = 8192;
417
418 let clamped = clamp_limits_to_adapter(&requested, &adapter);
419
420 assert_eq!(clamped.max_binding_array_elements_per_shader_stage, 256);
422 assert_eq!(clamped.max_texture_dimension_2d, 4096);
424 }
425}