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 MeshStage(String),
37 TaskPayload(String),
38 PerPrimitive,
39 IncomingRayPayload(String),
40}
41
42#[derive(Clone, Copy, Debug)]
55enum Indirection {
56 Ordinary,
62
63 Reference,
69}
70
71bitflags::bitflags! {
72 #[cfg_attr(feature = "serialize", derive(serde::Serialize))]
73 #[cfg_attr(feature = "deserialize", derive(serde::Deserialize))]
74 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
75 pub struct WriterFlags: u32 {
76 const EXPLICIT_TYPES = 0x1;
78 }
79}
80
81#[expect(missing_debug_implementations, reason = "would be way too verbose?")]
82pub struct Writer<W> {
83 out: W,
84 flags: WriterFlags,
85 names: crate::FastHashMap<NameKey, String>,
86 namer: proc::Namer,
87 named_expressions: crate::NamedExpressions,
88 required_polyfills: crate::FastIndexSet<InversePolyfill>,
89}
90
91impl<W: Write> Writer<W> {
92 pub fn new(out: W, flags: WriterFlags) -> Self {
93 Writer {
94 out,
95 flags,
96 names: crate::FastHashMap::default(),
97 namer: proc::Namer::default(),
98 named_expressions: crate::NamedExpressions::default(),
99 required_polyfills: crate::FastIndexSet::default(),
100 }
101 }
102
103 fn reset(&mut self, module: &Module) {
104 self.names.clear();
105 self.namer.reset(
106 module,
107 &crate::keywords::wgsl::RESERVED_SET,
108 &crate::keywords::wgsl::BUILTIN_IDENTIFIER_SET,
109 proc::CaseInsensitiveKeywordSet::empty(),
111 &["__", "_naga"],
112 &mut self.names,
113 );
114 self.named_expressions.clear();
115 self.required_polyfills.clear();
116 }
117
118 fn is_builtin_wgsl_struct(&self, module: &Module, ty: Handle<crate::Type>) -> bool {
134 module
135 .special_types
136 .predeclared_types
137 .values()
138 .any(|t| *t == ty)
139 || Some(ty) == module.special_types.external_texture_params
140 || Some(ty) == module.special_types.external_texture_transfer_function
141 }
142
143 pub fn write(&mut self, module: &Module, info: &valid::ModuleInfo) -> BackendResult {
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 let mut overrides = module.overrides.iter().peekable();
177 while let Some((handle, _)) = overrides.next() {
178 self.write_override(module, handle)?;
179 if overrides.peek().is_none() {
181 writeln!(self.out)?;
182 }
183 }
184
185 for (ty, global) in module.global_variables.iter() {
187 self.write_global(module, global, ty)?;
188 }
189
190 if !module.global_variables.is_empty() {
191 writeln!(self.out)?;
193 }
194
195 for (handle, function) in module.functions.iter() {
197 let fun_info = &info[handle];
198
199 let func_ctx = back::FunctionCtx {
200 ty: back::FunctionType::Function(handle),
201 info: fun_info,
202 expressions: &function.expressions,
203 named_expressions: &function.named_expressions,
204 };
205
206 self.write_function(module, function, &func_ctx)?;
208
209 writeln!(self.out)?;
210 }
211
212 for (index, ep) in module.entry_points.iter().enumerate() {
214 let attributes = match ep.stage {
215 ShaderStage::Vertex | ShaderStage::Fragment => vec![Attribute::Stage(ep.stage)],
216 ShaderStage::Compute => vec![
217 Attribute::Stage(ShaderStage::Compute),
218 Attribute::WorkGroupSize(ep.workgroup_size),
219 ],
220 ShaderStage::Mesh => {
221 let mesh_output_name = module.global_variables
222 [ep.mesh_info.as_ref().unwrap().output_variable]
223 .name
224 .clone()
225 .unwrap();
226 let mut mesh_attrs = vec![
227 Attribute::MeshStage(mesh_output_name),
228 Attribute::WorkGroupSize(ep.workgroup_size),
229 ];
230 if let Some(task_payload) = ep.task_payload {
231 let payload_name =
232 module.global_variables[task_payload].name.clone().unwrap();
233 mesh_attrs.push(Attribute::TaskPayload(payload_name));
234 }
235 mesh_attrs
236 }
237 ShaderStage::Task => {
238 let payload_name = module.global_variables[ep.task_payload.unwrap()]
239 .name
240 .clone()
241 .unwrap();
242 vec![
243 Attribute::Stage(ShaderStage::Task),
244 Attribute::TaskPayload(payload_name),
245 Attribute::WorkGroupSize(ep.workgroup_size),
246 ]
247 }
248 ShaderStage::RayGeneration => vec![Attribute::Stage(ShaderStage::RayGeneration)],
249 ShaderStage::AnyHit | ShaderStage::ClosestHit | ShaderStage::Miss => {
250 let payload_name = module.global_variables[ep.incoming_ray_payload.unwrap()]
251 .name
252 .clone()
253 .unwrap();
254 vec![
255 Attribute::Stage(ep.stage),
256 Attribute::IncomingRayPayload(payload_name),
257 ]
258 }
259 };
260 self.write_attributes_line(&attributes)?;
261
262 let func_ctx = back::FunctionCtx {
263 ty: back::FunctionType::EntryPoint(index as u16),
264 info: info.get_entry_point(index),
265 expressions: &ep.function.expressions,
266 named_expressions: &ep.function.named_expressions,
267 };
268 self.write_function(module, &ep.function, &func_ctx)?;
269
270 if index < module.entry_points.len() - 1 {
271 writeln!(self.out)?;
272 }
273 }
274
275 for polyfill in &self.required_polyfills {
277 writeln!(self.out)?;
278 write!(self.out, "{}", polyfill.source)?;
279 writeln!(self.out)?;
280 }
281
282 Ok(())
283 }
284
285 fn write_enable_declarations(&mut self, module: &Module) -> BackendResult {
288 #[derive(Default)]
289 struct RequiredEnabled {
290 f16: bool,
291 int16: bool,
292 dual_source_blending: bool,
293 clip_distances: bool,
294 mesh_shaders: bool,
295 primitive_index: bool,
296 cooperative_matrix: bool,
297 draw_index: bool,
298 ray_tracing_pipeline: bool,
299 per_vertex: bool,
300 binding_array: bool,
301 debug_printf: bool,
302 }
303 let mut needed = RequiredEnabled {
304 mesh_shaders: module.uses_mesh_shaders(),
305 ..Default::default()
306 };
307
308 let check_binding = |binding: &crate::Binding, needed: &mut RequiredEnabled| match *binding
309 {
310 crate::Binding::Location {
311 blend_src: Some(_), ..
312 } => {
313 needed.dual_source_blending = true;
314 }
315 crate::Binding::BuiltIn(crate::BuiltIn::ClipDistances) => {
316 needed.clip_distances = true;
317 }
318 crate::Binding::BuiltIn(crate::BuiltIn::PrimitiveIndex) => {
319 needed.primitive_index = true;
320 }
321 crate::Binding::Location {
322 per_primitive: true,
323 ..
324 } => {
325 needed.mesh_shaders = true;
326 }
327 crate::Binding::Location {
328 interpolation: Some(crate::Interpolation::PerVertex),
329 ..
330 } => {
331 needed.per_vertex = true;
332 }
333 crate::Binding::BuiltIn(crate::BuiltIn::DrawIndex) => needed.draw_index = true,
334 crate::Binding::BuiltIn(
335 crate::BuiltIn::RayInvocationId
336 | crate::BuiltIn::NumRayInvocations
337 | crate::BuiltIn::InstanceCustomData
338 | crate::BuiltIn::GeometryIndex
339 | crate::BuiltIn::WorldRayOrigin
340 | crate::BuiltIn::WorldRayDirection
341 | crate::BuiltIn::ObjectRayOrigin
342 | crate::BuiltIn::ObjectRayDirection
343 | crate::BuiltIn::RayTmin
344 | crate::BuiltIn::RayTCurrentMax
345 | crate::BuiltIn::ObjectToWorld
346 | crate::BuiltIn::WorldToObject
347 | crate::BuiltIn::HitKind
348 | crate::BuiltIn::HitBarycentrics,
349 ) => {
350 needed.ray_tracing_pipeline = true;
351 }
352 _ => {}
353 };
354
355 for (_, ty) in module.types.iter() {
357 match ty.inner {
358 TypeInner::Scalar(scalar)
359 | TypeInner::Vector { scalar, .. }
360 | TypeInner::Matrix { scalar, .. } => {
361 needed.f16 |= scalar == crate::Scalar::F16;
362 needed.int16 |= scalar == crate::Scalar::I16 || scalar == crate::Scalar::U16;
363 }
364 TypeInner::Struct { ref members, .. } => {
365 for binding in members.iter().filter_map(|m| m.binding.as_ref()) {
366 check_binding(binding, &mut needed);
367 }
368 }
369 TypeInner::CooperativeMatrix { .. } => {
370 needed.cooperative_matrix = true;
371 }
372 TypeInner::AccelerationStructure { .. } => {
373 needed.ray_tracing_pipeline = true;
374 }
375 TypeInner::BindingArray { .. } => {
376 needed.binding_array = true;
377 }
378 _ => {}
379 }
380 }
381
382 for ep in &module.entry_points {
383 if let Some(res) = ep.function.result.as_ref().and_then(|a| a.binding.as_ref()) {
384 check_binding(res, &mut needed);
385 }
386 for arg in ep
387 .function
388 .arguments
389 .iter()
390 .filter_map(|a| a.binding.as_ref())
391 {
392 check_binding(arg, &mut needed);
393 }
394 }
395
396 if module.global_variables.iter().any(|gv| {
397 gv.1.space == crate::AddressSpace::IncomingRayPayload
398 || gv.1.space == crate::AddressSpace::RayPayload
399 }) {
400 needed.ray_tracing_pipeline = true;
401 }
402
403 if module.entry_points.iter().any(|ep| {
404 matches!(
405 ep.stage,
406 ShaderStage::RayGeneration
407 | ShaderStage::AnyHit
408 | ShaderStage::ClosestHit
409 | ShaderStage::Miss
410 )
411 }) {
412 needed.ray_tracing_pipeline = true;
413 }
414
415 if module.global_variables.iter().any(|gv| {
416 gv.1.space == crate::AddressSpace::IncomingRayPayload
417 || gv.1.space == crate::AddressSpace::RayPayload
418 }) {
419 needed.ray_tracing_pipeline = true;
420 }
421
422 if module.entry_points.iter().any(|ep| {
423 matches!(
424 ep.stage,
425 ShaderStage::RayGeneration
426 | ShaderStage::AnyHit
427 | ShaderStage::ClosestHit
428 | ShaderStage::Miss
429 )
430 }) {
431 needed.ray_tracing_pipeline = true;
432 }
433
434 needed.debug_printf = module.uses_debug_printf();
435
436 let mut any_written = false;
438 if needed.f16 {
439 writeln!(self.out, "enable f16;")?;
440 any_written = true;
441 }
442 if needed.int16 {
443 writeln!(self.out, "enable wgpu_int16;")?;
444 any_written = true;
445 }
446 if needed.dual_source_blending {
447 writeln!(self.out, "enable dual_source_blending;")?;
448 any_written = true;
449 }
450 if needed.clip_distances {
451 writeln!(self.out, "enable clip_distances;")?;
452 any_written = true;
453 }
454 if module.uses_mesh_shaders() {
455 writeln!(self.out, "enable wgpu_mesh_shader;")?;
456 any_written = true;
457 }
458 if needed.binding_array {
459 writeln!(self.out, "enable wgpu_binding_array;")?;
460 any_written = true;
461 }
462 if needed.draw_index {
463 writeln!(self.out, "enable draw_index;")?;
464 any_written = true;
465 }
466 if needed.primitive_index {
467 writeln!(self.out, "enable primitive_index;")?;
468 any_written = true;
469 }
470 if needed.cooperative_matrix {
471 writeln!(self.out, "enable wgpu_cooperative_matrix;")?;
472 any_written = true;
473 }
474 if needed.ray_tracing_pipeline {
475 writeln!(self.out, "enable wgpu_ray_tracing_pipeline;")?;
476 any_written = true;
477 }
478 if needed.per_vertex {
479 writeln!(self.out, "enable wgpu_per_vertex;")?;
480 any_written = true;
481 }
482 if needed.debug_printf {
483 writeln!(self.out, "enable wgpu_debug_printf;")?;
484 any_written = true;
485 }
486 if any_written {
487 writeln!(self.out)?;
489 }
490
491 Ok(())
492 }
493
494 fn write_function(
500 &mut self,
501 module: &Module,
502 func: &crate::Function,
503 func_ctx: &back::FunctionCtx<'_>,
504 ) -> BackendResult {
505 let func_name = match func_ctx.ty {
506 back::FunctionType::EntryPoint(index) => &self.names[&NameKey::EntryPoint(index)],
507 back::FunctionType::Function(handle) => &self.names[&NameKey::Function(handle)],
508 };
509
510 write!(self.out, "fn {func_name}(")?;
512
513 for (index, arg) in func.arguments.iter().enumerate() {
515 if let Some(ref binding) = arg.binding {
517 self.write_attributes(&map_binding_to_attribute(binding))?;
518 }
519 let argument_name = &self.names[&func_ctx.argument_key(index as u32)];
521
522 write!(self.out, "{argument_name}: ")?;
523 self.write_type(module, arg.ty)?;
525 if index < func.arguments.len() - 1 {
526 write!(self.out, ", ")?;
528 }
529 }
530
531 write!(self.out, ")")?;
532
533 if let Some(ref result) = func.result {
535 write!(self.out, " -> ")?;
536 if let Some(ref binding) = result.binding {
537 self.write_attributes(&map_binding_to_attribute(binding))?;
538 }
539 self.write_type(module, result.ty)?;
540 }
541
542 write!(self.out, " {{")?;
543 writeln!(self.out)?;
544
545 for (handle, local) in func.local_variables.iter() {
547 write!(self.out, "{}", back::INDENT)?;
549
550 write!(self.out, "var {}: ", self.names[&func_ctx.name_key(handle)])?;
553
554 self.write_type(module, local.ty)?;
556
557 if let Some(init) = local.init {
559 write!(self.out, " = ")?;
562
563 self.write_expr(module, init, func_ctx)?;
566 }
567
568 writeln!(self.out, ";")?
570 }
571
572 if !func.local_variables.is_empty() {
573 writeln!(self.out)?;
574 }
575
576 for sta in func.body.iter() {
578 self.write_stmt(module, sta, func_ctx, back::Level(1))?;
580 }
581
582 writeln!(self.out, "}}")?;
583
584 self.named_expressions.clear();
585
586 Ok(())
587 }
588
589 fn write_attributes_to(out: &mut impl Write, attributes: &[Attribute]) -> BackendResult {
593 for attribute in attributes {
594 match *attribute {
595 Attribute::Location(id) => write!(out, "@location({id}) ")?,
596 Attribute::BlendSrc(blend_src) => write!(out, "@blend_src({blend_src}) ")?,
597 Attribute::BuiltIn(builtin_attrib) => {
598 let builtin = builtin_attrib.to_wgsl_if_implemented()?;
599 write!(out, "@builtin({builtin}) ")?;
600 }
601 Attribute::Stage(shader_stage) => {
602 let stage_str = match shader_stage {
603 ShaderStage::Vertex => "vertex",
604 ShaderStage::Fragment => "fragment",
605 ShaderStage::Compute => "compute",
606 ShaderStage::Task => "task",
607 ShaderStage::Mesh => unreachable!(),
609 ShaderStage::RayGeneration => "ray_generation",
610 ShaderStage::AnyHit => "any_hit",
611 ShaderStage::ClosestHit => "closest_hit",
612 ShaderStage::Miss => "miss",
613 };
614
615 write!(out, "@{stage_str} ")?;
616 }
617 Attribute::WorkGroupSize(size) => {
618 write!(
619 out,
620 "@workgroup_size({}, {}, {}) ",
621 size[0], size[1], size[2]
622 )?;
623 }
624 Attribute::Binding(id) => write!(out, "@binding({id}) ")?,
625 Attribute::Group(id) => write!(out, "@group({id}) ")?,
626 Attribute::Invariant => write!(out, "@invariant ")?,
627 Attribute::Interpolate(interpolation, sampling) => {
628 if sampling.is_some() && sampling != Some(crate::Sampling::Center) {
629 let interpolation = interpolation
630 .unwrap_or(crate::Interpolation::Perspective)
631 .to_wgsl();
632 let sampling = sampling.unwrap_or(crate::Sampling::Center).to_wgsl();
633 write!(out, "@interpolate({interpolation}, {sampling}) ")?;
634 } else if interpolation.is_some()
635 && interpolation != Some(crate::Interpolation::Perspective)
636 {
637 let interpolation = interpolation
638 .unwrap_or(crate::Interpolation::Perspective)
639 .to_wgsl();
640 write!(out, "@interpolate({interpolation}) ")?;
641 }
642 }
643 Attribute::MeshStage(ref name) => {
644 write!(out, "@mesh({name}) ")?;
645 }
646 Attribute::TaskPayload(ref payload_name) => {
647 write!(out, "@payload({payload_name}) ")?;
648 }
649 Attribute::PerPrimitive => write!(out, "@per_primitive ")?,
650 Attribute::IncomingRayPayload(ref payload_name) => {
651 write!(out, "@incoming_payload({payload_name}) ")?;
652 }
653 };
654 }
655 Ok(())
656 }
657
658 fn write_attributes(&mut self, attributes: &[Attribute]) -> BackendResult {
661 Self::write_attributes_to(&mut self.out, attributes)
662 }
663
664 fn write_attributes_line(&mut self, attributes: &[Attribute]) -> BackendResult {
668 let mut buf = String::new();
669 Self::write_attributes_to(&mut buf, attributes)?;
670 writeln!(self.out, "{}", buf.trim_end())?;
671 Ok(())
672 }
673
674 fn write_struct(
685 &mut self,
686 module: &Module,
687 handle: Handle<crate::Type>,
688 members: &[crate::StructMember],
689 ) -> BackendResult {
690 write!(self.out, "struct {}", self.names[&NameKey::Type(handle)])?;
691 write!(self.out, " {{")?;
692 writeln!(self.out)?;
693 for (index, member) in members.iter().enumerate() {
694 write!(self.out, "{}", back::INDENT)?;
696 if let Some(ref binding) = member.binding {
697 self.write_attributes(&map_binding_to_attribute(binding))?;
698 }
699 let member_name = &self.names[&NameKey::StructMember(handle, index as u32)];
701 write!(self.out, "{member_name}: ")?;
702 self.write_type(module, member.ty)?;
703 write!(self.out, ",")?;
704 writeln!(self.out)?;
705 }
706
707 writeln!(self.out, "}}")?;
708
709 Ok(())
710 }
711
712 fn write_type(&mut self, module: &Module, ty: Handle<crate::Type>) -> BackendResult {
713 let type_context = WriterTypeContext {
717 module,
718 names: &self.names,
719 };
720 type_context.write_type(ty, &mut self.out)?;
721
722 Ok(())
723 }
724
725 fn write_type_resolution(
726 &mut self,
727 module: &Module,
728 resolution: &proc::TypeResolution,
729 ) -> BackendResult {
730 let type_context = WriterTypeContext {
734 module,
735 names: &self.names,
736 };
737 type_context.write_type_resolution(resolution, &mut self.out)?;
738
739 Ok(())
740 }
741
742 fn write_stmt(
747 &mut self,
748 module: &Module,
749 stmt: &crate::Statement,
750 func_ctx: &back::FunctionCtx<'_>,
751 level: back::Level,
752 ) -> BackendResult {
753 use crate::{Expression, Statement};
754
755 match *stmt {
756 Statement::Emit(ref range) => {
757 for handle in range.clone() {
758 let info = &func_ctx.info[handle];
759 let expr_name = if let Some(name) = func_ctx.named_expressions.get(&handle) {
760 Some(self.namer.call(name))
765 } else {
766 let expr = &func_ctx.expressions[handle];
767 let min_ref_count = expr.bake_ref_count();
768 let required_baking_expr = match *expr {
770 Expression::ImageLoad { .. }
771 | Expression::ImageQuery { .. }
772 | Expression::ImageSample { .. } => true,
773 _ => false,
774 };
775 if min_ref_count <= info.ref_count || required_baking_expr {
776 Some(Baked(handle).to_string())
777 } else {
778 None
779 }
780 };
781
782 if let Some(name) = expr_name {
783 write!(self.out, "{level}")?;
784 self.start_named_expr(module, handle, func_ctx, &name)?;
785 self.write_expr(module, handle, func_ctx)?;
786 self.named_expressions.insert(handle, name);
787 writeln!(self.out, ";")?;
788 }
789 }
790 }
791 Statement::If {
793 condition,
794 ref accept,
795 ref reject,
796 } => {
797 write!(self.out, "{level}")?;
798 write!(self.out, "if ")?;
799 self.write_expr(module, condition, func_ctx)?;
800 writeln!(self.out, " {{")?;
801
802 let l2 = level.next();
803 for sta in accept {
804 self.write_stmt(module, sta, func_ctx, l2)?;
806 }
807
808 if !reject.is_empty() {
811 writeln!(self.out, "{level}}} else {{")?;
812
813 for sta in reject {
814 self.write_stmt(module, sta, func_ctx, l2)?;
816 }
817 }
818
819 writeln!(self.out, "{level}}}")?
820 }
821 Statement::Return { value } => {
822 write!(self.out, "{level}")?;
823 write!(self.out, "return")?;
824 if let Some(return_value) = value {
825 write!(self.out, " ")?;
827 self.write_expr(module, return_value, func_ctx)?;
828 }
829 writeln!(self.out, ";")?;
830 }
831 Statement::Kill => {
833 write!(self.out, "{level}")?;
834 writeln!(self.out, "discard;")?
835 }
836 Statement::Store { pointer, value } => {
837 write!(self.out, "{level}")?;
838
839 let is_atomic_pointer = func_ctx
840 .resolve_type(pointer, &module.types)
841 .is_atomic_pointer(&module.types);
842
843 if is_atomic_pointer {
844 write!(self.out, "atomicStore(")?;
845 self.write_expr(module, pointer, func_ctx)?;
846 write!(self.out, ", ")?;
847 self.write_expr(module, value, func_ctx)?;
848 write!(self.out, ")")?;
849 } else {
850 self.write_expr_with_indirection(
851 module,
852 pointer,
853 func_ctx,
854 Indirection::Reference,
855 )?;
856 write!(self.out, " = ")?;
857 self.write_expr(module, value, func_ctx)?;
858 }
859 writeln!(self.out, ";")?
860 }
861 Statement::Call {
862 function,
863 ref arguments,
864 result,
865 } => {
866 write!(self.out, "{level}")?;
867 if let Some(expr) = result {
868 let name = Baked(expr).to_string();
869 self.start_named_expr(module, expr, func_ctx, &name)?;
870 self.named_expressions.insert(expr, name);
871 }
872 let func_name = &self.names[&NameKey::Function(function)];
873 write!(self.out, "{func_name}(")?;
874 for (index, &argument) in arguments.iter().enumerate() {
875 if index != 0 {
876 write!(self.out, ", ")?;
877 }
878 self.write_expr(module, argument, func_ctx)?;
879 }
880 writeln!(self.out, ");")?
881 }
882 Statement::Atomic {
883 pointer,
884 ref fun,
885 value,
886 result,
887 } => {
888 write!(self.out, "{level}")?;
889 if let Some(result) = result {
890 let res_name = Baked(result).to_string();
891 self.start_named_expr(module, result, func_ctx, &res_name)?;
892 self.named_expressions.insert(result, res_name);
893 }
894
895 let fun_str = fun.to_wgsl();
896 write!(self.out, "atomic{fun_str}(")?;
897 self.write_expr(module, pointer, func_ctx)?;
898 if let crate::AtomicFunction::Exchange { compare: Some(cmp) } = *fun {
899 write!(self.out, ", ")?;
900 self.write_expr(module, cmp, func_ctx)?;
901 }
902 write!(self.out, ", ")?;
903 self.write_expr(module, value, func_ctx)?;
904 writeln!(self.out, ");")?
905 }
906 Statement::ImageAtomic {
907 image,
908 coordinate,
909 array_index,
910 ref fun,
911 value,
912 } => {
913 write!(self.out, "{level}")?;
914 let fun_str = fun.to_wgsl();
915 write!(self.out, "textureAtomic{fun_str}(")?;
916 self.write_expr(module, image, func_ctx)?;
917 write!(self.out, ", ")?;
918 self.write_expr(module, coordinate, func_ctx)?;
919 if let Some(array_index_expr) = array_index {
920 write!(self.out, ", ")?;
921 self.write_expr(module, array_index_expr, func_ctx)?;
922 }
923 write!(self.out, ", ")?;
924 self.write_expr(module, value, func_ctx)?;
925 writeln!(self.out, ");")?;
926 }
927 Statement::WorkGroupUniformLoad { pointer, result } => {
928 write!(self.out, "{level}")?;
929 let res_name = Baked(result).to_string();
931 self.start_named_expr(module, result, func_ctx, &res_name)?;
932 self.named_expressions.insert(result, res_name);
933 write!(self.out, "workgroupUniformLoad(")?;
934 self.write_expr(module, pointer, func_ctx)?;
935 writeln!(self.out, ");")?;
936 }
937 Statement::ImageStore {
938 image,
939 coordinate,
940 array_index,
941 value,
942 } => {
943 write!(self.out, "{level}")?;
944 write!(self.out, "textureStore(")?;
945 self.write_expr(module, image, func_ctx)?;
946 write!(self.out, ", ")?;
947 self.write_expr(module, coordinate, func_ctx)?;
948 if let Some(array_index_expr) = array_index {
949 write!(self.out, ", ")?;
950 self.write_expr(module, array_index_expr, func_ctx)?;
951 }
952 write!(self.out, ", ")?;
953 self.write_expr(module, value, func_ctx)?;
954 writeln!(self.out, ");")?;
955 }
956 Statement::Block(ref block) => {
958 write!(self.out, "{level}")?;
959 writeln!(self.out, "{{")?;
960 for sta in block.iter() {
961 self.write_stmt(module, sta, func_ctx, level.next())?
963 }
964 writeln!(self.out, "{level}}}")?
965 }
966 Statement::Switch {
967 selector,
968 ref cases,
969 } => {
970 write!(self.out, "{level}")?;
972 write!(self.out, "switch ")?;
973 self.write_expr(module, selector, func_ctx)?;
974 writeln!(self.out, " {{")?;
975
976 let l2 = level.next();
977 let mut new_case = true;
978 for case in cases {
979 if case.fall_through && !case.body.is_empty() {
980 return Err(Error::Unimplemented(
982 "fall-through switch case block".into(),
983 ));
984 }
985
986 match case.value {
987 crate::SwitchValue::I32(value) => {
988 if new_case {
989 write!(self.out, "{l2}case ")?;
990 }
991 write!(self.out, "{value}")?;
992 }
993 crate::SwitchValue::U32(value) => {
994 if new_case {
995 write!(self.out, "{l2}case ")?;
996 }
997 write!(self.out, "{value}u")?;
998 }
999 crate::SwitchValue::Default => {
1000 if new_case {
1001 if case.fall_through {
1002 write!(self.out, "{l2}case ")?;
1003 } else {
1004 write!(self.out, "{l2}")?;
1005 }
1006 }
1007 write!(self.out, "default")?;
1008 }
1009 }
1010
1011 new_case = !case.fall_through;
1012
1013 if case.fall_through {
1014 write!(self.out, ", ")?;
1015 } else {
1016 writeln!(self.out, ": {{")?;
1017 }
1018
1019 for sta in case.body.iter() {
1020 self.write_stmt(module, sta, func_ctx, l2.next())?;
1021 }
1022
1023 if !case.fall_through {
1024 writeln!(self.out, "{l2}}}")?;
1025 }
1026 }
1027
1028 writeln!(self.out, "{level}}}")?
1029 }
1030 Statement::Loop {
1031 ref body,
1032 ref continuing,
1033 break_if,
1034 } => {
1035 write!(self.out, "{level}")?;
1036 writeln!(self.out, "loop {{")?;
1037
1038 let l2 = level.next();
1039 for sta in body.iter() {
1040 self.write_stmt(module, sta, func_ctx, l2)?;
1041 }
1042
1043 if !continuing.is_empty() || break_if.is_some() {
1048 writeln!(self.out, "{l2}continuing {{")?;
1049 for sta in continuing.iter() {
1050 self.write_stmt(module, sta, func_ctx, l2.next())?;
1051 }
1052
1053 if let Some(condition) = break_if {
1056 write!(self.out, "{}break if ", l2.next())?;
1058 self.write_expr(module, condition, func_ctx)?;
1059 writeln!(self.out, ";")?;
1061 }
1062
1063 writeln!(self.out, "{l2}}}")?;
1064 }
1065
1066 writeln!(self.out, "{level}}}")?
1067 }
1068 Statement::Break => {
1069 writeln!(self.out, "{level}break;")?;
1070 }
1071 Statement::Continue => {
1072 writeln!(self.out, "{level}continue;")?;
1073 }
1074 Statement::ControlBarrier(barrier) | Statement::MemoryBarrier(barrier) => {
1075 if barrier.contains(crate::Barrier::STORAGE) {
1076 writeln!(self.out, "{level}storageBarrier();")?;
1077 }
1078
1079 if barrier.contains(crate::Barrier::WORK_GROUP) {
1080 writeln!(self.out, "{level}workgroupBarrier();")?;
1081 }
1082
1083 if barrier.contains(crate::Barrier::SUB_GROUP) {
1084 writeln!(self.out, "{level}subgroupBarrier();")?;
1085 }
1086
1087 if barrier.contains(crate::Barrier::TEXTURE) {
1088 writeln!(self.out, "{level}textureBarrier();")?;
1089 }
1090 }
1091 Statement::RayQuery { .. } => unreachable!(),
1092 Statement::SubgroupBallot { result, predicate } => {
1093 write!(self.out, "{level}")?;
1094 let res_name = Baked(result).to_string();
1095 self.start_named_expr(module, result, func_ctx, &res_name)?;
1096 self.named_expressions.insert(result, res_name);
1097
1098 write!(self.out, "subgroupBallot(")?;
1099 if let Some(predicate) = predicate {
1100 self.write_expr(module, predicate, func_ctx)?;
1101 }
1102 writeln!(self.out, ");")?;
1103 }
1104 Statement::SubgroupCollectiveOperation {
1105 op,
1106 collective_op,
1107 argument,
1108 result,
1109 } => {
1110 write!(self.out, "{level}")?;
1111 let res_name = Baked(result).to_string();
1112 self.start_named_expr(module, result, func_ctx, &res_name)?;
1113 self.named_expressions.insert(result, res_name);
1114
1115 match (collective_op, op) {
1116 (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::All) => {
1117 write!(self.out, "subgroupAll(")?
1118 }
1119 (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::Any) => {
1120 write!(self.out, "subgroupAny(")?
1121 }
1122 (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::Add) => {
1123 write!(self.out, "subgroupAdd(")?
1124 }
1125 (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::Mul) => {
1126 write!(self.out, "subgroupMul(")?
1127 }
1128 (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::Max) => {
1129 write!(self.out, "subgroupMax(")?
1130 }
1131 (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::Min) => {
1132 write!(self.out, "subgroupMin(")?
1133 }
1134 (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::And) => {
1135 write!(self.out, "subgroupAnd(")?
1136 }
1137 (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::Or) => {
1138 write!(self.out, "subgroupOr(")?
1139 }
1140 (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::Xor) => {
1141 write!(self.out, "subgroupXor(")?
1142 }
1143 (crate::CollectiveOperation::ExclusiveScan, crate::SubgroupOperation::Add) => {
1144 write!(self.out, "subgroupExclusiveAdd(")?
1145 }
1146 (crate::CollectiveOperation::ExclusiveScan, crate::SubgroupOperation::Mul) => {
1147 write!(self.out, "subgroupExclusiveMul(")?
1148 }
1149 (crate::CollectiveOperation::InclusiveScan, crate::SubgroupOperation::Add) => {
1150 write!(self.out, "subgroupInclusiveAdd(")?
1151 }
1152 (crate::CollectiveOperation::InclusiveScan, crate::SubgroupOperation::Mul) => {
1153 write!(self.out, "subgroupInclusiveMul(")?
1154 }
1155 _ => unimplemented!(),
1156 }
1157 self.write_expr(module, argument, func_ctx)?;
1158 writeln!(self.out, ");")?;
1159 }
1160 Statement::SubgroupGather {
1161 mode,
1162 argument,
1163 result,
1164 } => {
1165 write!(self.out, "{level}")?;
1166 let res_name = Baked(result).to_string();
1167 self.start_named_expr(module, result, func_ctx, &res_name)?;
1168 self.named_expressions.insert(result, res_name);
1169
1170 match mode {
1171 crate::GatherMode::BroadcastFirst => {
1172 write!(self.out, "subgroupBroadcastFirst(")?;
1173 }
1174 crate::GatherMode::Broadcast(_) => {
1175 write!(self.out, "subgroupBroadcast(")?;
1176 }
1177 crate::GatherMode::Shuffle(_) => {
1178 write!(self.out, "subgroupShuffle(")?;
1179 }
1180 crate::GatherMode::ShuffleDown(_) => {
1181 write!(self.out, "subgroupShuffleDown(")?;
1182 }
1183 crate::GatherMode::ShuffleUp(_) => {
1184 write!(self.out, "subgroupShuffleUp(")?;
1185 }
1186 crate::GatherMode::ShuffleXor(_) => {
1187 write!(self.out, "subgroupShuffleXor(")?;
1188 }
1189 crate::GatherMode::QuadBroadcast(_) => {
1190 write!(self.out, "quadBroadcast(")?;
1191 }
1192 crate::GatherMode::QuadSwap(direction) => match direction {
1193 crate::Direction::X => {
1194 write!(self.out, "quadSwapX(")?;
1195 }
1196 crate::Direction::Y => {
1197 write!(self.out, "quadSwapY(")?;
1198 }
1199 crate::Direction::Diagonal => {
1200 write!(self.out, "quadSwapDiagonal(")?;
1201 }
1202 },
1203 }
1204 self.write_expr(module, argument, func_ctx)?;
1205 match mode {
1206 crate::GatherMode::BroadcastFirst => {}
1207 crate::GatherMode::Broadcast(index)
1208 | crate::GatherMode::Shuffle(index)
1209 | crate::GatherMode::ShuffleDown(index)
1210 | crate::GatherMode::ShuffleUp(index)
1211 | crate::GatherMode::ShuffleXor(index)
1212 | crate::GatherMode::QuadBroadcast(index) => {
1213 write!(self.out, ", ")?;
1214 self.write_expr(module, index, func_ctx)?;
1215 }
1216 crate::GatherMode::QuadSwap(_) => {}
1217 }
1218 writeln!(self.out, ");")?;
1219 }
1220 Statement::CooperativeStore { target, ref data } => {
1221 let suffix = if data.row_major { "T" } else { "" };
1222 write!(self.out, "{level}coopStore{suffix}(")?;
1223 self.write_expr(module, target, func_ctx)?;
1224 write!(self.out, ", ")?;
1225 self.write_expr(module, data.pointer, func_ctx)?;
1226 write!(self.out, ", ")?;
1227 self.write_expr(module, data.stride, func_ctx)?;
1228 writeln!(self.out, ");")?
1229 }
1230 Statement::RayPipelineFunction(fun) => match fun {
1231 crate::RayPipelineFunction::TraceRay {
1232 acceleration_structure,
1233 descriptor,
1234 payload,
1235 } => {
1236 write!(self.out, "{level}traceRay(")?;
1237 self.write_expr(module, acceleration_structure, func_ctx)?;
1238 write!(self.out, ", ")?;
1239 self.write_expr(module, descriptor, func_ctx)?;
1240 write!(self.out, ", ")?;
1241 self.write_expr(module, payload, func_ctx)?;
1242 writeln!(self.out, ");")?
1243 }
1244 },
1245 Statement::DebugPrintf {
1246 ref format,
1247 ref arguments,
1248 } => {
1249 write!(self.out, "{level}debugPrintf(\"{}\"", format)?;
1250 for &arg in arguments {
1251 write!(self.out, ", ")?;
1252 self.write_expr(module, arg, func_ctx)?;
1253 }
1254 writeln!(self.out, ");")?;
1255 }
1256 }
1257
1258 Ok(())
1259 }
1260
1261 fn plain_form_indirection(
1284 &self,
1285 expr: Handle<crate::Expression>,
1286 module: &Module,
1287 func_ctx: &back::FunctionCtx<'_>,
1288 ) -> Indirection {
1289 use crate::Expression as Ex;
1290
1291 if self.named_expressions.contains_key(&expr) {
1295 return Indirection::Ordinary;
1296 }
1297
1298 match func_ctx.expressions[expr] {
1299 Ex::LocalVariable(_) => Indirection::Reference,
1300 Ex::GlobalVariable(handle) => {
1301 let global = &module.global_variables[handle];
1302 match global.space {
1303 crate::AddressSpace::Handle => Indirection::Ordinary,
1304 _ => Indirection::Reference,
1305 }
1306 }
1307 Ex::Access { base, .. } | Ex::AccessIndex { base, .. } => {
1308 let base_ty = func_ctx.resolve_type(base, &module.types);
1309 match *base_ty {
1310 TypeInner::Pointer { .. } | TypeInner::ValuePointer { .. } => {
1311 Indirection::Reference
1312 }
1313 _ => Indirection::Ordinary,
1314 }
1315 }
1316 _ => Indirection::Ordinary,
1317 }
1318 }
1319
1320 fn start_named_expr(
1321 &mut self,
1322 module: &Module,
1323 handle: Handle<crate::Expression>,
1324 func_ctx: &back::FunctionCtx,
1325 name: &str,
1326 ) -> BackendResult {
1327 write!(self.out, "let {name}")?;
1329 if self.flags.contains(WriterFlags::EXPLICIT_TYPES) {
1330 write!(self.out, ": ")?;
1331 self.write_type_resolution(module, &func_ctx.info[handle].ty)?;
1333 }
1334
1335 write!(self.out, " = ")?;
1336 Ok(())
1337 }
1338
1339 fn write_expr(
1343 &mut self,
1344 module: &Module,
1345 expr: Handle<crate::Expression>,
1346 func_ctx: &back::FunctionCtx<'_>,
1347 ) -> BackendResult {
1348 self.write_expr_with_indirection(module, expr, func_ctx, Indirection::Ordinary)
1349 }
1350
1351 fn write_expr_with_indirection(
1364 &mut self,
1365 module: &Module,
1366 expr: Handle<crate::Expression>,
1367 func_ctx: &back::FunctionCtx<'_>,
1368 requested: Indirection,
1369 ) -> BackendResult {
1370 let plain = self.plain_form_indirection(expr, module, func_ctx);
1373 log::trace!(
1374 "expression {:?}={:?} is {:?}, expected {:?}",
1375 expr,
1376 func_ctx.expressions[expr],
1377 plain,
1378 requested,
1379 );
1380 match (requested, plain) {
1381 (Indirection::Ordinary, Indirection::Reference) => {
1382 write!(self.out, "(&")?;
1383 self.write_expr_plain_form(module, expr, func_ctx, plain)?;
1384 write!(self.out, ")")?;
1385 }
1386 (Indirection::Reference, Indirection::Ordinary) => {
1387 write!(self.out, "(*")?;
1388 self.write_expr_plain_form(module, expr, func_ctx, plain)?;
1389 write!(self.out, ")")?;
1390 }
1391 (_, _) => self.write_expr_plain_form(module, expr, func_ctx, plain)?,
1392 }
1393
1394 Ok(())
1395 }
1396
1397 fn write_const_expression(
1398 &mut self,
1399 module: &Module,
1400 expr: Handle<crate::Expression>,
1401 arena: &crate::Arena<crate::Expression>,
1402 ) -> BackendResult {
1403 self.write_possibly_const_expression(module, expr, arena, |writer, expr| {
1404 writer.write_const_expression(module, expr, arena)
1405 })
1406 }
1407
1408 fn write_possibly_const_expression<E>(
1409 &mut self,
1410 module: &Module,
1411 expr: Handle<crate::Expression>,
1412 expressions: &crate::Arena<crate::Expression>,
1413 write_expression: E,
1414 ) -> BackendResult
1415 where
1416 E: Fn(&mut Self, Handle<crate::Expression>) -> BackendResult,
1417 {
1418 use crate::Expression;
1419
1420 match expressions[expr] {
1421 Expression::Literal(literal) => match literal {
1422 crate::Literal::F16(value) => write!(self.out, "{value}h")?,
1423 crate::Literal::F32(value) => write!(self.out, "{value}f")?,
1424 crate::Literal::U16(value) => write!(self.out, "u16({value})")?,
1425 crate::Literal::I16(value) => write!(self.out, "i16({value})")?,
1426 crate::Literal::U32(value) => write!(self.out, "{value}u")?,
1427 crate::Literal::I32(value) => {
1428 if value == i32::MIN {
1432 write!(self.out, "i32({value})")?;
1433 } else {
1434 write!(self.out, "{value}i")?;
1435 }
1436 }
1437 crate::Literal::Bool(value) => write!(self.out, "{value}")?,
1438 crate::Literal::F64(value) => write!(self.out, "{value:?}lf")?,
1439 crate::Literal::I64(value) => {
1440 if value == i64::MIN {
1445 write!(self.out, "i64({} - 1)", value + 1)?;
1446 } else {
1447 write!(self.out, "{value}li")?;
1448 }
1449 }
1450 crate::Literal::U64(value) => write!(self.out, "{value:?}lu")?,
1451 crate::Literal::AbstractInt(_) | crate::Literal::AbstractFloat(_) => {
1452 return Err(Error::Custom(
1453 "Abstract types should not appear in IR presented to backends".into(),
1454 ));
1455 }
1456 },
1457 Expression::Constant(handle) => {
1458 let constant = &module.constants[handle];
1459 if constant.name.is_some() {
1460 write!(self.out, "{}", self.names[&NameKey::Constant(handle)])?;
1461 } else {
1462 self.write_const_expression(module, constant.init, &module.global_expressions)?;
1463 }
1464 }
1465 Expression::ZeroValue(ty) => {
1466 self.write_type(module, ty)?;
1467 write!(self.out, "()")?;
1468 }
1469 Expression::Compose { ty, ref components } => {
1470 self.write_type(module, ty)?;
1471 write!(self.out, "(")?;
1472 for (index, component) in components.iter().enumerate() {
1473 if index != 0 {
1474 write!(self.out, ", ")?;
1475 }
1476 write_expression(self, *component)?;
1477 }
1478 write!(self.out, ")")?
1479 }
1480 Expression::Splat { size, value } => {
1481 let size = common::vector_size_str(size);
1482 write!(self.out, "vec{size}(")?;
1483 write_expression(self, value)?;
1484 write!(self.out, ")")?;
1485 }
1486 Expression::Override(handle) => {
1487 write!(self.out, "{}", self.names[&NameKey::Override(handle)])?;
1488 }
1489 _ => unreachable!(),
1490 }
1491
1492 Ok(())
1493 }
1494
1495 fn write_expr_plain_form(
1503 &mut self,
1504 module: &Module,
1505 expr: Handle<crate::Expression>,
1506 func_ctx: &back::FunctionCtx<'_>,
1507 indirection: Indirection,
1508 ) -> BackendResult {
1509 use crate::Expression;
1510
1511 if let Some(name) = self.named_expressions.get(&expr) {
1512 write!(self.out, "{name}")?;
1513 return Ok(());
1514 }
1515
1516 let expression = &func_ctx.expressions[expr];
1517
1518 match *expression {
1527 Expression::Literal(_)
1528 | Expression::Constant(_)
1529 | Expression::ZeroValue(_)
1530 | Expression::Compose { .. }
1531 | Expression::Splat { .. } => {
1532 self.write_possibly_const_expression(
1533 module,
1534 expr,
1535 func_ctx.expressions,
1536 |writer, expr| writer.write_expr(module, expr, func_ctx),
1537 )?;
1538 }
1539 Expression::Override(handle) => {
1540 write!(self.out, "{}", self.names[&NameKey::Override(handle)])?;
1541 }
1542 Expression::FunctionArgument(pos) => {
1543 let name_key = func_ctx.argument_key(pos);
1544 let name = &self.names[&name_key];
1545 write!(self.out, "{name}")?;
1546 }
1547 Expression::Binary { op, left, right } => {
1548 write!(self.out, "(")?;
1549 self.write_expr(module, left, func_ctx)?;
1550 write!(self.out, " {} ", back::binary_operation_str(op))?;
1551 self.write_expr(module, right, func_ctx)?;
1552 write!(self.out, ")")?;
1553 }
1554 Expression::Access { base, index } => {
1555 self.write_expr_with_indirection(module, base, func_ctx, indirection)?;
1556 write!(self.out, "[")?;
1557 self.write_expr(module, index, func_ctx)?;
1558 write!(self.out, "]")?
1559 }
1560 Expression::AccessIndex { base, index } => {
1561 let base_ty_res = &func_ctx.info[base].ty;
1562 let mut resolved = base_ty_res.inner_with(&module.types);
1563
1564 self.write_expr_with_indirection(module, base, func_ctx, indirection)?;
1565
1566 let base_ty_handle = match *resolved {
1567 TypeInner::Pointer { base, space: _ } => {
1568 resolved = &module.types[base].inner;
1569 Some(base)
1570 }
1571 _ => base_ty_res.handle(),
1572 };
1573
1574 match *resolved {
1575 TypeInner::Vector { .. } => {
1576 write!(self.out, ".{}", back::COMPONENTS[index as usize])?
1578 }
1579 TypeInner::Matrix { .. }
1580 | TypeInner::Array { .. }
1581 | TypeInner::BindingArray { .. }
1582 | TypeInner::ValuePointer { .. } => write!(self.out, "[{index}]")?,
1583 TypeInner::Struct { .. } => {
1584 let ty = base_ty_handle.unwrap();
1587
1588 write!(
1589 self.out,
1590 ".{}",
1591 self.names[&NameKey::StructMember(ty, index)]
1592 )?
1593 }
1594 ref other => return Err(Error::Custom(format!("Cannot index {other:?}"))),
1595 }
1596 }
1597 Expression::ImageSample {
1598 image,
1599 sampler,
1600 gather: None,
1601 coordinate,
1602 array_index,
1603 offset,
1604 level,
1605 depth_ref,
1606 clamp_to_edge,
1607 } => {
1608 use crate::SampleLevel as Sl;
1609
1610 let suffix_cmp = match depth_ref {
1611 Some(_) => "Compare",
1612 None => "",
1613 };
1614 let suffix_level = match level {
1615 Sl::Auto => "",
1616 Sl::Zero if clamp_to_edge => "BaseClampToEdge",
1617 Sl::Zero | Sl::Exact(_) => "Level",
1618 Sl::Bias(_) => "Bias",
1619 Sl::Gradient { .. } => "Grad",
1620 };
1621
1622 write!(self.out, "textureSample{suffix_cmp}{suffix_level}(")?;
1623 self.write_expr(module, image, func_ctx)?;
1624 write!(self.out, ", ")?;
1625 self.write_expr(module, sampler, func_ctx)?;
1626 write!(self.out, ", ")?;
1627 self.write_expr(module, coordinate, func_ctx)?;
1628
1629 if let Some(array_index) = array_index {
1630 write!(self.out, ", ")?;
1631 self.write_expr(module, array_index, func_ctx)?;
1632 }
1633
1634 if let Some(depth_ref) = depth_ref {
1635 write!(self.out, ", ")?;
1636 self.write_expr(module, depth_ref, func_ctx)?;
1637 }
1638
1639 match level {
1640 Sl::Auto => {}
1641 Sl::Zero => {
1642 if depth_ref.is_none() && !clamp_to_edge {
1644 write!(self.out, ", 0.0")?;
1645 }
1646 }
1647 Sl::Exact(expr) => {
1648 write!(self.out, ", ")?;
1649 self.write_expr(module, expr, func_ctx)?;
1650 }
1651 Sl::Bias(expr) => {
1652 write!(self.out, ", ")?;
1653 self.write_expr(module, expr, func_ctx)?;
1654 }
1655 Sl::Gradient { x, y } => {
1656 write!(self.out, ", ")?;
1657 self.write_expr(module, x, func_ctx)?;
1658 write!(self.out, ", ")?;
1659 self.write_expr(module, y, func_ctx)?;
1660 }
1661 }
1662
1663 if let Some(offset) = offset {
1664 write!(self.out, ", ")?;
1665 self.write_const_expression(module, offset, func_ctx.expressions)?;
1666 }
1667
1668 write!(self.out, ")")?;
1669 }
1670
1671 Expression::ImageSample {
1672 image,
1673 sampler,
1674 gather: Some(component),
1675 coordinate,
1676 array_index,
1677 offset,
1678 level: _,
1679 depth_ref,
1680 clamp_to_edge: _,
1681 } => {
1682 let suffix_cmp = match depth_ref {
1683 Some(_) => "Compare",
1684 None => "",
1685 };
1686
1687 write!(self.out, "textureGather{suffix_cmp}(")?;
1688 match *func_ctx.resolve_type(image, &module.types) {
1689 TypeInner::Image {
1690 class: crate::ImageClass::Depth { multi: _ },
1691 ..
1692 } => {}
1693 _ => {
1694 write!(self.out, "{}, ", component as u8)?;
1695 }
1696 }
1697 self.write_expr(module, image, func_ctx)?;
1698 write!(self.out, ", ")?;
1699 self.write_expr(module, sampler, func_ctx)?;
1700 write!(self.out, ", ")?;
1701 self.write_expr(module, coordinate, func_ctx)?;
1702
1703 if let Some(array_index) = array_index {
1704 write!(self.out, ", ")?;
1705 self.write_expr(module, array_index, func_ctx)?;
1706 }
1707
1708 if let Some(depth_ref) = depth_ref {
1709 write!(self.out, ", ")?;
1710 self.write_expr(module, depth_ref, func_ctx)?;
1711 }
1712
1713 if let Some(offset) = offset {
1714 write!(self.out, ", ")?;
1715 self.write_const_expression(module, offset, func_ctx.expressions)?;
1716 }
1717
1718 write!(self.out, ")")?;
1719 }
1720 Expression::ImageQuery { image, query } => {
1721 use crate::ImageQuery as Iq;
1722
1723 let texture_function = match query {
1724 Iq::Size { .. } => "textureDimensions",
1725 Iq::NumLevels => "textureNumLevels",
1726 Iq::NumLayers => "textureNumLayers",
1727 Iq::NumSamples => "textureNumSamples",
1728 };
1729
1730 write!(self.out, "{texture_function}(")?;
1731 self.write_expr(module, image, func_ctx)?;
1732 if let Iq::Size { level: Some(level) } = query {
1733 write!(self.out, ", ")?;
1734 self.write_expr(module, level, func_ctx)?;
1735 };
1736 write!(self.out, ")")?;
1737 }
1738
1739 Expression::ImageLoad {
1740 image,
1741 coordinate,
1742 array_index,
1743 sample,
1744 level,
1745 } => {
1746 write!(self.out, "textureLoad(")?;
1747 self.write_expr(module, image, func_ctx)?;
1748 write!(self.out, ", ")?;
1749 self.write_expr(module, coordinate, func_ctx)?;
1750 if let Some(array_index) = array_index {
1751 write!(self.out, ", ")?;
1752 self.write_expr(module, array_index, func_ctx)?;
1753 }
1754 if let Some(index) = sample.or(level) {
1755 write!(self.out, ", ")?;
1756 self.write_expr(module, index, func_ctx)?;
1757 }
1758 write!(self.out, ")")?;
1759 }
1760 Expression::GlobalVariable(handle) => {
1761 let name = &self.names[&NameKey::GlobalVariable(handle)];
1762 write!(self.out, "{name}")?;
1763 }
1764
1765 Expression::As { expr, target } => {
1766 let inner = func_ctx.resolve_type(expr, &module.types);
1767 let scalar_str = target.to_wgsl_if_implemented()?;
1768 match *inner {
1769 TypeInner::Matrix { columns, rows, .. } => write!(
1770 self.out,
1771 "mat{}x{}<{}>",
1772 common::vector_size_str(columns),
1773 common::vector_size_str(rows),
1774 scalar_str
1775 )?,
1776 TypeInner::Vector { size, .. } => {
1777 write!(
1778 self.out,
1779 "vec{}<{}>",
1780 common::vector_size_str(size),
1781 scalar_str
1782 )?;
1783 }
1784 TypeInner::Scalar(_) => write!(self.out, "{scalar_str}")?,
1785 _ => {
1786 return Err(Error::Unimplemented(format!(
1787 "write_expr expression::as {inner:?}"
1788 )));
1789 }
1790 };
1791 write!(self.out, "(")?;
1792 self.write_expr(module, expr, func_ctx)?;
1793 write!(self.out, ")")?;
1794 }
1795 Expression::Bitcast { expr, ty } => {
1796 write!(self.out, "bitcast<")?;
1797 self.write_type(module, ty)?;
1798 write!(self.out, ">(")?;
1799 self.write_expr(module, expr, func_ctx)?;
1800 write!(self.out, ")")?;
1801 }
1802 Expression::Load { pointer } => {
1803 let is_atomic_pointer = func_ctx
1804 .resolve_type(pointer, &module.types)
1805 .is_atomic_pointer(&module.types);
1806
1807 if is_atomic_pointer {
1808 write!(self.out, "atomicLoad(")?;
1809 self.write_expr(module, pointer, func_ctx)?;
1810 write!(self.out, ")")?;
1811 } else {
1812 self.write_expr_with_indirection(
1813 module,
1814 pointer,
1815 func_ctx,
1816 Indirection::Reference,
1817 )?;
1818 }
1819 }
1820 Expression::LocalVariable(handle) => {
1821 write!(self.out, "{}", self.names[&func_ctx.name_key(handle)])?
1822 }
1823 Expression::ArrayLength(expr) => {
1824 write!(self.out, "arrayLength(")?;
1825 self.write_expr(module, expr, func_ctx)?;
1826 write!(self.out, ")")?;
1827 }
1828
1829 Expression::Math {
1830 fun,
1831 arg,
1832 arg1,
1833 arg2,
1834 arg3,
1835 } => {
1836 use crate::MathFunction as Mf;
1837
1838 enum Function {
1839 Regular(&'static str),
1840 InversePolyfill(InversePolyfill),
1841 }
1842
1843 let function = match fun.try_to_wgsl() {
1844 Some(name) => Function::Regular(name),
1845 None => match fun {
1846 Mf::Inverse => {
1847 let ty = func_ctx.resolve_type(arg, &module.types);
1848 let Some(overload) = InversePolyfill::find_overload(ty) else {
1849 return Err(Error::unsupported("math function", fun));
1850 };
1851
1852 Function::InversePolyfill(overload)
1853 }
1854 _ => return Err(Error::unsupported("math function", fun)),
1855 },
1856 };
1857
1858 match function {
1859 Function::Regular(fun_name) => {
1860 write!(self.out, "{fun_name}(")?;
1861 self.write_expr(module, arg, func_ctx)?;
1862 for arg in IntoIterator::into_iter([arg1, arg2, arg3]).flatten() {
1863 write!(self.out, ", ")?;
1864 self.write_expr(module, arg, func_ctx)?;
1865 }
1866 write!(self.out, ")")?
1867 }
1868 Function::InversePolyfill(inverse) => {
1869 write!(self.out, "{}(", inverse.fun_name)?;
1870 self.write_expr(module, arg, func_ctx)?;
1871 write!(self.out, ")")?;
1872 self.required_polyfills.insert(inverse);
1873 }
1874 }
1875 }
1876
1877 Expression::Swizzle {
1878 size,
1879 vector,
1880 pattern,
1881 } => {
1882 self.write_expr(module, vector, func_ctx)?;
1883 write!(self.out, ".")?;
1884 for &sc in pattern[..size as usize].iter() {
1885 self.out.write_char(back::COMPONENTS[sc as usize])?;
1886 }
1887 }
1888 Expression::Unary { op, expr } => {
1889 let unary = match op {
1890 crate::UnaryOperator::Negate => "-",
1891 crate::UnaryOperator::LogicalNot => "!",
1892 crate::UnaryOperator::BitwiseNot => "~",
1893 };
1894
1895 write!(self.out, "{unary}(")?;
1896 self.write_expr(module, expr, func_ctx)?;
1897
1898 write!(self.out, ")")?
1899 }
1900
1901 Expression::Select {
1902 condition,
1903 accept,
1904 reject,
1905 } => {
1906 write!(self.out, "select(")?;
1907 self.write_expr(module, reject, func_ctx)?;
1908 write!(self.out, ", ")?;
1909 self.write_expr(module, accept, func_ctx)?;
1910 write!(self.out, ", ")?;
1911 self.write_expr(module, condition, func_ctx)?;
1912 write!(self.out, ")")?
1913 }
1914 Expression::Derivative { axis, ctrl, expr } => {
1915 use crate::{DerivativeAxis as Axis, DerivativeControl as Ctrl};
1916 let op = match (axis, ctrl) {
1917 (Axis::X, Ctrl::Coarse) => "dpdxCoarse",
1918 (Axis::X, Ctrl::Fine) => "dpdxFine",
1919 (Axis::X, Ctrl::None) => "dpdx",
1920 (Axis::Y, Ctrl::Coarse) => "dpdyCoarse",
1921 (Axis::Y, Ctrl::Fine) => "dpdyFine",
1922 (Axis::Y, Ctrl::None) => "dpdy",
1923 (Axis::Width, Ctrl::Coarse) => "fwidthCoarse",
1924 (Axis::Width, Ctrl::Fine) => "fwidthFine",
1925 (Axis::Width, Ctrl::None) => "fwidth",
1926 };
1927 write!(self.out, "{op}(")?;
1928 self.write_expr(module, expr, func_ctx)?;
1929 write!(self.out, ")")?
1930 }
1931 Expression::Relational { fun, argument } => {
1932 use crate::RelationalFunction as Rf;
1933
1934 let fun_name = match fun {
1935 Rf::All => "all",
1936 Rf::Any => "any",
1937 _ => return Err(Error::UnsupportedRelationalFunction(fun)),
1938 };
1939 write!(self.out, "{fun_name}(")?;
1940
1941 self.write_expr(module, argument, func_ctx)?;
1942
1943 write!(self.out, ")")?
1944 }
1945 Expression::RayQueryGetIntersection { .. }
1947 | Expression::RayQueryVertexPositions { .. } => unreachable!(),
1948 Expression::CallResult(_)
1950 | Expression::AtomicResult { .. }
1951 | Expression::RayQueryProceedResult
1952 | Expression::SubgroupBallotResult
1953 | Expression::SubgroupOperationResult { .. }
1954 | Expression::WorkGroupUniformLoadResult { .. } => {}
1955 Expression::CooperativeLoad {
1956 columns,
1957 rows,
1958 role,
1959 ref data,
1960 } => {
1961 let suffix = if data.row_major { "T" } else { "" };
1962 let scalar = func_ctx.info[data.pointer]
1963 .ty
1964 .inner_with(&module.types)
1965 .pointer_base_type()
1966 .unwrap()
1967 .inner_with(&module.types)
1968 .scalar()
1969 .unwrap();
1970 write!(
1971 self.out,
1972 "coopLoad{suffix}<coop_mat{}x{}<{},{:?}>>(",
1973 columns as u32,
1974 rows as u32,
1975 scalar.try_to_wgsl().unwrap(),
1976 role,
1977 )?;
1978 self.write_expr(module, data.pointer, func_ctx)?;
1979 write!(self.out, ", ")?;
1980 self.write_expr(module, data.stride, func_ctx)?;
1981 write!(self.out, ")")?;
1982 }
1983 Expression::CooperativeMultiplyAdd { a, b, c } => {
1984 write!(self.out, "coopMultiplyAdd(")?;
1985 self.write_expr(module, a, func_ctx)?;
1986 write!(self.out, ", ")?;
1987 self.write_expr(module, b, func_ctx)?;
1988 write!(self.out, ", ")?;
1989 self.write_expr(module, c, func_ctx)?;
1990 write!(self.out, ")")?;
1991 }
1992 }
1993
1994 Ok(())
1995 }
1996
1997 fn write_global(
2001 &mut self,
2002 module: &Module,
2003 global: &crate::GlobalVariable,
2004 handle: Handle<crate::GlobalVariable>,
2005 ) -> BackendResult {
2006 if let Some(ref binding) = global.binding {
2008 self.write_attributes_line(&[
2009 Attribute::Group(binding.group),
2010 Attribute::Binding(binding.binding),
2011 ])?;
2012 }
2013
2014 if global
2015 .memory_decorations
2016 .contains(crate::MemoryDecorations::COHERENT)
2017 {
2018 write!(self.out, "@coherent ")?;
2019 }
2020 if global
2021 .memory_decorations
2022 .contains(crate::MemoryDecorations::VOLATILE)
2023 {
2024 write!(self.out, "@volatile ")?;
2025 }
2026
2027 write!(self.out, "var")?;
2029 let (address, maybe_access) = address_space_str(global.space);
2030 if let Some(space) = address {
2031 write!(self.out, "<{space}")?;
2032 if let Some(access) = maybe_access {
2033 write!(self.out, ", {access}")?;
2034 }
2035 write!(self.out, ">")?;
2036 }
2037 write!(
2038 self.out,
2039 " {}: ",
2040 self.names[&NameKey::GlobalVariable(handle)]
2041 )?;
2042
2043 self.write_type(module, global.ty)?;
2045
2046 if let Some(init) = global.init {
2048 write!(self.out, " = ")?;
2049 self.write_const_expression(module, init, &module.global_expressions)?;
2050 }
2051
2052 writeln!(self.out, ";")?;
2054
2055 Ok(())
2056 }
2057
2058 fn write_global_constant(
2063 &mut self,
2064 module: &Module,
2065 handle: Handle<crate::Constant>,
2066 ) -> BackendResult {
2067 let name = &self.names[&NameKey::Constant(handle)];
2068 write!(self.out, "const {name}: ")?;
2070 self.write_type(module, module.constants[handle].ty)?;
2071 write!(self.out, " = ")?;
2072 let init = module.constants[handle].init;
2073 self.write_const_expression(module, init, &module.global_expressions)?;
2074 writeln!(self.out, ";")?;
2075
2076 Ok(())
2077 }
2078
2079 fn write_override(
2084 &mut self,
2085 module: &Module,
2086 handle: Handle<crate::Override>,
2087 ) -> BackendResult {
2088 let override_ = &module.overrides[handle];
2089 let name = &self.names[&NameKey::Override(handle)];
2090
2091 if let Some(id) = override_.id {
2093 write!(self.out, "@id({id}) ")?;
2094 }
2095
2096 write!(self.out, "override {name}: ")?;
2098 self.write_type(module, override_.ty)?;
2099
2100 if let Some(init) = override_.init {
2102 write!(self.out, " = ")?;
2103 self.write_const_expression(module, init, &module.global_expressions)?;
2104 }
2105
2106 writeln!(self.out, ";")?;
2107
2108 Ok(())
2109 }
2110
2111 pub fn finish(self) -> W {
2113 self.out
2114 }
2115}
2116
2117struct WriterTypeContext<'m> {
2118 module: &'m Module,
2119 names: &'m crate::FastHashMap<NameKey, String>,
2120}
2121
2122impl TypeContext for WriterTypeContext<'_> {
2123 fn lookup_type(&self, handle: Handle<crate::Type>) -> &crate::Type {
2124 &self.module.types[handle]
2125 }
2126
2127 fn type_name(&self, handle: Handle<crate::Type>) -> &str {
2128 self.names[&NameKey::Type(handle)].as_str()
2129 }
2130
2131 fn write_unnamed_struct<W: Write>(&self, _: &TypeInner, _: &mut W) -> core::fmt::Result {
2132 unreachable!("the WGSL back end should always provide type handles");
2133 }
2134
2135 fn write_override<W: Write>(
2136 &self,
2137 handle: Handle<crate::Override>,
2138 out: &mut W,
2139 ) -> core::fmt::Result {
2140 write!(out, "{}", self.names[&NameKey::Override(handle)])
2141 }
2142
2143 fn write_non_wgsl_inner<W: Write>(&self, _: &TypeInner, _: &mut W) -> core::fmt::Result {
2144 unreachable!("backends should only be passed validated modules");
2145 }
2146
2147 fn write_non_wgsl_scalar<W: Write>(&self, _: crate::Scalar, _: &mut W) -> core::fmt::Result {
2148 unreachable!("backends should only be passed validated modules");
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 blend_src: None,
2166 per_primitive,
2167 } => {
2168 let mut attrs = vec![
2169 Attribute::Location(location),
2170 Attribute::Interpolate(interpolation, sampling),
2171 ];
2172 if per_primitive {
2173 attrs.push(Attribute::PerPrimitive);
2174 }
2175 attrs
2176 }
2177 crate::Binding::Location {
2178 location,
2179 interpolation,
2180 sampling,
2181 blend_src: Some(blend_src),
2182 per_primitive,
2183 } => {
2184 let mut attrs = vec![
2185 Attribute::Location(location),
2186 Attribute::BlendSrc(blend_src),
2187 Attribute::Interpolate(interpolation, sampling),
2188 ];
2189 if per_primitive {
2190 attrs.push(Attribute::PerPrimitive);
2191 }
2192 attrs
2193 }
2194 }
2195}