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.max_texture_dimension_1d.max(other.max_texture_dimension_1d);
127 target.max_texture_dimension_2d = target.max_texture_dimension_2d.max(other.max_texture_dimension_2d);
128 target.max_texture_dimension_3d = target.max_texture_dimension_3d.max(other.max_texture_dimension_3d);
129 target.max_texture_array_layers = target.max_texture_array_layers.max(other.max_texture_array_layers);
130 target.max_bind_groups = target.max_bind_groups.max(other.max_bind_groups);
131 target.max_bindings_per_bind_group = target.max_bindings_per_bind_group.max(other.max_bindings_per_bind_group);
132 target.max_dynamic_uniform_buffers_per_pipeline_layout = target.max_dynamic_uniform_buffers_per_pipeline_layout.max(other.max_dynamic_uniform_buffers_per_pipeline_layout);
133 target.max_dynamic_storage_buffers_per_pipeline_layout = target.max_dynamic_storage_buffers_per_pipeline_layout.max(other.max_dynamic_storage_buffers_per_pipeline_layout);
134 target.max_sampled_textures_per_shader_stage = target.max_sampled_textures_per_shader_stage.max(other.max_sampled_textures_per_shader_stage);
135 target.max_samplers_per_shader_stage = target.max_samplers_per_shader_stage.max(other.max_samplers_per_shader_stage);
136 target.max_storage_buffers_per_shader_stage = target.max_storage_buffers_per_shader_stage.max(other.max_storage_buffers_per_shader_stage);
137 target.max_storage_textures_per_shader_stage = target.max_storage_textures_per_shader_stage.max(other.max_storage_textures_per_shader_stage);
138 target.max_uniform_buffers_per_shader_stage = target.max_uniform_buffers_per_shader_stage.max(other.max_uniform_buffers_per_shader_stage);
139 target.max_uniform_buffer_binding_size = target.max_uniform_buffer_binding_size.max(other.max_uniform_buffer_binding_size);
140 target.max_storage_buffer_binding_size = target.max_storage_buffer_binding_size.max(other.max_storage_buffer_binding_size);
141 target.max_vertex_buffers = target.max_vertex_buffers.max(other.max_vertex_buffers);
142 target.max_buffer_size = target.max_buffer_size.max(other.max_buffer_size);
143 target.max_vertex_attributes = target.max_vertex_attributes.max(other.max_vertex_attributes);
144 target.max_vertex_buffer_array_stride = target.max_vertex_buffer_array_stride.max(other.max_vertex_buffer_array_stride);
145 target.max_push_constant_size = target.max_push_constant_size.max(other.max_push_constant_size);
146 target.max_compute_workgroup_storage_size = target.max_compute_workgroup_storage_size.max(other.max_compute_workgroup_storage_size);
147 target.max_compute_invocations_per_workgroup = target.max_compute_invocations_per_workgroup.max(other.max_compute_invocations_per_workgroup);
148 target.max_compute_workgroup_size_x = target.max_compute_workgroup_size_x.max(other.max_compute_workgroup_size_x);
149 target.max_compute_workgroup_size_y = target.max_compute_workgroup_size_y.max(other.max_compute_workgroup_size_y);
150 target.max_compute_workgroup_size_z = target.max_compute_workgroup_size_z.max(other.max_compute_workgroup_size_z);
151 target.max_compute_workgroups_per_dimension = target.max_compute_workgroups_per_dimension.max(other.max_compute_workgroups_per_dimension);
152 target.max_binding_array_elements_per_shader_stage = target.max_binding_array_elements_per_shader_stage.max(other.max_binding_array_elements_per_shader_stage);
153
154 target.min_uniform_buffer_offset_alignment = target.min_uniform_buffer_offset_alignment.min(other.min_uniform_buffer_offset_alignment);
156 target.min_storage_buffer_offset_alignment = target.min_storage_buffer_offset_alignment.min(other.min_storage_buffer_offset_alignment);
157}
158
159pub fn clamp_limits_to_adapter(requested: &wgpu::Limits, adapter: &wgpu::Limits) -> wgpu::Limits {
165 let mut result = requested.clone();
166
167 result.max_texture_dimension_1d = result.max_texture_dimension_1d.min(adapter.max_texture_dimension_1d);
169 result.max_texture_dimension_2d = result.max_texture_dimension_2d.min(adapter.max_texture_dimension_2d);
170 result.max_texture_dimension_3d = result.max_texture_dimension_3d.min(adapter.max_texture_dimension_3d);
171 result.max_texture_array_layers = result.max_texture_array_layers.min(adapter.max_texture_array_layers);
172 result.max_bind_groups = result.max_bind_groups.min(adapter.max_bind_groups);
173 result.max_bindings_per_bind_group = result.max_bindings_per_bind_group.min(adapter.max_bindings_per_bind_group);
174 result.max_dynamic_uniform_buffers_per_pipeline_layout = result.max_dynamic_uniform_buffers_per_pipeline_layout.min(adapter.max_dynamic_uniform_buffers_per_pipeline_layout);
175 result.max_dynamic_storage_buffers_per_pipeline_layout = result.max_dynamic_storage_buffers_per_pipeline_layout.min(adapter.max_dynamic_storage_buffers_per_pipeline_layout);
176 result.max_sampled_textures_per_shader_stage = result.max_sampled_textures_per_shader_stage.min(adapter.max_sampled_textures_per_shader_stage);
177 result.max_samplers_per_shader_stage = result.max_samplers_per_shader_stage.min(adapter.max_samplers_per_shader_stage);
178 result.max_storage_buffers_per_shader_stage = result.max_storage_buffers_per_shader_stage.min(adapter.max_storage_buffers_per_shader_stage);
179 result.max_storage_textures_per_shader_stage = result.max_storage_textures_per_shader_stage.min(adapter.max_storage_textures_per_shader_stage);
180 result.max_uniform_buffers_per_shader_stage = result.max_uniform_buffers_per_shader_stage.min(adapter.max_uniform_buffers_per_shader_stage);
181 result.max_uniform_buffer_binding_size = result.max_uniform_buffer_binding_size.min(adapter.max_uniform_buffer_binding_size);
182 result.max_storage_buffer_binding_size = result.max_storage_buffer_binding_size.min(adapter.max_storage_buffer_binding_size);
183 result.max_vertex_buffers = result.max_vertex_buffers.min(adapter.max_vertex_buffers);
184 result.max_buffer_size = result.max_buffer_size.min(adapter.max_buffer_size);
185 result.max_vertex_attributes = result.max_vertex_attributes.min(adapter.max_vertex_attributes);
186 result.max_vertex_buffer_array_stride = result.max_vertex_buffer_array_stride.min(adapter.max_vertex_buffer_array_stride);
187 result.max_push_constant_size = result.max_push_constant_size.min(adapter.max_push_constant_size);
188 result.max_compute_workgroup_storage_size = result.max_compute_workgroup_storage_size.min(adapter.max_compute_workgroup_storage_size);
189 result.max_compute_invocations_per_workgroup = result.max_compute_invocations_per_workgroup.min(adapter.max_compute_invocations_per_workgroup);
190 result.max_compute_workgroup_size_x = result.max_compute_workgroup_size_x.min(adapter.max_compute_workgroup_size_x);
191 result.max_compute_workgroup_size_y = result.max_compute_workgroup_size_y.min(adapter.max_compute_workgroup_size_y);
192 result.max_compute_workgroup_size_z = result.max_compute_workgroup_size_z.min(adapter.max_compute_workgroup_size_z);
193 result.max_compute_workgroups_per_dimension = result.max_compute_workgroups_per_dimension.min(adapter.max_compute_workgroups_per_dimension);
194 result.max_binding_array_elements_per_shader_stage = result.max_binding_array_elements_per_shader_stage.min(adapter.max_binding_array_elements_per_shader_stage);
195 result.max_color_attachments = result.max_color_attachments.min(adapter.max_color_attachments);
196 result.max_color_attachment_bytes_per_sample = result.max_color_attachment_bytes_per_sample.min(adapter.max_color_attachment_bytes_per_sample);
197 result.max_inter_stage_shader_components = result.max_inter_stage_shader_components.min(adapter.max_inter_stage_shader_components);
198 result.max_non_sampler_bindings = result.max_non_sampler_bindings.min(adapter.max_non_sampler_bindings);
199
200 result.min_uniform_buffer_offset_alignment = result.min_uniform_buffer_offset_alignment.max(adapter.min_uniform_buffer_offset_alignment);
202 result.min_storage_buffer_offset_alignment = result.min_storage_buffer_offset_alignment.max(adapter.min_storage_buffer_offset_alignment);
203
204 result.min_subgroup_size = adapter.min_subgroup_size;
206 result.max_subgroup_size = adapter.max_subgroup_size;
207
208 result
209}
210
211#[cfg(test)]
212mod tests {
213 use super::*;
214
215 #[test]
216 fn test_gpu_requirements_none() {
217 let req = GpuRequirements::none();
218 assert!(req.required_features.is_empty());
219 assert!(req.requested_features.is_empty());
220 assert!(req.additional_wgpu_features.is_empty());
221 }
222
223 #[test]
224 fn test_gpu_requirements_builder() {
225 let req = GpuRequirements::new()
226 .require_features(GpuFeatures::INDIRECT_FIRST_INSTANCE)
227 .request_features(GpuFeatures::TIMESTAMP_QUERY)
228 .with_min_limits(|l| {
229 l.max_binding_array_elements_per_shader_stage = 256;
230 });
231
232 assert!(req.required_features.contains(GpuFeatures::INDIRECT_FIRST_INSTANCE));
233 assert!(req.requested_features.contains(GpuFeatures::TIMESTAMP_QUERY));
234 assert_eq!(req.min_limits.max_binding_array_elements_per_shader_stage, 256);
235 }
236
237 #[test]
238 fn test_gpu_requirements_merge() {
239 let mut a = GpuRequirements::new()
240 .require_features(GpuFeatures::INDIRECT_FIRST_INSTANCE)
241 .with_min_limits(|l| {
242 l.max_binding_array_elements_per_shader_stage = 128;
243 });
244
245 let b = GpuRequirements::new()
246 .require_features(GpuFeatures::TEXTURE_BINDING_ARRAY)
247 .request_features(GpuFeatures::TIMESTAMP_QUERY)
248 .with_min_limits(|l| {
249 l.max_binding_array_elements_per_shader_stage = 256;
250 });
251
252 a.merge(&b);
253
254 assert!(a.required_features.contains(GpuFeatures::INDIRECT_FIRST_INSTANCE));
255 assert!(a.required_features.contains(GpuFeatures::TEXTURE_BINDING_ARRAY));
256 assert!(a.requested_features.contains(GpuFeatures::TIMESTAMP_QUERY));
257 assert_eq!(a.min_limits.max_binding_array_elements_per_shader_stage, 256);
258 }
259
260 #[test]
261 fn test_merge_limits_max() {
262 let mut a = wgpu::Limits::default();
263 let mut b = wgpu::Limits::default();
264
265 a.max_texture_dimension_2d = 4096;
266 b.max_texture_dimension_2d = 8192;
267 b.max_bind_groups = 8;
268
269 merge_limits_max(&mut a, &b);
270
271 assert_eq!(a.max_texture_dimension_2d, 8192);
272 assert_eq!(a.max_bind_groups, 8);
273 }
274
275 #[test]
276 fn test_clamp_limits_to_adapter() {
277 let mut requested = wgpu::Limits::default();
278 let mut adapter = wgpu::Limits::default();
279
280 requested.max_binding_array_elements_per_shader_stage = 1024;
282 adapter.max_binding_array_elements_per_shader_stage = 256;
283
284 requested.max_texture_dimension_2d = 4096;
286 adapter.max_texture_dimension_2d = 8192;
287
288 let clamped = clamp_limits_to_adapter(&requested, &adapter);
289
290 assert_eq!(clamped.max_binding_array_elements_per_shader_stage, 256);
292 assert_eq!(clamped.max_texture_dimension_2d, 4096);
294 }
295}