1use alloc::{
72 format,
73 string::{String, ToString},
74 vec::Vec,
75};
76use core::fmt::{Error as FmtError, Write};
77
78use crate::{arena::Handle, ir, proc::index, valid::ModuleInfo};
79
80mod keywords;
81pub mod sampler;
82mod writer;
83
84pub use writer::Writer;
85
86pub type Slot = u8;
87pub type InlineSamplerIndex = u8;
88
89#[derive(Clone, Debug, PartialEq, Eq, Hash)]
90#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
91#[cfg_attr(feature = "deserialize", derive(serde::Deserialize))]
92pub enum BindSamplerTarget {
93 Resource(Slot),
94 Inline(InlineSamplerIndex),
95}
96
97#[derive(Clone, Debug, PartialEq, Eq, Hash)]
103#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
104#[cfg_attr(feature = "deserialize", derive(serde::Deserialize))]
105pub struct BindExternalTextureTarget {
106 pub planes: [Slot; 3],
107 pub params: Slot,
108}
109
110#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
111#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
112#[cfg_attr(feature = "deserialize", derive(serde::Deserialize))]
113#[cfg_attr(any(feature = "serialize", feature = "deserialize"), serde(default))]
114pub struct BindTarget {
115 pub buffer: Option<Slot>,
116 pub texture: Option<Slot>,
117 pub sampler: Option<BindSamplerTarget>,
118 pub external_texture: Option<BindExternalTextureTarget>,
119 pub mutable: bool,
120}
121
122#[cfg(feature = "deserialize")]
123#[derive(serde::Deserialize)]
124struct BindingMapSerialization {
125 resource_binding: crate::ResourceBinding,
126 bind_target: BindTarget,
127}
128
129#[cfg(feature = "deserialize")]
130fn deserialize_binding_map<'de, D>(deserializer: D) -> Result<BindingMap, D::Error>
131where
132 D: serde::Deserializer<'de>,
133{
134 use serde::Deserialize;
135
136 let vec = Vec::<BindingMapSerialization>::deserialize(deserializer)?;
137 let mut map = BindingMap::default();
138 for item in vec {
139 map.insert(item.resource_binding, item.bind_target);
140 }
141 Ok(map)
142}
143
144pub type BindingMap = alloc::collections::BTreeMap<crate::ResourceBinding, BindTarget>;
146
147#[derive(Clone, Debug, Default, Hash, Eq, PartialEq)]
148#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
149#[cfg_attr(feature = "deserialize", derive(serde::Deserialize))]
150#[cfg_attr(any(feature = "serialize", feature = "deserialize"), serde(default))]
151pub struct EntryPointResources {
152 #[cfg_attr(
153 feature = "deserialize",
154 serde(deserialize_with = "deserialize_binding_map")
155 )]
156 pub resources: BindingMap,
157
158 pub immediates_buffer: Option<Slot>,
159
160 pub sizes_buffer: Option<Slot>,
164}
165
166pub type EntryPointResourceMap = alloc::collections::BTreeMap<String, EntryPointResources>;
167
168enum ResolvedBinding {
169 BuiltIn(crate::BuiltIn),
170 Attribute(u32),
171 Color {
172 location: u32,
173 blend_src: Option<u32>,
174 },
175 User {
176 prefix: &'static str,
177 index: u32,
178 interpolation: Option<ResolvedInterpolation>,
179 },
180 Resource(BindTarget),
181}
182
183#[derive(Copy, Clone)]
184enum ResolvedInterpolation {
185 CenterPerspective,
186 CenterNoPerspective,
187 CentroidPerspective,
188 CentroidNoPerspective,
189 SamplePerspective,
190 SampleNoPerspective,
191 Flat,
192}
193
194#[derive(Debug, thiserror::Error)]
197pub enum Error {
198 #[error(transparent)]
199 Format(#[from] FmtError),
200 #[error("bind target {0:?} is empty")]
201 UnimplementedBindTarget(BindTarget),
202 #[error("composing of {0:?} is not implemented yet")]
203 UnsupportedCompose(Handle<crate::Type>),
204 #[error("operation {0:?} is not implemented yet")]
205 UnsupportedBinaryOp(crate::BinaryOperator),
206 #[error("standard function '{0}' is not implemented yet")]
207 UnsupportedCall(String),
208 #[error("feature '{0}' is not implemented yet")]
209 FeatureNotImplemented(String),
210 #[error("internal naga error: module should not have validated: {0}")]
211 GenericValidation(String),
212 #[error("BuiltIn {0:?} is not supported")]
213 UnsupportedBuiltIn(crate::BuiltIn),
214 #[error("capability {0:?} is not supported")]
215 CapabilityNotSupported(crate::valid::Capabilities),
216 #[error("attribute '{0}' is not supported for target MSL version")]
217 UnsupportedAttribute(String),
218 #[error("function '{0}' is not supported for target MSL version")]
219 UnsupportedFunction(String),
220 #[error("can not use writeable storage buffers in fragment stage prior to MSL 1.2")]
221 UnsupportedWriteableStorageBuffer,
222 #[error("can not use writeable storage textures in {0:?} stage prior to MSL 1.2")]
223 UnsupportedWriteableStorageTexture(ir::ShaderStage),
224 #[error("can not use read-write storage textures prior to MSL 1.2")]
225 UnsupportedRWStorageTexture,
226 #[error("array of '{0}' is not supported for target MSL version")]
227 UnsupportedArrayOf(String),
228 #[error("array of type '{0:?}' is not supported")]
229 UnsupportedArrayOfType(Handle<crate::Type>),
230 #[error("ray tracing is not supported prior to MSL 2.4")]
231 UnsupportedRayTracing,
232 #[error("cooperative matrix is not supported prior to MSL 2.3")]
233 UnsupportedCooperativeMatrix,
234 #[error("overrides should not be present at this stage")]
235 Override,
236 #[error("bitcasting to {0:?} is not supported")]
237 UnsupportedBitCast(crate::TypeInner),
238 #[error(transparent)]
239 ResolveArraySizeError(#[from] crate::proc::ResolveArraySizeError),
240 #[error("entry point with stage {0:?} and name '{1}' not found")]
241 EntryPointNotFound(ir::ShaderStage, String),
242}
243
244#[derive(Clone, Debug, PartialEq, thiserror::Error)]
245#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
246#[cfg_attr(feature = "deserialize", derive(serde::Deserialize))]
247pub enum EntryPointError {
248 #[error("global '{0}' doesn't have a binding")]
249 MissingBinding(String),
250 #[error("mapping of {0:?} is missing")]
251 MissingBindTarget(crate::ResourceBinding),
252 #[error("mapping for immediates is missing")]
253 MissingImmediateData,
254 #[error("mapping for sizes buffer is missing")]
255 MissingSizesBuffer,
256}
257
258#[derive(Clone, Copy, Debug)]
267enum LocationMode {
268 VertexInput,
270
271 VertexOutput,
273
274 FragmentInput,
276
277 FragmentOutput,
279
280 Uniform,
282}
283
284#[derive(Clone, Debug, Hash, PartialEq, Eq)]
285#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
286#[cfg_attr(feature = "deserialize", derive(serde::Deserialize))]
287#[cfg_attr(feature = "deserialize", serde(default))]
288pub struct Options {
289 pub lang_version: (u8, u8),
291 pub per_entry_point_map: EntryPointResourceMap,
293 pub inline_samplers: Vec<sampler::InlineSampler>,
295 pub spirv_cross_compatibility: bool,
297 pub fake_missing_bindings: bool,
299 pub bounds_check_policies: index::BoundsCheckPolicies,
301 pub zero_initialize_workgroup_memory: bool,
303 pub force_loop_bounding: bool,
306}
307
308impl Default for Options {
309 fn default() -> Self {
310 Options {
311 lang_version: (1, 0),
312 per_entry_point_map: EntryPointResourceMap::default(),
313 inline_samplers: Vec::new(),
314 spirv_cross_compatibility: false,
315 fake_missing_bindings: true,
316 bounds_check_policies: index::BoundsCheckPolicies::default(),
317 zero_initialize_workgroup_memory: true,
318 force_loop_bounding: true,
319 }
320 }
321}
322
323#[repr(u32)]
326#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
327#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
328#[cfg_attr(feature = "deserialize", derive(serde::Deserialize))]
329pub enum VertexFormat {
330 Uint8 = 0,
332 Uint8x2 = 1,
334 Uint8x4 = 2,
336 Sint8 = 3,
338 Sint8x2 = 4,
340 Sint8x4 = 5,
342 Unorm8 = 6,
344 Unorm8x2 = 7,
346 Unorm8x4 = 8,
348 Snorm8 = 9,
350 Snorm8x2 = 10,
352 Snorm8x4 = 11,
354 Uint16 = 12,
356 Uint16x2 = 13,
358 Uint16x4 = 14,
360 Sint16 = 15,
362 Sint16x2 = 16,
364 Sint16x4 = 17,
366 Unorm16 = 18,
368 Unorm16x2 = 19,
370 Unorm16x4 = 20,
372 Snorm16 = 21,
374 Snorm16x2 = 22,
376 Snorm16x4 = 23,
378 Float16 = 24,
380 Float16x2 = 25,
382 Float16x4 = 26,
384 Float32 = 27,
386 Float32x2 = 28,
388 Float32x3 = 29,
390 Float32x4 = 30,
392 Uint32 = 31,
394 Uint32x2 = 32,
396 Uint32x3 = 33,
398 Uint32x4 = 34,
400 Sint32 = 35,
402 Sint32x2 = 36,
404 Sint32x3 = 37,
406 Sint32x4 = 38,
408 #[cfg_attr(
410 any(feature = "serialize", feature = "deserialize"),
411 serde(rename = "unorm10-10-10-2")
412 )]
413 Unorm10_10_10_2 = 43,
414 #[cfg_attr(
416 any(feature = "serialize", feature = "deserialize"),
417 serde(rename = "unorm8x4-bgra")
418 )]
419 Unorm8x4Bgra = 44,
420}
421
422#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
424#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
425#[cfg_attr(feature = "deserialize", derive(serde::Deserialize))]
426pub enum VertexBufferStepMode {
427 Constant,
428 #[default]
429 ByVertex,
430 ByInstance,
431}
432
433#[derive(Debug, Clone, PartialEq, Eq, Hash)]
436#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
437#[cfg_attr(feature = "deserialize", derive(serde::Deserialize))]
438pub struct AttributeMapping {
439 pub shader_location: u32,
441 pub offset: u32,
443 pub format: VertexFormat,
449}
450
451#[derive(Debug, Default, Clone, PartialEq, Eq, Hash)]
454#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
455#[cfg_attr(feature = "deserialize", derive(serde::Deserialize))]
456pub struct VertexBufferMapping {
457 pub id: u32,
459 pub stride: u32,
461 pub step_mode: VertexBufferStepMode,
463 pub attributes: Vec<AttributeMapping>,
465}
466
467#[derive(Debug, Default, Clone)]
469#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
470#[cfg_attr(feature = "deserialize", derive(serde::Deserialize))]
471#[cfg_attr(feature = "deserialize", serde(default))]
472pub struct PipelineOptions {
473 pub entry_point: Option<(ir::ShaderStage, String)>,
481
482 pub allow_and_force_point_size: bool,
489
490 pub vertex_pulling_transform: bool,
498
499 pub vertex_buffer_mappings: Vec<VertexBufferMapping>,
502}
503
504impl Options {
505 fn resolve_local_binding(
506 &self,
507 binding: &crate::Binding,
508 mode: LocationMode,
509 ) -> Result<ResolvedBinding, Error> {
510 match *binding {
511 crate::Binding::BuiltIn(mut built_in) => {
512 match built_in {
513 crate::BuiltIn::Position { ref mut invariant } => {
514 if *invariant && self.lang_version < (2, 1) {
515 return Err(Error::UnsupportedAttribute("invariant".to_string()));
516 }
517
518 if !matches!(mode, LocationMode::VertexOutput) {
521 *invariant = false;
522 }
523 }
524 crate::BuiltIn::BaseInstance if self.lang_version < (1, 2) => {
525 return Err(Error::UnsupportedAttribute("base_instance".to_string()));
526 }
527 crate::BuiltIn::InstanceIndex if self.lang_version < (1, 2) => {
528 return Err(Error::UnsupportedAttribute("instance_id".to_string()));
529 }
530 crate::BuiltIn::PrimitiveIndex if self.lang_version < (2, 3) => {
533 return Err(Error::UnsupportedAttribute("primitive_id".to_string()));
534 }
535 crate::BuiltIn::ViewIndex if self.lang_version < (2, 2) => {
539 return Err(Error::UnsupportedAttribute("amplification_id".to_string()));
540 }
541 crate::BuiltIn::Barycentric { .. } if self.lang_version < (2, 3) => {
544 return Err(Error::UnsupportedAttribute("barycentric_coord".to_string()));
545 }
546 _ => {}
547 }
548
549 Ok(ResolvedBinding::BuiltIn(built_in))
550 }
551 crate::Binding::Location {
552 location,
553 interpolation,
554 sampling,
555 blend_src,
556 per_primitive: _,
557 } => match mode {
558 LocationMode::VertexInput => Ok(ResolvedBinding::Attribute(location)),
559 LocationMode::FragmentOutput => {
560 if blend_src.is_some() && self.lang_version < (1, 2) {
561 return Err(Error::UnsupportedAttribute("blend_src".to_string()));
562 }
563 Ok(ResolvedBinding::Color {
564 location,
565 blend_src,
566 })
567 }
568 LocationMode::VertexOutput | LocationMode::FragmentInput => {
569 Ok(ResolvedBinding::User {
570 prefix: if self.spirv_cross_compatibility {
571 "locn"
572 } else {
573 "loc"
574 },
575 index: location,
576 interpolation: {
577 let interpolation = interpolation.unwrap();
581 let sampling = sampling.unwrap_or(crate::Sampling::Center);
582 Some(ResolvedInterpolation::from_binding(interpolation, sampling))
583 },
584 })
585 }
586 LocationMode::Uniform => Err(Error::GenericValidation(format!(
587 "Unexpected Binding::Location({location}) for the Uniform mode"
588 ))),
589 },
590 }
591 }
592
593 fn get_entry_point_resources(&self, ep: &crate::EntryPoint) -> Option<&EntryPointResources> {
594 self.per_entry_point_map.get(&ep.name)
595 }
596
597 fn get_resource_binding_target(
598 &self,
599 ep: &crate::EntryPoint,
600 res_binding: &crate::ResourceBinding,
601 ) -> Option<&BindTarget> {
602 self.get_entry_point_resources(ep)
603 .and_then(|res| res.resources.get(res_binding))
604 }
605
606 fn resolve_resource_binding(
607 &self,
608 ep: &crate::EntryPoint,
609 res_binding: &crate::ResourceBinding,
610 ) -> Result<ResolvedBinding, EntryPointError> {
611 let target = self.get_resource_binding_target(ep, res_binding);
612 match target {
613 Some(target) => Ok(ResolvedBinding::Resource(target.clone())),
614 None if self.fake_missing_bindings => Ok(ResolvedBinding::User {
615 prefix: "fake",
616 index: 0,
617 interpolation: None,
618 }),
619 None => Err(EntryPointError::MissingBindTarget(*res_binding)),
620 }
621 }
622
623 fn resolve_immediates(
624 &self,
625 ep: &crate::EntryPoint,
626 ) -> Result<ResolvedBinding, EntryPointError> {
627 let slot = self
628 .get_entry_point_resources(ep)
629 .and_then(|res| res.immediates_buffer);
630 match slot {
631 Some(slot) => Ok(ResolvedBinding::Resource(BindTarget {
632 buffer: Some(slot),
633 ..Default::default()
634 })),
635 None if self.fake_missing_bindings => Ok(ResolvedBinding::User {
636 prefix: "fake",
637 index: 0,
638 interpolation: None,
639 }),
640 None => Err(EntryPointError::MissingImmediateData),
641 }
642 }
643
644 fn resolve_sizes_buffer(
645 &self,
646 ep: &crate::EntryPoint,
647 ) -> Result<ResolvedBinding, EntryPointError> {
648 let slot = self
649 .get_entry_point_resources(ep)
650 .and_then(|res| res.sizes_buffer);
651 match slot {
652 Some(slot) => Ok(ResolvedBinding::Resource(BindTarget {
653 buffer: Some(slot),
654 ..Default::default()
655 })),
656 None if self.fake_missing_bindings => Ok(ResolvedBinding::User {
657 prefix: "fake",
658 index: 0,
659 interpolation: None,
660 }),
661 None => Err(EntryPointError::MissingSizesBuffer),
662 }
663 }
664}
665
666impl ResolvedBinding {
667 fn as_inline_sampler<'a>(&self, options: &'a Options) -> Option<&'a sampler::InlineSampler> {
668 match *self {
669 Self::Resource(BindTarget {
670 sampler: Some(BindSamplerTarget::Inline(index)),
671 ..
672 }) => Some(&options.inline_samplers[index as usize]),
673 _ => None,
674 }
675 }
676
677 fn try_fmt<W: Write>(&self, out: &mut W) -> Result<(), Error> {
678 write!(out, " [[")?;
679 match *self {
680 Self::BuiltIn(built_in) => {
681 use crate::BuiltIn as Bi;
682 let name = match built_in {
683 Bi::Position { invariant: false } => "position",
684 Bi::Position { invariant: true } => "position, invariant",
685 Bi::ViewIndex => "amplification_id",
686 Bi::BaseInstance => "base_instance",
688 Bi::BaseVertex => "base_vertex",
689 Bi::ClipDistance => "clip_distance",
690 Bi::InstanceIndex => "instance_id",
691 Bi::PointSize => "point_size",
692 Bi::VertexIndex => "vertex_id",
693 Bi::FragDepth => "depth(any)",
695 Bi::PointCoord => "point_coord",
696 Bi::FrontFacing => "front_facing",
697 Bi::PrimitiveIndex => "primitive_id",
698 Bi::Barycentric { perspective: true } => "barycentric_coord",
699 Bi::Barycentric { perspective: false } => {
700 "barycentric_coord, center_no_perspective"
701 }
702 Bi::SampleIndex => "sample_id",
703 Bi::SampleMask => "sample_mask",
704 Bi::GlobalInvocationId => "thread_position_in_grid",
706 Bi::LocalInvocationId => "thread_position_in_threadgroup",
707 Bi::LocalInvocationIndex => "thread_index_in_threadgroup",
708 Bi::WorkGroupId => "threadgroup_position_in_grid",
709 Bi::WorkGroupSize => "dispatch_threads_per_threadgroup",
710 Bi::NumWorkGroups => "threadgroups_per_grid",
711 Bi::NumSubgroups => "simdgroups_per_threadgroup",
713 Bi::SubgroupId => "simdgroup_index_in_threadgroup",
714 Bi::SubgroupSize => "threads_per_simdgroup",
715 Bi::SubgroupInvocationId => "thread_index_in_simdgroup",
716 Bi::CullDistance | Bi::DrawID => {
717 return Err(Error::UnsupportedBuiltIn(built_in))
718 }
719 Bi::CullPrimitive => "primitive_culled",
720 Bi::PointIndex | Bi::LineIndices | Bi::TriangleIndices => unimplemented!(),
722 Bi::MeshTaskSize
723 | Bi::VertexCount
724 | Bi::PrimitiveCount
725 | Bi::Vertices
726 | Bi::Primitives => unreachable!(),
727 };
728 write!(out, "{name}")?;
729 }
730 Self::Attribute(index) => write!(out, "attribute({index})")?,
731 Self::Color {
732 location,
733 blend_src,
734 } => {
735 if let Some(blend_src) = blend_src {
736 write!(out, "color({location}) index({blend_src})")?
737 } else {
738 write!(out, "color({location})")?
739 }
740 }
741 Self::User {
742 prefix,
743 index,
744 interpolation,
745 } => {
746 write!(out, "user({prefix}{index})")?;
747 if let Some(interpolation) = interpolation {
748 write!(out, ", ")?;
749 interpolation.try_fmt(out)?;
750 }
751 }
752 Self::Resource(ref target) => {
753 if let Some(id) = target.buffer {
754 write!(out, "buffer({id})")?;
755 } else if let Some(id) = target.texture {
756 write!(out, "texture({id})")?;
757 } else if let Some(BindSamplerTarget::Resource(id)) = target.sampler {
758 write!(out, "sampler({id})")?;
759 } else {
760 return Err(Error::UnimplementedBindTarget(target.clone()));
761 }
762 }
763 }
764 write!(out, "]]")?;
765 Ok(())
766 }
767}
768
769impl ResolvedInterpolation {
770 const fn from_binding(interpolation: crate::Interpolation, sampling: crate::Sampling) -> Self {
771 use crate::Interpolation as I;
772 use crate::Sampling as S;
773
774 match (interpolation, sampling) {
775 (I::Perspective, S::Center) => Self::CenterPerspective,
776 (I::Perspective, S::Centroid) => Self::CentroidPerspective,
777 (I::Perspective, S::Sample) => Self::SamplePerspective,
778 (I::Linear, S::Center) => Self::CenterNoPerspective,
779 (I::Linear, S::Centroid) => Self::CentroidNoPerspective,
780 (I::Linear, S::Sample) => Self::SampleNoPerspective,
781 (I::Flat, _) => Self::Flat,
782 _ => unreachable!(),
783 }
784 }
785
786 fn try_fmt<W: Write>(self, out: &mut W) -> Result<(), Error> {
787 let identifier = match self {
788 Self::CenterPerspective => "center_perspective",
789 Self::CenterNoPerspective => "center_no_perspective",
790 Self::CentroidPerspective => "centroid_perspective",
791 Self::CentroidNoPerspective => "centroid_no_perspective",
792 Self::SamplePerspective => "sample_perspective",
793 Self::SampleNoPerspective => "sample_no_perspective",
794 Self::Flat => "flat",
795 };
796 out.write_str(identifier)?;
797 Ok(())
798 }
799}
800
801pub struct TranslationInfo {
804 pub entry_point_names: Vec<Result<String, EntryPointError>>,
809}
810
811pub fn write_string(
812 module: &crate::Module,
813 info: &ModuleInfo,
814 options: &Options,
815 pipeline_options: &PipelineOptions,
816) -> Result<(String, TranslationInfo), Error> {
817 let mut w = Writer::new(String::new());
818 let info = w.write(module, info, options, pipeline_options)?;
819 Ok((w.finish(), info))
820}
821
822#[test]
823fn test_error_size() {
824 assert_eq!(size_of::<Error>(), 40);
825}