1use super::Error;
2use crate::back::wgsl::polyfill::InversePolyfill;
3use crate::{
4 back::{self, Baked},
5 proc::{self, ExpressionKindTracker, NameKey},
6 valid, Handle, Module, ShaderStage, TypeInner,
7};
8use std::fmt::Write;
9
10type BackendResult = Result<(), Error>;
12
13enum Attribute {
15 Binding(u32),
16 BuiltIn(crate::BuiltIn),
17 Group(u32),
18 Invariant,
19 Interpolate(Option<crate::Interpolation>, Option<crate::Sampling>),
20 Location(u32),
21 SecondBlendSource,
22 Stage(ShaderStage),
23 WorkGroupSize([u32; 3]),
24}
25
26#[derive(Clone, Copy, Debug)]
39enum Indirection {
40 Ordinary,
46
47 Reference,
53}
54
55bitflags::bitflags! {
56 #[cfg_attr(feature = "serialize", derive(serde::Serialize))]
57 #[cfg_attr(feature = "deserialize", derive(serde::Deserialize))]
58 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
59 pub struct WriterFlags: u32 {
60 const EXPLICIT_TYPES = 0x1;
62 }
63}
64
65pub struct Writer<W> {
66 out: W,
67 flags: WriterFlags,
68 names: crate::FastHashMap<NameKey, String>,
69 namer: proc::Namer,
70 named_expressions: crate::NamedExpressions,
71 ep_results: Vec<(ShaderStage, Handle<crate::Type>)>,
72 required_polyfills: crate::FastIndexSet<InversePolyfill>,
73}
74
75impl<W: Write> Writer<W> {
76 pub fn new(out: W, flags: WriterFlags) -> Self {
77 Writer {
78 out,
79 flags,
80 names: crate::FastHashMap::default(),
81 namer: proc::Namer::default(),
82 named_expressions: crate::NamedExpressions::default(),
83 ep_results: vec![],
84 required_polyfills: crate::FastIndexSet::default(),
85 }
86 }
87
88 fn reset(&mut self, module: &Module) {
89 self.names.clear();
90 self.namer.reset(
91 module,
92 crate::keywords::wgsl::RESERVED,
93 &[],
95 &[],
96 &["__", "_naga"],
97 &mut self.names,
98 );
99 self.named_expressions.clear();
100 self.ep_results.clear();
101 self.required_polyfills.clear();
102 }
103
104 fn is_builtin_wgsl_struct(&self, module: &Module, handle: Handle<crate::Type>) -> bool {
105 module
106 .special_types
107 .predeclared_types
108 .values()
109 .any(|t| *t == handle)
110 }
111
112 pub fn write(&mut self, module: &Module, info: &valid::ModuleInfo) -> BackendResult {
113 if !module.overrides.is_empty() {
114 return Err(Error::Unimplemented(
115 "Pipeline constants are not yet supported for this back-end".to_string(),
116 ));
117 }
118
119 self.reset(module);
120
121 for ep in &module.entry_points {
123 if let Some(ref result) = ep.function.result {
124 self.ep_results.push((ep.stage, result.ty));
125 }
126 }
127
128 for (handle, ty) in module.types.iter() {
130 if let TypeInner::Struct { ref members, .. } = ty.inner {
131 {
132 if !self.is_builtin_wgsl_struct(module, handle) {
133 self.write_struct(module, handle, members)?;
134 writeln!(self.out)?;
135 }
136 }
137 }
138 }
139
140 let mut constants = module
142 .constants
143 .iter()
144 .filter(|&(_, c)| c.name.is_some())
145 .peekable();
146 while let Some((handle, _)) = constants.next() {
147 self.write_global_constant(module, handle)?;
148 if constants.peek().is_none() {
150 writeln!(self.out)?;
151 }
152 }
153
154 for (ty, global) in module.global_variables.iter() {
156 self.write_global(module, global, ty)?;
157 }
158
159 if !module.global_variables.is_empty() {
160 writeln!(self.out)?;
162 }
163
164 for (handle, function) in module.functions.iter() {
166 let fun_info = &info[handle];
167
168 let func_ctx = back::FunctionCtx {
169 ty: back::FunctionType::Function(handle),
170 info: fun_info,
171 expressions: &function.expressions,
172 named_expressions: &function.named_expressions,
173 expr_kind_tracker: ExpressionKindTracker::from_arena(&function.expressions),
174 };
175
176 self.write_function(module, function, &func_ctx)?;
178
179 writeln!(self.out)?;
180 }
181
182 for (index, ep) in module.entry_points.iter().enumerate() {
184 let attributes = match ep.stage {
185 ShaderStage::Vertex | ShaderStage::Fragment => vec![Attribute::Stage(ep.stage)],
186 ShaderStage::Compute => vec![
187 Attribute::Stage(ShaderStage::Compute),
188 Attribute::WorkGroupSize(ep.workgroup_size),
189 ],
190 };
191
192 self.write_attributes(&attributes)?;
193 writeln!(self.out)?;
195
196 let func_ctx = back::FunctionCtx {
197 ty: back::FunctionType::EntryPoint(index as u16),
198 info: info.get_entry_point(index),
199 expressions: &ep.function.expressions,
200 named_expressions: &ep.function.named_expressions,
201 expr_kind_tracker: ExpressionKindTracker::from_arena(&ep.function.expressions),
202 };
203 self.write_function(module, &ep.function, &func_ctx)?;
204
205 if index < module.entry_points.len() - 1 {
206 writeln!(self.out)?;
207 }
208 }
209
210 for polyfill in &self.required_polyfills {
212 writeln!(self.out)?;
213 write!(self.out, "{}", polyfill.source)?;
214 writeln!(self.out)?;
215 }
216
217 Ok(())
218 }
219
220 fn write_struct_name(&mut self, module: &Module, handle: Handle<crate::Type>) -> BackendResult {
225 if module.types[handle].name.is_none() {
226 if let Some(&(stage, _)) = self.ep_results.iter().find(|&&(_, ty)| ty == handle) {
227 let name = match stage {
228 ShaderStage::Compute => "ComputeOutput",
229 ShaderStage::Fragment => "FragmentOutput",
230 ShaderStage::Vertex => "VertexOutput",
231 };
232
233 write!(self.out, "{name}")?;
234 return Ok(());
235 }
236 }
237
238 write!(self.out, "{}", self.names[&NameKey::Type(handle)])?;
239
240 Ok(())
241 }
242
243 fn write_function(
249 &mut self,
250 module: &Module,
251 func: &crate::Function,
252 func_ctx: &back::FunctionCtx<'_>,
253 ) -> BackendResult {
254 let func_name = match func_ctx.ty {
255 back::FunctionType::EntryPoint(index) => &self.names[&NameKey::EntryPoint(index)],
256 back::FunctionType::Function(handle) => &self.names[&NameKey::Function(handle)],
257 };
258
259 write!(self.out, "fn {func_name}(")?;
261
262 for (index, arg) in func.arguments.iter().enumerate() {
264 if let Some(ref binding) = arg.binding {
266 self.write_attributes(&map_binding_to_attribute(binding))?;
267 }
268 let argument_name = &self.names[&func_ctx.argument_key(index as u32)];
270
271 write!(self.out, "{argument_name}: ")?;
272 self.write_type(module, arg.ty)?;
274 if index < func.arguments.len() - 1 {
275 write!(self.out, ", ")?;
277 }
278 }
279
280 write!(self.out, ")")?;
281
282 if let Some(ref result) = func.result {
284 write!(self.out, " -> ")?;
285 if let Some(ref binding) = result.binding {
286 self.write_attributes(&map_binding_to_attribute(binding))?;
287 }
288 self.write_type(module, result.ty)?;
289 }
290
291 write!(self.out, " {{")?;
292 writeln!(self.out)?;
293
294 for (handle, local) in func.local_variables.iter() {
296 write!(self.out, "{}", back::INDENT)?;
298
299 write!(self.out, "var {}: ", self.names[&func_ctx.name_key(handle)])?;
302
303 self.write_type(module, local.ty)?;
305
306 if let Some(init) = local.init {
308 write!(self.out, " = ")?;
311
312 self.write_expr(module, init, func_ctx)?;
315 }
316
317 writeln!(self.out, ";")?
319 }
320
321 if !func.local_variables.is_empty() {
322 writeln!(self.out)?;
323 }
324
325 for sta in func.body.iter() {
327 self.write_stmt(module, sta, func_ctx, back::Level(1))?;
329 }
330
331 writeln!(self.out, "}}")?;
332
333 self.named_expressions.clear();
334
335 Ok(())
336 }
337
338 fn write_attributes(&mut self, attributes: &[Attribute]) -> BackendResult {
340 for attribute in attributes {
341 match *attribute {
342 Attribute::Location(id) => write!(self.out, "@location({id}) ")?,
343 Attribute::SecondBlendSource => write!(self.out, "@second_blend_source ")?,
344 Attribute::BuiltIn(builtin_attrib) => {
345 let builtin = builtin_str(builtin_attrib)?;
346 write!(self.out, "@builtin({builtin}) ")?;
347 }
348 Attribute::Stage(shader_stage) => {
349 let stage_str = match shader_stage {
350 ShaderStage::Vertex => "vertex",
351 ShaderStage::Fragment => "fragment",
352 ShaderStage::Compute => "compute",
353 };
354 write!(self.out, "@{stage_str} ")?;
355 }
356 Attribute::WorkGroupSize(size) => {
357 write!(
358 self.out,
359 "@workgroup_size({}, {}, {}) ",
360 size[0], size[1], size[2]
361 )?;
362 }
363 Attribute::Binding(id) => write!(self.out, "@binding({id}) ")?,
364 Attribute::Group(id) => write!(self.out, "@group({id}) ")?,
365 Attribute::Invariant => write!(self.out, "@invariant ")?,
366 Attribute::Interpolate(interpolation, sampling) => {
367 if sampling.is_some() && sampling != Some(crate::Sampling::Center) {
368 write!(
369 self.out,
370 "@interpolate({}, {}) ",
371 interpolation_str(
372 interpolation.unwrap_or(crate::Interpolation::Perspective)
373 ),
374 sampling_str(sampling.unwrap_or(crate::Sampling::Center))
375 )?;
376 } else if interpolation.is_some()
377 && interpolation != Some(crate::Interpolation::Perspective)
378 {
379 write!(
380 self.out,
381 "@interpolate({}) ",
382 interpolation_str(
383 interpolation.unwrap_or(crate::Interpolation::Perspective)
384 )
385 )?;
386 }
387 }
388 };
389 }
390 Ok(())
391 }
392
393 fn write_struct(
398 &mut self,
399 module: &Module,
400 handle: Handle<crate::Type>,
401 members: &[crate::StructMember],
402 ) -> BackendResult {
403 write!(self.out, "struct ")?;
404 self.write_struct_name(module, handle)?;
405 write!(self.out, " {{")?;
406 writeln!(self.out)?;
407 for (index, member) in members.iter().enumerate() {
408 write!(self.out, "{}", back::INDENT)?;
410 if let Some(ref binding) = member.binding {
411 self.write_attributes(&map_binding_to_attribute(binding))?;
412 }
413 let member_name = &self.names[&NameKey::StructMember(handle, index as u32)];
415 write!(self.out, "{member_name}: ")?;
416 self.write_type(module, member.ty)?;
417 write!(self.out, ",")?;
418 writeln!(self.out)?;
419 }
420
421 write!(self.out, "}}")?;
422
423 writeln!(self.out)?;
424
425 Ok(())
426 }
427
428 fn write_type(&mut self, module: &Module, ty: Handle<crate::Type>) -> BackendResult {
433 let inner = &module.types[ty].inner;
434 match *inner {
435 TypeInner::Struct { .. } => self.write_struct_name(module, ty)?,
436 ref other => self.write_value_type(module, other)?,
437 }
438
439 Ok(())
440 }
441
442 fn write_value_type(&mut self, module: &Module, inner: &TypeInner) -> BackendResult {
447 match *inner {
448 TypeInner::Vector { size, scalar } => write!(
449 self.out,
450 "vec{}<{}>",
451 back::vector_size_str(size),
452 scalar_kind_str(scalar),
453 )?,
454 TypeInner::Sampler { comparison: false } => {
455 write!(self.out, "sampler")?;
456 }
457 TypeInner::Sampler { comparison: true } => {
458 write!(self.out, "sampler_comparison")?;
459 }
460 TypeInner::Image {
461 dim,
462 arrayed,
463 class,
464 } => {
465 use crate::ImageClass as Ic;
467
468 let dim_str = image_dimension_str(dim);
469 let arrayed_str = if arrayed { "_array" } else { "" };
470 let (class_str, multisampled_str, format_str, storage_str) = match class {
471 Ic::Sampled { kind, multi } => (
472 "",
473 if multi { "multisampled_" } else { "" },
474 scalar_kind_str(crate::Scalar { kind, width: 4 }),
475 "",
476 ),
477 Ic::Depth { multi } => {
478 ("depth_", if multi { "multisampled_" } else { "" }, "", "")
479 }
480 Ic::Storage { format, access } => (
481 "storage_",
482 "",
483 storage_format_str(format),
484 if access.contains(crate::StorageAccess::LOAD | crate::StorageAccess::STORE)
485 {
486 ",read_write"
487 } else if access.contains(crate::StorageAccess::LOAD) {
488 ",read"
489 } else {
490 ",write"
491 },
492 ),
493 };
494 write!(
495 self.out,
496 "texture_{class_str}{multisampled_str}{dim_str}{arrayed_str}"
497 )?;
498
499 if !format_str.is_empty() {
500 write!(self.out, "<{format_str}{storage_str}>")?;
501 }
502 }
503 TypeInner::Scalar(scalar) => {
504 write!(self.out, "{}", scalar_kind_str(scalar))?;
505 }
506 TypeInner::Atomic(scalar) => {
507 write!(self.out, "atomic<{}>", scalar_kind_str(scalar))?;
508 }
509 TypeInner::Array {
510 base,
511 size,
512 stride: _,
513 } => {
514 write!(self.out, "array<")?;
518 match size {
519 crate::ArraySize::Constant(len) => {
520 self.write_type(module, base)?;
521 write!(self.out, ", {len}")?;
522 }
523 crate::ArraySize::Dynamic => {
524 self.write_type(module, base)?;
525 }
526 }
527 write!(self.out, ">")?;
528 }
529 TypeInner::BindingArray { base, size } => {
530 write!(self.out, "binding_array<")?;
532 match size {
533 crate::ArraySize::Constant(len) => {
534 self.write_type(module, base)?;
535 write!(self.out, ", {len}")?;
536 }
537 crate::ArraySize::Dynamic => {
538 self.write_type(module, base)?;
539 }
540 }
541 write!(self.out, ">")?;
542 }
543 TypeInner::Matrix {
544 columns,
545 rows,
546 scalar,
547 } => {
548 write!(
549 self.out,
550 "mat{}x{}<{}>",
551 back::vector_size_str(columns),
552 back::vector_size_str(rows),
553 scalar_kind_str(scalar)
554 )?;
555 }
556 TypeInner::Pointer { base, space } => {
557 let (address, maybe_access) = address_space_str(space);
558 if let Some(space) = address {
562 write!(self.out, "ptr<{space}, ")?;
563 }
564 self.write_type(module, base)?;
565 if address.is_some() {
566 if let Some(access) = maybe_access {
567 write!(self.out, ", {access}")?;
568 }
569 write!(self.out, ">")?;
570 }
571 }
572 TypeInner::ValuePointer {
573 size: None,
574 scalar,
575 space,
576 } => {
577 let (address, maybe_access) = address_space_str(space);
578 if let Some(space) = address {
579 write!(self.out, "ptr<{}, {}", space, scalar_kind_str(scalar))?;
580 if let Some(access) = maybe_access {
581 write!(self.out, ", {access}")?;
582 }
583 write!(self.out, ">")?;
584 } else {
585 return Err(Error::Unimplemented(format!(
586 "ValuePointer to AddressSpace::Handle {inner:?}"
587 )));
588 }
589 }
590 TypeInner::ValuePointer {
591 size: Some(size),
592 scalar,
593 space,
594 } => {
595 let (address, maybe_access) = address_space_str(space);
596 if let Some(space) = address {
597 write!(
598 self.out,
599 "ptr<{}, vec{}<{}>",
600 space,
601 back::vector_size_str(size),
602 scalar_kind_str(scalar)
603 )?;
604 if let Some(access) = maybe_access {
605 write!(self.out, ", {access}")?;
606 }
607 write!(self.out, ">")?;
608 } else {
609 return Err(Error::Unimplemented(format!(
610 "ValuePointer to AddressSpace::Handle {inner:?}"
611 )));
612 }
613 write!(self.out, ">")?;
614 }
615 TypeInner::AccelerationStructure => write!(self.out, "acceleration_structure")?,
616 _ => {
617 return Err(Error::Unimplemented(format!("write_value_type {inner:?}")));
618 }
619 }
620
621 Ok(())
622 }
623 fn write_stmt(
628 &mut self,
629 module: &Module,
630 stmt: &crate::Statement,
631 func_ctx: &back::FunctionCtx<'_>,
632 level: back::Level,
633 ) -> BackendResult {
634 use crate::{Expression, Statement};
635
636 match *stmt {
637 Statement::Emit(ref range) => {
638 for handle in range.clone() {
639 let info = &func_ctx.info[handle];
640 let expr_name = if let Some(name) = func_ctx.named_expressions.get(&handle) {
641 Some(self.namer.call(name))
646 } else {
647 let expr = &func_ctx.expressions[handle];
648 let min_ref_count = expr.bake_ref_count();
649 let required_baking_expr = match *expr {
651 Expression::ImageLoad { .. }
652 | Expression::ImageQuery { .. }
653 | Expression::ImageSample { .. } => true,
654 _ => false,
655 };
656 if min_ref_count <= info.ref_count || required_baking_expr {
657 Some(Baked(handle).to_string())
658 } else {
659 None
660 }
661 };
662
663 if let Some(name) = expr_name {
664 write!(self.out, "{level}")?;
665 self.start_named_expr(module, handle, func_ctx, &name)?;
666 self.write_expr(module, handle, func_ctx)?;
667 self.named_expressions.insert(handle, name);
668 writeln!(self.out, ";")?;
669 }
670 }
671 }
672 Statement::If {
674 condition,
675 ref accept,
676 ref reject,
677 } => {
678 write!(self.out, "{level}")?;
679 write!(self.out, "if ")?;
680 self.write_expr(module, condition, func_ctx)?;
681 writeln!(self.out, " {{")?;
682
683 let l2 = level.next();
684 for sta in accept {
685 self.write_stmt(module, sta, func_ctx, l2)?;
687 }
688
689 if !reject.is_empty() {
692 writeln!(self.out, "{level}}} else {{")?;
693
694 for sta in reject {
695 self.write_stmt(module, sta, func_ctx, l2)?;
697 }
698 }
699
700 writeln!(self.out, "{level}}}")?
701 }
702 Statement::Return { value } => {
703 write!(self.out, "{level}")?;
704 write!(self.out, "return")?;
705 if let Some(return_value) = value {
706 write!(self.out, " ")?;
708 self.write_expr(module, return_value, func_ctx)?;
709 }
710 writeln!(self.out, ";")?;
711 }
712 Statement::Kill => {
714 write!(self.out, "{level}")?;
715 writeln!(self.out, "discard;")?
716 }
717 Statement::Store { pointer, value } => {
718 write!(self.out, "{level}")?;
719
720 let is_atomic_pointer = func_ctx
721 .resolve_type(pointer, &module.types)
722 .is_atomic_pointer(&module.types);
723
724 if is_atomic_pointer {
725 write!(self.out, "atomicStore(")?;
726 self.write_expr(module, pointer, func_ctx)?;
727 write!(self.out, ", ")?;
728 self.write_expr(module, value, func_ctx)?;
729 write!(self.out, ")")?;
730 } else {
731 self.write_expr_with_indirection(
732 module,
733 pointer,
734 func_ctx,
735 Indirection::Reference,
736 )?;
737 write!(self.out, " = ")?;
738 self.write_expr(module, value, func_ctx)?;
739 }
740 writeln!(self.out, ";")?
741 }
742 Statement::Call {
743 function,
744 ref arguments,
745 result,
746 } => {
747 write!(self.out, "{level}")?;
748 if let Some(expr) = result {
749 let name = Baked(expr).to_string();
750 self.start_named_expr(module, expr, func_ctx, &name)?;
751 self.named_expressions.insert(expr, name);
752 }
753 let func_name = &self.names[&NameKey::Function(function)];
754 write!(self.out, "{func_name}(")?;
755 for (index, &argument) in arguments.iter().enumerate() {
756 if index != 0 {
757 write!(self.out, ", ")?;
758 }
759 self.write_expr(module, argument, func_ctx)?;
760 }
761 writeln!(self.out, ");")?
762 }
763 Statement::Atomic {
764 pointer,
765 ref fun,
766 value,
767 result,
768 } => {
769 write!(self.out, "{level}")?;
770 if let Some(result) = result {
771 let res_name = Baked(result).to_string();
772 self.start_named_expr(module, result, func_ctx, &res_name)?;
773 self.named_expressions.insert(result, res_name);
774 }
775
776 let fun_str = fun.to_wgsl();
777 write!(self.out, "atomic{fun_str}(")?;
778 self.write_expr(module, pointer, func_ctx)?;
779 if let crate::AtomicFunction::Exchange { compare: Some(cmp) } = *fun {
780 write!(self.out, ", ")?;
781 self.write_expr(module, cmp, func_ctx)?;
782 }
783 write!(self.out, ", ")?;
784 self.write_expr(module, value, func_ctx)?;
785 writeln!(self.out, ");")?
786 }
787 Statement::WorkGroupUniformLoad { pointer, result } => {
788 write!(self.out, "{level}")?;
789 let res_name = Baked(result).to_string();
791 self.start_named_expr(module, result, func_ctx, &res_name)?;
792 self.named_expressions.insert(result, res_name);
793 write!(self.out, "workgroupUniformLoad(")?;
794 self.write_expr(module, pointer, func_ctx)?;
795 writeln!(self.out, ");")?;
796 }
797 Statement::ImageStore {
798 image,
799 coordinate,
800 array_index,
801 value,
802 } => {
803 write!(self.out, "{level}")?;
804 write!(self.out, "textureStore(")?;
805 self.write_expr(module, image, func_ctx)?;
806 write!(self.out, ", ")?;
807 self.write_expr(module, coordinate, func_ctx)?;
808 if let Some(array_index_expr) = array_index {
809 write!(self.out, ", ")?;
810 self.write_expr(module, array_index_expr, func_ctx)?;
811 }
812 write!(self.out, ", ")?;
813 self.write_expr(module, value, func_ctx)?;
814 writeln!(self.out, ");")?;
815 }
816 Statement::Block(ref block) => {
818 write!(self.out, "{level}")?;
819 writeln!(self.out, "{{")?;
820 for sta in block.iter() {
821 self.write_stmt(module, sta, func_ctx, level.next())?
823 }
824 writeln!(self.out, "{level}}}")?
825 }
826 Statement::Switch {
827 selector,
828 ref cases,
829 } => {
830 write!(self.out, "{level}")?;
832 write!(self.out, "switch ")?;
833 self.write_expr(module, selector, func_ctx)?;
834 writeln!(self.out, " {{")?;
835
836 let l2 = level.next();
837 let mut new_case = true;
838 for case in cases {
839 if case.fall_through && !case.body.is_empty() {
840 return Err(Error::Unimplemented(
842 "fall-through switch case block".into(),
843 ));
844 }
845
846 match case.value {
847 crate::SwitchValue::I32(value) => {
848 if new_case {
849 write!(self.out, "{l2}case ")?;
850 }
851 write!(self.out, "{value}")?;
852 }
853 crate::SwitchValue::U32(value) => {
854 if new_case {
855 write!(self.out, "{l2}case ")?;
856 }
857 write!(self.out, "{value}u")?;
858 }
859 crate::SwitchValue::Default => {
860 if new_case {
861 if case.fall_through {
862 write!(self.out, "{l2}case ")?;
863 } else {
864 write!(self.out, "{l2}")?;
865 }
866 }
867 write!(self.out, "default")?;
868 }
869 }
870
871 new_case = !case.fall_through;
872
873 if case.fall_through {
874 write!(self.out, ", ")?;
875 } else {
876 writeln!(self.out, ": {{")?;
877 }
878
879 for sta in case.body.iter() {
880 self.write_stmt(module, sta, func_ctx, l2.next())?;
881 }
882
883 if !case.fall_through {
884 writeln!(self.out, "{l2}}}")?;
885 }
886 }
887
888 writeln!(self.out, "{level}}}")?
889 }
890 Statement::Loop {
891 ref body,
892 ref continuing,
893 break_if,
894 } => {
895 write!(self.out, "{level}")?;
896 writeln!(self.out, "loop {{")?;
897
898 let l2 = level.next();
899 for sta in body.iter() {
900 self.write_stmt(module, sta, func_ctx, l2)?;
901 }
902
903 if !continuing.is_empty() || break_if.is_some() {
908 writeln!(self.out, "{l2}continuing {{")?;
909 for sta in continuing.iter() {
910 self.write_stmt(module, sta, func_ctx, l2.next())?;
911 }
912
913 if let Some(condition) = break_if {
916 write!(self.out, "{}break if ", l2.next())?;
918 self.write_expr(module, condition, func_ctx)?;
919 writeln!(self.out, ";")?;
921 }
922
923 writeln!(self.out, "{l2}}}")?;
924 }
925
926 writeln!(self.out, "{level}}}")?
927 }
928 Statement::Break => {
929 writeln!(self.out, "{level}break;")?;
930 }
931 Statement::Continue => {
932 writeln!(self.out, "{level}continue;")?;
933 }
934 Statement::Barrier(barrier) => {
935 if barrier.contains(crate::Barrier::STORAGE) {
936 writeln!(self.out, "{level}storageBarrier();")?;
937 }
938
939 if barrier.contains(crate::Barrier::WORK_GROUP) {
940 writeln!(self.out, "{level}workgroupBarrier();")?;
941 }
942
943 if barrier.contains(crate::Barrier::SUB_GROUP) {
944 writeln!(self.out, "{level}subgroupBarrier();")?;
945 }
946 }
947 Statement::RayQuery { .. } => unreachable!(),
948 Statement::SubgroupBallot { result, predicate } => {
949 write!(self.out, "{level}")?;
950 let res_name = Baked(result).to_string();
951 self.start_named_expr(module, result, func_ctx, &res_name)?;
952 self.named_expressions.insert(result, res_name);
953
954 write!(self.out, "subgroupBallot(")?;
955 if let Some(predicate) = predicate {
956 self.write_expr(module, predicate, func_ctx)?;
957 }
958 writeln!(self.out, ");")?;
959 }
960 Statement::SubgroupCollectiveOperation {
961 op,
962 collective_op,
963 argument,
964 result,
965 } => {
966 write!(self.out, "{level}")?;
967 let res_name = Baked(result).to_string();
968 self.start_named_expr(module, result, func_ctx, &res_name)?;
969 self.named_expressions.insert(result, res_name);
970
971 match (collective_op, op) {
972 (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::All) => {
973 write!(self.out, "subgroupAll(")?
974 }
975 (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::Any) => {
976 write!(self.out, "subgroupAny(")?
977 }
978 (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::Add) => {
979 write!(self.out, "subgroupAdd(")?
980 }
981 (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::Mul) => {
982 write!(self.out, "subgroupMul(")?
983 }
984 (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::Max) => {
985 write!(self.out, "subgroupMax(")?
986 }
987 (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::Min) => {
988 write!(self.out, "subgroupMin(")?
989 }
990 (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::And) => {
991 write!(self.out, "subgroupAnd(")?
992 }
993 (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::Or) => {
994 write!(self.out, "subgroupOr(")?
995 }
996 (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::Xor) => {
997 write!(self.out, "subgroupXor(")?
998 }
999 (crate::CollectiveOperation::ExclusiveScan, crate::SubgroupOperation::Add) => {
1000 write!(self.out, "subgroupExclusiveAdd(")?
1001 }
1002 (crate::CollectiveOperation::ExclusiveScan, crate::SubgroupOperation::Mul) => {
1003 write!(self.out, "subgroupExclusiveMul(")?
1004 }
1005 (crate::CollectiveOperation::InclusiveScan, crate::SubgroupOperation::Add) => {
1006 write!(self.out, "subgroupInclusiveAdd(")?
1007 }
1008 (crate::CollectiveOperation::InclusiveScan, crate::SubgroupOperation::Mul) => {
1009 write!(self.out, "subgroupInclusiveMul(")?
1010 }
1011 _ => unimplemented!(),
1012 }
1013 self.write_expr(module, argument, func_ctx)?;
1014 writeln!(self.out, ");")?;
1015 }
1016 Statement::SubgroupGather {
1017 mode,
1018 argument,
1019 result,
1020 } => {
1021 write!(self.out, "{level}")?;
1022 let res_name = Baked(result).to_string();
1023 self.start_named_expr(module, result, func_ctx, &res_name)?;
1024 self.named_expressions.insert(result, res_name);
1025
1026 match mode {
1027 crate::GatherMode::BroadcastFirst => {
1028 write!(self.out, "subgroupBroadcastFirst(")?;
1029 }
1030 crate::GatherMode::Broadcast(_) => {
1031 write!(self.out, "subgroupBroadcast(")?;
1032 }
1033 crate::GatherMode::Shuffle(_) => {
1034 write!(self.out, "subgroupShuffle(")?;
1035 }
1036 crate::GatherMode::ShuffleDown(_) => {
1037 write!(self.out, "subgroupShuffleDown(")?;
1038 }
1039 crate::GatherMode::ShuffleUp(_) => {
1040 write!(self.out, "subgroupShuffleUp(")?;
1041 }
1042 crate::GatherMode::ShuffleXor(_) => {
1043 write!(self.out, "subgroupShuffleXor(")?;
1044 }
1045 }
1046 self.write_expr(module, argument, func_ctx)?;
1047 match mode {
1048 crate::GatherMode::BroadcastFirst => {}
1049 crate::GatherMode::Broadcast(index)
1050 | crate::GatherMode::Shuffle(index)
1051 | crate::GatherMode::ShuffleDown(index)
1052 | crate::GatherMode::ShuffleUp(index)
1053 | crate::GatherMode::ShuffleXor(index) => {
1054 write!(self.out, ", ")?;
1055 self.write_expr(module, index, func_ctx)?;
1056 }
1057 }
1058 writeln!(self.out, ");")?;
1059 }
1060 }
1061
1062 Ok(())
1063 }
1064
1065 fn plain_form_indirection(
1088 &self,
1089 expr: Handle<crate::Expression>,
1090 module: &Module,
1091 func_ctx: &back::FunctionCtx<'_>,
1092 ) -> Indirection {
1093 use crate::Expression as Ex;
1094
1095 if self.named_expressions.contains_key(&expr) {
1099 return Indirection::Ordinary;
1100 }
1101
1102 match func_ctx.expressions[expr] {
1103 Ex::LocalVariable(_) => Indirection::Reference,
1104 Ex::GlobalVariable(handle) => {
1105 let global = &module.global_variables[handle];
1106 match global.space {
1107 crate::AddressSpace::Handle => Indirection::Ordinary,
1108 _ => Indirection::Reference,
1109 }
1110 }
1111 Ex::Access { base, .. } | Ex::AccessIndex { base, .. } => {
1112 let base_ty = func_ctx.resolve_type(base, &module.types);
1113 match *base_ty {
1114 TypeInner::Pointer { .. } | TypeInner::ValuePointer { .. } => {
1115 Indirection::Reference
1116 }
1117 _ => Indirection::Ordinary,
1118 }
1119 }
1120 _ => Indirection::Ordinary,
1121 }
1122 }
1123
1124 fn start_named_expr(
1125 &mut self,
1126 module: &Module,
1127 handle: Handle<crate::Expression>,
1128 func_ctx: &back::FunctionCtx,
1129 name: &str,
1130 ) -> BackendResult {
1131 let quantifier = if func_ctx.expr_kind_tracker.is_impl_const(handle) {
1133 "const"
1134 } else {
1135 "let"
1136 };
1137 write!(self.out, "{quantifier} {name}")?;
1139 if self.flags.contains(WriterFlags::EXPLICIT_TYPES) {
1140 write!(self.out, ": ")?;
1141 let ty = &func_ctx.info[handle].ty;
1142 match *ty {
1144 proc::TypeResolution::Handle(handle) => {
1145 self.write_type(module, handle)?;
1146 }
1147 proc::TypeResolution::Value(ref inner) => {
1148 self.write_value_type(module, inner)?;
1149 }
1150 }
1151 }
1152
1153 write!(self.out, " = ")?;
1154 Ok(())
1155 }
1156
1157 fn write_expr(
1161 &mut self,
1162 module: &Module,
1163 expr: Handle<crate::Expression>,
1164 func_ctx: &back::FunctionCtx<'_>,
1165 ) -> BackendResult {
1166 self.write_expr_with_indirection(module, expr, func_ctx, Indirection::Ordinary)
1167 }
1168
1169 fn write_expr_with_indirection(
1182 &mut self,
1183 module: &Module,
1184 expr: Handle<crate::Expression>,
1185 func_ctx: &back::FunctionCtx<'_>,
1186 requested: Indirection,
1187 ) -> BackendResult {
1188 let plain = self.plain_form_indirection(expr, module, func_ctx);
1191 match (requested, plain) {
1192 (Indirection::Ordinary, Indirection::Reference) => {
1193 write!(self.out, "(&")?;
1194 self.write_expr_plain_form(module, expr, func_ctx, plain)?;
1195 write!(self.out, ")")?;
1196 }
1197 (Indirection::Reference, Indirection::Ordinary) => {
1198 write!(self.out, "(*")?;
1199 self.write_expr_plain_form(module, expr, func_ctx, plain)?;
1200 write!(self.out, ")")?;
1201 }
1202 (_, _) => self.write_expr_plain_form(module, expr, func_ctx, plain)?,
1203 }
1204
1205 Ok(())
1206 }
1207
1208 fn write_const_expression(
1209 &mut self,
1210 module: &Module,
1211 expr: Handle<crate::Expression>,
1212 ) -> BackendResult {
1213 self.write_possibly_const_expression(
1214 module,
1215 expr,
1216 &module.global_expressions,
1217 |writer, expr| writer.write_const_expression(module, expr),
1218 )
1219 }
1220
1221 fn write_possibly_const_expression<E>(
1222 &mut self,
1223 module: &Module,
1224 expr: Handle<crate::Expression>,
1225 expressions: &crate::Arena<crate::Expression>,
1226 write_expression: E,
1227 ) -> BackendResult
1228 where
1229 E: Fn(&mut Self, Handle<crate::Expression>) -> BackendResult,
1230 {
1231 use crate::Expression;
1232
1233 match expressions[expr] {
1234 Expression::Literal(literal) => match literal {
1235 crate::Literal::F32(value) => write!(self.out, "{value}f")?,
1236 crate::Literal::U32(value) => write!(self.out, "{value}u")?,
1237 crate::Literal::I32(value) => {
1238 if value == i32::MIN {
1242 write!(self.out, "i32({value})")?;
1243 } else {
1244 write!(self.out, "{value}i")?;
1245 }
1246 }
1247 crate::Literal::Bool(value) => write!(self.out, "{value}")?,
1248 crate::Literal::F64(value) => write!(self.out, "{value:?}lf")?,
1249 crate::Literal::I64(value) => {
1250 if value == i64::MIN {
1254 write!(self.out, "i64({value})")?;
1255 } else {
1256 write!(self.out, "{value}li")?;
1257 }
1258 }
1259 crate::Literal::U64(value) => write!(self.out, "{value:?}lu")?,
1260 crate::Literal::AbstractInt(_) | crate::Literal::AbstractFloat(_) => {
1261 return Err(Error::Custom(
1262 "Abstract types should not appear in IR presented to backends".into(),
1263 ));
1264 }
1265 },
1266 Expression::Constant(handle) => {
1267 let constant = &module.constants[handle];
1268 if constant.name.is_some() {
1269 write!(self.out, "{}", self.names[&NameKey::Constant(handle)])?;
1270 } else {
1271 self.write_const_expression(module, constant.init)?;
1272 }
1273 }
1274 Expression::ZeroValue(ty) => {
1275 self.write_type(module, ty)?;
1276 write!(self.out, "()")?;
1277 }
1278 Expression::Compose { ty, ref components } => {
1279 self.write_type(module, ty)?;
1280 write!(self.out, "(")?;
1281 for (index, component) in components.iter().enumerate() {
1282 if index != 0 {
1283 write!(self.out, ", ")?;
1284 }
1285 write_expression(self, *component)?;
1286 }
1287 write!(self.out, ")")?
1288 }
1289 Expression::Splat { size, value } => {
1290 let size = back::vector_size_str(size);
1291 write!(self.out, "vec{size}(")?;
1292 write_expression(self, value)?;
1293 write!(self.out, ")")?;
1294 }
1295 _ => unreachable!(),
1296 }
1297
1298 Ok(())
1299 }
1300
1301 fn write_expr_plain_form(
1309 &mut self,
1310 module: &Module,
1311 expr: Handle<crate::Expression>,
1312 func_ctx: &back::FunctionCtx<'_>,
1313 indirection: Indirection,
1314 ) -> BackendResult {
1315 use crate::Expression;
1316
1317 if let Some(name) = self.named_expressions.get(&expr) {
1318 write!(self.out, "{name}")?;
1319 return Ok(());
1320 }
1321
1322 let expression = &func_ctx.expressions[expr];
1323
1324 match *expression {
1333 Expression::Literal(_)
1334 | Expression::Constant(_)
1335 | Expression::ZeroValue(_)
1336 | Expression::Compose { .. }
1337 | Expression::Splat { .. } => {
1338 self.write_possibly_const_expression(
1339 module,
1340 expr,
1341 func_ctx.expressions,
1342 |writer, expr| writer.write_expr(module, expr, func_ctx),
1343 )?;
1344 }
1345 Expression::Override(_) => unreachable!(),
1346 Expression::FunctionArgument(pos) => {
1347 let name_key = func_ctx.argument_key(pos);
1348 let name = &self.names[&name_key];
1349 write!(self.out, "{name}")?;
1350 }
1351 Expression::Binary { op, left, right } => {
1352 write!(self.out, "(")?;
1353 self.write_expr(module, left, func_ctx)?;
1354 write!(self.out, " {} ", back::binary_operation_str(op))?;
1355 self.write_expr(module, right, func_ctx)?;
1356 write!(self.out, ")")?;
1357 }
1358 Expression::Access { base, index } => {
1359 self.write_expr_with_indirection(module, base, func_ctx, indirection)?;
1360 write!(self.out, "[")?;
1361 self.write_expr(module, index, func_ctx)?;
1362 write!(self.out, "]")?
1363 }
1364 Expression::AccessIndex { base, index } => {
1365 let base_ty_res = &func_ctx.info[base].ty;
1366 let mut resolved = base_ty_res.inner_with(&module.types);
1367
1368 self.write_expr_with_indirection(module, base, func_ctx, indirection)?;
1369
1370 let base_ty_handle = match *resolved {
1371 TypeInner::Pointer { base, space: _ } => {
1372 resolved = &module.types[base].inner;
1373 Some(base)
1374 }
1375 _ => base_ty_res.handle(),
1376 };
1377
1378 match *resolved {
1379 TypeInner::Vector { .. } => {
1380 write!(self.out, ".{}", back::COMPONENTS[index as usize])?
1382 }
1383 TypeInner::Matrix { .. }
1384 | TypeInner::Array { .. }
1385 | TypeInner::BindingArray { .. }
1386 | TypeInner::ValuePointer { .. } => write!(self.out, "[{index}]")?,
1387 TypeInner::Struct { .. } => {
1388 let ty = base_ty_handle.unwrap();
1391
1392 write!(
1393 self.out,
1394 ".{}",
1395 &self.names[&NameKey::StructMember(ty, index)]
1396 )?
1397 }
1398 ref other => return Err(Error::Custom(format!("Cannot index {other:?}"))),
1399 }
1400 }
1401 Expression::ImageSample {
1402 image,
1403 sampler,
1404 gather: None,
1405 coordinate,
1406 array_index,
1407 offset,
1408 level,
1409 depth_ref,
1410 } => {
1411 use crate::SampleLevel as Sl;
1412
1413 let suffix_cmp = match depth_ref {
1414 Some(_) => "Compare",
1415 None => "",
1416 };
1417 let suffix_level = match level {
1418 Sl::Auto => "",
1419 Sl::Zero | Sl::Exact(_) => "Level",
1420 Sl::Bias(_) => "Bias",
1421 Sl::Gradient { .. } => "Grad",
1422 };
1423
1424 write!(self.out, "textureSample{suffix_cmp}{suffix_level}(")?;
1425 self.write_expr(module, image, func_ctx)?;
1426 write!(self.out, ", ")?;
1427 self.write_expr(module, sampler, func_ctx)?;
1428 write!(self.out, ", ")?;
1429 self.write_expr(module, coordinate, func_ctx)?;
1430
1431 if let Some(array_index) = array_index {
1432 write!(self.out, ", ")?;
1433 self.write_expr(module, array_index, func_ctx)?;
1434 }
1435
1436 if let Some(depth_ref) = depth_ref {
1437 write!(self.out, ", ")?;
1438 self.write_expr(module, depth_ref, func_ctx)?;
1439 }
1440
1441 match level {
1442 Sl::Auto => {}
1443 Sl::Zero => {
1444 if depth_ref.is_none() {
1446 write!(self.out, ", 0.0")?;
1447 }
1448 }
1449 Sl::Exact(expr) => {
1450 write!(self.out, ", ")?;
1451 self.write_expr(module, expr, func_ctx)?;
1452 }
1453 Sl::Bias(expr) => {
1454 write!(self.out, ", ")?;
1455 self.write_expr(module, expr, func_ctx)?;
1456 }
1457 Sl::Gradient { x, y } => {
1458 write!(self.out, ", ")?;
1459 self.write_expr(module, x, func_ctx)?;
1460 write!(self.out, ", ")?;
1461 self.write_expr(module, y, func_ctx)?;
1462 }
1463 }
1464
1465 if let Some(offset) = offset {
1466 write!(self.out, ", ")?;
1467 self.write_const_expression(module, offset)?;
1468 }
1469
1470 write!(self.out, ")")?;
1471 }
1472
1473 Expression::ImageSample {
1474 image,
1475 sampler,
1476 gather: Some(component),
1477 coordinate,
1478 array_index,
1479 offset,
1480 level: _,
1481 depth_ref,
1482 } => {
1483 let suffix_cmp = match depth_ref {
1484 Some(_) => "Compare",
1485 None => "",
1486 };
1487
1488 write!(self.out, "textureGather{suffix_cmp}(")?;
1489 match *func_ctx.resolve_type(image, &module.types) {
1490 TypeInner::Image {
1491 class: crate::ImageClass::Depth { multi: _ },
1492 ..
1493 } => {}
1494 _ => {
1495 write!(self.out, "{}, ", component as u8)?;
1496 }
1497 }
1498 self.write_expr(module, image, func_ctx)?;
1499 write!(self.out, ", ")?;
1500 self.write_expr(module, sampler, func_ctx)?;
1501 write!(self.out, ", ")?;
1502 self.write_expr(module, coordinate, func_ctx)?;
1503
1504 if let Some(array_index) = array_index {
1505 write!(self.out, ", ")?;
1506 self.write_expr(module, array_index, func_ctx)?;
1507 }
1508
1509 if let Some(depth_ref) = depth_ref {
1510 write!(self.out, ", ")?;
1511 self.write_expr(module, depth_ref, func_ctx)?;
1512 }
1513
1514 if let Some(offset) = offset {
1515 write!(self.out, ", ")?;
1516 self.write_const_expression(module, offset)?;
1517 }
1518
1519 write!(self.out, ")")?;
1520 }
1521 Expression::ImageQuery { image, query } => {
1522 use crate::ImageQuery as Iq;
1523
1524 let texture_function = match query {
1525 Iq::Size { .. } => "textureDimensions",
1526 Iq::NumLevels => "textureNumLevels",
1527 Iq::NumLayers => "textureNumLayers",
1528 Iq::NumSamples => "textureNumSamples",
1529 };
1530
1531 write!(self.out, "{texture_function}(")?;
1532 self.write_expr(module, image, func_ctx)?;
1533 if let Iq::Size { level: Some(level) } = query {
1534 write!(self.out, ", ")?;
1535 self.write_expr(module, level, func_ctx)?;
1536 };
1537 write!(self.out, ")")?;
1538 }
1539
1540 Expression::ImageLoad {
1541 image,
1542 coordinate,
1543 array_index,
1544 sample,
1545 level,
1546 } => {
1547 write!(self.out, "textureLoad(")?;
1548 self.write_expr(module, image, func_ctx)?;
1549 write!(self.out, ", ")?;
1550 self.write_expr(module, coordinate, func_ctx)?;
1551 if let Some(array_index) = array_index {
1552 write!(self.out, ", ")?;
1553 self.write_expr(module, array_index, func_ctx)?;
1554 }
1555 if let Some(index) = sample.or(level) {
1556 write!(self.out, ", ")?;
1557 self.write_expr(module, index, func_ctx)?;
1558 }
1559 write!(self.out, ")")?;
1560 }
1561 Expression::GlobalVariable(handle) => {
1562 let name = &self.names[&NameKey::GlobalVariable(handle)];
1563 write!(self.out, "{name}")?;
1564 }
1565
1566 Expression::As {
1567 expr,
1568 kind,
1569 convert,
1570 } => {
1571 let inner = func_ctx.resolve_type(expr, &module.types);
1572 match *inner {
1573 TypeInner::Matrix {
1574 columns,
1575 rows,
1576 scalar,
1577 } => {
1578 let scalar = crate::Scalar {
1579 kind,
1580 width: convert.unwrap_or(scalar.width),
1581 };
1582 let scalar_kind_str = scalar_kind_str(scalar);
1583 write!(
1584 self.out,
1585 "mat{}x{}<{}>",
1586 back::vector_size_str(columns),
1587 back::vector_size_str(rows),
1588 scalar_kind_str
1589 )?;
1590 }
1591 TypeInner::Vector {
1592 size,
1593 scalar: crate::Scalar { width, .. },
1594 } => {
1595 let scalar = crate::Scalar {
1596 kind,
1597 width: convert.unwrap_or(width),
1598 };
1599 let vector_size_str = back::vector_size_str(size);
1600 let scalar_kind_str = scalar_kind_str(scalar);
1601 if convert.is_some() {
1602 write!(self.out, "vec{vector_size_str}<{scalar_kind_str}>")?;
1603 } else {
1604 write!(self.out, "bitcast<vec{vector_size_str}<{scalar_kind_str}>>")?;
1605 }
1606 }
1607 TypeInner::Scalar(crate::Scalar { width, .. }) => {
1608 let scalar = crate::Scalar {
1609 kind,
1610 width: convert.unwrap_or(width),
1611 };
1612 let scalar_kind_str = scalar_kind_str(scalar);
1613 if convert.is_some() {
1614 write!(self.out, "{scalar_kind_str}")?
1615 } else {
1616 write!(self.out, "bitcast<{scalar_kind_str}>")?
1617 }
1618 }
1619 _ => {
1620 return Err(Error::Unimplemented(format!(
1621 "write_expr expression::as {inner:?}"
1622 )));
1623 }
1624 };
1625 write!(self.out, "(")?;
1626 self.write_expr(module, expr, func_ctx)?;
1627 write!(self.out, ")")?;
1628 }
1629 Expression::Load { pointer } => {
1630 let is_atomic_pointer = func_ctx
1631 .resolve_type(pointer, &module.types)
1632 .is_atomic_pointer(&module.types);
1633
1634 if is_atomic_pointer {
1635 write!(self.out, "atomicLoad(")?;
1636 self.write_expr(module, pointer, func_ctx)?;
1637 write!(self.out, ")")?;
1638 } else {
1639 self.write_expr_with_indirection(
1640 module,
1641 pointer,
1642 func_ctx,
1643 Indirection::Reference,
1644 )?;
1645 }
1646 }
1647 Expression::LocalVariable(handle) => {
1648 write!(self.out, "{}", self.names[&func_ctx.name_key(handle)])?
1649 }
1650 Expression::ArrayLength(expr) => {
1651 write!(self.out, "arrayLength(")?;
1652 self.write_expr(module, expr, func_ctx)?;
1653 write!(self.out, ")")?;
1654 }
1655
1656 Expression::Math {
1657 fun,
1658 arg,
1659 arg1,
1660 arg2,
1661 arg3,
1662 } => {
1663 use crate::MathFunction as Mf;
1664
1665 enum Function {
1666 Regular(&'static str),
1667 InversePolyfill(InversePolyfill),
1668 }
1669
1670 let function = match fun {
1671 Mf::Abs => Function::Regular("abs"),
1672 Mf::Min => Function::Regular("min"),
1673 Mf::Max => Function::Regular("max"),
1674 Mf::Clamp => Function::Regular("clamp"),
1675 Mf::Saturate => Function::Regular("saturate"),
1676 Mf::Cos => Function::Regular("cos"),
1678 Mf::Cosh => Function::Regular("cosh"),
1679 Mf::Sin => Function::Regular("sin"),
1680 Mf::Sinh => Function::Regular("sinh"),
1681 Mf::Tan => Function::Regular("tan"),
1682 Mf::Tanh => Function::Regular("tanh"),
1683 Mf::Acos => Function::Regular("acos"),
1684 Mf::Asin => Function::Regular("asin"),
1685 Mf::Atan => Function::Regular("atan"),
1686 Mf::Atan2 => Function::Regular("atan2"),
1687 Mf::Asinh => Function::Regular("asinh"),
1688 Mf::Acosh => Function::Regular("acosh"),
1689 Mf::Atanh => Function::Regular("atanh"),
1690 Mf::Radians => Function::Regular("radians"),
1691 Mf::Degrees => Function::Regular("degrees"),
1692 Mf::Ceil => Function::Regular("ceil"),
1694 Mf::Floor => Function::Regular("floor"),
1695 Mf::Round => Function::Regular("round"),
1696 Mf::Fract => Function::Regular("fract"),
1697 Mf::Trunc => Function::Regular("trunc"),
1698 Mf::Modf => Function::Regular("modf"),
1699 Mf::Frexp => Function::Regular("frexp"),
1700 Mf::Ldexp => Function::Regular("ldexp"),
1701 Mf::Exp => Function::Regular("exp"),
1703 Mf::Exp2 => Function::Regular("exp2"),
1704 Mf::Log => Function::Regular("log"),
1705 Mf::Log2 => Function::Regular("log2"),
1706 Mf::Pow => Function::Regular("pow"),
1707 Mf::Dot => Function::Regular("dot"),
1709 Mf::Cross => Function::Regular("cross"),
1710 Mf::Distance => Function::Regular("distance"),
1711 Mf::Length => Function::Regular("length"),
1712 Mf::Normalize => Function::Regular("normalize"),
1713 Mf::FaceForward => Function::Regular("faceForward"),
1714 Mf::Reflect => Function::Regular("reflect"),
1715 Mf::Refract => Function::Regular("refract"),
1716 Mf::Sign => Function::Regular("sign"),
1718 Mf::Fma => Function::Regular("fma"),
1719 Mf::Mix => Function::Regular("mix"),
1720 Mf::Step => Function::Regular("step"),
1721 Mf::SmoothStep => Function::Regular("smoothstep"),
1722 Mf::Sqrt => Function::Regular("sqrt"),
1723 Mf::InverseSqrt => Function::Regular("inverseSqrt"),
1724 Mf::Transpose => Function::Regular("transpose"),
1725 Mf::Determinant => Function::Regular("determinant"),
1726 Mf::CountTrailingZeros => Function::Regular("countTrailingZeros"),
1728 Mf::CountLeadingZeros => Function::Regular("countLeadingZeros"),
1729 Mf::CountOneBits => Function::Regular("countOneBits"),
1730 Mf::ReverseBits => Function::Regular("reverseBits"),
1731 Mf::ExtractBits => Function::Regular("extractBits"),
1732 Mf::InsertBits => Function::Regular("insertBits"),
1733 Mf::FirstTrailingBit => Function::Regular("firstTrailingBit"),
1734 Mf::FirstLeadingBit => Function::Regular("firstLeadingBit"),
1735 Mf::Pack4x8snorm => Function::Regular("pack4x8snorm"),
1737 Mf::Pack4x8unorm => Function::Regular("pack4x8unorm"),
1738 Mf::Pack2x16snorm => Function::Regular("pack2x16snorm"),
1739 Mf::Pack2x16unorm => Function::Regular("pack2x16unorm"),
1740 Mf::Pack2x16float => Function::Regular("pack2x16float"),
1741 Mf::Pack4xI8 => Function::Regular("pack4xI8"),
1742 Mf::Pack4xU8 => Function::Regular("pack4xU8"),
1743 Mf::Unpack4x8snorm => Function::Regular("unpack4x8snorm"),
1745 Mf::Unpack4x8unorm => Function::Regular("unpack4x8unorm"),
1746 Mf::Unpack2x16snorm => Function::Regular("unpack2x16snorm"),
1747 Mf::Unpack2x16unorm => Function::Regular("unpack2x16unorm"),
1748 Mf::Unpack2x16float => Function::Regular("unpack2x16float"),
1749 Mf::Unpack4xI8 => Function::Regular("unpack4xI8"),
1750 Mf::Unpack4xU8 => Function::Regular("unpack4xU8"),
1751 Mf::Inverse => {
1752 let typ = func_ctx.resolve_type(arg, &module.types);
1753
1754 let Some(overload) = InversePolyfill::find_overload(typ) else {
1755 return Err(Error::UnsupportedMathFunction(fun));
1756 };
1757
1758 Function::InversePolyfill(overload)
1759 }
1760 Mf::Outer => return Err(Error::UnsupportedMathFunction(fun)),
1761 };
1762
1763 match function {
1764 Function::Regular(fun_name) => {
1765 write!(self.out, "{fun_name}(")?;
1766 self.write_expr(module, arg, func_ctx)?;
1767 for arg in IntoIterator::into_iter([arg1, arg2, arg3]).flatten() {
1768 write!(self.out, ", ")?;
1769 self.write_expr(module, arg, func_ctx)?;
1770 }
1771 write!(self.out, ")")?
1772 }
1773 Function::InversePolyfill(inverse) => {
1774 write!(self.out, "{}(", inverse.fun_name)?;
1775 self.write_expr(module, arg, func_ctx)?;
1776 write!(self.out, ")")?;
1777 self.required_polyfills.insert(inverse);
1778 }
1779 }
1780 }
1781
1782 Expression::Swizzle {
1783 size,
1784 vector,
1785 pattern,
1786 } => {
1787 self.write_expr(module, vector, func_ctx)?;
1788 write!(self.out, ".")?;
1789 for &sc in pattern[..size as usize].iter() {
1790 self.out.write_char(back::COMPONENTS[sc as usize])?;
1791 }
1792 }
1793 Expression::Unary { op, expr } => {
1794 let unary = match op {
1795 crate::UnaryOperator::Negate => "-",
1796 crate::UnaryOperator::LogicalNot => "!",
1797 crate::UnaryOperator::BitwiseNot => "~",
1798 };
1799
1800 write!(self.out, "{unary}(")?;
1801 self.write_expr(module, expr, func_ctx)?;
1802
1803 write!(self.out, ")")?
1804 }
1805
1806 Expression::Select {
1807 condition,
1808 accept,
1809 reject,
1810 } => {
1811 write!(self.out, "select(")?;
1812 self.write_expr(module, reject, func_ctx)?;
1813 write!(self.out, ", ")?;
1814 self.write_expr(module, accept, func_ctx)?;
1815 write!(self.out, ", ")?;
1816 self.write_expr(module, condition, func_ctx)?;
1817 write!(self.out, ")")?
1818 }
1819 Expression::Derivative { axis, ctrl, expr } => {
1820 use crate::{DerivativeAxis as Axis, DerivativeControl as Ctrl};
1821 let op = match (axis, ctrl) {
1822 (Axis::X, Ctrl::Coarse) => "dpdxCoarse",
1823 (Axis::X, Ctrl::Fine) => "dpdxFine",
1824 (Axis::X, Ctrl::None) => "dpdx",
1825 (Axis::Y, Ctrl::Coarse) => "dpdyCoarse",
1826 (Axis::Y, Ctrl::Fine) => "dpdyFine",
1827 (Axis::Y, Ctrl::None) => "dpdy",
1828 (Axis::Width, Ctrl::Coarse) => "fwidthCoarse",
1829 (Axis::Width, Ctrl::Fine) => "fwidthFine",
1830 (Axis::Width, Ctrl::None) => "fwidth",
1831 };
1832 write!(self.out, "{op}(")?;
1833 self.write_expr(module, expr, func_ctx)?;
1834 write!(self.out, ")")?
1835 }
1836 Expression::Relational { fun, argument } => {
1837 use crate::RelationalFunction as Rf;
1838
1839 let fun_name = match fun {
1840 Rf::All => "all",
1841 Rf::Any => "any",
1842 _ => return Err(Error::UnsupportedRelationalFunction(fun)),
1843 };
1844 write!(self.out, "{fun_name}(")?;
1845
1846 self.write_expr(module, argument, func_ctx)?;
1847
1848 write!(self.out, ")")?
1849 }
1850 Expression::RayQueryGetIntersection { .. } => unreachable!(),
1852 Expression::CallResult(_)
1854 | Expression::AtomicResult { .. }
1855 | Expression::RayQueryProceedResult
1856 | Expression::SubgroupBallotResult
1857 | Expression::SubgroupOperationResult { .. }
1858 | Expression::WorkGroupUniformLoadResult { .. } => {}
1859 }
1860
1861 Ok(())
1862 }
1863
1864 fn write_global(
1868 &mut self,
1869 module: &Module,
1870 global: &crate::GlobalVariable,
1871 handle: Handle<crate::GlobalVariable>,
1872 ) -> BackendResult {
1873 if let Some(ref binding) = global.binding {
1875 self.write_attributes(&[
1876 Attribute::Group(binding.group),
1877 Attribute::Binding(binding.binding),
1878 ])?;
1879 writeln!(self.out)?;
1880 }
1881
1882 write!(self.out, "var")?;
1884 let (address, maybe_access) = address_space_str(global.space);
1885 if let Some(space) = address {
1886 write!(self.out, "<{space}")?;
1887 if let Some(access) = maybe_access {
1888 write!(self.out, ", {access}")?;
1889 }
1890 write!(self.out, ">")?;
1891 }
1892 write!(
1893 self.out,
1894 " {}: ",
1895 &self.names[&NameKey::GlobalVariable(handle)]
1896 )?;
1897
1898 self.write_type(module, global.ty)?;
1900
1901 if let Some(init) = global.init {
1903 write!(self.out, " = ")?;
1904 self.write_const_expression(module, init)?;
1905 }
1906
1907 writeln!(self.out, ";")?;
1909
1910 Ok(())
1911 }
1912
1913 fn write_global_constant(
1918 &mut self,
1919 module: &Module,
1920 handle: Handle<crate::Constant>,
1921 ) -> BackendResult {
1922 let name = &self.names[&NameKey::Constant(handle)];
1923 write!(self.out, "const {name}: ")?;
1925 self.write_type(module, module.constants[handle].ty)?;
1926 write!(self.out, " = ")?;
1927 let init = module.constants[handle].init;
1928 self.write_const_expression(module, init)?;
1929 writeln!(self.out, ";")?;
1930
1931 Ok(())
1932 }
1933
1934 #[allow(clippy::missing_const_for_fn)]
1936 pub fn finish(self) -> W {
1937 self.out
1938 }
1939}
1940
1941fn builtin_str(built_in: crate::BuiltIn) -> Result<&'static str, Error> {
1942 use crate::BuiltIn as Bi;
1943
1944 Ok(match built_in {
1945 Bi::VertexIndex => "vertex_index",
1946 Bi::InstanceIndex => "instance_index",
1947 Bi::Position { .. } => "position",
1948 Bi::FrontFacing => "front_facing",
1949 Bi::FragDepth => "frag_depth",
1950 Bi::LocalInvocationId => "local_invocation_id",
1951 Bi::LocalInvocationIndex => "local_invocation_index",
1952 Bi::GlobalInvocationId => "global_invocation_id",
1953 Bi::WorkGroupId => "workgroup_id",
1954 Bi::NumWorkGroups => "num_workgroups",
1955 Bi::SampleIndex => "sample_index",
1956 Bi::SampleMask => "sample_mask",
1957 Bi::PrimitiveIndex => "primitive_index",
1958 Bi::ViewIndex => "view_index",
1959 Bi::NumSubgroups => "num_subgroups",
1960 Bi::SubgroupId => "subgroup_id",
1961 Bi::SubgroupSize => "subgroup_size",
1962 Bi::SubgroupInvocationId => "subgroup_invocation_id",
1963 Bi::BaseInstance
1964 | Bi::BaseVertex
1965 | Bi::ClipDistance
1966 | Bi::CullDistance
1967 | Bi::PointSize
1968 | Bi::PointCoord
1969 | Bi::WorkGroupSize
1970 | Bi::DrawID => return Err(Error::Custom(format!("Unsupported builtin {built_in:?}"))),
1971 })
1972}
1973
1974const fn image_dimension_str(dim: crate::ImageDimension) -> &'static str {
1975 use crate::ImageDimension as IDim;
1976
1977 match dim {
1978 IDim::D1 => "1d",
1979 IDim::D2 => "2d",
1980 IDim::D3 => "3d",
1981 IDim::Cube => "cube",
1982 }
1983}
1984
1985const fn scalar_kind_str(scalar: crate::Scalar) -> &'static str {
1986 use crate::Scalar;
1987 use crate::ScalarKind as Sk;
1988
1989 match scalar {
1990 Scalar {
1991 kind: Sk::Float,
1992 width: 8,
1993 } => "f64",
1994 Scalar {
1995 kind: Sk::Float,
1996 width: 4,
1997 } => "f32",
1998 Scalar {
1999 kind: Sk::Sint,
2000 width: 4,
2001 } => "i32",
2002 Scalar {
2003 kind: Sk::Uint,
2004 width: 4,
2005 } => "u32",
2006 Scalar {
2007 kind: Sk::Sint,
2008 width: 8,
2009 } => "i64",
2010 Scalar {
2011 kind: Sk::Uint,
2012 width: 8,
2013 } => "u64",
2014 Scalar {
2015 kind: Sk::Bool,
2016 width: 1,
2017 } => "bool",
2018 _ => unreachable!(),
2019 }
2020}
2021
2022const fn storage_format_str(format: crate::StorageFormat) -> &'static str {
2023 use crate::StorageFormat as Sf;
2024
2025 match format {
2026 Sf::R8Unorm => "r8unorm",
2027 Sf::R8Snorm => "r8snorm",
2028 Sf::R8Uint => "r8uint",
2029 Sf::R8Sint => "r8sint",
2030 Sf::R16Uint => "r16uint",
2031 Sf::R16Sint => "r16sint",
2032 Sf::R16Float => "r16float",
2033 Sf::Rg8Unorm => "rg8unorm",
2034 Sf::Rg8Snorm => "rg8snorm",
2035 Sf::Rg8Uint => "rg8uint",
2036 Sf::Rg8Sint => "rg8sint",
2037 Sf::R32Uint => "r32uint",
2038 Sf::R32Sint => "r32sint",
2039 Sf::R32Float => "r32float",
2040 Sf::Rg16Uint => "rg16uint",
2041 Sf::Rg16Sint => "rg16sint",
2042 Sf::Rg16Float => "rg16float",
2043 Sf::Rgba8Unorm => "rgba8unorm",
2044 Sf::Rgba8Snorm => "rgba8snorm",
2045 Sf::Rgba8Uint => "rgba8uint",
2046 Sf::Rgba8Sint => "rgba8sint",
2047 Sf::Bgra8Unorm => "bgra8unorm",
2048 Sf::Rgb10a2Uint => "rgb10a2uint",
2049 Sf::Rgb10a2Unorm => "rgb10a2unorm",
2050 Sf::Rg11b10Ufloat => "rg11b10float",
2051 Sf::Rg32Uint => "rg32uint",
2052 Sf::Rg32Sint => "rg32sint",
2053 Sf::Rg32Float => "rg32float",
2054 Sf::Rgba16Uint => "rgba16uint",
2055 Sf::Rgba16Sint => "rgba16sint",
2056 Sf::Rgba16Float => "rgba16float",
2057 Sf::Rgba32Uint => "rgba32uint",
2058 Sf::Rgba32Sint => "rgba32sint",
2059 Sf::Rgba32Float => "rgba32float",
2060 Sf::R16Unorm => "r16unorm",
2061 Sf::R16Snorm => "r16snorm",
2062 Sf::Rg16Unorm => "rg16unorm",
2063 Sf::Rg16Snorm => "rg16snorm",
2064 Sf::Rgba16Unorm => "rgba16unorm",
2065 Sf::Rgba16Snorm => "rgba16snorm",
2066 }
2067}
2068
2069const fn interpolation_str(interpolation: crate::Interpolation) -> &'static str {
2071 use crate::Interpolation as I;
2072
2073 match interpolation {
2074 I::Perspective => "perspective",
2075 I::Linear => "linear",
2076 I::Flat => "flat",
2077 }
2078}
2079
2080const fn sampling_str(sampling: crate::Sampling) -> &'static str {
2082 use crate::Sampling as S;
2083
2084 match sampling {
2085 S::Center => "",
2086 S::Centroid => "centroid",
2087 S::Sample => "sample",
2088 S::First => "first",
2089 S::Either => "either",
2090 }
2091}
2092
2093const fn address_space_str(
2094 space: crate::AddressSpace,
2095) -> (Option<&'static str>, Option<&'static str>) {
2096 use crate::AddressSpace as As;
2097
2098 (
2099 Some(match space {
2100 As::Private => "private",
2101 As::Uniform => "uniform",
2102 As::Storage { access } => {
2103 if access.contains(crate::StorageAccess::STORE) {
2104 return (Some("storage"), Some("read_write"));
2105 } else {
2106 "storage"
2107 }
2108 }
2109 As::PushConstant => "push_constant",
2110 As::WorkGroup => "workgroup",
2111 As::Handle => return (None, None),
2112 As::Function => "function",
2113 }),
2114 None,
2115 )
2116}
2117
2118fn map_binding_to_attribute(binding: &crate::Binding) -> Vec<Attribute> {
2119 match *binding {
2120 crate::Binding::BuiltIn(built_in) => {
2121 if let crate::BuiltIn::Position { invariant: true } = built_in {
2122 vec![Attribute::BuiltIn(built_in), Attribute::Invariant]
2123 } else {
2124 vec![Attribute::BuiltIn(built_in)]
2125 }
2126 }
2127 crate::Binding::Location {
2128 location,
2129 interpolation,
2130 sampling,
2131 second_blend_source: false,
2132 } => vec![
2133 Attribute::Location(location),
2134 Attribute::Interpolate(interpolation, sampling),
2135 ],
2136 crate::Binding::Location {
2137 location,
2138 interpolation,
2139 sampling,
2140 second_blend_source: true,
2141 } => vec![
2142 Attribute::Location(location),
2143 Attribute::SecondBlendSource,
2144 Attribute::Interpolate(interpolation, sampling),
2145 ],
2146 }
2147}