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::ATOMIC) {
485 ",atomic"
486 } else if access
487 .contains(crate::StorageAccess::LOAD | crate::StorageAccess::STORE)
488 {
489 ",read_write"
490 } else if access.contains(crate::StorageAccess::LOAD) {
491 ",read"
492 } else {
493 ",write"
494 },
495 ),
496 };
497 write!(
498 self.out,
499 "texture_{class_str}{multisampled_str}{dim_str}{arrayed_str}"
500 )?;
501
502 if !format_str.is_empty() {
503 write!(self.out, "<{format_str}{storage_str}>")?;
504 }
505 }
506 TypeInner::Scalar(scalar) => {
507 write!(self.out, "{}", scalar_kind_str(scalar))?;
508 }
509 TypeInner::Atomic(scalar) => {
510 write!(self.out, "atomic<{}>", scalar_kind_str(scalar))?;
511 }
512 TypeInner::Array {
513 base,
514 size,
515 stride: _,
516 } => {
517 write!(self.out, "array<")?;
521 match size {
522 crate::ArraySize::Constant(len) => {
523 self.write_type(module, base)?;
524 write!(self.out, ", {len}")?;
525 }
526 crate::ArraySize::Pending(_) => {
527 unreachable!();
528 }
529 crate::ArraySize::Dynamic => {
530 self.write_type(module, base)?;
531 }
532 }
533 write!(self.out, ">")?;
534 }
535 TypeInner::BindingArray { base, size } => {
536 write!(self.out, "binding_array<")?;
538 match size {
539 crate::ArraySize::Constant(len) => {
540 self.write_type(module, base)?;
541 write!(self.out, ", {len}")?;
542 }
543 crate::ArraySize::Pending(_) => {
544 unreachable!();
545 }
546 crate::ArraySize::Dynamic => {
547 self.write_type(module, base)?;
548 }
549 }
550 write!(self.out, ">")?;
551 }
552 TypeInner::Matrix {
553 columns,
554 rows,
555 scalar,
556 } => {
557 write!(
558 self.out,
559 "mat{}x{}<{}>",
560 back::vector_size_str(columns),
561 back::vector_size_str(rows),
562 scalar_kind_str(scalar)
563 )?;
564 }
565 TypeInner::Pointer { base, space } => {
566 let (address, maybe_access) = address_space_str(space);
567 if let Some(space) = address {
571 write!(self.out, "ptr<{space}, ")?;
572 }
573 self.write_type(module, base)?;
574 if address.is_some() {
575 if let Some(access) = maybe_access {
576 write!(self.out, ", {access}")?;
577 }
578 write!(self.out, ">")?;
579 }
580 }
581 TypeInner::ValuePointer {
582 size: None,
583 scalar,
584 space,
585 } => {
586 let (address, maybe_access) = address_space_str(space);
587 if let Some(space) = address {
588 write!(self.out, "ptr<{}, {}", space, scalar_kind_str(scalar))?;
589 if let Some(access) = maybe_access {
590 write!(self.out, ", {access}")?;
591 }
592 write!(self.out, ">")?;
593 } else {
594 return Err(Error::Unimplemented(format!(
595 "ValuePointer to AddressSpace::Handle {inner:?}"
596 )));
597 }
598 }
599 TypeInner::ValuePointer {
600 size: Some(size),
601 scalar,
602 space,
603 } => {
604 let (address, maybe_access) = address_space_str(space);
605 if let Some(space) = address {
606 write!(
607 self.out,
608 "ptr<{}, vec{}<{}>",
609 space,
610 back::vector_size_str(size),
611 scalar_kind_str(scalar)
612 )?;
613 if let Some(access) = maybe_access {
614 write!(self.out, ", {access}")?;
615 }
616 write!(self.out, ">")?;
617 } else {
618 return Err(Error::Unimplemented(format!(
619 "ValuePointer to AddressSpace::Handle {inner:?}"
620 )));
621 }
622 write!(self.out, ">")?;
623 }
624 TypeInner::AccelerationStructure => write!(self.out, "acceleration_structure")?,
625 _ => {
626 return Err(Error::Unimplemented(format!("write_value_type {inner:?}")));
627 }
628 }
629
630 Ok(())
631 }
632 fn write_stmt(
637 &mut self,
638 module: &Module,
639 stmt: &crate::Statement,
640 func_ctx: &back::FunctionCtx<'_>,
641 level: back::Level,
642 ) -> BackendResult {
643 use crate::{Expression, Statement};
644
645 match *stmt {
646 Statement::Emit(ref range) => {
647 for handle in range.clone() {
648 let info = &func_ctx.info[handle];
649 let expr_name = if let Some(name) = func_ctx.named_expressions.get(&handle) {
650 Some(self.namer.call(name))
655 } else {
656 let expr = &func_ctx.expressions[handle];
657 let min_ref_count = expr.bake_ref_count();
658 let required_baking_expr = match *expr {
660 Expression::ImageLoad { .. }
661 | Expression::ImageQuery { .. }
662 | Expression::ImageSample { .. } => true,
663 _ => false,
664 };
665 if min_ref_count <= info.ref_count || required_baking_expr {
666 Some(Baked(handle).to_string())
667 } else {
668 None
669 }
670 };
671
672 if let Some(name) = expr_name {
673 write!(self.out, "{level}")?;
674 self.start_named_expr(module, handle, func_ctx, &name)?;
675 self.write_expr(module, handle, func_ctx)?;
676 self.named_expressions.insert(handle, name);
677 writeln!(self.out, ";")?;
678 }
679 }
680 }
681 Statement::If {
683 condition,
684 ref accept,
685 ref reject,
686 } => {
687 write!(self.out, "{level}")?;
688 write!(self.out, "if ")?;
689 self.write_expr(module, condition, func_ctx)?;
690 writeln!(self.out, " {{")?;
691
692 let l2 = level.next();
693 for sta in accept {
694 self.write_stmt(module, sta, func_ctx, l2)?;
696 }
697
698 if !reject.is_empty() {
701 writeln!(self.out, "{level}}} else {{")?;
702
703 for sta in reject {
704 self.write_stmt(module, sta, func_ctx, l2)?;
706 }
707 }
708
709 writeln!(self.out, "{level}}}")?
710 }
711 Statement::Return { value } => {
712 write!(self.out, "{level}")?;
713 write!(self.out, "return")?;
714 if let Some(return_value) = value {
715 write!(self.out, " ")?;
717 self.write_expr(module, return_value, func_ctx)?;
718 }
719 writeln!(self.out, ";")?;
720 }
721 Statement::Kill => {
723 write!(self.out, "{level}")?;
724 writeln!(self.out, "discard;")?
725 }
726 Statement::Store { pointer, value } => {
727 write!(self.out, "{level}")?;
728
729 let is_atomic_pointer = func_ctx
730 .resolve_type(pointer, &module.types)
731 .is_atomic_pointer(&module.types);
732
733 if is_atomic_pointer {
734 write!(self.out, "atomicStore(")?;
735 self.write_expr(module, pointer, func_ctx)?;
736 write!(self.out, ", ")?;
737 self.write_expr(module, value, func_ctx)?;
738 write!(self.out, ")")?;
739 } else {
740 self.write_expr_with_indirection(
741 module,
742 pointer,
743 func_ctx,
744 Indirection::Reference,
745 )?;
746 write!(self.out, " = ")?;
747 self.write_expr(module, value, func_ctx)?;
748 }
749 writeln!(self.out, ";")?
750 }
751 Statement::Call {
752 function,
753 ref arguments,
754 result,
755 } => {
756 write!(self.out, "{level}")?;
757 if let Some(expr) = result {
758 let name = Baked(expr).to_string();
759 self.start_named_expr(module, expr, func_ctx, &name)?;
760 self.named_expressions.insert(expr, name);
761 }
762 let func_name = &self.names[&NameKey::Function(function)];
763 write!(self.out, "{func_name}(")?;
764 for (index, &argument) in arguments.iter().enumerate() {
765 if index != 0 {
766 write!(self.out, ", ")?;
767 }
768 self.write_expr(module, argument, func_ctx)?;
769 }
770 writeln!(self.out, ");")?
771 }
772 Statement::Atomic {
773 pointer,
774 ref fun,
775 value,
776 result,
777 } => {
778 write!(self.out, "{level}")?;
779 if let Some(result) = result {
780 let res_name = Baked(result).to_string();
781 self.start_named_expr(module, result, func_ctx, &res_name)?;
782 self.named_expressions.insert(result, res_name);
783 }
784
785 let fun_str = fun.to_wgsl();
786 write!(self.out, "atomic{fun_str}(")?;
787 self.write_expr(module, pointer, func_ctx)?;
788 if let crate::AtomicFunction::Exchange { compare: Some(cmp) } = *fun {
789 write!(self.out, ", ")?;
790 self.write_expr(module, cmp, func_ctx)?;
791 }
792 write!(self.out, ", ")?;
793 self.write_expr(module, value, func_ctx)?;
794 writeln!(self.out, ");")?
795 }
796 Statement::ImageAtomic {
797 image,
798 coordinate,
799 array_index,
800 ref fun,
801 value,
802 } => {
803 write!(self.out, "{level}")?;
804 let fun_str = fun.to_wgsl();
805 write!(self.out, "textureAtomic{fun_str}(")?;
806 self.write_expr(module, image, func_ctx)?;
807 write!(self.out, ", ")?;
808 self.write_expr(module, coordinate, func_ctx)?;
809 if let Some(array_index_expr) = array_index {
810 write!(self.out, ", ")?;
811 self.write_expr(module, array_index_expr, func_ctx)?;
812 }
813 write!(self.out, ", ")?;
814 self.write_expr(module, value, func_ctx)?;
815 writeln!(self.out, ");")?;
816 }
817 Statement::WorkGroupUniformLoad { pointer, result } => {
818 write!(self.out, "{level}")?;
819 let res_name = Baked(result).to_string();
821 self.start_named_expr(module, result, func_ctx, &res_name)?;
822 self.named_expressions.insert(result, res_name);
823 write!(self.out, "workgroupUniformLoad(")?;
824 self.write_expr(module, pointer, func_ctx)?;
825 writeln!(self.out, ");")?;
826 }
827 Statement::ImageStore {
828 image,
829 coordinate,
830 array_index,
831 value,
832 } => {
833 write!(self.out, "{level}")?;
834 write!(self.out, "textureStore(")?;
835 self.write_expr(module, image, func_ctx)?;
836 write!(self.out, ", ")?;
837 self.write_expr(module, coordinate, func_ctx)?;
838 if let Some(array_index_expr) = array_index {
839 write!(self.out, ", ")?;
840 self.write_expr(module, array_index_expr, func_ctx)?;
841 }
842 write!(self.out, ", ")?;
843 self.write_expr(module, value, func_ctx)?;
844 writeln!(self.out, ");")?;
845 }
846 Statement::Block(ref block) => {
848 write!(self.out, "{level}")?;
849 writeln!(self.out, "{{")?;
850 for sta in block.iter() {
851 self.write_stmt(module, sta, func_ctx, level.next())?
853 }
854 writeln!(self.out, "{level}}}")?
855 }
856 Statement::Switch {
857 selector,
858 ref cases,
859 } => {
860 write!(self.out, "{level}")?;
862 write!(self.out, "switch ")?;
863 self.write_expr(module, selector, func_ctx)?;
864 writeln!(self.out, " {{")?;
865
866 let l2 = level.next();
867 let mut new_case = true;
868 for case in cases {
869 if case.fall_through && !case.body.is_empty() {
870 return Err(Error::Unimplemented(
872 "fall-through switch case block".into(),
873 ));
874 }
875
876 match case.value {
877 crate::SwitchValue::I32(value) => {
878 if new_case {
879 write!(self.out, "{l2}case ")?;
880 }
881 write!(self.out, "{value}")?;
882 }
883 crate::SwitchValue::U32(value) => {
884 if new_case {
885 write!(self.out, "{l2}case ")?;
886 }
887 write!(self.out, "{value}u")?;
888 }
889 crate::SwitchValue::Default => {
890 if new_case {
891 if case.fall_through {
892 write!(self.out, "{l2}case ")?;
893 } else {
894 write!(self.out, "{l2}")?;
895 }
896 }
897 write!(self.out, "default")?;
898 }
899 }
900
901 new_case = !case.fall_through;
902
903 if case.fall_through {
904 write!(self.out, ", ")?;
905 } else {
906 writeln!(self.out, ": {{")?;
907 }
908
909 for sta in case.body.iter() {
910 self.write_stmt(module, sta, func_ctx, l2.next())?;
911 }
912
913 if !case.fall_through {
914 writeln!(self.out, "{l2}}}")?;
915 }
916 }
917
918 writeln!(self.out, "{level}}}")?
919 }
920 Statement::Loop {
921 ref body,
922 ref continuing,
923 break_if,
924 } => {
925 write!(self.out, "{level}")?;
926 writeln!(self.out, "loop {{")?;
927
928 let l2 = level.next();
929 for sta in body.iter() {
930 self.write_stmt(module, sta, func_ctx, l2)?;
931 }
932
933 if !continuing.is_empty() || break_if.is_some() {
938 writeln!(self.out, "{l2}continuing {{")?;
939 for sta in continuing.iter() {
940 self.write_stmt(module, sta, func_ctx, l2.next())?;
941 }
942
943 if let Some(condition) = break_if {
946 write!(self.out, "{}break if ", l2.next())?;
948 self.write_expr(module, condition, func_ctx)?;
949 writeln!(self.out, ";")?;
951 }
952
953 writeln!(self.out, "{l2}}}")?;
954 }
955
956 writeln!(self.out, "{level}}}")?
957 }
958 Statement::Break => {
959 writeln!(self.out, "{level}break;")?;
960 }
961 Statement::Continue => {
962 writeln!(self.out, "{level}continue;")?;
963 }
964 Statement::Barrier(barrier) => {
965 if barrier.contains(crate::Barrier::STORAGE) {
966 writeln!(self.out, "{level}storageBarrier();")?;
967 }
968
969 if barrier.contains(crate::Barrier::WORK_GROUP) {
970 writeln!(self.out, "{level}workgroupBarrier();")?;
971 }
972
973 if barrier.contains(crate::Barrier::SUB_GROUP) {
974 writeln!(self.out, "{level}subgroupBarrier();")?;
975 }
976 }
977 Statement::RayQuery { .. } => unreachable!(),
978 Statement::SubgroupBallot { result, predicate } => {
979 write!(self.out, "{level}")?;
980 let res_name = Baked(result).to_string();
981 self.start_named_expr(module, result, func_ctx, &res_name)?;
982 self.named_expressions.insert(result, res_name);
983
984 write!(self.out, "subgroupBallot(")?;
985 if let Some(predicate) = predicate {
986 self.write_expr(module, predicate, func_ctx)?;
987 }
988 writeln!(self.out, ");")?;
989 }
990 Statement::SubgroupCollectiveOperation {
991 op,
992 collective_op,
993 argument,
994 result,
995 } => {
996 write!(self.out, "{level}")?;
997 let res_name = Baked(result).to_string();
998 self.start_named_expr(module, result, func_ctx, &res_name)?;
999 self.named_expressions.insert(result, res_name);
1000
1001 match (collective_op, op) {
1002 (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::All) => {
1003 write!(self.out, "subgroupAll(")?
1004 }
1005 (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::Any) => {
1006 write!(self.out, "subgroupAny(")?
1007 }
1008 (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::Add) => {
1009 write!(self.out, "subgroupAdd(")?
1010 }
1011 (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::Mul) => {
1012 write!(self.out, "subgroupMul(")?
1013 }
1014 (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::Max) => {
1015 write!(self.out, "subgroupMax(")?
1016 }
1017 (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::Min) => {
1018 write!(self.out, "subgroupMin(")?
1019 }
1020 (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::And) => {
1021 write!(self.out, "subgroupAnd(")?
1022 }
1023 (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::Or) => {
1024 write!(self.out, "subgroupOr(")?
1025 }
1026 (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::Xor) => {
1027 write!(self.out, "subgroupXor(")?
1028 }
1029 (crate::CollectiveOperation::ExclusiveScan, crate::SubgroupOperation::Add) => {
1030 write!(self.out, "subgroupExclusiveAdd(")?
1031 }
1032 (crate::CollectiveOperation::ExclusiveScan, crate::SubgroupOperation::Mul) => {
1033 write!(self.out, "subgroupExclusiveMul(")?
1034 }
1035 (crate::CollectiveOperation::InclusiveScan, crate::SubgroupOperation::Add) => {
1036 write!(self.out, "subgroupInclusiveAdd(")?
1037 }
1038 (crate::CollectiveOperation::InclusiveScan, crate::SubgroupOperation::Mul) => {
1039 write!(self.out, "subgroupInclusiveMul(")?
1040 }
1041 _ => unimplemented!(),
1042 }
1043 self.write_expr(module, argument, func_ctx)?;
1044 writeln!(self.out, ");")?;
1045 }
1046 Statement::SubgroupGather {
1047 mode,
1048 argument,
1049 result,
1050 } => {
1051 write!(self.out, "{level}")?;
1052 let res_name = Baked(result).to_string();
1053 self.start_named_expr(module, result, func_ctx, &res_name)?;
1054 self.named_expressions.insert(result, res_name);
1055
1056 match mode {
1057 crate::GatherMode::BroadcastFirst => {
1058 write!(self.out, "subgroupBroadcastFirst(")?;
1059 }
1060 crate::GatherMode::Broadcast(_) => {
1061 write!(self.out, "subgroupBroadcast(")?;
1062 }
1063 crate::GatherMode::Shuffle(_) => {
1064 write!(self.out, "subgroupShuffle(")?;
1065 }
1066 crate::GatherMode::ShuffleDown(_) => {
1067 write!(self.out, "subgroupShuffleDown(")?;
1068 }
1069 crate::GatherMode::ShuffleUp(_) => {
1070 write!(self.out, "subgroupShuffleUp(")?;
1071 }
1072 crate::GatherMode::ShuffleXor(_) => {
1073 write!(self.out, "subgroupShuffleXor(")?;
1074 }
1075 }
1076 self.write_expr(module, argument, func_ctx)?;
1077 match mode {
1078 crate::GatherMode::BroadcastFirst => {}
1079 crate::GatherMode::Broadcast(index)
1080 | crate::GatherMode::Shuffle(index)
1081 | crate::GatherMode::ShuffleDown(index)
1082 | crate::GatherMode::ShuffleUp(index)
1083 | crate::GatherMode::ShuffleXor(index) => {
1084 write!(self.out, ", ")?;
1085 self.write_expr(module, index, func_ctx)?;
1086 }
1087 }
1088 writeln!(self.out, ");")?;
1089 }
1090 }
1091
1092 Ok(())
1093 }
1094
1095 fn plain_form_indirection(
1118 &self,
1119 expr: Handle<crate::Expression>,
1120 module: &Module,
1121 func_ctx: &back::FunctionCtx<'_>,
1122 ) -> Indirection {
1123 use crate::Expression as Ex;
1124
1125 if self.named_expressions.contains_key(&expr) {
1129 return Indirection::Ordinary;
1130 }
1131
1132 match func_ctx.expressions[expr] {
1133 Ex::LocalVariable(_) => Indirection::Reference,
1134 Ex::GlobalVariable(handle) => {
1135 let global = &module.global_variables[handle];
1136 match global.space {
1137 crate::AddressSpace::Handle => Indirection::Ordinary,
1138 _ => Indirection::Reference,
1139 }
1140 }
1141 Ex::Access { base, .. } | Ex::AccessIndex { base, .. } => {
1142 let base_ty = func_ctx.resolve_type(base, &module.types);
1143 match *base_ty {
1144 TypeInner::Pointer { .. } | TypeInner::ValuePointer { .. } => {
1145 Indirection::Reference
1146 }
1147 _ => Indirection::Ordinary,
1148 }
1149 }
1150 _ => Indirection::Ordinary,
1151 }
1152 }
1153
1154 fn start_named_expr(
1155 &mut self,
1156 module: &Module,
1157 handle: Handle<crate::Expression>,
1158 func_ctx: &back::FunctionCtx,
1159 name: &str,
1160 ) -> BackendResult {
1161 let quantifier = if func_ctx.expr_kind_tracker.is_impl_const(handle) {
1163 "const"
1164 } else {
1165 "let"
1166 };
1167 write!(self.out, "{quantifier} {name}")?;
1169 if self.flags.contains(WriterFlags::EXPLICIT_TYPES) {
1170 write!(self.out, ": ")?;
1171 let ty = &func_ctx.info[handle].ty;
1172 match *ty {
1174 proc::TypeResolution::Handle(handle) => {
1175 self.write_type(module, handle)?;
1176 }
1177 proc::TypeResolution::Value(ref inner) => {
1178 self.write_value_type(module, inner)?;
1179 }
1180 }
1181 }
1182
1183 write!(self.out, " = ")?;
1184 Ok(())
1185 }
1186
1187 fn write_expr(
1191 &mut self,
1192 module: &Module,
1193 expr: Handle<crate::Expression>,
1194 func_ctx: &back::FunctionCtx<'_>,
1195 ) -> BackendResult {
1196 self.write_expr_with_indirection(module, expr, func_ctx, Indirection::Ordinary)
1197 }
1198
1199 fn write_expr_with_indirection(
1212 &mut self,
1213 module: &Module,
1214 expr: Handle<crate::Expression>,
1215 func_ctx: &back::FunctionCtx<'_>,
1216 requested: Indirection,
1217 ) -> BackendResult {
1218 let plain = self.plain_form_indirection(expr, module, func_ctx);
1221 match (requested, plain) {
1222 (Indirection::Ordinary, Indirection::Reference) => {
1223 write!(self.out, "(&")?;
1224 self.write_expr_plain_form(module, expr, func_ctx, plain)?;
1225 write!(self.out, ")")?;
1226 }
1227 (Indirection::Reference, Indirection::Ordinary) => {
1228 write!(self.out, "(*")?;
1229 self.write_expr_plain_form(module, expr, func_ctx, plain)?;
1230 write!(self.out, ")")?;
1231 }
1232 (_, _) => self.write_expr_plain_form(module, expr, func_ctx, plain)?,
1233 }
1234
1235 Ok(())
1236 }
1237
1238 fn write_const_expression(
1239 &mut self,
1240 module: &Module,
1241 expr: Handle<crate::Expression>,
1242 ) -> BackendResult {
1243 self.write_possibly_const_expression(
1244 module,
1245 expr,
1246 &module.global_expressions,
1247 |writer, expr| writer.write_const_expression(module, expr),
1248 )
1249 }
1250
1251 fn write_possibly_const_expression<E>(
1252 &mut self,
1253 module: &Module,
1254 expr: Handle<crate::Expression>,
1255 expressions: &crate::Arena<crate::Expression>,
1256 write_expression: E,
1257 ) -> BackendResult
1258 where
1259 E: Fn(&mut Self, Handle<crate::Expression>) -> BackendResult,
1260 {
1261 use crate::Expression;
1262
1263 match expressions[expr] {
1264 Expression::Literal(literal) => match literal {
1265 crate::Literal::F32(value) => write!(self.out, "{value}f")?,
1266 crate::Literal::U32(value) => write!(self.out, "{value}u")?,
1267 crate::Literal::I32(value) => {
1268 if value == i32::MIN {
1272 write!(self.out, "i32({value})")?;
1273 } else {
1274 write!(self.out, "{value}i")?;
1275 }
1276 }
1277 crate::Literal::Bool(value) => write!(self.out, "{value}")?,
1278 crate::Literal::F64(value) => write!(self.out, "{value:?}lf")?,
1279 crate::Literal::I64(value) => {
1280 if value == i64::MIN {
1284 write!(self.out, "i64({value})")?;
1285 } else {
1286 write!(self.out, "{value}li")?;
1287 }
1288 }
1289 crate::Literal::U64(value) => write!(self.out, "{value:?}lu")?,
1290 crate::Literal::AbstractInt(_) | crate::Literal::AbstractFloat(_) => {
1291 return Err(Error::Custom(
1292 "Abstract types should not appear in IR presented to backends".into(),
1293 ));
1294 }
1295 },
1296 Expression::Constant(handle) => {
1297 let constant = &module.constants[handle];
1298 if constant.name.is_some() {
1299 write!(self.out, "{}", self.names[&NameKey::Constant(handle)])?;
1300 } else {
1301 self.write_const_expression(module, constant.init)?;
1302 }
1303 }
1304 Expression::ZeroValue(ty) => {
1305 self.write_type(module, ty)?;
1306 write!(self.out, "()")?;
1307 }
1308 Expression::Compose { ty, ref components } => {
1309 self.write_type(module, ty)?;
1310 write!(self.out, "(")?;
1311 for (index, component) in components.iter().enumerate() {
1312 if index != 0 {
1313 write!(self.out, ", ")?;
1314 }
1315 write_expression(self, *component)?;
1316 }
1317 write!(self.out, ")")?
1318 }
1319 Expression::Splat { size, value } => {
1320 let size = back::vector_size_str(size);
1321 write!(self.out, "vec{size}(")?;
1322 write_expression(self, value)?;
1323 write!(self.out, ")")?;
1324 }
1325 _ => unreachable!(),
1326 }
1327
1328 Ok(())
1329 }
1330
1331 fn write_expr_plain_form(
1339 &mut self,
1340 module: &Module,
1341 expr: Handle<crate::Expression>,
1342 func_ctx: &back::FunctionCtx<'_>,
1343 indirection: Indirection,
1344 ) -> BackendResult {
1345 use crate::Expression;
1346
1347 if let Some(name) = self.named_expressions.get(&expr) {
1348 write!(self.out, "{name}")?;
1349 return Ok(());
1350 }
1351
1352 let expression = &func_ctx.expressions[expr];
1353
1354 match *expression {
1363 Expression::Literal(_)
1364 | Expression::Constant(_)
1365 | Expression::ZeroValue(_)
1366 | Expression::Compose { .. }
1367 | Expression::Splat { .. } => {
1368 self.write_possibly_const_expression(
1369 module,
1370 expr,
1371 func_ctx.expressions,
1372 |writer, expr| writer.write_expr(module, expr, func_ctx),
1373 )?;
1374 }
1375 Expression::Override(_) => unreachable!(),
1376 Expression::FunctionArgument(pos) => {
1377 let name_key = func_ctx.argument_key(pos);
1378 let name = &self.names[&name_key];
1379 write!(self.out, "{name}")?;
1380 }
1381 Expression::Binary { op, left, right } => {
1382 write!(self.out, "(")?;
1383 self.write_expr(module, left, func_ctx)?;
1384 write!(self.out, " {} ", back::binary_operation_str(op))?;
1385 self.write_expr(module, right, func_ctx)?;
1386 write!(self.out, ")")?;
1387 }
1388 Expression::Access { base, index } => {
1389 self.write_expr_with_indirection(module, base, func_ctx, indirection)?;
1390 write!(self.out, "[")?;
1391 self.write_expr(module, index, func_ctx)?;
1392 write!(self.out, "]")?
1393 }
1394 Expression::AccessIndex { base, index } => {
1395 let base_ty_res = &func_ctx.info[base].ty;
1396 let mut resolved = base_ty_res.inner_with(&module.types);
1397
1398 self.write_expr_with_indirection(module, base, func_ctx, indirection)?;
1399
1400 let base_ty_handle = match *resolved {
1401 TypeInner::Pointer { base, space: _ } => {
1402 resolved = &module.types[base].inner;
1403 Some(base)
1404 }
1405 _ => base_ty_res.handle(),
1406 };
1407
1408 match *resolved {
1409 TypeInner::Vector { .. } => {
1410 write!(self.out, ".{}", back::COMPONENTS[index as usize])?
1412 }
1413 TypeInner::Matrix { .. }
1414 | TypeInner::Array { .. }
1415 | TypeInner::BindingArray { .. }
1416 | TypeInner::ValuePointer { .. } => write!(self.out, "[{index}]")?,
1417 TypeInner::Struct { .. } => {
1418 let ty = base_ty_handle.unwrap();
1421
1422 write!(
1423 self.out,
1424 ".{}",
1425 &self.names[&NameKey::StructMember(ty, index)]
1426 )?
1427 }
1428 ref other => return Err(Error::Custom(format!("Cannot index {other:?}"))),
1429 }
1430 }
1431 Expression::ImageSample {
1432 image,
1433 sampler,
1434 gather: None,
1435 coordinate,
1436 array_index,
1437 offset,
1438 level,
1439 depth_ref,
1440 } => {
1441 use crate::SampleLevel as Sl;
1442
1443 let suffix_cmp = match depth_ref {
1444 Some(_) => "Compare",
1445 None => "",
1446 };
1447 let suffix_level = match level {
1448 Sl::Auto => "",
1449 Sl::Zero | Sl::Exact(_) => "Level",
1450 Sl::Bias(_) => "Bias",
1451 Sl::Gradient { .. } => "Grad",
1452 };
1453
1454 write!(self.out, "textureSample{suffix_cmp}{suffix_level}(")?;
1455 self.write_expr(module, image, func_ctx)?;
1456 write!(self.out, ", ")?;
1457 self.write_expr(module, sampler, func_ctx)?;
1458 write!(self.out, ", ")?;
1459 self.write_expr(module, coordinate, func_ctx)?;
1460
1461 if let Some(array_index) = array_index {
1462 write!(self.out, ", ")?;
1463 self.write_expr(module, array_index, func_ctx)?;
1464 }
1465
1466 if let Some(depth_ref) = depth_ref {
1467 write!(self.out, ", ")?;
1468 self.write_expr(module, depth_ref, func_ctx)?;
1469 }
1470
1471 match level {
1472 Sl::Auto => {}
1473 Sl::Zero => {
1474 if depth_ref.is_none() {
1476 write!(self.out, ", 0.0")?;
1477 }
1478 }
1479 Sl::Exact(expr) => {
1480 write!(self.out, ", ")?;
1481 self.write_expr(module, expr, func_ctx)?;
1482 }
1483 Sl::Bias(expr) => {
1484 write!(self.out, ", ")?;
1485 self.write_expr(module, expr, func_ctx)?;
1486 }
1487 Sl::Gradient { x, y } => {
1488 write!(self.out, ", ")?;
1489 self.write_expr(module, x, func_ctx)?;
1490 write!(self.out, ", ")?;
1491 self.write_expr(module, y, func_ctx)?;
1492 }
1493 }
1494
1495 if let Some(offset) = offset {
1496 write!(self.out, ", ")?;
1497 self.write_const_expression(module, offset)?;
1498 }
1499
1500 write!(self.out, ")")?;
1501 }
1502
1503 Expression::ImageSample {
1504 image,
1505 sampler,
1506 gather: Some(component),
1507 coordinate,
1508 array_index,
1509 offset,
1510 level: _,
1511 depth_ref,
1512 } => {
1513 let suffix_cmp = match depth_ref {
1514 Some(_) => "Compare",
1515 None => "",
1516 };
1517
1518 write!(self.out, "textureGather{suffix_cmp}(")?;
1519 match *func_ctx.resolve_type(image, &module.types) {
1520 TypeInner::Image {
1521 class: crate::ImageClass::Depth { multi: _ },
1522 ..
1523 } => {}
1524 _ => {
1525 write!(self.out, "{}, ", component as u8)?;
1526 }
1527 }
1528 self.write_expr(module, image, func_ctx)?;
1529 write!(self.out, ", ")?;
1530 self.write_expr(module, sampler, func_ctx)?;
1531 write!(self.out, ", ")?;
1532 self.write_expr(module, coordinate, func_ctx)?;
1533
1534 if let Some(array_index) = array_index {
1535 write!(self.out, ", ")?;
1536 self.write_expr(module, array_index, func_ctx)?;
1537 }
1538
1539 if let Some(depth_ref) = depth_ref {
1540 write!(self.out, ", ")?;
1541 self.write_expr(module, depth_ref, func_ctx)?;
1542 }
1543
1544 if let Some(offset) = offset {
1545 write!(self.out, ", ")?;
1546 self.write_const_expression(module, offset)?;
1547 }
1548
1549 write!(self.out, ")")?;
1550 }
1551 Expression::ImageQuery { image, query } => {
1552 use crate::ImageQuery as Iq;
1553
1554 let texture_function = match query {
1555 Iq::Size { .. } => "textureDimensions",
1556 Iq::NumLevels => "textureNumLevels",
1557 Iq::NumLayers => "textureNumLayers",
1558 Iq::NumSamples => "textureNumSamples",
1559 };
1560
1561 write!(self.out, "{texture_function}(")?;
1562 self.write_expr(module, image, func_ctx)?;
1563 if let Iq::Size { level: Some(level) } = query {
1564 write!(self.out, ", ")?;
1565 self.write_expr(module, level, func_ctx)?;
1566 };
1567 write!(self.out, ")")?;
1568 }
1569
1570 Expression::ImageLoad {
1571 image,
1572 coordinate,
1573 array_index,
1574 sample,
1575 level,
1576 } => {
1577 write!(self.out, "textureLoad(")?;
1578 self.write_expr(module, image, func_ctx)?;
1579 write!(self.out, ", ")?;
1580 self.write_expr(module, coordinate, func_ctx)?;
1581 if let Some(array_index) = array_index {
1582 write!(self.out, ", ")?;
1583 self.write_expr(module, array_index, func_ctx)?;
1584 }
1585 if let Some(index) = sample.or(level) {
1586 write!(self.out, ", ")?;
1587 self.write_expr(module, index, func_ctx)?;
1588 }
1589 write!(self.out, ")")?;
1590 }
1591 Expression::GlobalVariable(handle) => {
1592 let name = &self.names[&NameKey::GlobalVariable(handle)];
1593 write!(self.out, "{name}")?;
1594 }
1595
1596 Expression::As {
1597 expr,
1598 kind,
1599 convert,
1600 } => {
1601 let inner = func_ctx.resolve_type(expr, &module.types);
1602 match *inner {
1603 TypeInner::Matrix {
1604 columns,
1605 rows,
1606 scalar,
1607 } => {
1608 let scalar = crate::Scalar {
1609 kind,
1610 width: convert.unwrap_or(scalar.width),
1611 };
1612 let scalar_kind_str = scalar_kind_str(scalar);
1613 write!(
1614 self.out,
1615 "mat{}x{}<{}>",
1616 back::vector_size_str(columns),
1617 back::vector_size_str(rows),
1618 scalar_kind_str
1619 )?;
1620 }
1621 TypeInner::Vector {
1622 size,
1623 scalar: crate::Scalar { width, .. },
1624 } => {
1625 let scalar = crate::Scalar {
1626 kind,
1627 width: convert.unwrap_or(width),
1628 };
1629 let vector_size_str = back::vector_size_str(size);
1630 let scalar_kind_str = scalar_kind_str(scalar);
1631 if convert.is_some() {
1632 write!(self.out, "vec{vector_size_str}<{scalar_kind_str}>")?;
1633 } else {
1634 write!(self.out, "bitcast<vec{vector_size_str}<{scalar_kind_str}>>")?;
1635 }
1636 }
1637 TypeInner::Scalar(crate::Scalar { width, .. }) => {
1638 let scalar = crate::Scalar {
1639 kind,
1640 width: convert.unwrap_or(width),
1641 };
1642 let scalar_kind_str = scalar_kind_str(scalar);
1643 if convert.is_some() {
1644 write!(self.out, "{scalar_kind_str}")?
1645 } else {
1646 write!(self.out, "bitcast<{scalar_kind_str}>")?
1647 }
1648 }
1649 _ => {
1650 return Err(Error::Unimplemented(format!(
1651 "write_expr expression::as {inner:?}"
1652 )));
1653 }
1654 };
1655 write!(self.out, "(")?;
1656 self.write_expr(module, expr, func_ctx)?;
1657 write!(self.out, ")")?;
1658 }
1659 Expression::Load { pointer } => {
1660 let is_atomic_pointer = func_ctx
1661 .resolve_type(pointer, &module.types)
1662 .is_atomic_pointer(&module.types);
1663
1664 if is_atomic_pointer {
1665 write!(self.out, "atomicLoad(")?;
1666 self.write_expr(module, pointer, func_ctx)?;
1667 write!(self.out, ")")?;
1668 } else {
1669 self.write_expr_with_indirection(
1670 module,
1671 pointer,
1672 func_ctx,
1673 Indirection::Reference,
1674 )?;
1675 }
1676 }
1677 Expression::LocalVariable(handle) => {
1678 write!(self.out, "{}", self.names[&func_ctx.name_key(handle)])?
1679 }
1680 Expression::ArrayLength(expr) => {
1681 write!(self.out, "arrayLength(")?;
1682 self.write_expr(module, expr, func_ctx)?;
1683 write!(self.out, ")")?;
1684 }
1685
1686 Expression::Math {
1687 fun,
1688 arg,
1689 arg1,
1690 arg2,
1691 arg3,
1692 } => {
1693 use crate::MathFunction as Mf;
1694
1695 enum Function {
1696 Regular(&'static str),
1697 InversePolyfill(InversePolyfill),
1698 }
1699
1700 let function = match fun {
1701 Mf::Abs => Function::Regular("abs"),
1702 Mf::Min => Function::Regular("min"),
1703 Mf::Max => Function::Regular("max"),
1704 Mf::Clamp => Function::Regular("clamp"),
1705 Mf::Saturate => Function::Regular("saturate"),
1706 Mf::Cos => Function::Regular("cos"),
1708 Mf::Cosh => Function::Regular("cosh"),
1709 Mf::Sin => Function::Regular("sin"),
1710 Mf::Sinh => Function::Regular("sinh"),
1711 Mf::Tan => Function::Regular("tan"),
1712 Mf::Tanh => Function::Regular("tanh"),
1713 Mf::Acos => Function::Regular("acos"),
1714 Mf::Asin => Function::Regular("asin"),
1715 Mf::Atan => Function::Regular("atan"),
1716 Mf::Atan2 => Function::Regular("atan2"),
1717 Mf::Asinh => Function::Regular("asinh"),
1718 Mf::Acosh => Function::Regular("acosh"),
1719 Mf::Atanh => Function::Regular("atanh"),
1720 Mf::Radians => Function::Regular("radians"),
1721 Mf::Degrees => Function::Regular("degrees"),
1722 Mf::Ceil => Function::Regular("ceil"),
1724 Mf::Floor => Function::Regular("floor"),
1725 Mf::Round => Function::Regular("round"),
1726 Mf::Fract => Function::Regular("fract"),
1727 Mf::Trunc => Function::Regular("trunc"),
1728 Mf::Modf => Function::Regular("modf"),
1729 Mf::Frexp => Function::Regular("frexp"),
1730 Mf::Ldexp => Function::Regular("ldexp"),
1731 Mf::Exp => Function::Regular("exp"),
1733 Mf::Exp2 => Function::Regular("exp2"),
1734 Mf::Log => Function::Regular("log"),
1735 Mf::Log2 => Function::Regular("log2"),
1736 Mf::Pow => Function::Regular("pow"),
1737 Mf::Dot => Function::Regular("dot"),
1739 Mf::Cross => Function::Regular("cross"),
1740 Mf::Distance => Function::Regular("distance"),
1741 Mf::Length => Function::Regular("length"),
1742 Mf::Normalize => Function::Regular("normalize"),
1743 Mf::FaceForward => Function::Regular("faceForward"),
1744 Mf::Reflect => Function::Regular("reflect"),
1745 Mf::Refract => Function::Regular("refract"),
1746 Mf::Sign => Function::Regular("sign"),
1748 Mf::Fma => Function::Regular("fma"),
1749 Mf::Mix => Function::Regular("mix"),
1750 Mf::Step => Function::Regular("step"),
1751 Mf::SmoothStep => Function::Regular("smoothstep"),
1752 Mf::Sqrt => Function::Regular("sqrt"),
1753 Mf::InverseSqrt => Function::Regular("inverseSqrt"),
1754 Mf::Transpose => Function::Regular("transpose"),
1755 Mf::Determinant => Function::Regular("determinant"),
1756 Mf::QuantizeToF16 => Function::Regular("quantizeToF16"),
1757 Mf::CountTrailingZeros => Function::Regular("countTrailingZeros"),
1759 Mf::CountLeadingZeros => Function::Regular("countLeadingZeros"),
1760 Mf::CountOneBits => Function::Regular("countOneBits"),
1761 Mf::ReverseBits => Function::Regular("reverseBits"),
1762 Mf::ExtractBits => Function::Regular("extractBits"),
1763 Mf::InsertBits => Function::Regular("insertBits"),
1764 Mf::FirstTrailingBit => Function::Regular("firstTrailingBit"),
1765 Mf::FirstLeadingBit => Function::Regular("firstLeadingBit"),
1766 Mf::Pack4x8snorm => Function::Regular("pack4x8snorm"),
1768 Mf::Pack4x8unorm => Function::Regular("pack4x8unorm"),
1769 Mf::Pack2x16snorm => Function::Regular("pack2x16snorm"),
1770 Mf::Pack2x16unorm => Function::Regular("pack2x16unorm"),
1771 Mf::Pack2x16float => Function::Regular("pack2x16float"),
1772 Mf::Pack4xI8 => Function::Regular("pack4xI8"),
1773 Mf::Pack4xU8 => Function::Regular("pack4xU8"),
1774 Mf::Unpack4x8snorm => Function::Regular("unpack4x8snorm"),
1776 Mf::Unpack4x8unorm => Function::Regular("unpack4x8unorm"),
1777 Mf::Unpack2x16snorm => Function::Regular("unpack2x16snorm"),
1778 Mf::Unpack2x16unorm => Function::Regular("unpack2x16unorm"),
1779 Mf::Unpack2x16float => Function::Regular("unpack2x16float"),
1780 Mf::Unpack4xI8 => Function::Regular("unpack4xI8"),
1781 Mf::Unpack4xU8 => Function::Regular("unpack4xU8"),
1782 Mf::Inverse => {
1783 let typ = func_ctx.resolve_type(arg, &module.types);
1784
1785 let Some(overload) = InversePolyfill::find_overload(typ) else {
1786 return Err(Error::UnsupportedMathFunction(fun));
1787 };
1788
1789 Function::InversePolyfill(overload)
1790 }
1791 Mf::Outer => return Err(Error::UnsupportedMathFunction(fun)),
1792 };
1793
1794 match function {
1795 Function::Regular(fun_name) => {
1796 write!(self.out, "{fun_name}(")?;
1797 self.write_expr(module, arg, func_ctx)?;
1798 for arg in IntoIterator::into_iter([arg1, arg2, arg3]).flatten() {
1799 write!(self.out, ", ")?;
1800 self.write_expr(module, arg, func_ctx)?;
1801 }
1802 write!(self.out, ")")?
1803 }
1804 Function::InversePolyfill(inverse) => {
1805 write!(self.out, "{}(", inverse.fun_name)?;
1806 self.write_expr(module, arg, func_ctx)?;
1807 write!(self.out, ")")?;
1808 self.required_polyfills.insert(inverse);
1809 }
1810 }
1811 }
1812
1813 Expression::Swizzle {
1814 size,
1815 vector,
1816 pattern,
1817 } => {
1818 self.write_expr(module, vector, func_ctx)?;
1819 write!(self.out, ".")?;
1820 for &sc in pattern[..size as usize].iter() {
1821 self.out.write_char(back::COMPONENTS[sc as usize])?;
1822 }
1823 }
1824 Expression::Unary { op, expr } => {
1825 let unary = match op {
1826 crate::UnaryOperator::Negate => "-",
1827 crate::UnaryOperator::LogicalNot => "!",
1828 crate::UnaryOperator::BitwiseNot => "~",
1829 };
1830
1831 write!(self.out, "{unary}(")?;
1832 self.write_expr(module, expr, func_ctx)?;
1833
1834 write!(self.out, ")")?
1835 }
1836
1837 Expression::Select {
1838 condition,
1839 accept,
1840 reject,
1841 } => {
1842 write!(self.out, "select(")?;
1843 self.write_expr(module, reject, func_ctx)?;
1844 write!(self.out, ", ")?;
1845 self.write_expr(module, accept, func_ctx)?;
1846 write!(self.out, ", ")?;
1847 self.write_expr(module, condition, func_ctx)?;
1848 write!(self.out, ")")?
1849 }
1850 Expression::Derivative { axis, ctrl, expr } => {
1851 use crate::{DerivativeAxis as Axis, DerivativeControl as Ctrl};
1852 let op = match (axis, ctrl) {
1853 (Axis::X, Ctrl::Coarse) => "dpdxCoarse",
1854 (Axis::X, Ctrl::Fine) => "dpdxFine",
1855 (Axis::X, Ctrl::None) => "dpdx",
1856 (Axis::Y, Ctrl::Coarse) => "dpdyCoarse",
1857 (Axis::Y, Ctrl::Fine) => "dpdyFine",
1858 (Axis::Y, Ctrl::None) => "dpdy",
1859 (Axis::Width, Ctrl::Coarse) => "fwidthCoarse",
1860 (Axis::Width, Ctrl::Fine) => "fwidthFine",
1861 (Axis::Width, Ctrl::None) => "fwidth",
1862 };
1863 write!(self.out, "{op}(")?;
1864 self.write_expr(module, expr, func_ctx)?;
1865 write!(self.out, ")")?
1866 }
1867 Expression::Relational { fun, argument } => {
1868 use crate::RelationalFunction as Rf;
1869
1870 let fun_name = match fun {
1871 Rf::All => "all",
1872 Rf::Any => "any",
1873 _ => return Err(Error::UnsupportedRelationalFunction(fun)),
1874 };
1875 write!(self.out, "{fun_name}(")?;
1876
1877 self.write_expr(module, argument, func_ctx)?;
1878
1879 write!(self.out, ")")?
1880 }
1881 Expression::RayQueryGetIntersection { .. } => unreachable!(),
1883 Expression::CallResult(_)
1885 | Expression::AtomicResult { .. }
1886 | Expression::RayQueryProceedResult
1887 | Expression::SubgroupBallotResult
1888 | Expression::SubgroupOperationResult { .. }
1889 | Expression::WorkGroupUniformLoadResult { .. } => {}
1890 }
1891
1892 Ok(())
1893 }
1894
1895 fn write_global(
1899 &mut self,
1900 module: &Module,
1901 global: &crate::GlobalVariable,
1902 handle: Handle<crate::GlobalVariable>,
1903 ) -> BackendResult {
1904 if let Some(ref binding) = global.binding {
1906 self.write_attributes(&[
1907 Attribute::Group(binding.group),
1908 Attribute::Binding(binding.binding),
1909 ])?;
1910 writeln!(self.out)?;
1911 }
1912
1913 write!(self.out, "var")?;
1915 let (address, maybe_access) = address_space_str(global.space);
1916 if let Some(space) = address {
1917 write!(self.out, "<{space}")?;
1918 if let Some(access) = maybe_access {
1919 write!(self.out, ", {access}")?;
1920 }
1921 write!(self.out, ">")?;
1922 }
1923 write!(
1924 self.out,
1925 " {}: ",
1926 &self.names[&NameKey::GlobalVariable(handle)]
1927 )?;
1928
1929 self.write_type(module, global.ty)?;
1931
1932 if let Some(init) = global.init {
1934 write!(self.out, " = ")?;
1935 self.write_const_expression(module, init)?;
1936 }
1937
1938 writeln!(self.out, ";")?;
1940
1941 Ok(())
1942 }
1943
1944 fn write_global_constant(
1949 &mut self,
1950 module: &Module,
1951 handle: Handle<crate::Constant>,
1952 ) -> BackendResult {
1953 let name = &self.names[&NameKey::Constant(handle)];
1954 write!(self.out, "const {name}: ")?;
1956 self.write_type(module, module.constants[handle].ty)?;
1957 write!(self.out, " = ")?;
1958 let init = module.constants[handle].init;
1959 self.write_const_expression(module, init)?;
1960 writeln!(self.out, ";")?;
1961
1962 Ok(())
1963 }
1964
1965 #[allow(clippy::missing_const_for_fn)]
1967 pub fn finish(self) -> W {
1968 self.out
1969 }
1970}
1971
1972fn builtin_str(built_in: crate::BuiltIn) -> Result<&'static str, Error> {
1973 use crate::BuiltIn as Bi;
1974
1975 Ok(match built_in {
1976 Bi::VertexIndex => "vertex_index",
1977 Bi::InstanceIndex => "instance_index",
1978 Bi::Position { .. } => "position",
1979 Bi::FrontFacing => "front_facing",
1980 Bi::FragDepth => "frag_depth",
1981 Bi::LocalInvocationId => "local_invocation_id",
1982 Bi::LocalInvocationIndex => "local_invocation_index",
1983 Bi::GlobalInvocationId => "global_invocation_id",
1984 Bi::WorkGroupId => "workgroup_id",
1985 Bi::NumWorkGroups => "num_workgroups",
1986 Bi::SampleIndex => "sample_index",
1987 Bi::SampleMask => "sample_mask",
1988 Bi::PrimitiveIndex => "primitive_index",
1989 Bi::ViewIndex => "view_index",
1990 Bi::NumSubgroups => "num_subgroups",
1991 Bi::SubgroupId => "subgroup_id",
1992 Bi::SubgroupSize => "subgroup_size",
1993 Bi::SubgroupInvocationId => "subgroup_invocation_id",
1994 Bi::BaseInstance
1995 | Bi::BaseVertex
1996 | Bi::ClipDistance
1997 | Bi::CullDistance
1998 | Bi::PointSize
1999 | Bi::PointCoord
2000 | Bi::WorkGroupSize
2001 | Bi::DrawID => return Err(Error::Custom(format!("Unsupported builtin {built_in:?}"))),
2002 })
2003}
2004
2005const fn image_dimension_str(dim: crate::ImageDimension) -> &'static str {
2006 use crate::ImageDimension as IDim;
2007
2008 match dim {
2009 IDim::D1 => "1d",
2010 IDim::D2 => "2d",
2011 IDim::D3 => "3d",
2012 IDim::Cube => "cube",
2013 }
2014}
2015
2016const fn scalar_kind_str(scalar: crate::Scalar) -> &'static str {
2017 use crate::Scalar;
2018 use crate::ScalarKind as Sk;
2019
2020 match scalar {
2021 Scalar {
2022 kind: Sk::Float,
2023 width: 8,
2024 } => "f64",
2025 Scalar {
2026 kind: Sk::Float,
2027 width: 4,
2028 } => "f32",
2029 Scalar {
2030 kind: Sk::Sint,
2031 width: 4,
2032 } => "i32",
2033 Scalar {
2034 kind: Sk::Uint,
2035 width: 4,
2036 } => "u32",
2037 Scalar {
2038 kind: Sk::Sint,
2039 width: 8,
2040 } => "i64",
2041 Scalar {
2042 kind: Sk::Uint,
2043 width: 8,
2044 } => "u64",
2045 Scalar {
2046 kind: Sk::Bool,
2047 width: 1,
2048 } => "bool",
2049 _ => unreachable!(),
2050 }
2051}
2052
2053const fn storage_format_str(format: crate::StorageFormat) -> &'static str {
2054 use crate::StorageFormat as Sf;
2055
2056 match format {
2057 Sf::R8Unorm => "r8unorm",
2058 Sf::R8Snorm => "r8snorm",
2059 Sf::R8Uint => "r8uint",
2060 Sf::R8Sint => "r8sint",
2061 Sf::R16Uint => "r16uint",
2062 Sf::R16Sint => "r16sint",
2063 Sf::R16Float => "r16float",
2064 Sf::Rg8Unorm => "rg8unorm",
2065 Sf::Rg8Snorm => "rg8snorm",
2066 Sf::Rg8Uint => "rg8uint",
2067 Sf::Rg8Sint => "rg8sint",
2068 Sf::R32Uint => "r32uint",
2069 Sf::R32Sint => "r32sint",
2070 Sf::R32Float => "r32float",
2071 Sf::Rg16Uint => "rg16uint",
2072 Sf::Rg16Sint => "rg16sint",
2073 Sf::Rg16Float => "rg16float",
2074 Sf::Rgba8Unorm => "rgba8unorm",
2075 Sf::Rgba8Snorm => "rgba8snorm",
2076 Sf::Rgba8Uint => "rgba8uint",
2077 Sf::Rgba8Sint => "rgba8sint",
2078 Sf::Bgra8Unorm => "bgra8unorm",
2079 Sf::Rgb10a2Uint => "rgb10a2uint",
2080 Sf::Rgb10a2Unorm => "rgb10a2unorm",
2081 Sf::Rg11b10Ufloat => "rg11b10float",
2082 Sf::R64Uint => "r64uint",
2083 Sf::Rg32Uint => "rg32uint",
2084 Sf::Rg32Sint => "rg32sint",
2085 Sf::Rg32Float => "rg32float",
2086 Sf::Rgba16Uint => "rgba16uint",
2087 Sf::Rgba16Sint => "rgba16sint",
2088 Sf::Rgba16Float => "rgba16float",
2089 Sf::Rgba32Uint => "rgba32uint",
2090 Sf::Rgba32Sint => "rgba32sint",
2091 Sf::Rgba32Float => "rgba32float",
2092 Sf::R16Unorm => "r16unorm",
2093 Sf::R16Snorm => "r16snorm",
2094 Sf::Rg16Unorm => "rg16unorm",
2095 Sf::Rg16Snorm => "rg16snorm",
2096 Sf::Rgba16Unorm => "rgba16unorm",
2097 Sf::Rgba16Snorm => "rgba16snorm",
2098 }
2099}
2100
2101const fn interpolation_str(interpolation: crate::Interpolation) -> &'static str {
2103 use crate::Interpolation as I;
2104
2105 match interpolation {
2106 I::Perspective => "perspective",
2107 I::Linear => "linear",
2108 I::Flat => "flat",
2109 }
2110}
2111
2112const fn sampling_str(sampling: crate::Sampling) -> &'static str {
2114 use crate::Sampling as S;
2115
2116 match sampling {
2117 S::Center => "",
2118 S::Centroid => "centroid",
2119 S::Sample => "sample",
2120 S::First => "first",
2121 S::Either => "either",
2122 }
2123}
2124
2125const fn address_space_str(
2126 space: crate::AddressSpace,
2127) -> (Option<&'static str>, Option<&'static str>) {
2128 use crate::AddressSpace as As;
2129
2130 (
2131 Some(match space {
2132 As::Private => "private",
2133 As::Uniform => "uniform",
2134 As::Storage { access } => {
2135 if access.contains(crate::StorageAccess::ATOMIC) {
2136 return (Some("storage"), Some("atomic"));
2137 } else if access.contains(crate::StorageAccess::STORE) {
2138 return (Some("storage"), Some("read_write"));
2139 } else {
2140 "storage"
2141 }
2142 }
2143 As::PushConstant => "push_constant",
2144 As::WorkGroup => "workgroup",
2145 As::Handle => return (None, None),
2146 As::Function => "function",
2147 }),
2148 None,
2149 )
2150}
2151
2152fn map_binding_to_attribute(binding: &crate::Binding) -> Vec<Attribute> {
2153 match *binding {
2154 crate::Binding::BuiltIn(built_in) => {
2155 if let crate::BuiltIn::Position { invariant: true } = built_in {
2156 vec![Attribute::BuiltIn(built_in), Attribute::Invariant]
2157 } else {
2158 vec![Attribute::BuiltIn(built_in)]
2159 }
2160 }
2161 crate::Binding::Location {
2162 location,
2163 interpolation,
2164 sampling,
2165 second_blend_source: false,
2166 } => vec![
2167 Attribute::Location(location),
2168 Attribute::Interpolate(interpolation, sampling),
2169 ],
2170 crate::Binding::Location {
2171 location,
2172 interpolation,
2173 sampling,
2174 second_blend_source: true,
2175 } => vec![
2176 Attribute::Location(location),
2177 Attribute::SecondBlendSource,
2178 Attribute::Interpolate(interpolation, sampling),
2179 ],
2180 }
2181}