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