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