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