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