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 {
1766 expr,
1767 kind,
1768 convert,
1769 } => {
1770 let inner = func_ctx.resolve_type(expr, &module.types);
1771 match *inner {
1772 TypeInner::Matrix {
1773 columns,
1774 rows,
1775 scalar,
1776 } => {
1777 let scalar = crate::Scalar {
1778 kind,
1779 width: convert.unwrap_or(scalar.width),
1780 };
1781 let scalar_kind_str = scalar.to_wgsl_if_implemented()?;
1782 write!(
1783 self.out,
1784 "mat{}x{}<{}>",
1785 common::vector_size_str(columns),
1786 common::vector_size_str(rows),
1787 scalar_kind_str
1788 )?;
1789 }
1790 TypeInner::Vector {
1791 size,
1792 scalar: crate::Scalar { width, .. },
1793 } => {
1794 let scalar = crate::Scalar {
1795 kind,
1796 width: convert.unwrap_or(width),
1797 };
1798 let vector_size_str = common::vector_size_str(size);
1799 let scalar_kind_str = scalar.to_wgsl_if_implemented()?;
1800 if convert.is_some() {
1801 write!(self.out, "vec{vector_size_str}<{scalar_kind_str}>")?;
1802 } else {
1803 write!(self.out, "bitcast<vec{vector_size_str}<{scalar_kind_str}>>")?;
1804 }
1805 }
1806 TypeInner::Scalar(crate::Scalar { width, .. }) => {
1807 let scalar = crate::Scalar {
1808 kind,
1809 width: convert.unwrap_or(width),
1810 };
1811 let scalar_kind_str = scalar.to_wgsl_if_implemented()?;
1812 if convert.is_some() {
1813 write!(self.out, "{scalar_kind_str}")?
1814 } else {
1815 write!(self.out, "bitcast<{scalar_kind_str}>")?
1816 }
1817 }
1818 _ => {
1819 return Err(Error::Unimplemented(format!(
1820 "write_expr expression::as {inner:?}"
1821 )));
1822 }
1823 };
1824 write!(self.out, "(")?;
1825 self.write_expr(module, expr, func_ctx)?;
1826 write!(self.out, ")")?;
1827 }
1828 Expression::Load { pointer } => {
1829 let is_atomic_pointer = func_ctx
1830 .resolve_type(pointer, &module.types)
1831 .is_atomic_pointer(&module.types);
1832
1833 if is_atomic_pointer {
1834 write!(self.out, "atomicLoad(")?;
1835 self.write_expr(module, pointer, func_ctx)?;
1836 write!(self.out, ")")?;
1837 } else {
1838 self.write_expr_with_indirection(
1839 module,
1840 pointer,
1841 func_ctx,
1842 Indirection::Reference,
1843 )?;
1844 }
1845 }
1846 Expression::LocalVariable(handle) => {
1847 write!(self.out, "{}", self.names[&func_ctx.name_key(handle)])?
1848 }
1849 Expression::ArrayLength(expr) => {
1850 write!(self.out, "arrayLength(")?;
1851 self.write_expr(module, expr, func_ctx)?;
1852 write!(self.out, ")")?;
1853 }
1854
1855 Expression::Math {
1856 fun,
1857 arg,
1858 arg1,
1859 arg2,
1860 arg3,
1861 } => {
1862 use crate::MathFunction as Mf;
1863
1864 enum Function {
1865 Regular(&'static str),
1866 InversePolyfill(InversePolyfill),
1867 }
1868
1869 let function = match fun.try_to_wgsl() {
1870 Some(name) => Function::Regular(name),
1871 None => match fun {
1872 Mf::Inverse => {
1873 let ty = func_ctx.resolve_type(arg, &module.types);
1874 let Some(overload) = InversePolyfill::find_overload(ty) else {
1875 return Err(Error::unsupported("math function", fun));
1876 };
1877
1878 Function::InversePolyfill(overload)
1879 }
1880 _ => return Err(Error::unsupported("math function", fun)),
1881 },
1882 };
1883
1884 match function {
1885 Function::Regular(fun_name) => {
1886 write!(self.out, "{fun_name}(")?;
1887 self.write_expr(module, arg, func_ctx)?;
1888 for arg in IntoIterator::into_iter([arg1, arg2, arg3]).flatten() {
1889 write!(self.out, ", ")?;
1890 self.write_expr(module, arg, func_ctx)?;
1891 }
1892 write!(self.out, ")")?
1893 }
1894 Function::InversePolyfill(inverse) => {
1895 write!(self.out, "{}(", inverse.fun_name)?;
1896 self.write_expr(module, arg, func_ctx)?;
1897 write!(self.out, ")")?;
1898 self.required_polyfills.insert(inverse);
1899 }
1900 }
1901 }
1902
1903 Expression::Swizzle {
1904 size,
1905 vector,
1906 pattern,
1907 } => {
1908 self.write_expr(module, vector, func_ctx)?;
1909 write!(self.out, ".")?;
1910 for &sc in pattern[..size as usize].iter() {
1911 self.out.write_char(back::COMPONENTS[sc as usize])?;
1912 }
1913 }
1914 Expression::Unary { op, expr } => {
1915 let unary = match op {
1916 crate::UnaryOperator::Negate => "-",
1917 crate::UnaryOperator::LogicalNot => "!",
1918 crate::UnaryOperator::BitwiseNot => "~",
1919 };
1920
1921 write!(self.out, "{unary}(")?;
1922 self.write_expr(module, expr, func_ctx)?;
1923
1924 write!(self.out, ")")?
1925 }
1926
1927 Expression::Select {
1928 condition,
1929 accept,
1930 reject,
1931 } => {
1932 write!(self.out, "select(")?;
1933 self.write_expr(module, reject, func_ctx)?;
1934 write!(self.out, ", ")?;
1935 self.write_expr(module, accept, func_ctx)?;
1936 write!(self.out, ", ")?;
1937 self.write_expr(module, condition, func_ctx)?;
1938 write!(self.out, ")")?
1939 }
1940 Expression::Derivative { axis, ctrl, expr } => {
1941 use crate::{DerivativeAxis as Axis, DerivativeControl as Ctrl};
1942 let op = match (axis, ctrl) {
1943 (Axis::X, Ctrl::Coarse) => "dpdxCoarse",
1944 (Axis::X, Ctrl::Fine) => "dpdxFine",
1945 (Axis::X, Ctrl::None) => "dpdx",
1946 (Axis::Y, Ctrl::Coarse) => "dpdyCoarse",
1947 (Axis::Y, Ctrl::Fine) => "dpdyFine",
1948 (Axis::Y, Ctrl::None) => "dpdy",
1949 (Axis::Width, Ctrl::Coarse) => "fwidthCoarse",
1950 (Axis::Width, Ctrl::Fine) => "fwidthFine",
1951 (Axis::Width, Ctrl::None) => "fwidth",
1952 };
1953 write!(self.out, "{op}(")?;
1954 self.write_expr(module, expr, func_ctx)?;
1955 write!(self.out, ")")?
1956 }
1957 Expression::Relational { fun, argument } => {
1958 use crate::RelationalFunction as Rf;
1959
1960 let fun_name = match fun {
1961 Rf::All => "all",
1962 Rf::Any => "any",
1963 _ => return Err(Error::UnsupportedRelationalFunction(fun)),
1964 };
1965 write!(self.out, "{fun_name}(")?;
1966
1967 self.write_expr(module, argument, func_ctx)?;
1968
1969 write!(self.out, ")")?
1970 }
1971 Expression::RayQueryGetIntersection { .. }
1973 | Expression::RayQueryVertexPositions { .. } => unreachable!(),
1974 Expression::CallResult(_)
1976 | Expression::AtomicResult { .. }
1977 | Expression::RayQueryProceedResult
1978 | Expression::SubgroupBallotResult
1979 | Expression::SubgroupOperationResult { .. }
1980 | Expression::WorkGroupUniformLoadResult { .. } => {}
1981 Expression::CooperativeLoad {
1982 columns,
1983 rows,
1984 role,
1985 ref data,
1986 } => {
1987 let suffix = if data.row_major { "T" } else { "" };
1988 let scalar = func_ctx.info[data.pointer]
1989 .ty
1990 .inner_with(&module.types)
1991 .pointer_base_type()
1992 .unwrap()
1993 .inner_with(&module.types)
1994 .scalar()
1995 .unwrap();
1996 write!(
1997 self.out,
1998 "coopLoad{suffix}<coop_mat{}x{}<{},{:?}>>(",
1999 columns as u32,
2000 rows as u32,
2001 scalar.try_to_wgsl().unwrap(),
2002 role,
2003 )?;
2004 self.write_expr(module, data.pointer, func_ctx)?;
2005 write!(self.out, ", ")?;
2006 self.write_expr(module, data.stride, func_ctx)?;
2007 write!(self.out, ")")?;
2008 }
2009 Expression::CooperativeMultiplyAdd { a, b, c } => {
2010 write!(self.out, "coopMultiplyAdd(")?;
2011 self.write_expr(module, a, func_ctx)?;
2012 write!(self.out, ", ")?;
2013 self.write_expr(module, b, func_ctx)?;
2014 write!(self.out, ", ")?;
2015 self.write_expr(module, c, func_ctx)?;
2016 write!(self.out, ")")?;
2017 }
2018 }
2019
2020 Ok(())
2021 }
2022
2023 fn write_global(
2027 &mut self,
2028 module: &Module,
2029 global: &crate::GlobalVariable,
2030 handle: Handle<crate::GlobalVariable>,
2031 ) -> BackendResult {
2032 if let Some(ref binding) = global.binding {
2034 self.write_attributes_line(&[
2035 Attribute::Group(binding.group),
2036 Attribute::Binding(binding.binding),
2037 ])?;
2038 }
2039
2040 if global
2041 .memory_decorations
2042 .contains(crate::MemoryDecorations::COHERENT)
2043 {
2044 write!(self.out, "@coherent ")?;
2045 }
2046 if global
2047 .memory_decorations
2048 .contains(crate::MemoryDecorations::VOLATILE)
2049 {
2050 write!(self.out, "@volatile ")?;
2051 }
2052
2053 write!(self.out, "var")?;
2055 let (address, maybe_access) = address_space_str(global.space);
2056 if let Some(space) = address {
2057 write!(self.out, "<{space}")?;
2058 if let Some(access) = maybe_access {
2059 write!(self.out, ", {access}")?;
2060 }
2061 write!(self.out, ">")?;
2062 }
2063 write!(
2064 self.out,
2065 " {}: ",
2066 &self.names[&NameKey::GlobalVariable(handle)]
2067 )?;
2068
2069 self.write_type(module, global.ty)?;
2071
2072 if let Some(init) = global.init {
2074 write!(self.out, " = ")?;
2075 self.write_const_expression(module, init, &module.global_expressions)?;
2076 }
2077
2078 writeln!(self.out, ";")?;
2080
2081 Ok(())
2082 }
2083
2084 fn write_global_constant(
2089 &mut self,
2090 module: &Module,
2091 handle: Handle<crate::Constant>,
2092 ) -> BackendResult {
2093 let name = &self.names[&NameKey::Constant(handle)];
2094 write!(self.out, "const {name}: ")?;
2096 self.write_type(module, module.constants[handle].ty)?;
2097 write!(self.out, " = ")?;
2098 let init = module.constants[handle].init;
2099 self.write_const_expression(module, init, &module.global_expressions)?;
2100 writeln!(self.out, ";")?;
2101
2102 Ok(())
2103 }
2104
2105 fn write_override(
2110 &mut self,
2111 module: &Module,
2112 handle: Handle<crate::Override>,
2113 ) -> BackendResult {
2114 let override_ = &module.overrides[handle];
2115 let name = &self.names[&NameKey::Override(handle)];
2116
2117 if let Some(id) = override_.id {
2119 write!(self.out, "@id({id}) ")?;
2120 }
2121
2122 write!(self.out, "override {name}: ")?;
2124 self.write_type(module, override_.ty)?;
2125
2126 if let Some(init) = override_.init {
2128 write!(self.out, " = ")?;
2129 self.write_const_expression(module, init, &module.global_expressions)?;
2130 }
2131
2132 writeln!(self.out, ";")?;
2133
2134 Ok(())
2135 }
2136
2137 pub fn finish(self) -> W {
2139 self.out
2140 }
2141}
2142
2143struct WriterTypeContext<'m> {
2144 module: &'m Module,
2145 names: &'m crate::FastHashMap<NameKey, String>,
2146}
2147
2148impl TypeContext for WriterTypeContext<'_> {
2149 fn lookup_type(&self, handle: Handle<crate::Type>) -> &crate::Type {
2150 &self.module.types[handle]
2151 }
2152
2153 fn type_name(&self, handle: Handle<crate::Type>) -> &str {
2154 self.names[&NameKey::Type(handle)].as_str()
2155 }
2156
2157 fn write_unnamed_struct<W: Write>(&self, _: &TypeInner, _: &mut W) -> core::fmt::Result {
2158 unreachable!("the WGSL back end should always provide type handles");
2159 }
2160
2161 fn write_override<W: Write>(
2162 &self,
2163 handle: Handle<crate::Override>,
2164 out: &mut W,
2165 ) -> core::fmt::Result {
2166 write!(out, "{}", self.names[&NameKey::Override(handle)])
2167 }
2168
2169 fn write_non_wgsl_inner<W: Write>(&self, _: &TypeInner, _: &mut W) -> core::fmt::Result {
2170 unreachable!("backends should only be passed validated modules");
2171 }
2172
2173 fn write_non_wgsl_scalar<W: Write>(&self, _: crate::Scalar, _: &mut W) -> core::fmt::Result {
2174 unreachable!("backends should only be passed validated modules");
2175 }
2176}
2177
2178fn map_binding_to_attribute(binding: &crate::Binding) -> Vec<Attribute> {
2179 match *binding {
2180 crate::Binding::BuiltIn(built_in) => {
2181 if let crate::BuiltIn::Position { invariant: true } = built_in {
2182 vec![Attribute::BuiltIn(built_in), Attribute::Invariant]
2183 } else {
2184 vec![Attribute::BuiltIn(built_in)]
2185 }
2186 }
2187 crate::Binding::Location {
2188 location,
2189 interpolation,
2190 sampling,
2191 blend_src: None,
2192 per_primitive,
2193 } => {
2194 let mut attrs = vec![
2195 Attribute::Location(location),
2196 Attribute::Interpolate(interpolation, sampling),
2197 ];
2198 if per_primitive {
2199 attrs.push(Attribute::PerPrimitive);
2200 }
2201 attrs
2202 }
2203 crate::Binding::Location {
2204 location,
2205 interpolation,
2206 sampling,
2207 blend_src: Some(blend_src),
2208 per_primitive,
2209 } => {
2210 let mut attrs = vec![
2211 Attribute::Location(location),
2212 Attribute::BlendSrc(blend_src),
2213 Attribute::Interpolate(interpolation, sampling),
2214 ];
2215 if per_primitive {
2216 attrs.push(Attribute::PerPrimitive);
2217 }
2218 attrs
2219 }
2220 }
2221}