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