wgpu_types/limits.rs
1//! [`Limits`] and downlevel-related types.
2
3use core::cmp::Ordering;
4
5#[cfg(any(feature = "serde", test))]
6use serde::{Deserialize, Serialize};
7
8#[cfg(doc)]
9use crate::{Features, TextureFormat};
10
11/// Invoke a macro for each of the limits.
12///
13/// The supplied macro should take two arguments. The first is a limit name, as
14/// an identifier, typically used to access a member of `struct Limits`. The
15/// second is `Ordering::Less` if valid values are less than the limit (the
16/// common case), or `Ordering::Greater` if valid values are more than the limit
17/// (for limits like alignments, which are minima instead of maxima).
18macro_rules! with_limits {
19 ($macro_name:ident) => {
20 $macro_name!(max_texture_dimension_1d, Ordering::Less);
21 $macro_name!(max_texture_dimension_2d, Ordering::Less);
22 $macro_name!(max_texture_dimension_3d, Ordering::Less);
23 $macro_name!(max_texture_array_layers, Ordering::Less);
24 $macro_name!(max_bind_groups, Ordering::Less);
25 $macro_name!(max_bindings_per_bind_group, Ordering::Less);
26 $macro_name!(
27 max_dynamic_uniform_buffers_per_pipeline_layout,
28 Ordering::Less
29 );
30 $macro_name!(
31 max_dynamic_storage_buffers_per_pipeline_layout,
32 Ordering::Less
33 );
34 $macro_name!(max_sampled_textures_per_shader_stage, Ordering::Less);
35 $macro_name!(max_samplers_per_shader_stage, Ordering::Less);
36 $macro_name!(max_storage_buffers_per_shader_stage, Ordering::Less);
37 $macro_name!(max_storage_textures_per_shader_stage, Ordering::Less);
38 $macro_name!(max_uniform_buffers_per_shader_stage, Ordering::Less);
39 $macro_name!(max_binding_array_elements_per_shader_stage, Ordering::Less);
40 $macro_name!(
41 max_binding_array_acceleration_structure_elements_per_shader_stage,
42 Ordering::Less
43 );
44 $macro_name!(
45 max_binding_array_sampler_elements_per_shader_stage,
46 Ordering::Less
47 );
48
49 $macro_name!(max_uniform_buffer_binding_size, Ordering::Less);
50 $macro_name!(max_storage_buffer_binding_size, Ordering::Less);
51 $macro_name!(max_vertex_buffers, Ordering::Less);
52 $macro_name!(max_buffer_size, Ordering::Less);
53 $macro_name!(max_vertex_attributes, Ordering::Less);
54 $macro_name!(max_vertex_buffer_array_stride, Ordering::Less);
55 $macro_name!(max_inter_stage_shader_variables, Ordering::Less);
56 $macro_name!(min_uniform_buffer_offset_alignment, Ordering::Greater);
57 $macro_name!(min_storage_buffer_offset_alignment, Ordering::Greater);
58 $macro_name!(max_color_attachments, Ordering::Less);
59 $macro_name!(max_color_attachment_bytes_per_sample, Ordering::Less);
60 $macro_name!(max_compute_workgroup_storage_size, Ordering::Less);
61 $macro_name!(max_compute_invocations_per_workgroup, Ordering::Less);
62 $macro_name!(max_compute_workgroup_size_x, Ordering::Less);
63 $macro_name!(max_compute_workgroup_size_y, Ordering::Less);
64 $macro_name!(max_compute_workgroup_size_z, Ordering::Less);
65 $macro_name!(max_compute_workgroups_per_dimension, Ordering::Less);
66
67 $macro_name!(max_immediate_size, Ordering::Less);
68 $macro_name!(max_non_sampler_bindings, Ordering::Less);
69
70 $macro_name!(max_task_workgroup_total_count, Ordering::Less);
71 $macro_name!(max_task_workgroups_per_dimension, Ordering::Less);
72 $macro_name!(max_mesh_workgroup_total_count, Ordering::Less);
73 $macro_name!(max_mesh_workgroups_per_dimension, Ordering::Less);
74 $macro_name!(max_task_invocations_per_workgroup, Ordering::Less);
75 $macro_name!(max_task_invocations_per_dimension, Ordering::Less);
76 $macro_name!(max_mesh_invocations_per_workgroup, Ordering::Less);
77 $macro_name!(max_mesh_invocations_per_dimension, Ordering::Less);
78
79 $macro_name!(max_task_payload_size, Ordering::Less);
80 $macro_name!(max_mesh_output_vertices, Ordering::Less);
81 $macro_name!(max_mesh_output_primitives, Ordering::Less);
82 $macro_name!(max_mesh_output_layers, Ordering::Less);
83 $macro_name!(max_mesh_multiview_view_count, Ordering::Less);
84
85 $macro_name!(max_blas_primitive_count, Ordering::Less);
86 $macro_name!(max_blas_geometry_count, Ordering::Less);
87 $macro_name!(max_tlas_instance_count, Ordering::Less);
88 $macro_name!(max_acceleration_structures_per_shader_stage, Ordering::Less);
89
90 $macro_name!(max_multiview_view_count, Ordering::Less);
91 };
92}
93
94/// Represents the sets of limits an adapter/device supports.
95///
96/// We provide three different defaults.
97/// - [`Limits::downlevel_defaults()`]. This is a set of limits that is guaranteed to work on almost
98/// all backends, including "downlevel" backends such as OpenGL and D3D11, other than WebGL. For
99/// most applications we recommend using these limits, assuming they are high enough for your
100/// application, and you do not intend to support WebGL.
101/// - [`Limits::downlevel_webgl2_defaults()`] This is a set of limits that is lower even than the
102/// [`downlevel_defaults()`], configured to be low enough to support running in the browser using
103/// WebGL2.
104/// - [`Limits::default()`]. This is the set of limits that is guaranteed to work on all modern
105/// backends and is guaranteed to be supported by WebGPU. Applications needing more modern
106/// features can use this as a reasonable set of limits if they are targeting only desktop and
107/// modern mobile devices.
108///
109/// We recommend starting with the most restrictive limits you can and manually increasing the
110/// limits you need boosted. This will let you stay running on all hardware that supports the limits
111/// you need.
112///
113/// Limits "better" than the default must be supported by the adapter and requested when requesting
114/// a device. If limits "better" than the adapter supports are requested, requesting a device will
115/// panic. Once a device is requested, you may only use resources up to the limits requested _even_
116/// if the adapter supports "better" limits.
117///
118/// Requesting limits that are "better" than you need may cause performance to decrease because the
119/// implementation needs to support more than is needed. You should ideally only request exactly
120/// what you need.
121///
122/// Corresponds to [WebGPU `GPUSupportedLimits`](
123/// https://gpuweb.github.io/gpuweb/#gpusupportedlimits).
124///
125/// [`downlevel_defaults()`]: Limits::downlevel_defaults
126#[repr(C)]
127#[derive(Clone, Debug, PartialEq, Eq, Hash)]
128#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
129#[cfg_attr(feature = "serde", serde(rename_all = "camelCase", default))]
130pub struct Limits {
131 /// Maximum allowed value for the `size.width` of a texture created with `TextureDimension::D1`.
132 /// Defaults to 8192. Higher is "better".
133 #[cfg_attr(feature = "serde", serde(rename = "maxTextureDimension1D"))]
134 pub max_texture_dimension_1d: u32,
135 /// Maximum allowed value for the `size.width` and `size.height` of a texture created with `TextureDimension::D2`.
136 /// Defaults to 8192. Higher is "better".
137 #[cfg_attr(feature = "serde", serde(rename = "maxTextureDimension2D"))]
138 pub max_texture_dimension_2d: u32,
139 /// Maximum allowed value for the `size.width`, `size.height`, and `size.depth_or_array_layers`
140 /// of a texture created with `TextureDimension::D3`.
141 /// Defaults to 2048. Higher is "better".
142 #[cfg_attr(feature = "serde", serde(rename = "maxTextureDimension3D"))]
143 pub max_texture_dimension_3d: u32,
144 /// Maximum allowed value for the `size.depth_or_array_layers` of a texture created with `TextureDimension::D2`.
145 /// Defaults to 256. Higher is "better".
146 pub max_texture_array_layers: u32,
147 /// Amount of bind groups that can be attached to a pipeline at the same time. Defaults to 4. Higher is "better".
148 pub max_bind_groups: u32,
149 /// Maximum binding index allowed in `create_bind_group_layout`. Defaults to 1000. Higher is "better".
150 pub max_bindings_per_bind_group: u32,
151 /// Amount of uniform buffer bindings that can be dynamic in a single pipeline. Defaults to 8. Higher is "better".
152 pub max_dynamic_uniform_buffers_per_pipeline_layout: u32,
153 /// Amount of storage buffer bindings that can be dynamic in a single pipeline. Defaults to 4. Higher is "better".
154 pub max_dynamic_storage_buffers_per_pipeline_layout: u32,
155 /// Amount of sampled textures visible in a single shader stage. Defaults to 16. Higher is "better".
156 pub max_sampled_textures_per_shader_stage: u32,
157 /// Amount of samplers visible in a single shader stage. Defaults to 16. Higher is "better".
158 pub max_samplers_per_shader_stage: u32,
159 /// Amount of storage buffers visible in a single shader stage. Defaults to 8. Higher is "better".
160 pub max_storage_buffers_per_shader_stage: u32,
161 /// Amount of storage textures visible in a single shader stage. Defaults to 4. Higher is "better".
162 pub max_storage_textures_per_shader_stage: u32,
163 /// Amount of uniform buffers visible in a single shader stage. Defaults to 12. Higher is "better".
164 pub max_uniform_buffers_per_shader_stage: u32,
165 /// Amount of individual resources within binding arrays that can be accessed in a single shader stage. Applies
166 /// to all types of bindings except samplers.
167 ///
168 /// This "defaults" to 0. However if binding arrays are supported, all devices can support 500,000. Higher is "better".
169 pub max_binding_array_elements_per_shader_stage: u32,
170 /// Amount of individual acceleration structures within binding arrays that can be accessed in a single shader stage.
171 ///
172 /// This "defaults" to 0. Higher is "better".
173 pub max_binding_array_acceleration_structure_elements_per_shader_stage: u32,
174 /// Amount of individual samplers within binding arrays that can be accessed in a single shader stage.
175 ///
176 /// This "defaults" to 0. However if binding arrays are supported, all devices can support 1,000. Higher is "better".
177 pub max_binding_array_sampler_elements_per_shader_stage: u32,
178 /// Maximum size in bytes of a binding to a uniform buffer. Defaults to 64 KiB. Higher is "better".
179 pub max_uniform_buffer_binding_size: u64,
180 /// Maximum size in bytes of a binding to a storage buffer. Defaults to 128 MiB. Higher is "better".
181 pub max_storage_buffer_binding_size: u64,
182 /// Maximum length of `VertexState::buffers` when creating a `RenderPipeline`.
183 /// Defaults to 8. Higher is "better".
184 pub max_vertex_buffers: u32,
185 /// A limit above which buffer allocations are guaranteed to fail.
186 /// Defaults to 256 MiB. Higher is "better".
187 ///
188 /// Buffer allocations below the maximum buffer size may not succeed depending on available memory,
189 /// fragmentation and other factors.
190 pub max_buffer_size: u64,
191 /// Maximum length of `VertexBufferLayout::attributes`, summed over all `VertexState::buffers`,
192 /// when creating a `RenderPipeline`.
193 /// Defaults to 16. Higher is "better".
194 pub max_vertex_attributes: u32,
195 /// Maximum value for `VertexBufferLayout::array_stride` when creating a `RenderPipeline`.
196 /// Defaults to 2048. Higher is "better".
197 pub max_vertex_buffer_array_stride: u32,
198 /// Maximum value for the number of input or output variables for inter-stage communication
199 /// (like vertex outputs or fragment inputs) `@location(…)`s (in WGSL parlance)
200 /// when creating a `RenderPipeline`.
201 /// Defaults to 16. Higher is "better".
202 pub max_inter_stage_shader_variables: u32,
203 /// Required `BufferBindingType::Uniform` alignment for `BufferBinding::offset`
204 /// when creating a `BindGroup`, or for `set_bind_group` `dynamicOffsets`.
205 /// Defaults to 256. Lower is "better".
206 pub min_uniform_buffer_offset_alignment: u32,
207 /// Required `BufferBindingType::Storage` alignment for `BufferBinding::offset`
208 /// when creating a `BindGroup`, or for `set_bind_group` `dynamicOffsets`.
209 /// Defaults to 256. Lower is "better".
210 pub min_storage_buffer_offset_alignment: u32,
211 /// The maximum allowed number of color attachments.
212 pub max_color_attachments: u32,
213 /// The maximum number of bytes necessary to hold one sample (pixel or subpixel) of render
214 /// pipeline output data, across all color attachments as described by [`TextureFormat::target_pixel_byte_cost`]
215 /// and [`TextureFormat::target_component_alignment`]. Defaults to 32. Higher is "better".
216 ///
217 /// ⚠️ `Rgba8Unorm`/`Rgba8Snorm`/`Bgra8Unorm`/`Bgra8Snorm` are deceptively 8 bytes per sample. ⚠️
218 pub max_color_attachment_bytes_per_sample: u32,
219 /// Maximum number of bytes used for workgroup memory in a compute entry point. Defaults to
220 /// 16384. Higher is "better".
221 pub max_compute_workgroup_storage_size: u32,
222 /// Maximum value of the product of the `workgroup_size` dimensions for a compute entry-point.
223 /// Defaults to 256. Higher is "better".
224 pub max_compute_invocations_per_workgroup: u32,
225 /// The maximum value of the `workgroup_size` X dimension for a compute stage `ShaderModule` entry-point.
226 /// Defaults to 256. Higher is "better".
227 pub max_compute_workgroup_size_x: u32,
228 /// The maximum value of the `workgroup_size` Y dimension for a compute stage `ShaderModule` entry-point.
229 /// Defaults to 256. Higher is "better".
230 pub max_compute_workgroup_size_y: u32,
231 /// The maximum value of the `workgroup_size` Z dimension for a compute stage `ShaderModule` entry-point.
232 /// Defaults to 64. Higher is "better".
233 pub max_compute_workgroup_size_z: u32,
234 /// The maximum value for each dimension of a `ComputePass::dispatch_workgroups(x, y, z)` operation.
235 /// Defaults to 65535. Higher is "better".
236 pub max_compute_workgroups_per_dimension: u32,
237
238 /// Amount of storage available for immediates in bytes. Defaults to 0. Higher is "better".
239 /// Requesting more than 0 during device creation requires [`Features::IMMEDIATES`] to be enabled.
240 ///
241 /// Expect the size to be:
242 /// - Vulkan: 128-256 bytes
243 /// - DX12: 128 bytes
244 /// - Metal: 4096 bytes
245 /// - OpenGL doesn't natively support immediates, and are emulated with uniforms,
246 /// so this number is less useful but likely 256.
247 pub max_immediate_size: u32,
248 /// Maximum number of live non-sampler bindings.
249 ///
250 /// <div class="warning">
251 /// The default value is **1_000_000**, On systems with integrated GPUs (iGPUs)—particularly on Windows using the D3D12
252 /// backend—this can lead to significant system RAM consumption since iGPUs share system memory directly with the CPU.
253 /// </div>
254 ///
255 /// This limit only affects the d3d12 backend. Using a large number will allow the device
256 /// to create many bind groups at the cost of a large up-front allocation at device creation.
257 pub max_non_sampler_bindings: u32,
258
259 /// The maximum total value for a `RenderPass::draw_mesh_tasks(x, y, z)` call on a mesh pipeline with a task shader.
260 /// Higher is "better".
261 pub max_task_workgroup_total_count: u32,
262 /// The maximum value for each dimension of a `RenderPass::draw_mesh_tasks(x, y, z)` call on a mesh pipeline with a task shader.
263 /// Higher is "better".
264 pub max_task_workgroups_per_dimension: u32,
265 /// The maximum product of arguments of a `RenderPass::draw_mesh_tasks(x, y, z)` operation on a mesh shader pipeline
266 /// without task shaders.
267 /// Also for task shader outputs. Higher is "better".
268 pub max_mesh_workgroup_total_count: u32,
269 /// The maximum value for each dimension of a `RenderPass::draw_mesh_tasks(x, y, z)` operation on a mesh shader pipeline
270 /// without task shaders.
271 /// Also for task shader outputs. Higher is "better".
272 pub max_mesh_workgroups_per_dimension: u32,
273 // These are fundamentally different. It is very common for limits on mesh shaders to be much lower.
274 /// Maximum total number of invocations, or threads, per task shader workgroup. Higher is "better".
275 pub max_task_invocations_per_workgroup: u32,
276 /// The maximum value for each dimension of a task shader's workgroup size. Higher is "better".
277 pub max_task_invocations_per_dimension: u32,
278 /// Maximum total number of invocations, or threads, per mesh shader workgroup. Higher is "better".
279 pub max_mesh_invocations_per_workgroup: u32,
280 /// The maximum value for each dimension of a mesh shader's workgroup size. Higher is "better".
281 pub max_mesh_invocations_per_dimension: u32,
282
283 /// The maximum size of the payload passed from task to mesh shader. Higher is "better".
284 pub max_task_payload_size: u32,
285 /// The maximum number of vertices that a mesh shader may output. Higher is "better".
286 pub max_mesh_output_vertices: u32,
287 /// The maximum number of primitives that a mesh shader may output. Higher is "better".
288 pub max_mesh_output_primitives: u32,
289 /// The maximum number of layers that can be output from a mesh shader. Higher is "better".
290 /// See [#8509](https://github.com/gfx-rs/wgpu/issues/8509).
291 pub max_mesh_output_layers: u32,
292 /// The maximum number of views that can be used by a mesh shader in multiview rendering.
293 /// Higher is "better".
294 pub max_mesh_multiview_view_count: u32,
295
296 /// The maximum number of primitive (ex: triangles, aabbs) a BLAS is allowed to have. Requesting
297 /// more than 0 during device creation only makes sense if [`Features::EXPERIMENTAL_RAY_QUERY`]
298 /// is enabled.
299 pub max_blas_primitive_count: u32,
300 /// The maximum number of geometry descriptors a BLAS is allowed to have. Requesting
301 /// more than 0 during device creation only makes sense if [`Features::EXPERIMENTAL_RAY_QUERY`]
302 /// is enabled.
303 pub max_blas_geometry_count: u32,
304 /// The maximum number of instances a TLAS is allowed to have. Requesting more than 0 during
305 /// device creation only makes sense if [`Features::EXPERIMENTAL_RAY_QUERY`]
306 /// is enabled.
307 pub max_tlas_instance_count: u32,
308 /// The maximum number of acceleration structures allowed to be used in a shader stage.
309 /// Requesting more than 0 during device creation only makes sense if [`Features::EXPERIMENTAL_RAY_QUERY`]
310 /// is enabled.
311 pub max_acceleration_structures_per_shader_stage: u32,
312
313 /// The maximum number of views that can be used in multiview rendering
314 pub max_multiview_view_count: u32,
315}
316
317impl Default for Limits {
318 fn default() -> Self {
319 Self::defaults()
320 }
321}
322
323impl Limits {
324 /// These default limits are guaranteed to to work on all modern
325 /// backends and guaranteed to be supported by WebGPU
326 ///
327 /// Those limits are as follows:
328 /// ```rust
329 /// # use wgpu_types::Limits;
330 /// assert_eq!(Limits::defaults(), Limits {
331 /// max_texture_dimension_1d: 8192,
332 /// max_texture_dimension_2d: 8192,
333 /// max_texture_dimension_3d: 2048,
334 /// max_texture_array_layers: 256,
335 /// max_bind_groups: 4,
336 /// max_bindings_per_bind_group: 1000,
337 /// max_dynamic_uniform_buffers_per_pipeline_layout: 8,
338 /// max_dynamic_storage_buffers_per_pipeline_layout: 4,
339 /// max_sampled_textures_per_shader_stage: 16,
340 /// max_samplers_per_shader_stage: 16,
341 /// max_storage_buffers_per_shader_stage: 8,
342 /// max_storage_textures_per_shader_stage: 4,
343 /// max_uniform_buffers_per_shader_stage: 12,
344 /// max_binding_array_elements_per_shader_stage: 0,
345 /// max_binding_array_acceleration_structure_elements_per_shader_stage: 0,
346 /// max_binding_array_sampler_elements_per_shader_stage: 0,
347 /// max_uniform_buffer_binding_size: 64 << 10, // (64 KiB)
348 /// max_storage_buffer_binding_size: 128 << 20, // (128 MiB)
349 /// max_vertex_buffers: 8,
350 /// max_buffer_size: 256 << 20, // (256 MiB)
351 /// max_vertex_attributes: 16,
352 /// max_vertex_buffer_array_stride: 2048,
353 /// max_inter_stage_shader_variables: 16,
354 /// min_uniform_buffer_offset_alignment: 256,
355 /// min_storage_buffer_offset_alignment: 256,
356 /// max_color_attachments: 8,
357 /// max_color_attachment_bytes_per_sample: 32,
358 /// max_compute_workgroup_storage_size: 16384,
359 /// max_compute_invocations_per_workgroup: 256,
360 /// max_compute_workgroup_size_x: 256,
361 /// max_compute_workgroup_size_y: 256,
362 /// max_compute_workgroup_size_z: 64,
363 /// max_compute_workgroups_per_dimension: 65535,
364 /// max_immediate_size: 0,
365 /// max_non_sampler_bindings: 1_000_000,
366 /// max_task_workgroup_total_count: 0,
367 /// max_task_workgroups_per_dimension: 0,
368 /// max_mesh_workgroup_total_count: 0,
369 /// max_mesh_workgroups_per_dimension: 0,
370 /// max_task_invocations_per_workgroup: 0,
371 /// max_task_invocations_per_dimension: 0,
372 /// max_mesh_invocations_per_workgroup: 0,
373 /// max_mesh_invocations_per_dimension: 0,
374 /// max_task_payload_size: 0,
375 /// max_mesh_output_vertices: 0,
376 /// max_mesh_output_primitives: 0,
377 /// max_mesh_output_layers: 0,
378 /// max_mesh_multiview_view_count: 0,
379 /// max_blas_primitive_count: 0,
380 /// max_blas_geometry_count: 0,
381 /// max_tlas_instance_count: 0,
382 /// max_acceleration_structures_per_shader_stage: 0,
383 /// max_multiview_view_count: 0,
384 /// });
385 /// ```
386 ///
387 /// Rust doesn't allow const in trait implementations, so we break this out
388 /// to allow reusing these defaults in const contexts
389 #[must_use]
390 pub const fn defaults() -> Self {
391 Self {
392 max_texture_dimension_1d: 8192,
393 max_texture_dimension_2d: 8192,
394 max_texture_dimension_3d: 2048,
395 max_texture_array_layers: 256,
396 max_bind_groups: 4,
397 max_bindings_per_bind_group: 1000,
398 max_dynamic_uniform_buffers_per_pipeline_layout: 8,
399 max_dynamic_storage_buffers_per_pipeline_layout: 4,
400 max_sampled_textures_per_shader_stage: 16,
401 max_samplers_per_shader_stage: 16,
402 max_storage_buffers_per_shader_stage: 8,
403 max_storage_textures_per_shader_stage: 4,
404 max_uniform_buffers_per_shader_stage: 12,
405 max_binding_array_elements_per_shader_stage: 0,
406 max_binding_array_acceleration_structure_elements_per_shader_stage: 0,
407 max_binding_array_sampler_elements_per_shader_stage: 0,
408 max_uniform_buffer_binding_size: 64 << 10, // (64 KiB)
409 max_storage_buffer_binding_size: 128 << 20, // (128 MiB)
410 max_vertex_buffers: 8,
411 max_buffer_size: 256 << 20, // (256 MiB)
412 max_vertex_attributes: 16,
413 max_vertex_buffer_array_stride: 2048,
414 max_inter_stage_shader_variables: 16,
415 min_uniform_buffer_offset_alignment: 256,
416 min_storage_buffer_offset_alignment: 256,
417 max_color_attachments: 8,
418 max_color_attachment_bytes_per_sample: 32,
419 max_compute_workgroup_storage_size: 16384,
420 max_compute_invocations_per_workgroup: 256,
421 max_compute_workgroup_size_x: 256,
422 max_compute_workgroup_size_y: 256,
423 max_compute_workgroup_size_z: 64,
424 max_compute_workgroups_per_dimension: 65535,
425 max_immediate_size: 0,
426 max_non_sampler_bindings: 1_000_000,
427
428 max_task_workgroup_total_count: 0,
429 max_task_workgroups_per_dimension: 0,
430 max_mesh_workgroup_total_count: 0,
431 max_mesh_workgroups_per_dimension: 0,
432 max_task_invocations_per_workgroup: 0,
433 max_task_invocations_per_dimension: 0,
434 max_mesh_invocations_per_workgroup: 0,
435 max_mesh_invocations_per_dimension: 0,
436 max_task_payload_size: 0,
437 max_mesh_output_vertices: 0,
438 max_mesh_output_primitives: 0,
439 max_mesh_output_layers: 0,
440 max_mesh_multiview_view_count: 0,
441
442 max_blas_primitive_count: 0,
443 max_blas_geometry_count: 0,
444 max_tlas_instance_count: 0,
445 max_acceleration_structures_per_shader_stage: 0,
446
447 max_multiview_view_count: 0,
448 }
449 }
450
451 /// These default limits are guaranteed to be compatible with GLES-3.1, and D3D11
452 ///
453 /// Those limits are as follows (different from default are marked with *):
454 /// ```rust
455 /// # use wgpu_types::Limits;
456 /// assert_eq!(Limits::downlevel_defaults(), Limits {
457 /// max_texture_dimension_1d: 2048, // *
458 /// max_texture_dimension_2d: 2048, // *
459 /// max_texture_dimension_3d: 256, // *
460 /// max_texture_array_layers: 256,
461 /// max_bind_groups: 4,
462 /// max_bindings_per_bind_group: 1000,
463 /// max_dynamic_uniform_buffers_per_pipeline_layout: 8,
464 /// max_dynamic_storage_buffers_per_pipeline_layout: 4,
465 /// max_sampled_textures_per_shader_stage: 16,
466 /// max_samplers_per_shader_stage: 16,
467 /// max_storage_buffers_per_shader_stage: 4, // *
468 /// max_storage_textures_per_shader_stage: 4,
469 /// max_uniform_buffers_per_shader_stage: 12,
470 /// max_binding_array_elements_per_shader_stage: 0,
471 /// max_binding_array_acceleration_structure_elements_per_shader_stage: 0,
472 /// max_binding_array_sampler_elements_per_shader_stage: 0,
473 /// max_uniform_buffer_binding_size: 16 << 10, // * (16 KiB)
474 /// max_storage_buffer_binding_size: 128 << 20, // (128 MiB)
475 /// max_vertex_buffers: 8,
476 /// max_vertex_attributes: 16,
477 /// max_vertex_buffer_array_stride: 2048,
478 /// max_immediate_size: 0,
479 /// min_uniform_buffer_offset_alignment: 256,
480 /// min_storage_buffer_offset_alignment: 256,
481 /// max_inter_stage_shader_variables: 15,
482 /// max_color_attachments: 4,
483 /// max_color_attachment_bytes_per_sample: 32,
484 /// max_compute_workgroup_storage_size: 16352, // *
485 /// max_compute_invocations_per_workgroup: 256,
486 /// max_compute_workgroup_size_x: 256,
487 /// max_compute_workgroup_size_y: 256,
488 /// max_compute_workgroup_size_z: 64,
489 /// max_compute_workgroups_per_dimension: 65535,
490 /// max_buffer_size: 256 << 20, // (256 MiB)
491 /// max_non_sampler_bindings: 1_000_000,
492 ///
493 /// max_task_workgroup_total_count: 0,
494 /// max_task_workgroups_per_dimension: 0,
495 /// max_mesh_workgroup_total_count: 0,
496 /// max_mesh_workgroups_per_dimension: 0,
497 /// max_task_invocations_per_workgroup: 0,
498 /// max_task_invocations_per_dimension: 0,
499 /// max_mesh_invocations_per_workgroup: 0,
500 /// max_mesh_invocations_per_dimension: 0,
501 /// max_task_payload_size: 0,
502 /// max_mesh_output_vertices: 0,
503 /// max_mesh_output_primitives: 0,
504 /// max_mesh_output_layers: 0,
505 /// max_mesh_multiview_view_count: 0,
506 ///
507 /// max_blas_primitive_count: 0,
508 /// max_blas_geometry_count: 0,
509 /// max_tlas_instance_count: 0,
510 /// max_acceleration_structures_per_shader_stage: 0,
511 ///
512 /// max_multiview_view_count: 0,
513 /// });
514 /// ```
515 #[must_use]
516 pub const fn downlevel_defaults() -> Self {
517 Self {
518 max_texture_dimension_1d: 2048,
519 max_texture_dimension_2d: 2048,
520 max_texture_dimension_3d: 256,
521 max_storage_buffers_per_shader_stage: 4,
522 max_uniform_buffer_binding_size: 16 << 10, // (16 KiB)
523 max_inter_stage_shader_variables: 15,
524 max_color_attachments: 4,
525 // see: https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf#page=7
526 max_compute_workgroup_storage_size: 16352,
527 ..Self::defaults()
528 }
529 }
530
531 /// These default limits are guaranteed to be compatible with GLES-3.0, and D3D11, and WebGL2
532 ///
533 /// Those limits are as follows (different from `downlevel_defaults` are marked with +,
534 /// *'s from `downlevel_defaults` shown as well.):
535 /// ```rust
536 /// # use wgpu_types::Limits;
537 /// assert_eq!(Limits::downlevel_webgl2_defaults(), Limits {
538 /// max_texture_dimension_1d: 2048, // *
539 /// max_texture_dimension_2d: 2048, // *
540 /// max_texture_dimension_3d: 256, // *
541 /// max_texture_array_layers: 256,
542 /// max_bind_groups: 4,
543 /// max_bindings_per_bind_group: 1000,
544 /// max_dynamic_uniform_buffers_per_pipeline_layout: 8,
545 /// max_dynamic_storage_buffers_per_pipeline_layout: 0, // +
546 /// max_sampled_textures_per_shader_stage: 16,
547 /// max_samplers_per_shader_stage: 16,
548 /// max_storage_buffers_per_shader_stage: 0, // * +
549 /// max_storage_textures_per_shader_stage: 0, // +
550 /// max_uniform_buffers_per_shader_stage: 11, // +
551 /// max_binding_array_elements_per_shader_stage: 0,
552 /// max_binding_array_acceleration_structure_elements_per_shader_stage: 0,
553 /// max_binding_array_sampler_elements_per_shader_stage: 0,
554 /// max_uniform_buffer_binding_size: 16 << 10, // * (16 KiB)
555 /// max_storage_buffer_binding_size: 0, // * +
556 /// max_vertex_buffers: 8,
557 /// max_vertex_attributes: 16,
558 /// max_vertex_buffer_array_stride: 255, // +
559 /// max_immediate_size: 0,
560 /// min_uniform_buffer_offset_alignment: 256,
561 /// min_storage_buffer_offset_alignment: 256,
562 /// max_inter_stage_shader_variables: 15,
563 /// max_color_attachments: 4,
564 /// max_color_attachment_bytes_per_sample: 32,
565 /// max_compute_workgroup_storage_size: 0, // +
566 /// max_compute_invocations_per_workgroup: 0, // +
567 /// max_compute_workgroup_size_x: 0, // +
568 /// max_compute_workgroup_size_y: 0, // +
569 /// max_compute_workgroup_size_z: 0, // +
570 /// max_compute_workgroups_per_dimension: 0, // +
571 /// max_buffer_size: 256 << 20, // (256 MiB),
572 /// max_non_sampler_bindings: 1_000_000,
573 ///
574 /// max_task_workgroup_total_count: 0,
575 /// max_task_workgroups_per_dimension: 0,
576 /// max_mesh_workgroup_total_count: 0,
577 /// max_mesh_workgroups_per_dimension: 0,
578 /// max_task_invocations_per_workgroup: 0,
579 /// max_task_invocations_per_dimension: 0,
580 /// max_mesh_invocations_per_workgroup: 0,
581 /// max_mesh_invocations_per_dimension: 0,
582 /// max_task_payload_size: 0,
583 /// max_mesh_output_vertices: 0,
584 /// max_mesh_output_primitives: 0,
585 /// max_mesh_output_layers: 0,
586 /// max_mesh_multiview_view_count: 0,
587 ///
588 /// max_blas_primitive_count: 0,
589 /// max_blas_geometry_count: 0,
590 /// max_tlas_instance_count: 0,
591 /// max_acceleration_structures_per_shader_stage: 0,
592 ///
593 /// max_multiview_view_count: 0,
594 /// });
595 /// ```
596 #[must_use]
597 pub const fn downlevel_webgl2_defaults() -> Self {
598 Self {
599 max_uniform_buffers_per_shader_stage: 11,
600 max_storage_buffers_per_shader_stage: 0,
601 max_storage_textures_per_shader_stage: 0,
602 max_dynamic_storage_buffers_per_pipeline_layout: 0,
603 max_storage_buffer_binding_size: 0,
604 max_vertex_buffer_array_stride: 255,
605 max_compute_workgroup_storage_size: 0,
606 max_compute_invocations_per_workgroup: 0,
607 max_compute_workgroup_size_x: 0,
608 max_compute_workgroup_size_y: 0,
609 max_compute_workgroup_size_z: 0,
610 max_compute_workgroups_per_dimension: 0,
611
612 // Value supported by Intel Celeron B830 on Windows (OpenGL 3.1)
613 max_inter_stage_shader_variables: 15,
614
615 // Most of the values should be the same as the downlevel defaults
616 ..Self::downlevel_defaults()
617 }
618 }
619
620 /// Sets each limit to `i32::MAX` (or 1, in the case of lower-is-better limits).
621 ///
622 /// These values do not reflect the capabilities of any actual device. They are
623 /// used by the noop backend, and by the test that makes sure `with_limits!` is
624 /// exhaustive.
625 #[must_use]
626 pub const fn unlimited() -> Self {
627 /// Guaranteed to be no bigger than isize::MAX which is the maximum size of an allocation,
628 /// except on 16-bit platforms which we certainly don’t fit in.
629 const ALLOC_MAX_U32: u32 = i32::MAX as u32;
630 /// Guaranteed to be no bigger than isize::MAX which is the maximum size of an allocation,
631 /// except on 16-bit platforms which we certainly don’t fit in.
632 const ALLOC_MAX_U64: u64 = i32::MAX as u64;
633
634 Self {
635 max_texture_dimension_1d: ALLOC_MAX_U32,
636 max_texture_dimension_2d: ALLOC_MAX_U32,
637 max_texture_dimension_3d: ALLOC_MAX_U32,
638 max_texture_array_layers: ALLOC_MAX_U32,
639 max_bind_groups: ALLOC_MAX_U32,
640 max_bindings_per_bind_group: ALLOC_MAX_U32,
641 max_dynamic_uniform_buffers_per_pipeline_layout: ALLOC_MAX_U32,
642 max_dynamic_storage_buffers_per_pipeline_layout: ALLOC_MAX_U32,
643 max_sampled_textures_per_shader_stage: ALLOC_MAX_U32,
644 max_samplers_per_shader_stage: ALLOC_MAX_U32,
645 max_storage_buffers_per_shader_stage: ALLOC_MAX_U32,
646 max_storage_textures_per_shader_stage: ALLOC_MAX_U32,
647 max_uniform_buffers_per_shader_stage: ALLOC_MAX_U32,
648 max_binding_array_elements_per_shader_stage: ALLOC_MAX_U32,
649 max_binding_array_sampler_elements_per_shader_stage: ALLOC_MAX_U32,
650 max_binding_array_acceleration_structure_elements_per_shader_stage: ALLOC_MAX_U32,
651 max_uniform_buffer_binding_size: ALLOC_MAX_U64,
652 max_storage_buffer_binding_size: ALLOC_MAX_U64,
653 max_vertex_buffers: ALLOC_MAX_U32,
654 max_buffer_size: ALLOC_MAX_U64,
655 max_vertex_attributes: ALLOC_MAX_U32,
656 max_vertex_buffer_array_stride: ALLOC_MAX_U32,
657 max_inter_stage_shader_variables: ALLOC_MAX_U32,
658 min_uniform_buffer_offset_alignment: 1,
659 min_storage_buffer_offset_alignment: 1,
660 max_color_attachments: ALLOC_MAX_U32,
661 max_color_attachment_bytes_per_sample: ALLOC_MAX_U32,
662 max_compute_workgroup_storage_size: ALLOC_MAX_U32,
663 max_compute_invocations_per_workgroup: ALLOC_MAX_U32,
664 max_compute_workgroup_size_x: ALLOC_MAX_U32,
665 max_compute_workgroup_size_y: ALLOC_MAX_U32,
666 max_compute_workgroup_size_z: ALLOC_MAX_U32,
667 max_compute_workgroups_per_dimension: ALLOC_MAX_U32,
668 max_immediate_size: ALLOC_MAX_U32,
669 max_non_sampler_bindings: ALLOC_MAX_U32,
670
671 max_task_workgroup_total_count: ALLOC_MAX_U32,
672 max_task_workgroups_per_dimension: ALLOC_MAX_U32,
673 max_mesh_workgroup_total_count: ALLOC_MAX_U32,
674 max_mesh_workgroups_per_dimension: ALLOC_MAX_U32,
675 max_task_invocations_per_workgroup: ALLOC_MAX_U32,
676 max_task_invocations_per_dimension: ALLOC_MAX_U32,
677 max_mesh_invocations_per_workgroup: ALLOC_MAX_U32,
678 max_mesh_invocations_per_dimension: ALLOC_MAX_U32,
679 max_task_payload_size: ALLOC_MAX_U32,
680 max_mesh_output_vertices: ALLOC_MAX_U32,
681 max_mesh_output_primitives: ALLOC_MAX_U32,
682 max_mesh_output_layers: ALLOC_MAX_U32,
683 max_mesh_multiview_view_count: ALLOC_MAX_U32,
684
685 max_blas_primitive_count: ALLOC_MAX_U32,
686 max_blas_geometry_count: ALLOC_MAX_U32,
687 max_tlas_instance_count: ALLOC_MAX_U32,
688 max_acceleration_structures_per_shader_stage: ALLOC_MAX_U32,
689
690 max_multiview_view_count: ALLOC_MAX_U32,
691 }
692 }
693
694 /// Modify the current limits to use the resolution limits of the other.
695 ///
696 /// This is useful because the swapchain might need to be larger than any other image in the application.
697 ///
698 /// If your application only needs 512x512, you might be running on a 4k display and need extremely high resolution limits.
699 #[must_use]
700 pub const fn using_resolution(self, other: Self) -> Self {
701 Self {
702 max_texture_dimension_1d: other.max_texture_dimension_1d,
703 max_texture_dimension_2d: other.max_texture_dimension_2d,
704 max_texture_dimension_3d: other.max_texture_dimension_3d,
705 ..self
706 }
707 }
708
709 /// Modify the current limits to use the buffer alignment limits of the adapter.
710 ///
711 /// This is useful for when you'd like to dynamically use the "best" supported buffer alignments.
712 #[must_use]
713 pub const fn using_alignment(self, other: Self) -> Self {
714 Self {
715 min_uniform_buffer_offset_alignment: other.min_uniform_buffer_offset_alignment,
716 min_storage_buffer_offset_alignment: other.min_storage_buffer_offset_alignment,
717 ..self
718 }
719 }
720
721 /// The minimum guaranteed limits for acceleration structures if you enable [`Features::EXPERIMENTAL_RAY_QUERY`]
722 #[must_use]
723 pub const fn using_minimum_supported_acceleration_structure_values(self) -> Self {
724 Self {
725 max_blas_geometry_count: (1 << 24) - 1, // 2^24 - 1: Vulkan's minimum
726 max_tlas_instance_count: (1 << 24) - 1, // 2^24 - 1: Vulkan's minimum
727 max_blas_primitive_count: 1 << 28, // 2^28: Metal's minimum
728 // On metal acceleration structures are limited because they share buffer slots
729 max_acceleration_structures_per_shader_stage: 1,
730 ..self
731 }
732 }
733
734 /// Modify the current limits to use the acceleration structure limits of `other` (`other` could
735 /// be the limits of the adapter).
736 #[must_use]
737 pub const fn using_acceleration_structure_values(self, other: Self) -> Self {
738 Self {
739 max_blas_geometry_count: other.max_blas_geometry_count,
740 max_tlas_instance_count: other.max_tlas_instance_count,
741 max_blas_primitive_count: other.max_blas_primitive_count,
742 max_acceleration_structures_per_shader_stage: other
743 .max_acceleration_structures_per_shader_stage,
744 ..self
745 }
746 }
747
748 /// The recommended minimum limits for mesh shaders if you enable [`Features::EXPERIMENTAL_MESH_SHADER`]
749 ///
750 /// These are chosen somewhat arbitrarily. They are small enough that they should cover all physical devices,
751 /// but not necessarily all use cases.
752 #[must_use]
753 pub const fn using_recommended_minimum_mesh_shader_values(self) -> Self {
754 Self {
755 // These are DirectX limitations (both nvidia and AMD match these exactly on vulkan)
756 // Note that Mac2 (newest intel macs) support up to 1024, but this is low enough,
757 // to make use of mesh shaders nonviable in most cases.
758 // We therefore, don't expose mesh shading on these devices.
759 // In contrast, here is no limit for any A-series or M-series chip.
760 max_task_workgroup_total_count: 2u32.pow(22),
761 max_task_workgroups_per_dimension: 65535,
762 // These are metal limitations
763 // M3 ups both of these to 1M
764 max_mesh_workgroup_total_count: 1024,
765 max_mesh_workgroups_per_dimension: 1024,
766 // Nvidia limit on vulkan
767 max_task_invocations_per_workgroup: 128,
768 max_task_invocations_per_dimension: 64,
769
770 // DX12 limitation, revisit for vulkan
771 max_mesh_invocations_per_workgroup: 128,
772 max_mesh_invocations_per_dimension: 128,
773
774 // Metal specifies this as its max
775 max_task_payload_size: 16384 - 32,
776 // DX12 limitation, revisit for vulkan
777 max_mesh_output_vertices: 256,
778 max_mesh_output_primitives: 256,
779 // llvmpipe once again requires this to be 8. An RTX 3060 supports well over 1024.
780 // Also DX12 vaguely suggests going over this is illegal in some cases.
781 max_mesh_output_layers: 8,
782 // llvmpipe reports 0 multiview count, which just means no multiview is allowed
783 max_mesh_multiview_view_count: 0,
784 ..self
785 }
786 }
787
788 /// Compares every limits within self is within the limits given in `allowed`.
789 ///
790 /// If you need detailed information on failures, look at [`Limits::check_limits_with_fail_fn`].
791 #[must_use]
792 pub fn check_limits(&self, allowed: &Self) -> bool {
793 let mut within = true;
794 self.check_limits_with_fail_fn(allowed, true, |_, _, _| within = false);
795 within
796 }
797
798 /// Compares every limits within self is within the limits given in `allowed`.
799 /// For an easy to use binary choice, use [`Limits::check_limits`].
800 ///
801 /// If a value is not within the allowed limit, this function calls the `fail_fn`
802 /// with the:
803 /// - limit name
804 /// - self's limit
805 /// - allowed's limit.
806 ///
807 /// If fatal is true, a single failure bails out the comparison after a single failure.
808 pub fn check_limits_with_fail_fn(
809 &self,
810 allowed: &Self,
811 fatal: bool,
812 mut fail_fn: impl FnMut(&'static str, u64, u64),
813 ) {
814 macro_rules! check_with_fail_fn {
815 ($name:ident, $ordering:expr) => {
816 let invalid_ord = $ordering.reverse();
817 if self.$name.cmp(&allowed.$name) == invalid_ord {
818 fail_fn(stringify!($name), self.$name as u64, allowed.$name as u64);
819 if fatal {
820 return;
821 }
822 }
823 };
824 }
825
826 with_limits!(check_with_fail_fn);
827 }
828
829 /// For each limit in `other` that is better than the value in `self`,
830 /// replace the value in `self` with the value from `other`.
831 ///
832 /// A request for a limit value less than the WebGPU-specified default must
833 /// be ignored. This function is used to clamp such requests to the default
834 /// value.
835 ///
836 /// This function is not for clamping requests for values beyond the
837 /// supported limits. For that purpose the desired function would be
838 /// `or_worse_values_from`.
839 #[must_use]
840 pub fn or_better_values_from(mut self, other: &Self) -> Self {
841 macro_rules! or_better_value_from {
842 ($name:ident, $ordering:expr) => {
843 match $ordering {
844 // Limits that are maximum values (most of them)
845 Ordering::Less => self.$name = self.$name.max(other.$name),
846 // Limits that are minimum values
847 Ordering::Greater => self.$name = self.$name.min(other.$name),
848 Ordering::Equal => unreachable!(),
849 }
850 };
851 }
852
853 with_limits!(or_better_value_from);
854
855 self
856 }
857
858 /// For each limit in `other` that is worse than the value in `self`,
859 /// replace the value in `self` with the value from `other`.
860 ///
861 /// This function is for clamping requests for values beyond the
862 /// supported limits.
863 #[must_use]
864 pub fn or_worse_values_from(mut self, other: &Self) -> Self {
865 macro_rules! or_worse_value_from {
866 ($name:ident, $ordering:expr) => {
867 match $ordering {
868 // Limits that are maximum values (most of them)
869 Ordering::Less => self.$name = self.$name.min(other.$name),
870 // Limits that are minimum values
871 Ordering::Greater => self.$name = self.$name.max(other.$name),
872 Ordering::Equal => unreachable!(),
873 }
874 };
875 }
876
877 with_limits!(or_worse_value_from);
878
879 self
880 }
881}
882
883/// Represents the sets of additional limits on an adapter,
884/// which take place when running on downlevel backends.
885#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
886#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
887pub struct DownlevelLimits {}
888
889#[allow(clippy::derivable_impls)]
890impl Default for DownlevelLimits {
891 fn default() -> Self {
892 DownlevelLimits {}
893 }
894}
895
896/// Lists various ways the underlying platform does not conform to the WebGPU standard.
897#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
898#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
899pub struct DownlevelCapabilities {
900 /// Combined boolean flags.
901 pub flags: DownlevelFlags,
902 /// Additional limits
903 pub limits: DownlevelLimits,
904 /// Which collections of features shaders support. Defined in terms of D3D's shader models.
905 pub shader_model: ShaderModel,
906}
907
908impl Default for DownlevelCapabilities {
909 fn default() -> Self {
910 Self {
911 flags: DownlevelFlags::all(),
912 limits: DownlevelLimits::default(),
913 shader_model: ShaderModel::Sm5,
914 }
915 }
916}
917
918impl DownlevelCapabilities {
919 /// Returns true if the underlying platform offers complete support of the baseline WebGPU standard.
920 ///
921 /// If this returns false, some parts of the API will result in validation errors where they would not normally.
922 /// These parts can be determined by the values in this structure.
923 #[must_use]
924 pub fn is_webgpu_compliant(&self) -> bool {
925 self.flags.contains(DownlevelFlags::compliant())
926 && self.limits == DownlevelLimits::default()
927 && self.shader_model >= ShaderModel::Sm5
928 }
929}
930
931bitflags::bitflags! {
932 /// Binary flags listing features that may or may not be present on downlevel adapters.
933 ///
934 /// A downlevel adapter is a GPU adapter that wgpu supports, but with potentially limited
935 /// features, due to the lack of hardware feature support.
936 ///
937 /// Flags that are **not** present for a downlevel adapter or device usually indicates
938 /// non-compliance with the WebGPU specification, but not always.
939 ///
940 /// You can check whether a set of flags is compliant through the
941 /// [`DownlevelCapabilities::is_webgpu_compliant()`] function.
942 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
943 #[cfg_attr(feature = "serde", serde(transparent))]
944 #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
945 pub struct DownlevelFlags: u32 {
946 /// The device supports compiling and using compute shaders.
947 ///
948 /// WebGL2, and GLES3.0 devices do not support compute.
949 const COMPUTE_SHADERS = 1 << 0;
950 /// Supports binding storage buffers and textures to fragment shaders.
951 const FRAGMENT_WRITABLE_STORAGE = 1 << 1;
952 /// Supports indirect drawing and dispatching.
953 ///
954 /// [`Self::COMPUTE_SHADERS`] must be present for this flag.
955 ///
956 /// WebGL2, GLES 3.0, and Metal on Apple1/Apple2 GPUs do not support indirect.
957 const INDIRECT_EXECUTION = 1 << 2;
958 /// Supports non-zero `base_vertex` parameter to direct indexed draw calls.
959 ///
960 /// Indirect calls, if supported, always support non-zero `base_vertex`.
961 ///
962 /// Supported by:
963 /// - Vulkan
964 /// - DX12
965 /// - Metal on Apple3+ or Mac1+
966 /// - OpenGL 3.2+
967 /// - OpenGL ES 3.2
968 const BASE_VERTEX = 1 << 3;
969 /// Supports reading from a depth/stencil texture while using it as a read-only
970 /// depth/stencil attachment.
971 ///
972 /// The WebGL2 and GLES backends do not support RODS.
973 const READ_ONLY_DEPTH_STENCIL = 1 << 4;
974 /// Supports textures with mipmaps which have a non power of two size.
975 const NON_POWER_OF_TWO_MIPMAPPED_TEXTURES = 1 << 5;
976 /// Supports textures that are cube arrays.
977 const CUBE_ARRAY_TEXTURES = 1 << 6;
978 /// Supports comparison samplers.
979 const COMPARISON_SAMPLERS = 1 << 7;
980 /// Supports different blend operations per color attachment.
981 const INDEPENDENT_BLEND = 1 << 8;
982 /// Supports storage buffers in vertex shaders.
983 const VERTEX_STORAGE = 1 << 9;
984
985 /// Supports samplers with anisotropic filtering. Note this isn't actually required by
986 /// WebGPU, the implementation is allowed to completely ignore aniso clamp. This flag is
987 /// here for native backends so they can communicate to the user of aniso is enabled.
988 ///
989 /// All backends and all devices support anisotropic filtering.
990 const ANISOTROPIC_FILTERING = 1 << 10;
991
992 /// Supports storage buffers in fragment shaders.
993 const FRAGMENT_STORAGE = 1 << 11;
994
995 /// Supports sample-rate shading.
996 const MULTISAMPLED_SHADING = 1 << 12;
997
998 /// Supports copies between depth textures and buffers.
999 ///
1000 /// GLES/WebGL don't support this.
1001 const DEPTH_TEXTURE_AND_BUFFER_COPIES = 1 << 13;
1002
1003 /// Supports all the texture usages described in WebGPU. If this isn't supported, you
1004 /// should call `get_texture_format_features` to get how you can use textures of a given format
1005 const WEBGPU_TEXTURE_FORMAT_SUPPORT = 1 << 14;
1006
1007 /// Supports buffer bindings with sizes that aren't a multiple of 16.
1008 ///
1009 /// WebGL doesn't support this.
1010 const BUFFER_BINDINGS_NOT_16_BYTE_ALIGNED = 1 << 15;
1011
1012 /// Supports buffers to combine [`BufferUsages::INDEX`] with usages other than [`BufferUsages::COPY_DST`] and [`BufferUsages::COPY_SRC`].
1013 /// Furthermore, in absence of this feature it is not allowed to copy index buffers from/to buffers with a set of usage flags containing
1014 /// [`BufferUsages::VERTEX`]/[`BufferUsages::UNIFORM`]/[`BufferUsages::STORAGE`] or [`BufferUsages::INDIRECT`].
1015 ///
1016 /// WebGL doesn't support this.
1017 const UNRESTRICTED_INDEX_BUFFER = 1 << 16;
1018
1019 /// Supports full 32-bit range indices (2^32-1 as opposed to 2^24-1 without this flag)
1020 ///
1021 /// Corresponds to Vulkan's `VkPhysicalDeviceFeatures.fullDrawIndexUint32`
1022 const FULL_DRAW_INDEX_UINT32 = 1 << 17;
1023
1024 /// Supports depth bias clamping
1025 ///
1026 /// Corresponds to Vulkan's `VkPhysicalDeviceFeatures.depthBiasClamp`
1027 const DEPTH_BIAS_CLAMP = 1 << 18;
1028
1029 /// Supports specifying which view format values are allowed when create_view() is called on a texture.
1030 ///
1031 /// The WebGL and GLES backends doesn't support this.
1032 const VIEW_FORMATS = 1 << 19;
1033
1034 /// With this feature not present, there are the following restrictions on `Queue::copy_external_image_to_texture`:
1035 /// - The source must not be [`web_sys::OffscreenCanvas`]
1036 /// - [`CopyExternalImageSourceInfo::origin`] must be zero.
1037 /// - [`CopyExternalImageDestInfo::color_space`] must be srgb.
1038 /// - If the source is an [`web_sys::ImageBitmap`]:
1039 /// - [`CopyExternalImageSourceInfo::flip_y`] must be false.
1040 /// - [`CopyExternalImageDestInfo::premultiplied_alpha`] must be false.
1041 ///
1042 /// WebGL doesn't support this. WebGPU does.
1043 const UNRESTRICTED_EXTERNAL_TEXTURE_COPIES = 1 << 20;
1044
1045 /// Supports specifying which view formats are allowed when calling create_view on the texture returned by
1046 /// `Surface::get_current_texture`.
1047 ///
1048 /// The GLES/WebGL and Vulkan on Android doesn't support this.
1049 const SURFACE_VIEW_FORMATS = 1 << 21;
1050
1051 /// If this is true, calls to `CommandEncoder::resolve_query_set` will be performed on the queue timeline.
1052 ///
1053 /// If this is false, calls to `CommandEncoder::resolve_query_set` will be performed on the device (i.e. cpu) timeline
1054 /// and will block that timeline until the query has data. You may work around this limitation by waiting until the submit
1055 /// whose queries you are resolving is fully finished (through use of `queue.on_submitted_work_done`) and only
1056 /// then submitting the resolve_query_set command. The queries will be guaranteed finished, so will not block.
1057 ///
1058 /// Supported by:
1059 /// - Vulkan,
1060 /// - DX12
1061 /// - Metal
1062 /// - OpenGL 4.4+
1063 ///
1064 /// Not Supported by:
1065 /// - GL ES / WebGL
1066 const NONBLOCKING_QUERY_RESOLVE = 1 << 22;
1067
1068 /// Allows shaders to use `quantizeToF16`, `pack2x16float`, and `unpack2x16float`, which
1069 /// operate on `f16`-precision values stored in `f32`s.
1070 ///
1071 /// Not supported by Vulkan on Mesa when [`Features::SHADER_F16`] is absent.
1072 const SHADER_F16_IN_F32 = 1 << 23;
1073
1074 /// Supports features introduced in MSL 2.1.
1075 const MSL2_1 = 1 << 24;
1076 }
1077}
1078
1079impl DownlevelFlags {
1080 /// All flags that indicate if the backend is WebGPU compliant
1081 #[must_use]
1082 pub const fn compliant() -> Self {
1083 // We use manual bit twiddling to make this a const fn as `Sub` and `.remove` aren't const
1084
1085 // WebGPU doesn't actually require aniso
1086 Self::from_bits_truncate(Self::all().bits() & !Self::ANISOTROPIC_FILTERING.bits())
1087 }
1088}
1089
1090/// Collections of shader features a device supports if they support less than WebGPU normally allows.
1091// TODO: Fill out the differences between shader models more completely
1092#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
1093#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1094pub enum ShaderModel {
1095 /// Extremely limited shaders, including a total instruction limit.
1096 Sm2,
1097 /// Missing minor features and storage images.
1098 Sm4,
1099 /// WebGPU supports shader module 5.
1100 Sm5,
1101}
1102
1103#[cfg(test)]
1104mod tests {
1105 use super::*;
1106 use alloc::{format, string::String, vec::Vec};
1107
1108 fn side_by_side(left: &str, right: &str) -> String {
1109 let left_lines: Vec<&str> = left.lines().map(str::trim).collect();
1110 let right_lines: Vec<&str> = right.lines().map(str::trim).collect();
1111 let max_lines = left_lines.len().max(right_lines.len());
1112 let diffs: Vec<(&str, &str)> = (0..max_lines)
1113 .map(|i| {
1114 let l = *left_lines.get(i).unwrap_or(&"");
1115 let r = *right_lines.get(i).unwrap_or(&"");
1116 (l, r)
1117 })
1118 .filter(|(l, r)| l != r)
1119 .collect();
1120 let left_width = diffs.iter().map(|(l, _)| l.len()).max().unwrap_or(0);
1121 let mut out = String::new();
1122 for (l, r) in &diffs {
1123 out += &format!("{:<width$} | {}\n", l, r, width = left_width);
1124 }
1125 out
1126 }
1127
1128 #[test]
1129 fn with_limits_exhaustive() {
1130 // Check that all limits are included in `with_limits!`, by using it to
1131 // replicate `Limits::unlimited()`.
1132 let mut limits = Limits::default();
1133
1134 macro_rules! set_to_max {
1135 ($name:ident, $ordering:expr) => {
1136 if $ordering == Ordering::Less {
1137 limits.$name = i32::MAX as _;
1138 } else {
1139 limits.$name = 1;
1140 }
1141 };
1142 }
1143
1144 with_limits!(set_to_max);
1145
1146 assert_eq!(
1147 limits,
1148 Limits::unlimited(),
1149 "with_limits! did not replicate Limits::unlimited():\n{}",
1150 side_by_side(
1151 &format!("with_limits!\n------------\n{:#?}", limits),
1152 &format!(
1153 "Limits::unlimited()\n-------------------\n{:#?}",
1154 Limits::unlimited()
1155 ),
1156 )
1157 );
1158 }
1159}