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