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