1use alloc::{
2 format,
3 string::{String, ToString},
4 vec,
5 vec::Vec,
6};
7use core::fmt::Write;
8
9use super::Error;
10use super::ToWgslIfImplemented as _;
11use crate::{back::wgsl::polyfill::InversePolyfill, common::wgsl::TypeContext};
12use crate::{
13 back::{self, Baked},
14 common::{
15 self,
16 wgsl::{address_space_str, ToWgsl, TryToWgsl},
17 },
18 proc::{self, NameKey},
19 valid, Handle, Module, ShaderStage, TypeInner,
20};
21
22type BackendResult = Result<(), Error>;
24
25enum Attribute {
27 Binding(u32),
28 BuiltIn(crate::BuiltIn),
29 Group(u32),
30 Invariant,
31 Interpolate(Option<crate::Interpolation>, Option<crate::Sampling>),
32 Location(u32),
33 BlendSrc(u32),
34 Stage(ShaderStage),
35 WorkGroupSize([u32; 3]),
36}
37
38#[derive(Clone, Copy, Debug)]
51enum Indirection {
52 Ordinary,
58
59 Reference,
65}
66
67bitflags::bitflags! {
68 #[cfg_attr(feature = "serialize", derive(serde::Serialize))]
69 #[cfg_attr(feature = "deserialize", derive(serde::Deserialize))]
70 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
71 pub struct WriterFlags: u32 {
72 const EXPLICIT_TYPES = 0x1;
74 }
75}
76
77pub struct Writer<W> {
78 out: W,
79 flags: WriterFlags,
80 names: crate::FastHashMap<NameKey, String>,
81 namer: proc::Namer,
82 named_expressions: crate::NamedExpressions,
83 required_polyfills: crate::FastIndexSet<InversePolyfill>,
84}
85
86impl<W: Write> Writer<W> {
87 pub fn new(out: W, flags: WriterFlags) -> Self {
88 Writer {
89 out,
90 flags,
91 names: crate::FastHashMap::default(),
92 namer: proc::Namer::default(),
93 named_expressions: crate::NamedExpressions::default(),
94 required_polyfills: crate::FastIndexSet::default(),
95 }
96 }
97
98 fn reset(&mut self, module: &Module) {
99 self.names.clear();
100 self.namer.reset(
101 module,
102 &crate::keywords::wgsl::RESERVED_SET,
103 proc::CaseInsensitiveKeywordSet::empty(),
105 &["__", "_naga"],
106 &mut self.names,
107 );
108 self.named_expressions.clear();
109 self.required_polyfills.clear();
110 }
111
112 fn is_builtin_wgsl_struct(&self, module: &Module, ty: Handle<crate::Type>) -> bool {
128 module
129 .special_types
130 .predeclared_types
131 .values()
132 .any(|t| *t == ty)
133 || Some(ty) == module.special_types.external_texture_params
134 || Some(ty) == module.special_types.external_texture_transfer_function
135 }
136
137 pub fn write(&mut self, module: &Module, info: &valid::ModuleInfo) -> BackendResult {
138 if !module.overrides.is_empty() {
139 return Err(Error::Unimplemented(
140 "Pipeline constants are not yet supported for this back-end".to_string(),
141 ));
142 }
143
144 self.reset(module);
145
146 self.write_enable_declarations(module)?;
148
149 for (handle, ty) in module.types.iter() {
151 if let TypeInner::Struct { ref members, .. } = ty.inner {
152 {
153 if !self.is_builtin_wgsl_struct(module, handle) {
154 self.write_struct(module, handle, members)?;
155 writeln!(self.out)?;
156 }
157 }
158 }
159 }
160
161 let mut constants = module
163 .constants
164 .iter()
165 .filter(|&(_, c)| c.name.is_some())
166 .peekable();
167 while let Some((handle, _)) = constants.next() {
168 self.write_global_constant(module, handle)?;
169 if constants.peek().is_none() {
171 writeln!(self.out)?;
172 }
173 }
174
175 for (ty, global) in module.global_variables.iter() {
177 self.write_global(module, global, ty)?;
178 }
179
180 if !module.global_variables.is_empty() {
181 writeln!(self.out)?;
183 }
184
185 for (handle, function) in module.functions.iter() {
187 let fun_info = &info[handle];
188
189 let func_ctx = back::FunctionCtx {
190 ty: back::FunctionType::Function(handle),
191 info: fun_info,
192 expressions: &function.expressions,
193 named_expressions: &function.named_expressions,
194 };
195
196 self.write_function(module, function, &func_ctx)?;
198
199 writeln!(self.out)?;
200 }
201
202 for (index, ep) in module.entry_points.iter().enumerate() {
204 let attributes = match ep.stage {
205 ShaderStage::Vertex | ShaderStage::Fragment => vec![Attribute::Stage(ep.stage)],
206 ShaderStage::Compute => vec![
207 Attribute::Stage(ShaderStage::Compute),
208 Attribute::WorkGroupSize(ep.workgroup_size),
209 ],
210 ShaderStage::Mesh | ShaderStage::Task => unreachable!(),
211 };
212
213 self.write_attributes(&attributes)?;
214 writeln!(self.out)?;
216
217 let func_ctx = back::FunctionCtx {
218 ty: back::FunctionType::EntryPoint(index as u16),
219 info: info.get_entry_point(index),
220 expressions: &ep.function.expressions,
221 named_expressions: &ep.function.named_expressions,
222 };
223 self.write_function(module, &ep.function, &func_ctx)?;
224
225 if index < module.entry_points.len() - 1 {
226 writeln!(self.out)?;
227 }
228 }
229
230 for polyfill in &self.required_polyfills {
232 writeln!(self.out)?;
233 write!(self.out, "{}", polyfill.source)?;
234 writeln!(self.out)?;
235 }
236
237 Ok(())
238 }
239
240 fn write_enable_declarations(&mut self, module: &Module) -> BackendResult {
243 let mut needs_f16 = false;
244 let mut needs_dual_source_blending = false;
245 let mut needs_clip_distances = false;
246
247 for (_, ty) in module.types.iter() {
249 match ty.inner {
250 TypeInner::Scalar(scalar)
251 | TypeInner::Vector { scalar, .. }
252 | TypeInner::Matrix { scalar, .. } => {
253 needs_f16 |= scalar == crate::Scalar::F16;
254 }
255 TypeInner::Struct { ref members, .. } => {
256 for binding in members.iter().filter_map(|m| m.binding.as_ref()) {
257 match *binding {
258 crate::Binding::Location {
259 blend_src: Some(_), ..
260 } => {
261 needs_dual_source_blending = true;
262 }
263 crate::Binding::BuiltIn(crate::BuiltIn::ClipDistance) => {
264 needs_clip_distances = true;
265 }
266 _ => {}
267 }
268 }
269 }
270 _ => {}
271 }
272 }
273
274 let mut any_written = false;
276 if needs_f16 {
277 writeln!(self.out, "enable f16;")?;
278 any_written = true;
279 }
280 if needs_dual_source_blending {
281 writeln!(self.out, "enable dual_source_blending;")?;
282 any_written = true;
283 }
284 if needs_clip_distances {
285 writeln!(self.out, "enable clip_distances;")?;
286 any_written = true;
287 }
288 if any_written {
289 writeln!(self.out)?;
291 }
292
293 Ok(())
294 }
295
296 fn write_function(
302 &mut self,
303 module: &Module,
304 func: &crate::Function,
305 func_ctx: &back::FunctionCtx<'_>,
306 ) -> BackendResult {
307 let func_name = match func_ctx.ty {
308 back::FunctionType::EntryPoint(index) => &self.names[&NameKey::EntryPoint(index)],
309 back::FunctionType::Function(handle) => &self.names[&NameKey::Function(handle)],
310 };
311
312 write!(self.out, "fn {func_name}(")?;
314
315 for (index, arg) in func.arguments.iter().enumerate() {
317 if let Some(ref binding) = arg.binding {
319 self.write_attributes(&map_binding_to_attribute(binding))?;
320 }
321 let argument_name = &self.names[&func_ctx.argument_key(index as u32)];
323
324 write!(self.out, "{argument_name}: ")?;
325 self.write_type(module, arg.ty)?;
327 if index < func.arguments.len() - 1 {
328 write!(self.out, ", ")?;
330 }
331 }
332
333 write!(self.out, ")")?;
334
335 if let Some(ref result) = func.result {
337 write!(self.out, " -> ")?;
338 if let Some(ref binding) = result.binding {
339 self.write_attributes(&map_binding_to_attribute(binding))?;
340 }
341 self.write_type(module, result.ty)?;
342 }
343
344 write!(self.out, " {{")?;
345 writeln!(self.out)?;
346
347 for (handle, local) in func.local_variables.iter() {
349 write!(self.out, "{}", back::INDENT)?;
351
352 write!(self.out, "var {}: ", self.names[&func_ctx.name_key(handle)])?;
355
356 self.write_type(module, local.ty)?;
358
359 if let Some(init) = local.init {
361 write!(self.out, " = ")?;
364
365 self.write_expr(module, init, func_ctx)?;
368 }
369
370 writeln!(self.out, ";")?
372 }
373
374 if !func.local_variables.is_empty() {
375 writeln!(self.out)?;
376 }
377
378 for sta in func.body.iter() {
380 self.write_stmt(module, sta, func_ctx, back::Level(1))?;
382 }
383
384 writeln!(self.out, "}}")?;
385
386 self.named_expressions.clear();
387
388 Ok(())
389 }
390
391 fn write_attributes(&mut self, attributes: &[Attribute]) -> BackendResult {
393 for attribute in attributes {
394 match *attribute {
395 Attribute::Location(id) => write!(self.out, "@location({id}) ")?,
396 Attribute::BlendSrc(blend_src) => write!(self.out, "@blend_src({blend_src}) ")?,
397 Attribute::BuiltIn(builtin_attrib) => {
398 let builtin = builtin_attrib.to_wgsl_if_implemented()?;
399 write!(self.out, "@builtin({builtin}) ")?;
400 }
401 Attribute::Stage(shader_stage) => {
402 let stage_str = match shader_stage {
403 ShaderStage::Vertex => "vertex",
404 ShaderStage::Fragment => "fragment",
405 ShaderStage::Compute => "compute",
406 ShaderStage::Task | ShaderStage::Mesh => unreachable!(),
407 };
408 write!(self.out, "@{stage_str} ")?;
409 }
410 Attribute::WorkGroupSize(size) => {
411 write!(
412 self.out,
413 "@workgroup_size({}, {}, {}) ",
414 size[0], size[1], size[2]
415 )?;
416 }
417 Attribute::Binding(id) => write!(self.out, "@binding({id}) ")?,
418 Attribute::Group(id) => write!(self.out, "@group({id}) ")?,
419 Attribute::Invariant => write!(self.out, "@invariant ")?,
420 Attribute::Interpolate(interpolation, sampling) => {
421 if sampling.is_some() && sampling != Some(crate::Sampling::Center) {
422 let interpolation = interpolation
423 .unwrap_or(crate::Interpolation::Perspective)
424 .to_wgsl();
425 let sampling = sampling.unwrap_or(crate::Sampling::Center).to_wgsl();
426 write!(self.out, "@interpolate({interpolation}, {sampling}) ")?;
427 } else if interpolation.is_some()
428 && interpolation != Some(crate::Interpolation::Perspective)
429 {
430 let interpolation = interpolation
431 .unwrap_or(crate::Interpolation::Perspective)
432 .to_wgsl();
433 write!(self.out, "@interpolate({interpolation}) ")?;
434 }
435 }
436 };
437 }
438 Ok(())
439 }
440
441 fn write_struct(
452 &mut self,
453 module: &Module,
454 handle: Handle<crate::Type>,
455 members: &[crate::StructMember],
456 ) -> BackendResult {
457 write!(self.out, "struct {}", self.names[&NameKey::Type(handle)])?;
458 write!(self.out, " {{")?;
459 writeln!(self.out)?;
460 for (index, member) in members.iter().enumerate() {
461 write!(self.out, "{}", back::INDENT)?;
463 if let Some(ref binding) = member.binding {
464 self.write_attributes(&map_binding_to_attribute(binding))?;
465 }
466 let member_name = &self.names[&NameKey::StructMember(handle, index as u32)];
468 write!(self.out, "{member_name}: ")?;
469 self.write_type(module, member.ty)?;
470 write!(self.out, ",")?;
471 writeln!(self.out)?;
472 }
473
474 writeln!(self.out, "}}")?;
475
476 Ok(())
477 }
478
479 fn write_type(&mut self, module: &Module, ty: Handle<crate::Type>) -> BackendResult {
480 let type_context = WriterTypeContext {
484 module,
485 names: &self.names,
486 };
487 type_context.write_type(ty, &mut self.out)?;
488
489 Ok(())
490 }
491
492 fn write_type_resolution(
493 &mut self,
494 module: &Module,
495 resolution: &proc::TypeResolution,
496 ) -> BackendResult {
497 let type_context = WriterTypeContext {
501 module,
502 names: &self.names,
503 };
504 type_context.write_type_resolution(resolution, &mut self.out)?;
505
506 Ok(())
507 }
508
509 fn write_stmt(
514 &mut self,
515 module: &Module,
516 stmt: &crate::Statement,
517 func_ctx: &back::FunctionCtx<'_>,
518 level: back::Level,
519 ) -> BackendResult {
520 use crate::{Expression, Statement};
521
522 match *stmt {
523 Statement::Emit(ref range) => {
524 for handle in range.clone() {
525 let info = &func_ctx.info[handle];
526 let expr_name = if let Some(name) = func_ctx.named_expressions.get(&handle) {
527 Some(self.namer.call(name))
532 } else {
533 let expr = &func_ctx.expressions[handle];
534 let min_ref_count = expr.bake_ref_count();
535 let required_baking_expr = match *expr {
537 Expression::ImageLoad { .. }
538 | Expression::ImageQuery { .. }
539 | Expression::ImageSample { .. } => true,
540 _ => false,
541 };
542 if min_ref_count <= info.ref_count || required_baking_expr {
543 Some(Baked(handle).to_string())
544 } else {
545 None
546 }
547 };
548
549 if let Some(name) = expr_name {
550 write!(self.out, "{level}")?;
551 self.start_named_expr(module, handle, func_ctx, &name)?;
552 self.write_expr(module, handle, func_ctx)?;
553 self.named_expressions.insert(handle, name);
554 writeln!(self.out, ";")?;
555 }
556 }
557 }
558 Statement::If {
560 condition,
561 ref accept,
562 ref reject,
563 } => {
564 write!(self.out, "{level}")?;
565 write!(self.out, "if ")?;
566 self.write_expr(module, condition, func_ctx)?;
567 writeln!(self.out, " {{")?;
568
569 let l2 = level.next();
570 for sta in accept {
571 self.write_stmt(module, sta, func_ctx, l2)?;
573 }
574
575 if !reject.is_empty() {
578 writeln!(self.out, "{level}}} else {{")?;
579
580 for sta in reject {
581 self.write_stmt(module, sta, func_ctx, l2)?;
583 }
584 }
585
586 writeln!(self.out, "{level}}}")?
587 }
588 Statement::Return { value } => {
589 write!(self.out, "{level}")?;
590 write!(self.out, "return")?;
591 if let Some(return_value) = value {
592 write!(self.out, " ")?;
594 self.write_expr(module, return_value, func_ctx)?;
595 }
596 writeln!(self.out, ";")?;
597 }
598 Statement::Kill => {
600 write!(self.out, "{level}")?;
601 writeln!(self.out, "discard;")?
602 }
603 Statement::Store { pointer, value } => {
604 write!(self.out, "{level}")?;
605
606 let is_atomic_pointer = func_ctx
607 .resolve_type(pointer, &module.types)
608 .is_atomic_pointer(&module.types);
609
610 if is_atomic_pointer {
611 write!(self.out, "atomicStore(")?;
612 self.write_expr(module, pointer, func_ctx)?;
613 write!(self.out, ", ")?;
614 self.write_expr(module, value, func_ctx)?;
615 write!(self.out, ")")?;
616 } else {
617 self.write_expr_with_indirection(
618 module,
619 pointer,
620 func_ctx,
621 Indirection::Reference,
622 )?;
623 write!(self.out, " = ")?;
624 self.write_expr(module, value, func_ctx)?;
625 }
626 writeln!(self.out, ";")?
627 }
628 Statement::Call {
629 function,
630 ref arguments,
631 result,
632 } => {
633 write!(self.out, "{level}")?;
634 if let Some(expr) = result {
635 let name = Baked(expr).to_string();
636 self.start_named_expr(module, expr, func_ctx, &name)?;
637 self.named_expressions.insert(expr, name);
638 }
639 let func_name = &self.names[&NameKey::Function(function)];
640 write!(self.out, "{func_name}(")?;
641 for (index, &argument) in arguments.iter().enumerate() {
642 if index != 0 {
643 write!(self.out, ", ")?;
644 }
645 self.write_expr(module, argument, func_ctx)?;
646 }
647 writeln!(self.out, ");")?
648 }
649 Statement::Atomic {
650 pointer,
651 ref fun,
652 value,
653 result,
654 } => {
655 write!(self.out, "{level}")?;
656 if let Some(result) = result {
657 let res_name = Baked(result).to_string();
658 self.start_named_expr(module, result, func_ctx, &res_name)?;
659 self.named_expressions.insert(result, res_name);
660 }
661
662 let fun_str = fun.to_wgsl();
663 write!(self.out, "atomic{fun_str}(")?;
664 self.write_expr(module, pointer, func_ctx)?;
665 if let crate::AtomicFunction::Exchange { compare: Some(cmp) } = *fun {
666 write!(self.out, ", ")?;
667 self.write_expr(module, cmp, func_ctx)?;
668 }
669 write!(self.out, ", ")?;
670 self.write_expr(module, value, func_ctx)?;
671 writeln!(self.out, ");")?
672 }
673 Statement::ImageAtomic {
674 image,
675 coordinate,
676 array_index,
677 ref fun,
678 value,
679 } => {
680 write!(self.out, "{level}")?;
681 let fun_str = fun.to_wgsl();
682 write!(self.out, "textureAtomic{fun_str}(")?;
683 self.write_expr(module, image, func_ctx)?;
684 write!(self.out, ", ")?;
685 self.write_expr(module, coordinate, func_ctx)?;
686 if let Some(array_index_expr) = array_index {
687 write!(self.out, ", ")?;
688 self.write_expr(module, array_index_expr, func_ctx)?;
689 }
690 write!(self.out, ", ")?;
691 self.write_expr(module, value, func_ctx)?;
692 writeln!(self.out, ");")?;
693 }
694 Statement::WorkGroupUniformLoad { pointer, result } => {
695 write!(self.out, "{level}")?;
696 let res_name = Baked(result).to_string();
698 self.start_named_expr(module, result, func_ctx, &res_name)?;
699 self.named_expressions.insert(result, res_name);
700 write!(self.out, "workgroupUniformLoad(")?;
701 self.write_expr(module, pointer, func_ctx)?;
702 writeln!(self.out, ");")?;
703 }
704 Statement::ImageStore {
705 image,
706 coordinate,
707 array_index,
708 value,
709 } => {
710 write!(self.out, "{level}")?;
711 write!(self.out, "textureStore(")?;
712 self.write_expr(module, image, func_ctx)?;
713 write!(self.out, ", ")?;
714 self.write_expr(module, coordinate, func_ctx)?;
715 if let Some(array_index_expr) = array_index {
716 write!(self.out, ", ")?;
717 self.write_expr(module, array_index_expr, func_ctx)?;
718 }
719 write!(self.out, ", ")?;
720 self.write_expr(module, value, func_ctx)?;
721 writeln!(self.out, ");")?;
722 }
723 Statement::Block(ref block) => {
725 write!(self.out, "{level}")?;
726 writeln!(self.out, "{{")?;
727 for sta in block.iter() {
728 self.write_stmt(module, sta, func_ctx, level.next())?
730 }
731 writeln!(self.out, "{level}}}")?
732 }
733 Statement::Switch {
734 selector,
735 ref cases,
736 } => {
737 write!(self.out, "{level}")?;
739 write!(self.out, "switch ")?;
740 self.write_expr(module, selector, func_ctx)?;
741 writeln!(self.out, " {{")?;
742
743 let l2 = level.next();
744 let mut new_case = true;
745 for case in cases {
746 if case.fall_through && !case.body.is_empty() {
747 return Err(Error::Unimplemented(
749 "fall-through switch case block".into(),
750 ));
751 }
752
753 match case.value {
754 crate::SwitchValue::I32(value) => {
755 if new_case {
756 write!(self.out, "{l2}case ")?;
757 }
758 write!(self.out, "{value}")?;
759 }
760 crate::SwitchValue::U32(value) => {
761 if new_case {
762 write!(self.out, "{l2}case ")?;
763 }
764 write!(self.out, "{value}u")?;
765 }
766 crate::SwitchValue::Default => {
767 if new_case {
768 if case.fall_through {
769 write!(self.out, "{l2}case ")?;
770 } else {
771 write!(self.out, "{l2}")?;
772 }
773 }
774 write!(self.out, "default")?;
775 }
776 }
777
778 new_case = !case.fall_through;
779
780 if case.fall_through {
781 write!(self.out, ", ")?;
782 } else {
783 writeln!(self.out, ": {{")?;
784 }
785
786 for sta in case.body.iter() {
787 self.write_stmt(module, sta, func_ctx, l2.next())?;
788 }
789
790 if !case.fall_through {
791 writeln!(self.out, "{l2}}}")?;
792 }
793 }
794
795 writeln!(self.out, "{level}}}")?
796 }
797 Statement::Loop {
798 ref body,
799 ref continuing,
800 break_if,
801 } => {
802 write!(self.out, "{level}")?;
803 writeln!(self.out, "loop {{")?;
804
805 let l2 = level.next();
806 for sta in body.iter() {
807 self.write_stmt(module, sta, func_ctx, l2)?;
808 }
809
810 if !continuing.is_empty() || break_if.is_some() {
815 writeln!(self.out, "{l2}continuing {{")?;
816 for sta in continuing.iter() {
817 self.write_stmt(module, sta, func_ctx, l2.next())?;
818 }
819
820 if let Some(condition) = break_if {
823 write!(self.out, "{}break if ", l2.next())?;
825 self.write_expr(module, condition, func_ctx)?;
826 writeln!(self.out, ";")?;
828 }
829
830 writeln!(self.out, "{l2}}}")?;
831 }
832
833 writeln!(self.out, "{level}}}")?
834 }
835 Statement::Break => {
836 writeln!(self.out, "{level}break;")?;
837 }
838 Statement::Continue => {
839 writeln!(self.out, "{level}continue;")?;
840 }
841 Statement::ControlBarrier(barrier) | Statement::MemoryBarrier(barrier) => {
842 if barrier.contains(crate::Barrier::STORAGE) {
843 writeln!(self.out, "{level}storageBarrier();")?;
844 }
845
846 if barrier.contains(crate::Barrier::WORK_GROUP) {
847 writeln!(self.out, "{level}workgroupBarrier();")?;
848 }
849
850 if barrier.contains(crate::Barrier::SUB_GROUP) {
851 writeln!(self.out, "{level}subgroupBarrier();")?;
852 }
853
854 if barrier.contains(crate::Barrier::TEXTURE) {
855 writeln!(self.out, "{level}textureBarrier();")?;
856 }
857 }
858 Statement::RayQuery { .. } => unreachable!(),
859 Statement::MeshFunction(..) => unreachable!(),
860 Statement::SubgroupBallot { result, predicate } => {
861 write!(self.out, "{level}")?;
862 let res_name = Baked(result).to_string();
863 self.start_named_expr(module, result, func_ctx, &res_name)?;
864 self.named_expressions.insert(result, res_name);
865
866 write!(self.out, "subgroupBallot(")?;
867 if let Some(predicate) = predicate {
868 self.write_expr(module, predicate, func_ctx)?;
869 }
870 writeln!(self.out, ");")?;
871 }
872 Statement::SubgroupCollectiveOperation {
873 op,
874 collective_op,
875 argument,
876 result,
877 } => {
878 write!(self.out, "{level}")?;
879 let res_name = Baked(result).to_string();
880 self.start_named_expr(module, result, func_ctx, &res_name)?;
881 self.named_expressions.insert(result, res_name);
882
883 match (collective_op, op) {
884 (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::All) => {
885 write!(self.out, "subgroupAll(")?
886 }
887 (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::Any) => {
888 write!(self.out, "subgroupAny(")?
889 }
890 (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::Add) => {
891 write!(self.out, "subgroupAdd(")?
892 }
893 (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::Mul) => {
894 write!(self.out, "subgroupMul(")?
895 }
896 (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::Max) => {
897 write!(self.out, "subgroupMax(")?
898 }
899 (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::Min) => {
900 write!(self.out, "subgroupMin(")?
901 }
902 (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::And) => {
903 write!(self.out, "subgroupAnd(")?
904 }
905 (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::Or) => {
906 write!(self.out, "subgroupOr(")?
907 }
908 (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::Xor) => {
909 write!(self.out, "subgroupXor(")?
910 }
911 (crate::CollectiveOperation::ExclusiveScan, crate::SubgroupOperation::Add) => {
912 write!(self.out, "subgroupExclusiveAdd(")?
913 }
914 (crate::CollectiveOperation::ExclusiveScan, crate::SubgroupOperation::Mul) => {
915 write!(self.out, "subgroupExclusiveMul(")?
916 }
917 (crate::CollectiveOperation::InclusiveScan, crate::SubgroupOperation::Add) => {
918 write!(self.out, "subgroupInclusiveAdd(")?
919 }
920 (crate::CollectiveOperation::InclusiveScan, crate::SubgroupOperation::Mul) => {
921 write!(self.out, "subgroupInclusiveMul(")?
922 }
923 _ => unimplemented!(),
924 }
925 self.write_expr(module, argument, func_ctx)?;
926 writeln!(self.out, ");")?;
927 }
928 Statement::SubgroupGather {
929 mode,
930 argument,
931 result,
932 } => {
933 write!(self.out, "{level}")?;
934 let res_name = Baked(result).to_string();
935 self.start_named_expr(module, result, func_ctx, &res_name)?;
936 self.named_expressions.insert(result, res_name);
937
938 match mode {
939 crate::GatherMode::BroadcastFirst => {
940 write!(self.out, "subgroupBroadcastFirst(")?;
941 }
942 crate::GatherMode::Broadcast(_) => {
943 write!(self.out, "subgroupBroadcast(")?;
944 }
945 crate::GatherMode::Shuffle(_) => {
946 write!(self.out, "subgroupShuffle(")?;
947 }
948 crate::GatherMode::ShuffleDown(_) => {
949 write!(self.out, "subgroupShuffleDown(")?;
950 }
951 crate::GatherMode::ShuffleUp(_) => {
952 write!(self.out, "subgroupShuffleUp(")?;
953 }
954 crate::GatherMode::ShuffleXor(_) => {
955 write!(self.out, "subgroupShuffleXor(")?;
956 }
957 crate::GatherMode::QuadBroadcast(_) => {
958 write!(self.out, "quadBroadcast(")?;
959 }
960 crate::GatherMode::QuadSwap(direction) => match direction {
961 crate::Direction::X => {
962 write!(self.out, "quadSwapX(")?;
963 }
964 crate::Direction::Y => {
965 write!(self.out, "quadSwapY(")?;
966 }
967 crate::Direction::Diagonal => {
968 write!(self.out, "quadSwapDiagonal(")?;
969 }
970 },
971 }
972 self.write_expr(module, argument, func_ctx)?;
973 match mode {
974 crate::GatherMode::BroadcastFirst => {}
975 crate::GatherMode::Broadcast(index)
976 | crate::GatherMode::Shuffle(index)
977 | crate::GatherMode::ShuffleDown(index)
978 | crate::GatherMode::ShuffleUp(index)
979 | crate::GatherMode::ShuffleXor(index)
980 | crate::GatherMode::QuadBroadcast(index) => {
981 write!(self.out, ", ")?;
982 self.write_expr(module, index, func_ctx)?;
983 }
984 crate::GatherMode::QuadSwap(_) => {}
985 }
986 writeln!(self.out, ");")?;
987 }
988 }
989
990 Ok(())
991 }
992
993 fn plain_form_indirection(
1016 &self,
1017 expr: Handle<crate::Expression>,
1018 module: &Module,
1019 func_ctx: &back::FunctionCtx<'_>,
1020 ) -> Indirection {
1021 use crate::Expression as Ex;
1022
1023 if self.named_expressions.contains_key(&expr) {
1027 return Indirection::Ordinary;
1028 }
1029
1030 match func_ctx.expressions[expr] {
1031 Ex::LocalVariable(_) => Indirection::Reference,
1032 Ex::GlobalVariable(handle) => {
1033 let global = &module.global_variables[handle];
1034 match global.space {
1035 crate::AddressSpace::Handle => Indirection::Ordinary,
1036 _ => Indirection::Reference,
1037 }
1038 }
1039 Ex::Access { base, .. } | Ex::AccessIndex { base, .. } => {
1040 let base_ty = func_ctx.resolve_type(base, &module.types);
1041 match *base_ty {
1042 TypeInner::Pointer { .. } | TypeInner::ValuePointer { .. } => {
1043 Indirection::Reference
1044 }
1045 _ => Indirection::Ordinary,
1046 }
1047 }
1048 _ => Indirection::Ordinary,
1049 }
1050 }
1051
1052 fn start_named_expr(
1053 &mut self,
1054 module: &Module,
1055 handle: Handle<crate::Expression>,
1056 func_ctx: &back::FunctionCtx,
1057 name: &str,
1058 ) -> BackendResult {
1059 write!(self.out, "let {name}")?;
1061 if self.flags.contains(WriterFlags::EXPLICIT_TYPES) {
1062 write!(self.out, ": ")?;
1063 self.write_type_resolution(module, &func_ctx.info[handle].ty)?;
1065 }
1066
1067 write!(self.out, " = ")?;
1068 Ok(())
1069 }
1070
1071 fn write_expr(
1075 &mut self,
1076 module: &Module,
1077 expr: Handle<crate::Expression>,
1078 func_ctx: &back::FunctionCtx<'_>,
1079 ) -> BackendResult {
1080 self.write_expr_with_indirection(module, expr, func_ctx, Indirection::Ordinary)
1081 }
1082
1083 fn write_expr_with_indirection(
1096 &mut self,
1097 module: &Module,
1098 expr: Handle<crate::Expression>,
1099 func_ctx: &back::FunctionCtx<'_>,
1100 requested: Indirection,
1101 ) -> BackendResult {
1102 let plain = self.plain_form_indirection(expr, module, func_ctx);
1105 match (requested, plain) {
1106 (Indirection::Ordinary, Indirection::Reference) => {
1107 write!(self.out, "(&")?;
1108 self.write_expr_plain_form(module, expr, func_ctx, plain)?;
1109 write!(self.out, ")")?;
1110 }
1111 (Indirection::Reference, Indirection::Ordinary) => {
1112 write!(self.out, "(*")?;
1113 self.write_expr_plain_form(module, expr, func_ctx, plain)?;
1114 write!(self.out, ")")?;
1115 }
1116 (_, _) => self.write_expr_plain_form(module, expr, func_ctx, plain)?,
1117 }
1118
1119 Ok(())
1120 }
1121
1122 fn write_const_expression(
1123 &mut self,
1124 module: &Module,
1125 expr: Handle<crate::Expression>,
1126 arena: &crate::Arena<crate::Expression>,
1127 ) -> BackendResult {
1128 self.write_possibly_const_expression(module, expr, arena, |writer, expr| {
1129 writer.write_const_expression(module, expr, arena)
1130 })
1131 }
1132
1133 fn write_possibly_const_expression<E>(
1134 &mut self,
1135 module: &Module,
1136 expr: Handle<crate::Expression>,
1137 expressions: &crate::Arena<crate::Expression>,
1138 write_expression: E,
1139 ) -> BackendResult
1140 where
1141 E: Fn(&mut Self, Handle<crate::Expression>) -> BackendResult,
1142 {
1143 use crate::Expression;
1144
1145 match expressions[expr] {
1146 Expression::Literal(literal) => match literal {
1147 crate::Literal::F16(value) => write!(self.out, "{value}h")?,
1148 crate::Literal::F32(value) => write!(self.out, "{value}f")?,
1149 crate::Literal::U32(value) => write!(self.out, "{value}u")?,
1150 crate::Literal::I32(value) => {
1151 if value == i32::MIN {
1155 write!(self.out, "i32({value})")?;
1156 } else {
1157 write!(self.out, "{value}i")?;
1158 }
1159 }
1160 crate::Literal::Bool(value) => write!(self.out, "{value}")?,
1161 crate::Literal::F64(value) => write!(self.out, "{value:?}lf")?,
1162 crate::Literal::I64(value) => {
1163 if value == i64::MIN {
1168 write!(self.out, "i64({} - 1)", value + 1)?;
1169 } else {
1170 write!(self.out, "{value}li")?;
1171 }
1172 }
1173 crate::Literal::U64(value) => write!(self.out, "{value:?}lu")?,
1174 crate::Literal::AbstractInt(_) | crate::Literal::AbstractFloat(_) => {
1175 return Err(Error::Custom(
1176 "Abstract types should not appear in IR presented to backends".into(),
1177 ));
1178 }
1179 },
1180 Expression::Constant(handle) => {
1181 let constant = &module.constants[handle];
1182 if constant.name.is_some() {
1183 write!(self.out, "{}", self.names[&NameKey::Constant(handle)])?;
1184 } else {
1185 self.write_const_expression(module, constant.init, &module.global_expressions)?;
1186 }
1187 }
1188 Expression::ZeroValue(ty) => {
1189 self.write_type(module, ty)?;
1190 write!(self.out, "()")?;
1191 }
1192 Expression::Compose { ty, ref components } => {
1193 self.write_type(module, ty)?;
1194 write!(self.out, "(")?;
1195 for (index, component) in components.iter().enumerate() {
1196 if index != 0 {
1197 write!(self.out, ", ")?;
1198 }
1199 write_expression(self, *component)?;
1200 }
1201 write!(self.out, ")")?
1202 }
1203 Expression::Splat { size, value } => {
1204 let size = common::vector_size_str(size);
1205 write!(self.out, "vec{size}(")?;
1206 write_expression(self, value)?;
1207 write!(self.out, ")")?;
1208 }
1209 _ => unreachable!(),
1210 }
1211
1212 Ok(())
1213 }
1214
1215 fn write_expr_plain_form(
1223 &mut self,
1224 module: &Module,
1225 expr: Handle<crate::Expression>,
1226 func_ctx: &back::FunctionCtx<'_>,
1227 indirection: Indirection,
1228 ) -> BackendResult {
1229 use crate::Expression;
1230
1231 if let Some(name) = self.named_expressions.get(&expr) {
1232 write!(self.out, "{name}")?;
1233 return Ok(());
1234 }
1235
1236 let expression = &func_ctx.expressions[expr];
1237
1238 match *expression {
1247 Expression::Literal(_)
1248 | Expression::Constant(_)
1249 | Expression::ZeroValue(_)
1250 | Expression::Compose { .. }
1251 | Expression::Splat { .. } => {
1252 self.write_possibly_const_expression(
1253 module,
1254 expr,
1255 func_ctx.expressions,
1256 |writer, expr| writer.write_expr(module, expr, func_ctx),
1257 )?;
1258 }
1259 Expression::Override(_) => unreachable!(),
1260 Expression::FunctionArgument(pos) => {
1261 let name_key = func_ctx.argument_key(pos);
1262 let name = &self.names[&name_key];
1263 write!(self.out, "{name}")?;
1264 }
1265 Expression::Binary { op, left, right } => {
1266 write!(self.out, "(")?;
1267 self.write_expr(module, left, func_ctx)?;
1268 write!(self.out, " {} ", back::binary_operation_str(op))?;
1269 self.write_expr(module, right, func_ctx)?;
1270 write!(self.out, ")")?;
1271 }
1272 Expression::Access { base, index } => {
1273 self.write_expr_with_indirection(module, base, func_ctx, indirection)?;
1274 write!(self.out, "[")?;
1275 self.write_expr(module, index, func_ctx)?;
1276 write!(self.out, "]")?
1277 }
1278 Expression::AccessIndex { base, index } => {
1279 let base_ty_res = &func_ctx.info[base].ty;
1280 let mut resolved = base_ty_res.inner_with(&module.types);
1281
1282 self.write_expr_with_indirection(module, base, func_ctx, indirection)?;
1283
1284 let base_ty_handle = match *resolved {
1285 TypeInner::Pointer { base, space: _ } => {
1286 resolved = &module.types[base].inner;
1287 Some(base)
1288 }
1289 _ => base_ty_res.handle(),
1290 };
1291
1292 match *resolved {
1293 TypeInner::Vector { .. } => {
1294 write!(self.out, ".{}", back::COMPONENTS[index as usize])?
1296 }
1297 TypeInner::Matrix { .. }
1298 | TypeInner::Array { .. }
1299 | TypeInner::BindingArray { .. }
1300 | TypeInner::ValuePointer { .. } => write!(self.out, "[{index}]")?,
1301 TypeInner::Struct { .. } => {
1302 let ty = base_ty_handle.unwrap();
1305
1306 write!(
1307 self.out,
1308 ".{}",
1309 &self.names[&NameKey::StructMember(ty, index)]
1310 )?
1311 }
1312 ref other => return Err(Error::Custom(format!("Cannot index {other:?}"))),
1313 }
1314 }
1315 Expression::ImageSample {
1316 image,
1317 sampler,
1318 gather: None,
1319 coordinate,
1320 array_index,
1321 offset,
1322 level,
1323 depth_ref,
1324 clamp_to_edge,
1325 } => {
1326 use crate::SampleLevel as Sl;
1327
1328 let suffix_cmp = match depth_ref {
1329 Some(_) => "Compare",
1330 None => "",
1331 };
1332 let suffix_level = match level {
1333 Sl::Auto => "",
1334 Sl::Zero if clamp_to_edge => "BaseClampToEdge",
1335 Sl::Zero | Sl::Exact(_) => "Level",
1336 Sl::Bias(_) => "Bias",
1337 Sl::Gradient { .. } => "Grad",
1338 };
1339
1340 write!(self.out, "textureSample{suffix_cmp}{suffix_level}(")?;
1341 self.write_expr(module, image, func_ctx)?;
1342 write!(self.out, ", ")?;
1343 self.write_expr(module, sampler, func_ctx)?;
1344 write!(self.out, ", ")?;
1345 self.write_expr(module, coordinate, func_ctx)?;
1346
1347 if let Some(array_index) = array_index {
1348 write!(self.out, ", ")?;
1349 self.write_expr(module, array_index, func_ctx)?;
1350 }
1351
1352 if let Some(depth_ref) = depth_ref {
1353 write!(self.out, ", ")?;
1354 self.write_expr(module, depth_ref, func_ctx)?;
1355 }
1356
1357 match level {
1358 Sl::Auto => {}
1359 Sl::Zero => {
1360 if depth_ref.is_none() && !clamp_to_edge {
1362 write!(self.out, ", 0.0")?;
1363 }
1364 }
1365 Sl::Exact(expr) => {
1366 write!(self.out, ", ")?;
1367 self.write_expr(module, expr, func_ctx)?;
1368 }
1369 Sl::Bias(expr) => {
1370 write!(self.out, ", ")?;
1371 self.write_expr(module, expr, func_ctx)?;
1372 }
1373 Sl::Gradient { x, y } => {
1374 write!(self.out, ", ")?;
1375 self.write_expr(module, x, func_ctx)?;
1376 write!(self.out, ", ")?;
1377 self.write_expr(module, y, func_ctx)?;
1378 }
1379 }
1380
1381 if let Some(offset) = offset {
1382 write!(self.out, ", ")?;
1383 self.write_const_expression(module, offset, func_ctx.expressions)?;
1384 }
1385
1386 write!(self.out, ")")?;
1387 }
1388
1389 Expression::ImageSample {
1390 image,
1391 sampler,
1392 gather: Some(component),
1393 coordinate,
1394 array_index,
1395 offset,
1396 level: _,
1397 depth_ref,
1398 clamp_to_edge: _,
1399 } => {
1400 let suffix_cmp = match depth_ref {
1401 Some(_) => "Compare",
1402 None => "",
1403 };
1404
1405 write!(self.out, "textureGather{suffix_cmp}(")?;
1406 match *func_ctx.resolve_type(image, &module.types) {
1407 TypeInner::Image {
1408 class: crate::ImageClass::Depth { multi: _ },
1409 ..
1410 } => {}
1411 _ => {
1412 write!(self.out, "{}, ", component as u8)?;
1413 }
1414 }
1415 self.write_expr(module, image, func_ctx)?;
1416 write!(self.out, ", ")?;
1417 self.write_expr(module, sampler, func_ctx)?;
1418 write!(self.out, ", ")?;
1419 self.write_expr(module, coordinate, func_ctx)?;
1420
1421 if let Some(array_index) = array_index {
1422 write!(self.out, ", ")?;
1423 self.write_expr(module, array_index, func_ctx)?;
1424 }
1425
1426 if let Some(depth_ref) = depth_ref {
1427 write!(self.out, ", ")?;
1428 self.write_expr(module, depth_ref, func_ctx)?;
1429 }
1430
1431 if let Some(offset) = offset {
1432 write!(self.out, ", ")?;
1433 self.write_const_expression(module, offset, func_ctx.expressions)?;
1434 }
1435
1436 write!(self.out, ")")?;
1437 }
1438 Expression::ImageQuery { image, query } => {
1439 use crate::ImageQuery as Iq;
1440
1441 let texture_function = match query {
1442 Iq::Size { .. } => "textureDimensions",
1443 Iq::NumLevels => "textureNumLevels",
1444 Iq::NumLayers => "textureNumLayers",
1445 Iq::NumSamples => "textureNumSamples",
1446 };
1447
1448 write!(self.out, "{texture_function}(")?;
1449 self.write_expr(module, image, func_ctx)?;
1450 if let Iq::Size { level: Some(level) } = query {
1451 write!(self.out, ", ")?;
1452 self.write_expr(module, level, func_ctx)?;
1453 };
1454 write!(self.out, ")")?;
1455 }
1456
1457 Expression::ImageLoad {
1458 image,
1459 coordinate,
1460 array_index,
1461 sample,
1462 level,
1463 } => {
1464 write!(self.out, "textureLoad(")?;
1465 self.write_expr(module, image, func_ctx)?;
1466 write!(self.out, ", ")?;
1467 self.write_expr(module, coordinate, func_ctx)?;
1468 if let Some(array_index) = array_index {
1469 write!(self.out, ", ")?;
1470 self.write_expr(module, array_index, func_ctx)?;
1471 }
1472 if let Some(index) = sample.or(level) {
1473 write!(self.out, ", ")?;
1474 self.write_expr(module, index, func_ctx)?;
1475 }
1476 write!(self.out, ")")?;
1477 }
1478 Expression::GlobalVariable(handle) => {
1479 let name = &self.names[&NameKey::GlobalVariable(handle)];
1480 write!(self.out, "{name}")?;
1481 }
1482
1483 Expression::As {
1484 expr,
1485 kind,
1486 convert,
1487 } => {
1488 let inner = func_ctx.resolve_type(expr, &module.types);
1489 match *inner {
1490 TypeInner::Matrix {
1491 columns,
1492 rows,
1493 scalar,
1494 } => {
1495 let scalar = crate::Scalar {
1496 kind,
1497 width: convert.unwrap_or(scalar.width),
1498 };
1499 let scalar_kind_str = scalar.to_wgsl_if_implemented()?;
1500 write!(
1501 self.out,
1502 "mat{}x{}<{}>",
1503 common::vector_size_str(columns),
1504 common::vector_size_str(rows),
1505 scalar_kind_str
1506 )?;
1507 }
1508 TypeInner::Vector {
1509 size,
1510 scalar: crate::Scalar { width, .. },
1511 } => {
1512 let scalar = crate::Scalar {
1513 kind,
1514 width: convert.unwrap_or(width),
1515 };
1516 let vector_size_str = common::vector_size_str(size);
1517 let scalar_kind_str = scalar.to_wgsl_if_implemented()?;
1518 if convert.is_some() {
1519 write!(self.out, "vec{vector_size_str}<{scalar_kind_str}>")?;
1520 } else {
1521 write!(self.out, "bitcast<vec{vector_size_str}<{scalar_kind_str}>>")?;
1522 }
1523 }
1524 TypeInner::Scalar(crate::Scalar { width, .. }) => {
1525 let scalar = crate::Scalar {
1526 kind,
1527 width: convert.unwrap_or(width),
1528 };
1529 let scalar_kind_str = scalar.to_wgsl_if_implemented()?;
1530 if convert.is_some() {
1531 write!(self.out, "{scalar_kind_str}")?
1532 } else {
1533 write!(self.out, "bitcast<{scalar_kind_str}>")?
1534 }
1535 }
1536 _ => {
1537 return Err(Error::Unimplemented(format!(
1538 "write_expr expression::as {inner:?}"
1539 )));
1540 }
1541 };
1542 write!(self.out, "(")?;
1543 self.write_expr(module, expr, func_ctx)?;
1544 write!(self.out, ")")?;
1545 }
1546 Expression::Load { pointer } => {
1547 let is_atomic_pointer = func_ctx
1548 .resolve_type(pointer, &module.types)
1549 .is_atomic_pointer(&module.types);
1550
1551 if is_atomic_pointer {
1552 write!(self.out, "atomicLoad(")?;
1553 self.write_expr(module, pointer, func_ctx)?;
1554 write!(self.out, ")")?;
1555 } else {
1556 self.write_expr_with_indirection(
1557 module,
1558 pointer,
1559 func_ctx,
1560 Indirection::Reference,
1561 )?;
1562 }
1563 }
1564 Expression::LocalVariable(handle) => {
1565 write!(self.out, "{}", self.names[&func_ctx.name_key(handle)])?
1566 }
1567 Expression::ArrayLength(expr) => {
1568 write!(self.out, "arrayLength(")?;
1569 self.write_expr(module, expr, func_ctx)?;
1570 write!(self.out, ")")?;
1571 }
1572
1573 Expression::Math {
1574 fun,
1575 arg,
1576 arg1,
1577 arg2,
1578 arg3,
1579 } => {
1580 use crate::MathFunction as Mf;
1581
1582 enum Function {
1583 Regular(&'static str),
1584 InversePolyfill(InversePolyfill),
1585 }
1586
1587 let function = match fun.try_to_wgsl() {
1588 Some(name) => Function::Regular(name),
1589 None => match fun {
1590 Mf::Inverse => {
1591 let ty = func_ctx.resolve_type(arg, &module.types);
1592 let Some(overload) = InversePolyfill::find_overload(ty) else {
1593 return Err(Error::unsupported("math function", fun));
1594 };
1595
1596 Function::InversePolyfill(overload)
1597 }
1598 _ => return Err(Error::unsupported("math function", fun)),
1599 },
1600 };
1601
1602 match function {
1603 Function::Regular(fun_name) => {
1604 write!(self.out, "{fun_name}(")?;
1605 self.write_expr(module, arg, func_ctx)?;
1606 for arg in IntoIterator::into_iter([arg1, arg2, arg3]).flatten() {
1607 write!(self.out, ", ")?;
1608 self.write_expr(module, arg, func_ctx)?;
1609 }
1610 write!(self.out, ")")?
1611 }
1612 Function::InversePolyfill(inverse) => {
1613 write!(self.out, "{}(", inverse.fun_name)?;
1614 self.write_expr(module, arg, func_ctx)?;
1615 write!(self.out, ")")?;
1616 self.required_polyfills.insert(inverse);
1617 }
1618 }
1619 }
1620
1621 Expression::Swizzle {
1622 size,
1623 vector,
1624 pattern,
1625 } => {
1626 self.write_expr(module, vector, func_ctx)?;
1627 write!(self.out, ".")?;
1628 for &sc in pattern[..size as usize].iter() {
1629 self.out.write_char(back::COMPONENTS[sc as usize])?;
1630 }
1631 }
1632 Expression::Unary { op, expr } => {
1633 let unary = match op {
1634 crate::UnaryOperator::Negate => "-",
1635 crate::UnaryOperator::LogicalNot => "!",
1636 crate::UnaryOperator::BitwiseNot => "~",
1637 };
1638
1639 write!(self.out, "{unary}(")?;
1640 self.write_expr(module, expr, func_ctx)?;
1641
1642 write!(self.out, ")")?
1643 }
1644
1645 Expression::Select {
1646 condition,
1647 accept,
1648 reject,
1649 } => {
1650 write!(self.out, "select(")?;
1651 self.write_expr(module, reject, func_ctx)?;
1652 write!(self.out, ", ")?;
1653 self.write_expr(module, accept, func_ctx)?;
1654 write!(self.out, ", ")?;
1655 self.write_expr(module, condition, func_ctx)?;
1656 write!(self.out, ")")?
1657 }
1658 Expression::Derivative { axis, ctrl, expr } => {
1659 use crate::{DerivativeAxis as Axis, DerivativeControl as Ctrl};
1660 let op = match (axis, ctrl) {
1661 (Axis::X, Ctrl::Coarse) => "dpdxCoarse",
1662 (Axis::X, Ctrl::Fine) => "dpdxFine",
1663 (Axis::X, Ctrl::None) => "dpdx",
1664 (Axis::Y, Ctrl::Coarse) => "dpdyCoarse",
1665 (Axis::Y, Ctrl::Fine) => "dpdyFine",
1666 (Axis::Y, Ctrl::None) => "dpdy",
1667 (Axis::Width, Ctrl::Coarse) => "fwidthCoarse",
1668 (Axis::Width, Ctrl::Fine) => "fwidthFine",
1669 (Axis::Width, Ctrl::None) => "fwidth",
1670 };
1671 write!(self.out, "{op}(")?;
1672 self.write_expr(module, expr, func_ctx)?;
1673 write!(self.out, ")")?
1674 }
1675 Expression::Relational { fun, argument } => {
1676 use crate::RelationalFunction as Rf;
1677
1678 let fun_name = match fun {
1679 Rf::All => "all",
1680 Rf::Any => "any",
1681 _ => return Err(Error::UnsupportedRelationalFunction(fun)),
1682 };
1683 write!(self.out, "{fun_name}(")?;
1684
1685 self.write_expr(module, argument, func_ctx)?;
1686
1687 write!(self.out, ")")?
1688 }
1689 Expression::RayQueryGetIntersection { .. }
1691 | Expression::RayQueryVertexPositions { .. } => unreachable!(),
1692 Expression::CallResult(_)
1694 | Expression::AtomicResult { .. }
1695 | Expression::RayQueryProceedResult
1696 | Expression::SubgroupBallotResult
1697 | Expression::SubgroupOperationResult { .. }
1698 | Expression::WorkGroupUniformLoadResult { .. } => {}
1699 }
1700
1701 Ok(())
1702 }
1703
1704 fn write_global(
1708 &mut self,
1709 module: &Module,
1710 global: &crate::GlobalVariable,
1711 handle: Handle<crate::GlobalVariable>,
1712 ) -> BackendResult {
1713 if let Some(ref binding) = global.binding {
1715 self.write_attributes(&[
1716 Attribute::Group(binding.group),
1717 Attribute::Binding(binding.binding),
1718 ])?;
1719 writeln!(self.out)?;
1720 }
1721
1722 write!(self.out, "var")?;
1724 let (address, maybe_access) = address_space_str(global.space);
1725 if let Some(space) = address {
1726 write!(self.out, "<{space}")?;
1727 if let Some(access) = maybe_access {
1728 write!(self.out, ", {access}")?;
1729 }
1730 write!(self.out, ">")?;
1731 }
1732 write!(
1733 self.out,
1734 " {}: ",
1735 &self.names[&NameKey::GlobalVariable(handle)]
1736 )?;
1737
1738 self.write_type(module, global.ty)?;
1740
1741 if let Some(init) = global.init {
1743 write!(self.out, " = ")?;
1744 self.write_const_expression(module, init, &module.global_expressions)?;
1745 }
1746
1747 writeln!(self.out, ";")?;
1749
1750 Ok(())
1751 }
1752
1753 fn write_global_constant(
1758 &mut self,
1759 module: &Module,
1760 handle: Handle<crate::Constant>,
1761 ) -> BackendResult {
1762 let name = &self.names[&NameKey::Constant(handle)];
1763 write!(self.out, "const {name}: ")?;
1765 self.write_type(module, module.constants[handle].ty)?;
1766 write!(self.out, " = ")?;
1767 let init = module.constants[handle].init;
1768 self.write_const_expression(module, init, &module.global_expressions)?;
1769 writeln!(self.out, ";")?;
1770
1771 Ok(())
1772 }
1773
1774 #[allow(clippy::missing_const_for_fn)]
1776 pub fn finish(self) -> W {
1777 self.out
1778 }
1779}
1780
1781struct WriterTypeContext<'m> {
1782 module: &'m Module,
1783 names: &'m crate::FastHashMap<NameKey, String>,
1784}
1785
1786impl TypeContext for WriterTypeContext<'_> {
1787 fn lookup_type(&self, handle: Handle<crate::Type>) -> &crate::Type {
1788 &self.module.types[handle]
1789 }
1790
1791 fn type_name(&self, handle: Handle<crate::Type>) -> &str {
1792 self.names[&NameKey::Type(handle)].as_str()
1793 }
1794
1795 fn write_unnamed_struct<W: Write>(&self, _: &TypeInner, _: &mut W) -> core::fmt::Result {
1796 unreachable!("the WGSL back end should always provide type handles");
1797 }
1798
1799 fn write_override<W: Write>(&self, _: Handle<crate::Override>, _: &mut W) -> core::fmt::Result {
1800 unreachable!("overrides should be validated out");
1801 }
1802
1803 fn write_non_wgsl_inner<W: Write>(&self, _: &TypeInner, _: &mut W) -> core::fmt::Result {
1804 unreachable!("backends should only be passed validated modules");
1805 }
1806
1807 fn write_non_wgsl_scalar<W: Write>(&self, _: crate::Scalar, _: &mut W) -> core::fmt::Result {
1808 unreachable!("backends should only be passed validated modules");
1809 }
1810}
1811
1812fn map_binding_to_attribute(binding: &crate::Binding) -> Vec<Attribute> {
1813 match *binding {
1814 crate::Binding::BuiltIn(built_in) => {
1815 if let crate::BuiltIn::Position { invariant: true } = built_in {
1816 vec![Attribute::BuiltIn(built_in), Attribute::Invariant]
1817 } else {
1818 vec![Attribute::BuiltIn(built_in)]
1819 }
1820 }
1821 crate::Binding::Location {
1822 location,
1823 interpolation,
1824 sampling,
1825 blend_src: None,
1826 per_primitive: _,
1827 } => vec![
1828 Attribute::Location(location),
1829 Attribute::Interpolate(interpolation, sampling),
1830 ],
1831 crate::Binding::Location {
1832 location,
1833 interpolation,
1834 sampling,
1835 blend_src: Some(blend_src),
1836 per_primitive: _,
1837 } => vec![
1838 Attribute::Location(location),
1839 Attribute::BlendSrc(blend_src),
1840 Attribute::Interpolate(interpolation, sampling),
1841 ],
1842 }
1843}