1use alloc::{
72 format,
73 string::{String, ToString},
74 vec::Vec,
75};
76use core::fmt::{Error as FmtError, Write};
77
78use crate::{arena::Handle, back::TaskDispatchLimits, ir, proc::index, valid::ModuleInfo};
79
80mod keywords;
81mod mesh_shader;
82mod ray;
83pub mod sampler;
84mod writer;
85
86pub use writer::Writer;
87
88pub type Slot = u8;
89pub type InlineSamplerIndex = u8;
90
91#[derive(Clone, Debug, PartialEq, Eq, Hash)]
92#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
93#[cfg_attr(feature = "deserialize", derive(serde::Deserialize))]
94pub enum BindSamplerTarget {
95 Resource(Slot),
96 Inline(InlineSamplerIndex),
97}
98
99#[derive(Clone, Debug, PartialEq, Eq, Hash)]
105#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
106#[cfg_attr(feature = "deserialize", derive(serde::Deserialize))]
107pub struct BindExternalTextureTarget {
108 pub planes: [Slot; 3],
109 pub params: Slot,
110}
111
112#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
113#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
114#[cfg_attr(feature = "deserialize", derive(serde::Deserialize))]
115#[cfg_attr(any(feature = "serialize", feature = "deserialize"), serde(default))]
116pub struct BindTarget {
117 pub buffer: Option<Slot>,
118 pub texture: Option<Slot>,
119 pub sampler: Option<BindSamplerTarget>,
120 pub external_texture: Option<BindExternalTextureTarget>,
121 pub mutable: bool,
122}
123
124#[cfg(feature = "deserialize")]
125#[derive(serde::Deserialize)]
126struct BindingMapSerialization {
127 resource_binding: crate::ResourceBinding,
128 bind_target: BindTarget,
129}
130
131#[cfg(feature = "deserialize")]
132fn deserialize_binding_map<'de, D>(deserializer: D) -> Result<BindingMap, D::Error>
133where
134 D: serde::Deserializer<'de>,
135{
136 use serde::Deserialize;
137
138 let vec = Vec::<BindingMapSerialization>::deserialize(deserializer)?;
139 let mut map = BindingMap::default();
140 for item in vec {
141 map.insert(item.resource_binding, item.bind_target);
142 }
143 Ok(map)
144}
145
146pub type BindingMap = alloc::collections::BTreeMap<crate::ResourceBinding, BindTarget>;
148
149#[derive(Clone, Debug, Default, Hash, Eq, PartialEq)]
150#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
151#[cfg_attr(feature = "deserialize", derive(serde::Deserialize))]
152#[cfg_attr(any(feature = "serialize", feature = "deserialize"), serde(default))]
153pub struct EntryPointResources {
154 #[cfg_attr(
155 feature = "deserialize",
156 serde(deserialize_with = "deserialize_binding_map")
157 )]
158 pub resources: BindingMap,
159
160 pub immediates_buffer: Option<Slot>,
161
162 pub sizes_buffer: Option<Slot>,
166}
167
168pub type EntryPointResourceMap = alloc::collections::BTreeMap<String, EntryPointResources>;
169
170enum ResolvedBinding {
171 BuiltIn(crate::BuiltIn),
172 Attribute(u32),
173 Color {
174 location: u32,
175 blend_src: Option<u32>,
176 },
177 User {
178 prefix: &'static str,
179 index: u32,
180 interpolation: Option<ResolvedInterpolation>,
181 },
182 Resource(BindTarget),
183 Payload,
184}
185
186#[derive(Copy, Clone)]
187enum ResolvedInterpolation {
188 CenterPerspective,
189 CenterNoPerspective,
190 CentroidPerspective,
191 CentroidNoPerspective,
192 SamplePerspective,
193 SampleNoPerspective,
194 Flat,
195 PerVertex,
196}
197
198#[derive(Debug, thiserror::Error)]
201pub enum Error {
202 #[error(transparent)]
203 Format(#[from] FmtError),
204 #[error("bind target {0:?} is empty")]
205 UnimplementedBindTarget(BindTarget),
206 #[error("composing of {0:?} is not implemented yet")]
207 UnsupportedCompose(Handle<crate::Type>),
208 #[error("operation {0:?} is not implemented yet")]
209 UnsupportedBinaryOp(crate::BinaryOperator),
210 #[error("standard function '{0}' is not implemented yet")]
211 UnsupportedCall(String),
212 #[error("feature '{0}' is not implemented yet")]
213 FeatureNotImplemented(String),
214 #[error("internal naga error: module should not have validated: {0}")]
215 GenericValidation(String),
216 #[error("BuiltIn {0:?} is not supported")]
217 UnsupportedBuiltIn(crate::BuiltIn),
218 #[error("capability {0:?} is not supported")]
219 CapabilityNotSupported(crate::valid::Capabilities),
220 #[error("attribute '{0}' is not supported for target MSL version")]
221 UnsupportedAttribute(String),
222 #[error("function '{0}' is not supported for target MSL version")]
223 UnsupportedFunction(String),
224 #[error("can not use writable storage buffers in fragment stage prior to MSL 1.2")]
225 UnsupportedWritableStorageBuffer,
226 #[error("can not use writable storage textures in {0:?} stage prior to MSL 1.2")]
227 UnsupportedWritableStorageTexture(ir::ShaderStage),
228 #[error("can not use read-write storage textures prior to MSL 1.2")]
229 UnsupportedRWStorageTexture,
230 #[error("array of '{0}' is not supported for target MSL version")]
231 UnsupportedArrayOf(String),
232 #[error("array of type '{0:?}' is not supported")]
233 UnsupportedArrayOfType(Handle<crate::Type>),
234 #[error("ray tracing is not supported prior to MSL 2.4")]
235 UnsupportedRayTracing,
236 #[error("cooperative matrix is not supported prior to MSL 2.3")]
237 UnsupportedCooperativeMatrix,
238 #[error("debugPrintf is not supported prior to MSL 3.2")]
239 UnsupportedDebugPrintf,
240 #[error("overrides should not be present at this stage")]
241 Override,
242 #[error("bitcasting to {0:?} is not supported")]
243 UnsupportedBitCast(crate::TypeInner),
244 #[error(transparent)]
245 ResolveArraySizeError(#[from] crate::proc::ResolveArraySizeError),
246 #[error("entry point with stage {0:?} and name '{1}' not found")]
247 EntryPointNotFound(ir::ShaderStage, String),
248 #[error("Cannot use mesh shader syntax prior to MSL 3.0")]
249 UnsupportedMeshShader,
250 #[error("Per vertex fragment inputs are not supported prior to MSL 4.0")]
251 PerVertexNotSupported,
252}
253
254#[derive(Clone, Debug, PartialEq, thiserror::Error)]
255#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
256#[cfg_attr(feature = "deserialize", derive(serde::Deserialize))]
257pub enum EntryPointError {
258 #[error("global '{0}' doesn't have a binding")]
259 MissingBinding(String),
260 #[error("mapping of {0:?} is missing")]
261 MissingBindTarget(crate::ResourceBinding),
262 #[error("mapping for immediates is missing")]
263 MissingImmediateData,
264 #[error("mapping for sizes buffer is missing")]
265 MissingSizesBuffer,
266}
267
268#[derive(Clone, Copy, Debug)]
277enum LocationMode {
278 VertexInput,
280
281 VertexOutput,
283
284 FragmentInput,
286
287 FragmentOutput,
289
290 MeshOutput,
292
293 Uniform,
295}
296
297#[derive(Clone, Debug, Hash, PartialEq, Eq)]
298#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
299#[cfg_attr(feature = "deserialize", derive(serde::Deserialize))]
300#[cfg_attr(feature = "deserialize", serde(default))]
301pub struct Options {
302 pub lang_version: (u8, u8),
304 pub per_entry_point_map: EntryPointResourceMap,
306 pub inline_samplers: Vec<sampler::InlineSampler>,
308 pub spirv_cross_compatibility: bool,
310 pub fake_missing_bindings: bool,
312 pub bounds_check_policies: index::BoundsCheckPolicies,
314 pub zero_initialize_workgroup_memory: bool,
316 pub force_loop_bounding: bool,
319 pub task_dispatch_limits: Option<TaskDispatchLimits>,
322 pub mesh_shader_primitive_indices_clamp: bool,
324 pub emit_int_div_checks: bool,
329 pub ray_query_initialization_tracking: bool,
331}
332
333impl Default for Options {
334 fn default() -> Self {
335 Options {
336 lang_version: (1, 0),
337 per_entry_point_map: EntryPointResourceMap::default(),
338 inline_samplers: Vec::new(),
339 spirv_cross_compatibility: false,
340 fake_missing_bindings: true,
341 bounds_check_policies: index::BoundsCheckPolicies::default(),
342 zero_initialize_workgroup_memory: true,
343 force_loop_bounding: true,
344 task_dispatch_limits: None,
345 mesh_shader_primitive_indices_clamp: true,
346 ray_query_initialization_tracking: true,
347 emit_int_div_checks: true,
348 }
349 }
350}
351
352#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
354#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
355#[cfg_attr(feature = "deserialize", derive(serde::Deserialize))]
356pub enum VertexBufferStepMode {
357 Constant,
358 #[default]
359 ByVertex,
360 ByInstance,
361}
362
363#[derive(Debug, Clone, PartialEq, Eq, Hash)]
366#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
367#[cfg_attr(feature = "deserialize", derive(serde::Deserialize))]
368pub struct AttributeMapping {
369 pub shader_location: u32,
371 pub offset: u32,
373 pub format: nt::VertexFormat,
379}
380
381#[derive(Debug, Default, Clone, PartialEq, Eq, Hash)]
384#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
385#[cfg_attr(feature = "deserialize", derive(serde::Deserialize))]
386pub struct VertexBufferMapping {
387 pub id: u32,
389 pub stride: u32,
391 pub step_mode: VertexBufferStepMode,
393 pub attributes: Vec<AttributeMapping>,
395}
396
397#[derive(Debug, Default, Clone)]
399#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
400#[cfg_attr(feature = "deserialize", derive(serde::Deserialize))]
401#[cfg_attr(feature = "deserialize", serde(default))]
402pub struct PipelineOptions {
403 pub entry_point: Option<(ir::ShaderStage, String)>,
411
412 pub allow_and_force_point_size: bool,
419
420 pub vertex_pulling_transform: bool,
428
429 pub vertex_buffer_mappings: Vec<VertexBufferMapping>,
432
433 #[cfg_attr(
437 feature = "deserialize",
438 serde(deserialize_with = "deserialize_binding_array_length_map")
439 )]
440 pub binding_array_length_map: crate::FastHashMap<crate::ResourceBinding, u32>,
441}
442
443#[cfg(feature = "deserialize")]
444#[derive(serde::Deserialize)]
445struct BindingArrayLengthMapSerialization {
446 resource_binding: crate::ResourceBinding,
447 count: u32,
448}
449
450#[cfg(feature = "deserialize")]
451fn deserialize_binding_array_length_map<'de, D>(
452 deserializer: D,
453) -> Result<crate::FastHashMap<crate::ResourceBinding, u32>, D::Error>
454where
455 D: serde::Deserializer<'de>,
456{
457 use serde::Deserialize;
458
459 let vec = Vec::<BindingArrayLengthMapSerialization>::deserialize(deserializer)?;
460 let mut map = crate::FastHashMap::default();
461 for item in vec {
462 map.insert(item.resource_binding, item.count);
463 }
464 Ok(map)
465}
466
467impl Options {
468 fn resolve_local_binding(
469 &self,
470 binding: &crate::Binding,
471 mode: LocationMode,
472 ) -> Result<ResolvedBinding, Error> {
473 match *binding {
474 crate::Binding::BuiltIn(mut built_in) => {
475 match built_in {
476 crate::BuiltIn::Position { ref mut invariant } => {
477 if *invariant && self.lang_version < (2, 1) {
478 return Err(Error::UnsupportedAttribute("invariant".to_string()));
479 }
480
481 if !matches!(mode, LocationMode::VertexOutput) {
484 *invariant = false;
485 }
486 }
487 crate::BuiltIn::BaseInstance if self.lang_version < (1, 2) => {
488 return Err(Error::UnsupportedAttribute("base_instance".to_string()));
489 }
490 crate::BuiltIn::InstanceIndex if self.lang_version < (1, 2) => {
491 return Err(Error::UnsupportedAttribute("instance_id".to_string()));
492 }
493 crate::BuiltIn::PrimitiveIndex if self.lang_version < (2, 3) => {
496 return Err(Error::UnsupportedAttribute("primitive_id".to_string()));
497 }
498 crate::BuiltIn::ViewIndex if self.lang_version < (2, 2) => {
502 return Err(Error::UnsupportedAttribute("amplification_id".to_string()));
503 }
504 crate::BuiltIn::Barycentric { .. } if self.lang_version < (2, 3) => {
507 return Err(Error::UnsupportedAttribute("barycentric_coord".to_string()));
508 }
509 _ => {}
510 }
511
512 Ok(ResolvedBinding::BuiltIn(built_in))
513 }
514 crate::Binding::Location {
515 location,
516 interpolation,
517 sampling,
518 blend_src,
519 per_primitive,
520 } => match mode {
521 LocationMode::VertexInput => Ok(ResolvedBinding::Attribute(location)),
522 LocationMode::FragmentOutput => {
523 if blend_src.is_some() && self.lang_version < (1, 2) {
524 return Err(Error::UnsupportedAttribute("blend_src".to_string()));
525 }
526 Ok(ResolvedBinding::Color {
527 location,
528 blend_src,
529 })
530 }
531 LocationMode::VertexOutput
532 | LocationMode::FragmentInput
533 | LocationMode::MeshOutput => {
534 Ok(ResolvedBinding::User {
535 prefix: if self.spirv_cross_compatibility {
536 "locn"
537 } else {
538 "loc"
539 },
540 index: location,
541 interpolation: {
542 let interpolation = interpolation.unwrap();
546 let sampling = sampling.unwrap_or(crate::Sampling::Center);
547 Some(ResolvedInterpolation::from_binding(
548 interpolation,
549 sampling,
550 per_primitive,
551 ))
552 },
553 })
554 }
555 LocationMode::Uniform => Err(Error::GenericValidation(format!(
556 "Unexpected Binding::Location({location}) for the Uniform mode"
557 ))),
558 },
559 }
560 }
561
562 fn get_entry_point_resources(&self, ep: &crate::EntryPoint) -> Option<&EntryPointResources> {
563 self.per_entry_point_map.get(&ep.name)
564 }
565
566 fn get_resource_binding_target(
567 &self,
568 ep: &crate::EntryPoint,
569 res_binding: &crate::ResourceBinding,
570 ) -> Option<&BindTarget> {
571 self.get_entry_point_resources(ep)
572 .and_then(|res| res.resources.get(res_binding))
573 }
574
575 fn resolve_resource_binding(
576 &self,
577 ep: &crate::EntryPoint,
578 res_binding: &crate::ResourceBinding,
579 ) -> Result<ResolvedBinding, EntryPointError> {
580 let target = self.get_resource_binding_target(ep, res_binding);
581 match target {
582 Some(target) => Ok(ResolvedBinding::Resource(target.clone())),
583 None if self.fake_missing_bindings => Ok(ResolvedBinding::User {
584 prefix: "fake",
585 index: 0,
586 interpolation: None,
587 }),
588 None => Err(EntryPointError::MissingBindTarget(*res_binding)),
589 }
590 }
591
592 fn resolve_immediates(
593 &self,
594 ep: &crate::EntryPoint,
595 ) -> Result<ResolvedBinding, EntryPointError> {
596 let slot = self
597 .get_entry_point_resources(ep)
598 .and_then(|res| res.immediates_buffer);
599 match slot {
600 Some(slot) => Ok(ResolvedBinding::Resource(BindTarget {
601 buffer: Some(slot),
602 ..Default::default()
603 })),
604 None if self.fake_missing_bindings => Ok(ResolvedBinding::User {
605 prefix: "fake",
606 index: 0,
607 interpolation: None,
608 }),
609 None => Err(EntryPointError::MissingImmediateData),
610 }
611 }
612
613 fn resolve_sizes_buffer(
614 &self,
615 ep: &crate::EntryPoint,
616 ) -> Result<ResolvedBinding, EntryPointError> {
617 let slot = self
618 .get_entry_point_resources(ep)
619 .and_then(|res| res.sizes_buffer);
620 match slot {
621 Some(slot) => Ok(ResolvedBinding::Resource(BindTarget {
622 buffer: Some(slot),
623 ..Default::default()
624 })),
625 None if self.fake_missing_bindings => Ok(ResolvedBinding::User {
626 prefix: "fake",
627 index: 0,
628 interpolation: None,
629 }),
630 None => Err(EntryPointError::MissingSizesBuffer),
631 }
632 }
633}
634
635impl ResolvedBinding {
636 fn as_inline_sampler<'a>(&self, options: &'a Options) -> Option<&'a sampler::InlineSampler> {
637 match *self {
638 Self::Resource(BindTarget {
639 sampler: Some(BindSamplerTarget::Inline(index)),
640 ..
641 }) => Some(&options.inline_samplers[index as usize]),
642 _ => None,
643 }
644 }
645
646 fn try_fmt<W: Write>(&self, out: &mut W) -> Result<(), Error> {
647 write!(out, " [[")?;
648 match *self {
649 Self::BuiltIn(built_in) => {
650 use crate::BuiltIn as Bi;
651 let name = match built_in {
652 Bi::Position { invariant: false } => "position",
653 Bi::Position { invariant: true } => "position, invariant",
654 Bi::ViewIndex => "amplification_id",
655 Bi::BaseInstance => "base_instance",
657 Bi::BaseVertex => "base_vertex",
658 Bi::ClipDistances => "clip_distance",
659 Bi::InstanceIndex => "instance_id",
660 Bi::PointSize => "point_size",
661 Bi::VertexIndex => "vertex_id",
662 Bi::FragDepth => "depth(any)",
664 Bi::PointCoord => "point_coord",
665 Bi::FrontFacing => "front_facing",
666 Bi::PrimitiveIndex => "primitive_id",
667 Bi::Barycentric { perspective: true } => "barycentric_coord",
668 Bi::Barycentric { perspective: false } => {
669 "barycentric_coord, center_no_perspective"
670 }
671 Bi::SampleIndex => "sample_id",
672 Bi::SampleMask => "sample_mask",
673 Bi::GlobalInvocationId => "thread_position_in_grid",
675 Bi::LocalInvocationId => "thread_position_in_threadgroup",
676 Bi::LocalInvocationIndex => "thread_index_in_threadgroup",
677 Bi::WorkGroupId => "threadgroup_position_in_grid",
678 Bi::WorkGroupSize => "dispatch_threads_per_threadgroup",
679 Bi::NumWorkGroups => "threadgroups_per_grid",
680 Bi::NumSubgroups => "simdgroups_per_threadgroup",
682 Bi::SubgroupId => "simdgroup_index_in_threadgroup",
683 Bi::SubgroupSize => "threads_per_simdgroup",
684 Bi::SubgroupInvocationId => "thread_index_in_simdgroup",
685 Bi::CullDistance | Bi::DrawIndex => {
686 return Err(Error::UnsupportedBuiltIn(built_in))
687 }
688 Bi::CullPrimitive => "primitive_culled",
689 Bi::PointIndex | Bi::LineIndices | Bi::TriangleIndices => unimplemented!(),
691 Bi::MeshTaskSize
694 | Bi::VertexCount
695 | Bi::PrimitiveCount
696 | Bi::Vertices
697 | Bi::Primitives
698 | Bi::RayInvocationId
699 | Bi::NumRayInvocations
700 | Bi::InstanceCustomData
701 | Bi::GeometryIndex
702 | Bi::WorldRayOrigin
703 | Bi::WorldRayDirection
704 | Bi::ObjectRayOrigin
705 | Bi::ObjectRayDirection
706 | Bi::RayTmin
707 | Bi::RayTCurrentMax
708 | Bi::ObjectToWorld
709 | Bi::WorldToObject
710 | Bi::HitKind
711 | Bi::HitBarycentrics => unreachable!(),
712 };
713 write!(out, "{name}")?;
714 }
715 Self::Attribute(index) => write!(out, "attribute({index})")?,
716 Self::Color {
717 location,
718 blend_src,
719 } => {
720 if let Some(blend_src) = blend_src {
721 write!(out, "color({location}) index({blend_src})")?
722 } else {
723 write!(out, "color({location})")?
724 }
725 }
726 Self::User {
727 prefix,
728 index,
729 interpolation,
730 } => {
731 write!(out, "user({prefix}{index})")?;
732 if let Some(interpolation) = interpolation {
733 write!(out, ", ")?;
734 interpolation.try_fmt(out)?;
735 }
736 }
737 Self::Resource(ref target) => {
738 if let Some(id) = target.buffer {
739 write!(out, "buffer({id})")?;
740 } else if let Some(id) = target.texture {
741 write!(out, "texture({id})")?;
742 } else if let Some(BindSamplerTarget::Resource(id)) = target.sampler {
743 write!(out, "sampler({id})")?;
744 } else {
745 return Err(Error::UnimplementedBindTarget(target.clone()));
746 }
747 }
748 Self::Payload => write!(out, "payload")?,
749 }
750 write!(out, "]]")?;
751 Ok(())
752 }
753}
754
755impl ResolvedInterpolation {
756 const fn from_binding(
757 interpolation: crate::Interpolation,
758 sampling: crate::Sampling,
759 per_primitive: bool,
760 ) -> Self {
761 use crate::Interpolation as I;
762 use crate::Sampling as S;
763
764 if per_primitive {
765 return Self::Flat;
766 }
767
768 match (interpolation, sampling) {
769 (I::Perspective, S::Center) => Self::CenterPerspective,
770 (I::Perspective, S::Centroid) => Self::CentroidPerspective,
771 (I::Perspective, S::Sample) => Self::SamplePerspective,
772 (I::Linear, S::Center) => Self::CenterNoPerspective,
773 (I::Linear, S::Centroid) => Self::CentroidNoPerspective,
774 (I::Linear, S::Sample) => Self::SampleNoPerspective,
775 (I::Flat, _) => Self::Flat,
776 (I::PerVertex, S::Center) => Self::PerVertex,
777 _ => unreachable!(),
778 }
779 }
780
781 fn try_fmt<W: Write>(self, out: &mut W) -> Result<(), Error> {
782 let identifier = match self {
783 Self::CenterPerspective => "center_perspective",
784 Self::CenterNoPerspective => "center_no_perspective",
785 Self::CentroidPerspective => "centroid_perspective",
786 Self::CentroidNoPerspective => "centroid_no_perspective",
787 Self::SamplePerspective => "sample_perspective",
788 Self::SampleNoPerspective => "sample_no_perspective",
789 Self::Flat => "flat",
790 Self::PerVertex => unreachable!(),
791 };
792 out.write_str(identifier)?;
793 Ok(())
794 }
795}
796
797struct EntryPointArgument {
798 ty_name: String,
799 name: String,
800 binding: String,
801 init: Option<Handle<crate::Expression>>,
802}
803
804type BackendResult = Result<(), Error>;
806
807const NAMESPACE: &str = "metal";
808
809const WRAPPED_ARRAY_FIELD: &str = "inner";
813
814#[derive(Debug)]
817pub struct TranslationInfo {
818 pub entry_point_names: Vec<Result<String, EntryPointError>>,
823}
824
825pub fn write_string(
826 module: &crate::Module,
827 info: &ModuleInfo,
828 options: &Options,
829 pipeline_options: &PipelineOptions,
830) -> Result<(String, TranslationInfo), Error> {
831 let mut w = Writer::new(String::new());
832 let info = w.write(module, info, options, pipeline_options)?;
833 Ok((w.finish(), info))
834}
835
836pub fn supported_capabilities() -> crate::valid::Capabilities {
837 use crate::valid::Capabilities as Caps;
838 Caps::IMMEDIATES
839 | Caps::PRIMITIVE_INDEX
841 | Caps::TEXTURE_AND_SAMPLER_BINDING_ARRAY
842 | Caps::STORAGE_TEXTURE_BINDING_ARRAY
844 | Caps::STORAGE_BUFFER_BINDING_ARRAY
845 | Caps::CLIP_DISTANCES
846 | Caps::STORAGE_TEXTURE_16BIT_NORM_FORMATS
848 | Caps::MULTIVIEW
849 | Caps::MULTISAMPLED_SHADING
851 | Caps::RAY_QUERY
852 | Caps::DUAL_SOURCE_BLENDING
853 | Caps::CUBE_ARRAY_TEXTURES
854 | Caps::SHADER_INT64
855 | Caps::SUBGROUP
856 | Caps::SUBGROUP_BARRIER
857 | Caps::SHADER_INT64_ATOMIC_MIN_MAX
859 | Caps::SHADER_FLOAT32_ATOMIC
861 | Caps::TEXTURE_ATOMIC
862 | Caps::TEXTURE_INT64_ATOMIC
863 | Caps::SHADER_FLOAT16
865 | Caps::SHADER_INT16
866 | Caps::TEXTURE_EXTERNAL
867 | Caps::SHADER_FLOAT16_IN_FLOAT32
868 | Caps::SHADER_BARYCENTRICS
869 | Caps::MESH_SHADER
870 | Caps::MESH_SHADER_POINT_TOPOLOGY
871 | Caps::TEXTURE_AND_SAMPLER_BINDING_ARRAY_NON_UNIFORM_INDEXING
872 | Caps::STORAGE_TEXTURE_BINDING_ARRAY_NON_UNIFORM_INDEXING
874 | Caps::STORAGE_BUFFER_BINDING_ARRAY_NON_UNIFORM_INDEXING
875 | Caps::COOPERATIVE_MATRIX
876 | Caps::PER_VERTEX
877 | Caps::MEMORY_DECORATION_COHERENT
881 | Caps::LINEAR_INTERPOLATION
882 | Caps::DEBUG_PRINTF
883}
884
885#[test]
886fn test_error_size() {
887 assert_eq!(size_of::<Error>(), 40);
888}