1use alloc::{
2 format,
3 string::{String, ToString},
4 vec::Vec,
5};
6use core::{
7 fmt::{self, Write as _},
8 mem,
9};
10
11use super::{
12 help,
13 help::{
14 WrappedArrayLength, WrappedConstructor, WrappedImageQuery, WrappedStructMatrixAccess,
15 WrappedZeroValue,
16 },
17 storage::StoreValue,
18 BackendResult, Error, FragmentEntryPoint, Options, PipelineOptions, ShaderModel,
19};
20use crate::{
21 back::{self, get_entry_points, Baked},
22 common,
23 proc::{self, index, ExternalTextureNameKey, NameKey},
24 valid, Handle, Module, RayQueryFunction, Scalar, ScalarKind, ShaderStage, TypeInner,
25};
26
27const LOCATION_SEMANTIC: &str = "LOC";
28const SPECIAL_CBUF_TYPE: &str = "NagaConstants";
29const SPECIAL_CBUF_VAR: &str = "_NagaConstants";
30const SPECIAL_FIRST_VERTEX: &str = "first_vertex";
31const SPECIAL_FIRST_INSTANCE: &str = "first_instance";
32const SPECIAL_OTHER: &str = "other";
33
34pub(crate) const MODF_FUNCTION: &str = "naga_modf";
35pub(crate) const FREXP_FUNCTION: &str = "naga_frexp";
36pub(crate) const EXTRACT_BITS_FUNCTION: &str = "naga_extractBits";
37pub(crate) const INSERT_BITS_FUNCTION: &str = "naga_insertBits";
38pub(crate) const SAMPLER_HEAP_VAR: &str = "nagaSamplerHeap";
39pub(crate) const COMPARISON_SAMPLER_HEAP_VAR: &str = "nagaComparisonSamplerHeap";
40pub(crate) const SAMPLE_EXTERNAL_TEXTURE_FUNCTION: &str = "nagaSampleExternalTexture";
41pub(crate) const ABS_FUNCTION: &str = "naga_abs";
42pub(crate) const DIV_FUNCTION: &str = "naga_div";
43pub(crate) const MOD_FUNCTION: &str = "naga_mod";
44pub(crate) const NEG_FUNCTION: &str = "naga_neg";
45pub(crate) const F2I32_FUNCTION: &str = "naga_f2i32";
46pub(crate) const F2U32_FUNCTION: &str = "naga_f2u32";
47pub(crate) const F2I64_FUNCTION: &str = "naga_f2i64";
48pub(crate) const F2U64_FUNCTION: &str = "naga_f2u64";
49pub(crate) const IMAGE_SAMPLE_BASE_CLAMP_TO_EDGE_FUNCTION: &str =
50 "nagaTextureSampleBaseClampToEdge";
51pub(crate) const IMAGE_LOAD_EXTERNAL_FUNCTION: &str = "nagaTextureLoadExternal";
52pub(crate) const RAY_QUERY_TRACKER_VARIABLE_PREFIX: &str = "naga_query_init_tracker_for_";
53pub(crate) const INTERNAL_PREFIX: &str = "naga_";
55
56enum Index {
57 Expression(Handle<crate::Expression>),
58 Static(u32),
59}
60
61pub(super) struct EpStructMember {
62 pub(super) name: String,
63 pub(super) ty: Handle<crate::Type>,
64 pub(super) binding: Option<crate::Binding>,
67 pub(super) index: u32,
68}
69
70pub(super) struct EntryPointBinding {
73 pub(super) arg_name: String,
76 pub(super) ty_name: String,
78 pub(super) members: Vec<EpStructMember>,
80 pub(super) local_invocation_index_name: Option<String>,
81}
82
83pub(super) struct EntryPointInterface {
84 pub(crate) input: Option<EntryPointBinding>,
89 pub(crate) output: Option<EntryPointBinding>,
93 pub(crate) mesh_vertices: Option<EntryPointBinding>,
94 pub(crate) mesh_primitives: Option<EntryPointBinding>,
95 pub(crate) mesh_indices: Option<EntryPointBinding>,
96}
97
98#[derive(Clone, Eq, PartialEq, PartialOrd, Ord)]
99enum InterfaceKey {
100 Location(u32),
101 BuiltIn(crate::BuiltIn),
102 Other,
103}
104
105impl InterfaceKey {
106 const fn new(binding: Option<&crate::Binding>) -> Self {
107 match binding {
108 Some(&crate::Binding::Location { location, .. }) => Self::Location(location),
109 Some(&crate::Binding::BuiltIn(built_in)) => Self::BuiltIn(built_in),
110 None => Self::Other,
111 }
112 }
113}
114
115#[derive(Copy, Clone, PartialEq)]
116pub(super) enum Io {
117 Input,
118 Output,
119 MeshVertices,
120 MeshPrimitives,
121}
122
123pub(super) struct NestedEntryPointArgs {
125 pub user_args: Vec<String>,
127 pub task_payload: Option<String>,
128 pub local_invocation_index: String,
129}
130
131const fn is_subgroup_builtin_binding(binding: &Option<crate::Binding>) -> bool {
132 let &Some(crate::Binding::BuiltIn(builtin)) = binding else {
133 return false;
134 };
135 matches!(
136 builtin,
137 crate::BuiltIn::SubgroupSize
138 | crate::BuiltIn::SubgroupInvocationId
139 | crate::BuiltIn::NumSubgroups
140 | crate::BuiltIn::SubgroupId
141 )
142}
143
144struct BindingArraySamplerInfo {
146 sampler_heap_name: &'static str,
148 sampler_index_buffer_name: String,
150 binding_array_base_index_name: String,
152}
153
154impl<'a, W: fmt::Write> super::Writer<'a, W> {
155 pub fn new(out: W, options: &'a Options, pipeline_options: &'a PipelineOptions) -> Self {
156 Self {
157 out,
158 names: crate::FastHashMap::default(),
159 namer: proc::Namer::default(),
160 options,
161 pipeline_options,
162 entry_point_io: crate::FastHashMap::default(),
163 named_expressions: crate::NamedExpressions::default(),
164 wrapped: super::Wrapped::default(),
165 written_committed_intersection: false,
166 written_candidate_intersection: false,
167 continue_ctx: back::continue_forward::ContinueCtx::default(),
168 temp_access_chain: Vec::new(),
169 need_bake_expressions: Default::default(),
170 function_task_payload_var: Default::default(),
171 }
172 }
173
174 fn reset(&mut self, module: &Module) {
175 self.names.clear();
176 self.namer.reset(
177 module,
178 &super::keywords::RESERVED_SET,
179 proc::KeywordSet::empty(),
180 &super::keywords::RESERVED_CASE_INSENSITIVE_SET,
181 super::keywords::RESERVED_PREFIXES,
182 &mut self.names,
183 );
184 self.entry_point_io.clear();
185 self.named_expressions.clear();
186 self.wrapped.clear();
187 self.written_committed_intersection = false;
188 self.written_candidate_intersection = false;
189 self.continue_ctx.clear();
190 self.need_bake_expressions.clear();
191 self.function_task_payload_var.clear();
192 }
193
194 fn gen_force_bounded_loop_statements(
202 &mut self,
203 level: back::Level,
204 ) -> Option<(String, String)> {
205 if !self.options.force_loop_bounding {
206 return None;
207 }
208
209 let loop_bound_name = self.namer.call("loop_bound");
210 let max = u32::MAX;
211 let decl = format!("{level}uint2 {loop_bound_name} = uint2({max}u, {max}u);");
214 let level = level.next();
215 let break_and_inc = format!(
216 "{level}if (all({loop_bound_name} == uint2(0u, 0u))) {{ break; }}
217{level}{loop_bound_name} -= uint2({loop_bound_name}.y == 0u, 1u);"
218 );
219
220 Some((decl, break_and_inc))
221 }
222
223 fn update_expressions_to_bake(
228 &mut self,
229 module: &Module,
230 func: &crate::Function,
231 info: &valid::FunctionInfo,
232 ) {
233 use crate::Expression;
234 self.need_bake_expressions.clear();
235 for (exp_handle, expr) in func.expressions.iter() {
236 let expr_info = &info[exp_handle];
237 let min_ref_count = func.expressions[exp_handle].bake_ref_count();
238 if min_ref_count <= expr_info.ref_count {
239 self.need_bake_expressions.insert(exp_handle);
240 }
241 if let Expression::Load { pointer } = *expr {
242 if info[pointer]
243 .ty
244 .inner_with(&module.types)
245 .is_atomic_pointer(&module.types)
246 {
247 self.need_bake_expressions.insert(exp_handle);
248 }
249 }
250
251 if let Expression::Math { fun, arg, arg1, .. } = *expr {
252 match fun {
253 crate::MathFunction::Asinh
254 | crate::MathFunction::Acosh
255 | crate::MathFunction::Atanh
256 | crate::MathFunction::Unpack2x16float
257 | crate::MathFunction::Unpack2x16snorm
258 | crate::MathFunction::Unpack2x16unorm
259 | crate::MathFunction::Unpack4x8snorm
260 | crate::MathFunction::Unpack4x8unorm
261 | crate::MathFunction::Unpack4xI8
262 | crate::MathFunction::Unpack4xU8
263 | crate::MathFunction::Pack2x16float
264 | crate::MathFunction::Pack2x16snorm
265 | crate::MathFunction::Pack2x16unorm
266 | crate::MathFunction::Pack4x8snorm
267 | crate::MathFunction::Pack4x8unorm
268 | crate::MathFunction::Pack4xI8
269 | crate::MathFunction::Pack4xU8
270 | crate::MathFunction::Pack4xI8Clamp
271 | crate::MathFunction::Pack4xU8Clamp => {
272 self.need_bake_expressions.insert(arg);
273 }
274 crate::MathFunction::CountLeadingZeros => {
275 let inner = info[exp_handle].ty.inner_with(&module.types);
276 if let Some(ScalarKind::Sint) = inner.scalar_kind() {
277 self.need_bake_expressions.insert(arg);
278 }
279 }
280 crate::MathFunction::Dot4U8Packed | crate::MathFunction::Dot4I8Packed => {
281 self.need_bake_expressions.insert(arg);
282 self.need_bake_expressions.insert(arg1.unwrap());
283 }
284 _ => {}
285 }
286 }
287
288 if let Expression::Derivative { axis, ctrl, expr } = *expr {
289 use crate::{DerivativeAxis as Axis, DerivativeControl as Ctrl};
290 if axis == Axis::Width && (ctrl == Ctrl::Coarse || ctrl == Ctrl::Fine) {
291 self.need_bake_expressions.insert(expr);
292 }
293 }
294
295 if let Expression::GlobalVariable(_) = *expr {
296 let inner = info[exp_handle].ty.inner_with(&module.types);
297
298 if let TypeInner::Sampler { .. } = *inner {
299 self.need_bake_expressions.insert(exp_handle);
300 }
301 }
302 }
303 for statement in func.body.iter() {
304 match *statement {
305 crate::Statement::SubgroupCollectiveOperation {
306 op: _,
307 collective_op: crate::CollectiveOperation::InclusiveScan,
308 argument,
309 result: _,
310 } => {
311 self.need_bake_expressions.insert(argument);
312 }
313 crate::Statement::Atomic {
314 fun: crate::AtomicFunction::Exchange { compare: Some(cmp) },
315 ..
316 } => {
317 self.need_bake_expressions.insert(cmp);
318 }
319 _ => {}
320 }
321 }
322 }
323
324 pub fn write(
325 &mut self,
326 module: &Module,
327 module_info: &valid::ModuleInfo,
328 fragment_entry_point: Option<&FragmentEntryPoint<'_>>,
329 ) -> Result<super::ReflectionInfo, Error> {
330 self.reset(module);
331
332 if module.uses_mesh_shaders() && self.options.shader_model < ShaderModel::V6_5 {
333 return Err(Error::ShaderModelTooLow(
334 "mesh shaders".to_string(),
335 ShaderModel::V6_5,
336 ));
337 }
338
339 if let Some(ref bt) = self.options.special_constants_binding {
341 writeln!(self.out, "struct {SPECIAL_CBUF_TYPE} {{")?;
342 writeln!(self.out, "{}int {};", back::INDENT, SPECIAL_FIRST_VERTEX)?;
343 writeln!(self.out, "{}int {};", back::INDENT, SPECIAL_FIRST_INSTANCE)?;
344 writeln!(self.out, "{}uint {};", back::INDENT, SPECIAL_OTHER)?;
345 writeln!(self.out, "}};")?;
346 write!(
347 self.out,
348 "ConstantBuffer<{}> {}: register(b{}",
349 SPECIAL_CBUF_TYPE, SPECIAL_CBUF_VAR, bt.register
350 )?;
351 if bt.space != 0 {
352 write!(self.out, ", space{}", bt.space)?;
353 }
354 writeln!(self.out, ");")?;
355
356 writeln!(self.out)?;
358 }
359
360 for (group, bt) in self.options.dynamic_storage_buffer_offsets_targets.iter() {
361 writeln!(self.out, "struct __dynamic_buffer_offsetsTy{group} {{")?;
362 for i in 0..bt.size {
363 writeln!(self.out, "{}uint _{};", back::INDENT, i)?;
364 }
365 writeln!(self.out, "}};")?;
366 writeln!(
367 self.out,
368 "ConstantBuffer<__dynamic_buffer_offsetsTy{}> __dynamic_buffer_offsets{}: register(b{}, space{});",
369 group, group, bt.register, bt.space
370 )?;
371
372 writeln!(self.out)?;
374 }
375
376 let ep_results = module
378 .entry_points
379 .iter()
380 .map(|ep| (ep.stage, ep.function.result.clone()))
381 .collect::<Vec<(ShaderStage, Option<crate::FunctionResult>)>>();
382
383 self.write_all_mat_cx2_typedefs_and_functions(module)?;
384
385 for (handle, ty) in module.types.iter() {
387 if let TypeInner::Struct { ref members, span } = ty.inner {
388 if module.types[members.last().unwrap().ty]
389 .inner
390 .is_dynamically_sized(&module.types)
391 {
392 continue;
395 }
396
397 let ep_result = ep_results.iter().find(|e| {
398 if let Some(ref result) = e.1 {
399 result.ty == handle
400 } else {
401 false
402 }
403 });
404
405 self.write_struct(
406 module,
407 handle,
408 members,
409 span,
410 ep_result.map(|r| (r.0, Io::Output)),
411 )?;
412 writeln!(self.out)?;
413 }
414 }
415
416 self.write_special_functions(module)?;
417
418 self.write_wrapped_expression_functions(module, &module.global_expressions, None)?;
419 self.write_wrapped_zero_value_functions(module, &module.global_expressions)?;
420
421 let mut constants = module
423 .constants
424 .iter()
425 .filter(|&(_, c)| c.name.is_some())
426 .peekable();
427 while let Some((handle, _)) = constants.next() {
428 self.write_global_constant(module, handle)?;
429 if constants.peek().is_none() {
431 writeln!(self.out)?;
432 }
433 }
434
435 for (global, _) in module.global_variables.iter() {
437 self.write_global(module, global)?;
438 }
439
440 if !module.global_variables.is_empty() {
441 writeln!(self.out)?;
443 }
444
445 let ep_range = get_entry_points(module, self.pipeline_options.entry_point.as_ref())
446 .map_err(|(stage, name)| Error::EntryPointNotFound(stage, name))?;
447
448 for index in ep_range.clone() {
450 let ep = &module.entry_points[index];
451 let ep_name = self.names[&NameKey::EntryPoint(index as u16)].clone();
452 let ep_io = self.write_ep_interface(module, ep, &ep_name, fragment_entry_point)?;
453 self.entry_point_io.insert(index, ep_io);
454 }
455
456 for (handle, function) in module.functions.iter() {
458 let info = &module_info[handle];
459
460 if !self.options.fake_missing_bindings {
462 if let Some((var_handle, _)) =
463 module
464 .global_variables
465 .iter()
466 .find(|&(var_handle, var)| match var.binding {
467 Some(ref binding) if !info[var_handle].is_empty() => {
468 self.options.resolve_resource_binding(binding).is_err()
469 && self
470 .options
471 .resolve_external_texture_resource_binding(binding)
472 .is_err()
473 }
474 _ => false,
475 })
476 {
477 log::debug!(
478 "Skipping function {:?} (name {:?}) because global {:?} is inaccessible",
479 handle,
480 function.name,
481 var_handle
482 );
483 continue;
484 }
485 }
486
487 let ctx = back::FunctionCtx {
488 ty: back::FunctionType::Function(handle),
489 info,
490 expressions: &function.expressions,
491 named_expressions: &function.named_expressions,
492 };
493 let name = self.names[&NameKey::Function(handle)].clone();
494
495 self.write_wrapped_functions(module, &ctx)?;
496
497 self.write_function(module, name.as_str(), function, &ctx, info, String::new())?;
498
499 writeln!(self.out)?;
500 }
501
502 let mut translated_ep_names = Vec::with_capacity(ep_range.len());
503
504 for index in ep_range {
506 let ep = &module.entry_points[index];
507 let info = module_info.get_entry_point(index);
508
509 if !self.options.fake_missing_bindings {
510 let mut ep_error = None;
511 for (var_handle, var) in module.global_variables.iter() {
512 match var.binding {
513 Some(ref binding) if !info[var_handle].is_empty() => {
514 if let Err(err) = self.options.resolve_resource_binding(binding) {
515 if self
516 .options
517 .resolve_external_texture_resource_binding(binding)
518 .is_err()
519 {
520 ep_error = Some(err);
521 break;
522 }
523 }
524 }
525 _ => {}
526 }
527 }
528 if let Some(err) = ep_error {
529 translated_ep_names.push(Err(err));
530 continue;
531 }
532 }
533
534 let ctx = back::FunctionCtx {
535 ty: back::FunctionType::EntryPoint(index as u16),
536 info,
537 expressions: &ep.function.expressions,
538 named_expressions: &ep.function.named_expressions,
539 };
540
541 self.write_wrapped_functions(module, &ctx)?;
542
543 let mut attribute_string = String::new();
546 if ep.stage.compute_like() {
547 let num_threads = ep.workgroup_size;
549 writeln!(
550 attribute_string,
551 "[numthreads({}, {}, {})]",
552 num_threads[0], num_threads[1], num_threads[2]
553 )?;
554 }
555 if let Some(ref info) = ep.mesh_info {
556 let topology_str = match info.topology {
557 crate::MeshOutputTopology::Points => unreachable!(),
558 crate::MeshOutputTopology::Lines => "line",
559 crate::MeshOutputTopology::Triangles => "triangle",
560 };
561 writeln!(attribute_string, "[outputtopology(\"{topology_str}\")]")?;
562 }
563
564 let name = self.names[&NameKey::EntryPoint(index as u16)].clone();
565 self.write_function(module, &name, &ep.function, &ctx, info, attribute_string)?;
566
567 if index < module.entry_points.len() - 1 {
568 writeln!(self.out)?;
569 }
570
571 translated_ep_names.push(Ok(name));
572 }
573
574 Ok(super::ReflectionInfo {
575 entry_point_names: translated_ep_names,
576 })
577 }
578
579 fn write_modifier(&mut self, binding: &crate::Binding) -> BackendResult {
580 match *binding {
581 crate::Binding::BuiltIn(crate::BuiltIn::Position { invariant: true }) => {
582 write!(self.out, "precise ")?;
583 }
584 crate::Binding::BuiltIn(crate::BuiltIn::Barycentric { perspective: false }) => {
585 write!(self.out, "noperspective ")?;
586 }
587 crate::Binding::Location {
588 interpolation,
589 sampling,
590 ..
591 } => {
592 if let Some(interpolation) = interpolation {
593 if let Some(string) = interpolation.to_hlsl_str() {
594 write!(self.out, "{string} ")?
595 }
596 }
597
598 if let Some(sampling) = sampling {
599 if let Some(string) = sampling.to_hlsl_str() {
600 write!(self.out, "{string} ")?
601 }
602 }
603 }
604 crate::Binding::BuiltIn(_) => {}
605 }
606
607 Ok(())
608 }
609
610 pub(super) fn write_semantic(
613 &mut self,
614 binding: &Option<crate::Binding>,
615 stage: Option<(ShaderStage, Io)>,
616 ) -> BackendResult {
617 let is_per_primitive = match *binding {
618 Some(crate::Binding::BuiltIn(builtin)) if !is_subgroup_builtin_binding(binding) => {
619 if builtin == crate::BuiltIn::ViewIndex
620 && self.options.shader_model < ShaderModel::V6_1
621 {
622 return Err(Error::ShaderModelTooLow(
623 "used @builtin(view_index) or SV_ViewID".to_string(),
624 ShaderModel::V6_1,
625 ));
626 }
627 if let Some(builtin_str) = builtin.to_hlsl_str()? {
628 write!(self.out, " : {builtin_str}")?;
629 }
630 false
631 }
632 Some(crate::Binding::Location {
633 blend_src: Some(1),
634 per_primitive,
635 ..
636 }) => {
637 write!(self.out, " : SV_Target1")?;
638 per_primitive
639 }
640 Some(crate::Binding::Location {
641 location,
642 per_primitive,
643 ..
644 }) => {
645 if stage == Some((ShaderStage::Fragment, Io::Output)) {
646 write!(self.out, " : SV_Target{location}")?;
647 } else {
648 write!(self.out, " : {LOCATION_SEMANTIC}{location}")?;
649 }
650 per_primitive
651 }
652 _ => false,
653 };
654 if is_per_primitive {
655 write!(self.out, " : primitive")?;
656 }
657
658 Ok(())
659 }
660
661 pub(super) fn write_interface_struct(
662 &mut self,
663 module: &Module,
664 shader_stage: (ShaderStage, Io),
665 struct_name: String,
666 var_name: Option<&str>,
667 mut members: Vec<EpStructMember>,
668 ) -> Result<EntryPointBinding, Error> {
669 let struct_name = self.namer.call(&struct_name);
670 members.sort_by_key(|m| InterfaceKey::new(m.binding.as_ref()));
674
675 write!(self.out, "struct {struct_name}")?;
676 writeln!(self.out, " {{")?;
677 let mut local_invocation_index_name = None;
678 let mut subgroup_id_used = false;
679 for m in members.iter() {
680 debug_assert!(m.binding.is_some());
683
684 match m.binding {
685 Some(crate::Binding::BuiltIn(crate::BuiltIn::SubgroupId)) => {
686 subgroup_id_used = true;
687 }
688 Some(crate::Binding::BuiltIn(crate::BuiltIn::LocalInvocationIndex)) => {
689 local_invocation_index_name = Some(m.name.clone());
690 }
691 _ => (),
692 }
693
694 if is_subgroup_builtin_binding(&m.binding) {
695 continue;
696 }
697 write!(self.out, "{}", back::INDENT)?;
698 if let Some(ref binding) = m.binding {
699 self.write_modifier(binding)?;
700 }
701 self.write_type(module, m.ty)?;
702 write!(self.out, " {}", &m.name)?;
703 self.write_semantic(&m.binding, Some(shader_stage))?;
704 writeln!(self.out, ";")?;
705 }
706 if subgroup_id_used && local_invocation_index_name.is_none() {
707 let name = self.namer.call("local_invocation_index");
708 writeln!(self.out, "{}uint {name} : SV_GroupIndex;", back::INDENT)?;
709 local_invocation_index_name = Some(name);
710 }
711 writeln!(self.out, "}};")?;
712 writeln!(self.out)?;
713
714 match shader_stage.1 {
716 Io::Input => {
717 members.sort_by_key(|m| m.index);
719 }
720 Io::Output | Io::MeshVertices | Io::MeshPrimitives => {
721 }
723 }
724
725 Ok(EntryPointBinding {
726 arg_name: self
727 .namer
728 .call(var_name.unwrap_or(struct_name.to_lowercase().as_str())),
729 ty_name: struct_name,
730 members,
731 local_invocation_index_name,
732 })
733 }
734
735 fn write_ep_input_struct(
739 &mut self,
740 module: &Module,
741 func: &crate::Function,
742 stage: ShaderStage,
743 entry_point_name: &str,
744 ) -> Result<EntryPointBinding, Error> {
745 let struct_name = format!("{stage:?}Input_{entry_point_name}");
746
747 let mut fake_members = Vec::new();
748 for arg in func.arguments.iter() {
749 match module.types[arg.ty].inner {
754 TypeInner::Struct { ref members, .. } => {
755 for member in members.iter() {
756 let name = self.namer.call_or(&member.name, "member");
757 let index = fake_members.len() as u32;
758 fake_members.push(EpStructMember {
759 name,
760 ty: member.ty,
761 binding: member.binding.clone(),
762 index,
763 });
764 }
765 }
766 _ => {
767 let member_name = self.namer.call_or(&arg.name, "member");
768 let index = fake_members.len() as u32;
769 fake_members.push(EpStructMember {
770 name: member_name,
771 ty: arg.ty,
772 binding: arg.binding.clone(),
773 index,
774 });
775 }
776 }
777 }
778
779 self.write_interface_struct(module, (stage, Io::Input), struct_name, None, fake_members)
780 }
781
782 fn write_ep_output_struct(
786 &mut self,
787 module: &Module,
788 result: &crate::FunctionResult,
789 stage: ShaderStage,
790 entry_point_name: &str,
791 frag_ep: Option<&FragmentEntryPoint<'_>>,
792 ) -> Result<EntryPointBinding, Error> {
793 let struct_name = format!("{stage:?}Output_{entry_point_name}");
794
795 let empty = [];
796 let members = match module.types[result.ty].inner {
797 TypeInner::Struct { ref members, .. } => members,
798 ref other => {
799 log::error!("Unexpected {other:?} output type without a binding");
800 &empty[..]
801 }
802 };
803
804 let fs_input_locs = if let (Some(frag_ep), ShaderStage::Vertex) = (frag_ep, stage) {
809 let mut fs_input_locs = Vec::new();
810 for arg in frag_ep.func.arguments.iter() {
811 let mut push_if_location = |binding: &Option<crate::Binding>| match *binding {
812 Some(crate::Binding::Location { location, .. }) => fs_input_locs.push(location),
813 Some(crate::Binding::BuiltIn(_)) | None => {}
814 };
815
816 match frag_ep.module.types[arg.ty].inner {
819 TypeInner::Struct { ref members, .. } => {
820 for member in members.iter() {
821 push_if_location(&member.binding);
822 }
823 }
824 _ => push_if_location(&arg.binding),
825 }
826 }
827 fs_input_locs.sort();
828 Some(fs_input_locs)
829 } else {
830 None
831 };
832
833 let mut fake_members = Vec::new();
834 for (index, member) in members.iter().enumerate() {
835 if let Some(ref fs_input_locs) = fs_input_locs {
836 match member.binding {
837 Some(crate::Binding::Location { location, .. }) => {
838 if fs_input_locs.binary_search(&location).is_err() {
839 continue;
840 }
841 }
842 Some(crate::Binding::BuiltIn(_)) | None => {}
843 }
844 }
845
846 let member_name = self.namer.call_or(&member.name, "member");
847 fake_members.push(EpStructMember {
848 name: member_name,
849 ty: member.ty,
850 binding: member.binding.clone(),
851 index: index as u32,
852 });
853 }
854
855 self.write_interface_struct(module, (stage, Io::Output), struct_name, None, fake_members)
856 }
857
858 fn write_ep_interface(
862 &mut self,
863 module: &Module,
864 ep: &crate::EntryPoint,
865 ep_name: &str,
866 frag_ep: Option<&FragmentEntryPoint<'_>>,
867 ) -> Result<EntryPointInterface, Error> {
868 let func = &ep.function;
869 let stage = ep.stage;
870 Ok(EntryPointInterface {
871 input: if !func.arguments.is_empty()
872 && (stage == ShaderStage::Fragment
873 || func
874 .arguments
875 .iter()
876 .any(|arg| is_subgroup_builtin_binding(&arg.binding)))
877 {
878 Some(self.write_ep_input_struct(module, func, stage, ep_name)?)
879 } else {
880 None
881 },
882 output: match func.result {
883 Some(ref fr) if fr.binding.is_none() && stage == ShaderStage::Vertex => {
884 Some(self.write_ep_output_struct(module, fr, stage, ep_name, frag_ep)?)
885 }
886 _ => None,
887 },
888 mesh_vertices: if let Some(ref info) = ep.mesh_info {
889 Some(self.write_ep_mesh_output_struct(module, ep_name, false, info)?)
890 } else {
891 None
892 },
893 mesh_primitives: if let Some(ref info) = ep.mesh_info {
894 Some(self.write_ep_mesh_output_struct(module, ep_name, true, info)?)
895 } else {
896 None
897 },
898 mesh_indices: if let Some(ref info) = ep.mesh_info {
899 Some(self.write_ep_mesh_output_indices(info.topology)?)
900 } else {
901 None
902 },
903 })
904 }
905
906 fn write_ep_argument_initialization(
907 &mut self,
908 ep: &crate::EntryPoint,
909 ep_input: &EntryPointBinding,
910 fake_member: &EpStructMember,
911 ) -> BackendResult {
912 match fake_member.binding {
913 Some(crate::Binding::BuiltIn(crate::BuiltIn::SubgroupSize)) => {
914 write!(self.out, "WaveGetLaneCount()")?
915 }
916 Some(crate::Binding::BuiltIn(crate::BuiltIn::SubgroupInvocationId)) => {
917 write!(self.out, "WaveGetLaneIndex()")?
918 }
919 Some(crate::Binding::BuiltIn(crate::BuiltIn::NumSubgroups)) => write!(
920 self.out,
921 "({}u + WaveGetLaneCount() - 1u) / WaveGetLaneCount()",
922 ep.workgroup_size[0] * ep.workgroup_size[1] * ep.workgroup_size[2]
923 )?,
924 Some(crate::Binding::BuiltIn(crate::BuiltIn::SubgroupId)) => {
925 write!(
926 self.out,
927 "{}.{} / WaveGetLaneCount()",
928 ep_input.arg_name,
929 ep_input.local_invocation_index_name.as_ref().unwrap()
931 )?;
932 }
933 Some(crate::Binding::Location {
934 interpolation: Some(crate::Interpolation::PerVertex),
935 ..
936 }) => {
937 if self.options.shader_model < ShaderModel::V6_1 {
938 return Err(Error::ShaderModelTooLow(
939 "per_vertex fragment inputs".to_string(),
940 ShaderModel::V6_1,
941 ));
942 }
943 write!(
944 self.out,
945 "{{ GetAttributeAtVertex({0}.{1}, 0), GetAttributeAtVertex({0}.{1}, 1), GetAttributeAtVertex({0}.{1}, 2) }}",
946 ep_input.arg_name,
947 fake_member.name,
948 )?;
949 }
950 _ => {
951 write!(self.out, "{}.{}", ep_input.arg_name, fake_member.name)?;
952 }
953 }
954 Ok(())
955 }
956
957 fn write_ep_arguments_initialization(
959 &mut self,
960 module: &Module,
961 func: &crate::Function,
962 ep_index: u16,
963 ) -> BackendResult {
964 let ep = &module.entry_points[ep_index as usize];
965 let ep_input = match self
966 .entry_point_io
967 .get_mut(&(ep_index as usize))
968 .unwrap()
969 .input
970 .take()
971 {
972 Some(ep_input) => ep_input,
973 None => return Ok(()),
974 };
975 let mut fake_iter = ep_input.members.iter();
976 for (arg_index, arg) in func.arguments.iter().enumerate() {
977 write!(self.out, "{}", back::INDENT)?;
978 self.write_type(module, arg.ty)?;
979 let arg_name = &self.names[&NameKey::EntryPointArgument(ep_index, arg_index as u32)];
980 write!(self.out, " {arg_name}")?;
981 match module.types[arg.ty].inner {
982 TypeInner::Array { base, size, .. } => {
983 self.write_array_size(module, base, size)?;
984 write!(self.out, " = ")?;
985 self.write_ep_argument_initialization(
986 ep,
987 &ep_input,
988 fake_iter.next().unwrap(),
989 )?;
990 writeln!(self.out, ";")?;
991 }
992 TypeInner::Struct { ref members, .. } => {
993 write!(self.out, " = {{ ")?;
994 for index in 0..members.len() {
995 if index != 0 {
996 write!(self.out, ", ")?;
997 }
998 self.write_ep_argument_initialization(
999 ep,
1000 &ep_input,
1001 fake_iter.next().unwrap(),
1002 )?;
1003 }
1004 writeln!(self.out, " }};")?;
1005 }
1006 _ => {
1007 write!(self.out, " = ")?;
1008 self.write_ep_argument_initialization(
1009 ep,
1010 &ep_input,
1011 fake_iter.next().unwrap(),
1012 )?;
1013 writeln!(self.out, ";")?;
1014 }
1015 }
1016 }
1017 assert!(fake_iter.next().is_none());
1018 Ok(())
1019 }
1020
1021 fn write_global(
1025 &mut self,
1026 module: &Module,
1027 handle: Handle<crate::GlobalVariable>,
1028 ) -> BackendResult {
1029 let global = &module.global_variables[handle];
1030 let inner = &module.types[global.ty].inner;
1031
1032 let handle_ty = match *inner {
1033 TypeInner::BindingArray { ref base, .. } => &module.types[*base].inner,
1034 _ => inner,
1035 };
1036
1037 let is_external_texture = matches!(
1041 *handle_ty,
1042 TypeInner::Image {
1043 class: crate::ImageClass::External,
1044 ..
1045 }
1046 );
1047 if is_external_texture {
1048 return self.write_global_external_texture(module, handle, global);
1049 }
1050
1051 if let Some(ref binding) = global.binding {
1052 if let Err(err) = self.options.resolve_resource_binding(binding) {
1053 log::debug!(
1054 "Skipping global {:?} (name {:?}) for being inaccessible: {}",
1055 handle,
1056 global.name,
1057 err,
1058 );
1059 return Ok(());
1060 }
1061 }
1062
1063 let is_sampler = matches!(*handle_ty, TypeInner::Sampler { .. });
1065
1066 if is_sampler {
1067 return self.write_global_sampler(module, handle, global);
1068 }
1069
1070 let register_ty = match global.space {
1072 crate::AddressSpace::Function => unreachable!("Function address space"),
1073 crate::AddressSpace::Private => {
1074 write!(self.out, "static ")?;
1075 self.write_type(module, global.ty)?;
1076 ""
1077 }
1078 crate::AddressSpace::WorkGroup | crate::AddressSpace::TaskPayload => {
1079 write!(self.out, "groupshared ")?;
1080 self.write_type(module, global.ty)?;
1081 ""
1082 }
1083 crate::AddressSpace::Uniform => {
1084 write!(self.out, "cbuffer")?;
1087 "b"
1088 }
1089 crate::AddressSpace::Storage { access } => {
1090 if global
1091 .memory_decorations
1092 .contains(crate::MemoryDecorations::COHERENT)
1093 {
1094 write!(self.out, "globallycoherent ")?;
1095 }
1096 let (prefix, register) = if access.contains(crate::StorageAccess::STORE) {
1097 ("RW", "u")
1098 } else {
1099 ("", "t")
1100 };
1101 write!(self.out, "{prefix}ByteAddressBuffer")?;
1102 register
1103 }
1104 crate::AddressSpace::Handle => {
1105 let register = match *handle_ty {
1106 TypeInner::Image {
1108 class: crate::ImageClass::Storage { .. },
1109 ..
1110 } => "u",
1111 _ => "t",
1112 };
1113 self.write_type(module, global.ty)?;
1114 register
1115 }
1116 crate::AddressSpace::Immediate => {
1117 write!(self.out, "ConstantBuffer<")?;
1119 "b"
1120 }
1121 crate::AddressSpace::RayPayload | crate::AddressSpace::IncomingRayPayload => {
1122 unimplemented!()
1123 }
1124 };
1125
1126 if global.space == crate::AddressSpace::Immediate {
1129 self.write_global_type(module, global.ty)?;
1130
1131 if let TypeInner::Array { base, size, .. } = module.types[global.ty].inner {
1133 self.write_array_size(module, base, size)?;
1134 }
1135
1136 write!(self.out, ">")?;
1138 }
1139
1140 let name = &self.names[&NameKey::GlobalVariable(handle)];
1141 write!(self.out, " {name}")?;
1142
1143 if global.space == crate::AddressSpace::Immediate {
1146 match module.types[global.ty].inner {
1147 TypeInner::Struct { .. } => {}
1148 _ => {
1149 return Err(Error::Unimplemented(format!(
1150 "push-constant '{name}' has non-struct type; tracked by: https://github.com/gfx-rs/wgpu/issues/5683"
1151 )));
1152 }
1153 }
1154
1155 let target = self
1156 .options
1157 .immediates_target
1158 .as_ref()
1159 .expect("No bind target was defined for the immediates block");
1160 write!(self.out, ": register(b{}", target.register)?;
1161 if target.space != 0 {
1162 write!(self.out, ", space{}", target.space)?;
1163 }
1164 write!(self.out, ")")?;
1165 }
1166
1167 if let Some(ref binding) = global.binding {
1168 let bt = self.options.resolve_resource_binding(binding).unwrap();
1170
1171 if let TypeInner::BindingArray { base, size, .. } = module.types[global.ty].inner {
1173 if let Some(overridden_size) = bt.binding_array_size {
1174 write!(self.out, "[{overridden_size}]")?;
1175 } else {
1176 self.write_array_size(module, base, size)?;
1177 }
1178 }
1179
1180 write!(self.out, " : register({}{}", register_ty, bt.register)?;
1181 if bt.space != 0 {
1182 write!(self.out, ", space{}", bt.space)?;
1183 }
1184 write!(self.out, ")")?;
1185 } else {
1186 if let TypeInner::Array { base, size, .. } = module.types[global.ty].inner {
1188 self.write_array_size(module, base, size)?;
1189 }
1190 if global.space == crate::AddressSpace::Private {
1191 write!(self.out, " = ")?;
1192 if let Some(init) = global.init {
1193 self.write_const_expression(module, init, &module.global_expressions)?;
1194 } else {
1195 self.write_default_init(module, global.ty)?;
1196 }
1197 }
1198 }
1199
1200 if global.space == crate::AddressSpace::Uniform {
1201 write!(self.out, " {{ ")?;
1202
1203 self.write_global_type(module, global.ty)?;
1204
1205 write!(
1206 self.out,
1207 " {}",
1208 &self.names[&NameKey::GlobalVariable(handle)]
1209 )?;
1210
1211 if let TypeInner::Array { base, size, .. } = module.types[global.ty].inner {
1213 self.write_array_size(module, base, size)?;
1214 }
1215
1216 writeln!(self.out, "; }}")?;
1217 } else {
1218 writeln!(self.out, ";")?;
1219 }
1220
1221 Ok(())
1222 }
1223
1224 fn write_global_sampler(
1225 &mut self,
1226 module: &Module,
1227 handle: Handle<crate::GlobalVariable>,
1228 global: &crate::GlobalVariable,
1229 ) -> BackendResult {
1230 let binding = *global.binding.as_ref().unwrap();
1231
1232 let key = super::SamplerIndexBufferKey {
1233 group: binding.group,
1234 };
1235 self.write_wrapped_sampler_buffer(key)?;
1236
1237 let bt = self.options.resolve_resource_binding(&binding).unwrap();
1239
1240 match module.types[global.ty].inner {
1241 TypeInner::Sampler { comparison } => {
1242 write!(self.out, "static const ")?;
1249 self.write_type(module, global.ty)?;
1250
1251 let heap_var = if comparison {
1252 COMPARISON_SAMPLER_HEAP_VAR
1253 } else {
1254 SAMPLER_HEAP_VAR
1255 };
1256
1257 let index_buffer_name = &self.wrapped.sampler_index_buffers[&key];
1258 let name = &self.names[&NameKey::GlobalVariable(handle)];
1259 writeln!(
1260 self.out,
1261 " {name} = {heap_var}[{index_buffer_name}[{register}]];",
1262 register = bt.register
1263 )?;
1264 }
1265 TypeInner::BindingArray { .. } => {
1266 let name = &self.names[&NameKey::GlobalVariable(handle)];
1272 writeln!(
1273 self.out,
1274 "static const uint {name} = {register};",
1275 register = bt.register
1276 )?;
1277 }
1278 _ => unreachable!(),
1279 };
1280
1281 Ok(())
1282 }
1283
1284 fn write_global_external_texture(
1288 &mut self,
1289 module: &Module,
1290 handle: Handle<crate::GlobalVariable>,
1291 global: &crate::GlobalVariable,
1292 ) -> BackendResult {
1293 let res_binding = global
1294 .binding
1295 .as_ref()
1296 .expect("External texture global variables must have a resource binding");
1297 let ext_tex_bindings = match self
1298 .options
1299 .resolve_external_texture_resource_binding(res_binding)
1300 {
1301 Ok(bindings) => bindings,
1302 Err(err) => {
1303 log::debug!(
1304 "Skipping global {:?} (name {:?}) for being inaccessible: {}",
1305 handle,
1306 global.name,
1307 err,
1308 );
1309 return Ok(());
1310 }
1311 };
1312
1313 let mut write_plane = |bt: &super::BindTarget, name| -> BackendResult {
1314 write!(
1315 self.out,
1316 "Texture2D<float4> {}: register(t{}",
1317 name, bt.register
1318 )?;
1319 if bt.space != 0 {
1320 write!(self.out, ", space{}", bt.space)?;
1321 }
1322 writeln!(self.out, ");")?;
1323 Ok(())
1324 };
1325 for (i, bt) in ext_tex_bindings.planes.iter().enumerate() {
1326 let plane_name = &self.names
1327 [&NameKey::ExternalTextureGlobalVariable(handle, ExternalTextureNameKey::Plane(i))];
1328 write_plane(bt, plane_name)?;
1329 }
1330
1331 let params_name = &self.names
1332 [&NameKey::ExternalTextureGlobalVariable(handle, ExternalTextureNameKey::Params)];
1333 let params_ty_name =
1334 &self.names[&NameKey::Type(module.special_types.external_texture_params.unwrap())];
1335 write!(
1336 self.out,
1337 "cbuffer {}: register(b{}",
1338 params_name, ext_tex_bindings.params.register
1339 )?;
1340 if ext_tex_bindings.params.space != 0 {
1341 write!(self.out, ", space{}", ext_tex_bindings.params.space)?;
1342 }
1343 writeln!(self.out, ") {{ {params_ty_name} {params_name}; }};")?;
1344
1345 Ok(())
1346 }
1347
1348 fn write_global_constant(
1353 &mut self,
1354 module: &Module,
1355 handle: Handle<crate::Constant>,
1356 ) -> BackendResult {
1357 write!(self.out, "static const ")?;
1358 let constant = &module.constants[handle];
1359 self.write_type(module, constant.ty)?;
1360 let name = &self.names[&NameKey::Constant(handle)];
1361 write!(self.out, " {name}")?;
1362 if let TypeInner::Array { base, size, .. } = module.types[constant.ty].inner {
1364 self.write_array_size(module, base, size)?;
1365 }
1366 write!(self.out, " = ")?;
1367 self.write_const_expression(module, constant.init, &module.global_expressions)?;
1368 writeln!(self.out, ";")?;
1369 Ok(())
1370 }
1371
1372 pub(super) fn write_array_size(
1373 &mut self,
1374 module: &Module,
1375 base: Handle<crate::Type>,
1376 size: crate::ArraySize,
1377 ) -> BackendResult {
1378 write!(self.out, "[")?;
1379
1380 match size.resolve(module.to_ctx())? {
1381 proc::IndexableLength::Known(size) => {
1382 write!(self.out, "{size}")?;
1383 }
1384 proc::IndexableLength::Dynamic => unreachable!(),
1385 }
1386
1387 write!(self.out, "]")?;
1388
1389 if let TypeInner::Array {
1390 base: next_base,
1391 size: next_size,
1392 ..
1393 } = module.types[base].inner
1394 {
1395 self.write_array_size(module, next_base, next_size)?;
1396 }
1397
1398 Ok(())
1399 }
1400
1401 fn write_struct(
1406 &mut self,
1407 module: &Module,
1408 handle: Handle<crate::Type>,
1409 members: &[crate::StructMember],
1410 span: u32,
1411 shader_stage: Option<(ShaderStage, Io)>,
1412 ) -> BackendResult {
1413 let struct_name = &self.names[&NameKey::Type(handle)];
1415 writeln!(self.out, "struct {struct_name} {{")?;
1416
1417 let mut last_offset = 0;
1418 for (index, member) in members.iter().enumerate() {
1419 if member.binding.is_none() && member.offset > last_offset {
1420 let padding = (member.offset - last_offset) / 4;
1424 for i in 0..padding {
1425 writeln!(self.out, "{}int _pad{}_{};", back::INDENT, index, i)?;
1426 }
1427 }
1428 let ty_inner = &module.types[member.ty].inner;
1429 last_offset = member.offset + ty_inner.size_hlsl(module.to_ctx())?;
1430
1431 write!(self.out, "{}", back::INDENT)?;
1433
1434 match module.types[member.ty].inner {
1435 TypeInner::Array { base, size, .. } => {
1436 self.write_global_type(module, member.ty)?;
1439
1440 write!(
1442 self.out,
1443 " {}",
1444 &self.names[&NameKey::StructMember(handle, index as u32)]
1445 )?;
1446 self.write_array_size(module, base, size)?;
1448 }
1449 TypeInner::Matrix {
1452 rows,
1453 columns,
1454 scalar,
1455 } if member.binding.is_none() && rows == crate::VectorSize::Bi => {
1456 let vec_ty = TypeInner::Vector { size: rows, scalar };
1457 let field_name_key = NameKey::StructMember(handle, index as u32);
1458
1459 for i in 0..columns as u8 {
1460 if i != 0 {
1461 write!(self.out, "; ")?;
1462 }
1463 self.write_value_type(module, &vec_ty)?;
1464 write!(self.out, " {}_{}", &self.names[&field_name_key], i)?;
1465 }
1466 }
1467 _ => {
1468 if let Some(ref binding) = member.binding {
1470 self.write_modifier(binding)?;
1471 }
1472
1473 if let TypeInner::Matrix { .. } = module.types[member.ty].inner {
1477 write!(self.out, "row_major ")?;
1478 }
1479
1480 self.write_type(module, member.ty)?;
1482 write!(
1483 self.out,
1484 " {}",
1485 &self.names[&NameKey::StructMember(handle, index as u32)]
1486 )?;
1487 }
1488 }
1489
1490 self.write_semantic(&member.binding, shader_stage)?;
1491 writeln!(self.out, ";")?;
1492 }
1493
1494 if members.last().unwrap().binding.is_none() && span > last_offset {
1496 let padding = (span - last_offset) / 4;
1497 for i in 0..padding {
1498 writeln!(self.out, "{}int _end_pad_{};", back::INDENT, i)?;
1499 }
1500 }
1501
1502 writeln!(self.out, "}};")?;
1503 Ok(())
1504 }
1505
1506 pub(super) fn write_global_type(
1511 &mut self,
1512 module: &Module,
1513 ty: Handle<crate::Type>,
1514 ) -> BackendResult {
1515 let matrix_data = get_inner_matrix_data(module, ty);
1516
1517 if let Some(MatrixType {
1520 columns,
1521 rows: crate::VectorSize::Bi,
1522 width,
1523 }) = matrix_data
1524 {
1525 write!(self.out, "__mat{}x2_f{}", columns as u8, width * 8)?;
1526 } else {
1527 if matrix_data.is_some() {
1531 write!(self.out, "row_major ")?;
1532 }
1533
1534 self.write_type(module, ty)?;
1535 }
1536
1537 Ok(())
1538 }
1539
1540 pub(super) fn write_type(&mut self, module: &Module, ty: Handle<crate::Type>) -> BackendResult {
1545 let inner = &module.types[ty].inner;
1546 match *inner {
1547 TypeInner::Struct { .. } => write!(self.out, "{}", self.names[&NameKey::Type(ty)])?,
1548 TypeInner::Array { base, .. } | TypeInner::BindingArray { base, .. } => {
1550 self.write_type(module, base)?
1551 }
1552 ref other => self.write_value_type(module, other)?,
1553 }
1554
1555 Ok(())
1556 }
1557
1558 pub(super) fn write_value_type(&mut self, module: &Module, inner: &TypeInner) -> BackendResult {
1563 match *inner {
1564 TypeInner::Scalar(scalar) | TypeInner::Atomic(scalar) => {
1565 write!(self.out, "{}", scalar.to_hlsl_str()?)?;
1566 }
1567 TypeInner::Vector { size, scalar } => {
1568 write!(
1569 self.out,
1570 "{}{}",
1571 scalar.to_hlsl_str()?,
1572 common::vector_size_str(size)
1573 )?;
1574 }
1575 TypeInner::Matrix {
1576 columns,
1577 rows,
1578 scalar,
1579 } => {
1580 write!(
1585 self.out,
1586 "{}{}x{}",
1587 scalar.to_hlsl_str()?,
1588 common::vector_size_str(columns),
1589 common::vector_size_str(rows),
1590 )?;
1591 }
1592 TypeInner::Image {
1593 dim,
1594 arrayed,
1595 class,
1596 } => {
1597 self.write_image_type(dim, arrayed, class)?;
1598 }
1599 TypeInner::Sampler { comparison } => {
1600 let sampler = if comparison {
1601 "SamplerComparisonState"
1602 } else {
1603 "SamplerState"
1604 };
1605 write!(self.out, "{sampler}")?;
1606 }
1607 TypeInner::Array { base, size, .. } | TypeInner::BindingArray { base, size } => {
1611 self.write_array_size(module, base, size)?;
1612 }
1613 TypeInner::AccelerationStructure { .. } => {
1614 write!(self.out, "RaytracingAccelerationStructure")?;
1615 }
1616 TypeInner::RayQuery { .. } => {
1617 write!(self.out, "RayQuery<RAY_FLAG_NONE>")?;
1619 }
1620 _ => return Err(Error::Unimplemented(format!("write_value_type {inner:?}"))),
1621 }
1622
1623 Ok(())
1624 }
1625
1626 fn write_function(
1630 &mut self,
1631 module: &Module,
1632 name: &str,
1633 func: &crate::Function,
1634 func_ctx: &back::FunctionCtx<'_>,
1635 info: &valid::FunctionInfo,
1636 header: String,
1637 ) -> BackendResult {
1638 self.update_expressions_to_bake(module, func, info);
1641 let ep = match func_ctx.ty {
1642 back::FunctionType::EntryPoint(idx) => Some(&module.entry_points[idx as usize]),
1643 back::FunctionType::Function(_) => None,
1644 };
1645
1646 let nested = matches!(
1647 ep,
1648 Some(crate::EntryPoint {
1649 stage: ShaderStage::Task | ShaderStage::Mesh,
1650 ..
1651 })
1652 );
1653 if !nested {
1654 write!(self.out, "{header}")?;
1655 }
1656
1657 if let Some(ref result) = func.result {
1658 let array_return_type = match module.types[result.ty].inner {
1660 TypeInner::Array { base, size, .. } => {
1661 let array_return_type = self.namer.call(&format!("ret_{name}"));
1662 write!(self.out, "typedef ")?;
1663 self.write_type(module, result.ty)?;
1664 write!(self.out, " {array_return_type}")?;
1665 self.write_array_size(module, base, size)?;
1666 writeln!(self.out, ";")?;
1667 Some(array_return_type)
1668 }
1669 _ => None,
1670 };
1671
1672 if let Some(
1674 ref binding @ crate::Binding::BuiltIn(crate::BuiltIn::Position { invariant: true }),
1675 ) = result.binding
1676 {
1677 self.write_modifier(binding)?;
1678 }
1679
1680 match func_ctx.ty {
1682 back::FunctionType::Function(_) => {
1683 if let Some(array_return_type) = array_return_type {
1684 write!(self.out, "{array_return_type}")?;
1685 } else {
1686 self.write_type(module, result.ty)?;
1687 }
1688 }
1689 back::FunctionType::EntryPoint(index) => {
1690 if let Some(ref ep_output) =
1691 self.entry_point_io.get(&(index as usize)).unwrap().output
1692 {
1693 write!(self.out, "{}", ep_output.ty_name)?;
1694 } else {
1695 self.write_type(module, result.ty)?;
1696 }
1697 }
1698 }
1699 } else {
1700 write!(self.out, "void")?;
1701 }
1702
1703 let nested_name = if nested {
1704 self.namer.call(&format!("_{name}"))
1705 } else {
1706 name.to_string()
1707 };
1708
1709 write!(self.out, " {nested_name}(")?;
1711
1712 let need_workgroup_variables_initialization =
1713 self.need_workgroup_variables_initialization(func_ctx, module);
1714
1715 let mut any_args_written = false;
1716 let mut separator = || {
1717 if any_args_written {
1718 ", "
1719 } else {
1720 any_args_written = true;
1721 ""
1722 }
1723 };
1724
1725 let needs_local_invocation_index_name = need_workgroup_variables_initialization || nested;
1726 let mut local_invocation_index_name = None;
1727 let mut nested_wgsl_args: Vec<String> = Vec::new();
1730 let mut nested_task_payload_name: Option<String> = None;
1731 match func_ctx.ty {
1733 back::FunctionType::Function(handle) => {
1734 for (index, arg) in func.arguments.iter().enumerate() {
1735 write!(self.out, "{}", separator())?;
1736 self.write_function_argument(module, handle, arg, index)?;
1737 }
1738 for (var_handle, var) in module.global_variables.iter() {
1740 let uses = info[var_handle];
1741 if uses.contains(valid::GlobalUse::READ)
1742 && !uses.contains(valid::GlobalUse::WRITE)
1743 && var.space == crate::AddressSpace::TaskPayload
1744 {
1745 self.function_task_payload_var.insert(handle, var_handle);
1746 write!(self.out, "{}in ", separator())?;
1747
1748 self.write_type(module, var.ty)?;
1749 let name = &self.names[&NameKey::GlobalVariable(var_handle)];
1750 write!(self.out, " {name}")?;
1751 break;
1752 }
1753 }
1754 }
1755 back::FunctionType::EntryPoint(ep_index) => {
1756 let ep = &module.entry_points[ep_index as usize];
1757 if let Some(ref ep_input) =
1758 self.entry_point_io.get(&(ep_index as usize)).unwrap().input
1759 {
1760 write!(self.out, "{} {}", ep_input.ty_name, ep_input.arg_name)?;
1761 separator();
1762 nested_wgsl_args.push(ep_input.arg_name.clone());
1763 } else {
1764 let stage = ep.stage;
1765 for (index, arg) in func.arguments.iter().enumerate() {
1766 write!(self.out, "{}", separator())?;
1767 self.write_type(module, arg.ty)?;
1768
1769 let argument_name =
1770 &self.names[&NameKey::EntryPointArgument(ep_index, index as u32)];
1771
1772 if arg.binding
1773 == Some(crate::Binding::BuiltIn(
1774 crate::BuiltIn::LocalInvocationIndex,
1775 ))
1776 {
1777 local_invocation_index_name = Some(argument_name.clone());
1778 }
1779
1780 nested_wgsl_args.push(argument_name.clone());
1781 write!(self.out, " {argument_name}")?;
1782 if let TypeInner::Array { base, size, .. } = module.types[arg.ty].inner {
1783 self.write_array_size(module, base, size)?;
1784 }
1785
1786 self.write_semantic(&arg.binding, Some((stage, Io::Input)))?;
1787 }
1788 }
1789 if ep.stage == ShaderStage::Mesh {
1790 if let Some(var_handle) = ep.task_payload {
1791 let var = &module.global_variables[var_handle];
1792 write!(self.out, "{}in ", separator())?;
1793 self.write_type(module, var.ty)?;
1794 let arg_name = &self.names[&NameKey::GlobalVariable(var_handle)];
1795 write!(self.out, " {arg_name}")?;
1796 nested_task_payload_name = Some(arg_name.clone());
1797 if let TypeInner::Array { base, size, .. } = module.types[var.ty].inner {
1798 self.write_array_size(module, base, size)?;
1799 }
1800 }
1801 }
1802 if needs_local_invocation_index_name && local_invocation_index_name.is_none() {
1803 let name = self.namer.call("local_invocation_index");
1804 write!(self.out, "{}uint {name}", separator())?;
1805 write!(self.out, " : SV_GroupIndex")?;
1806 local_invocation_index_name = Some(name);
1807 }
1808 }
1809 }
1810 write!(self.out, ")")?;
1812
1813 if let back::FunctionType::EntryPoint(index) = func_ctx.ty {
1815 let stage = module.entry_points[index as usize].stage;
1816 if let Some(crate::FunctionResult { ref binding, .. }) = func.result {
1817 self.write_semantic(binding, Some((stage, Io::Output)))?;
1818 }
1819 }
1820
1821 writeln!(self.out)?;
1823 writeln!(self.out, "{{")?;
1824
1825 if need_workgroup_variables_initialization && !nested {
1826 let back::FunctionType::EntryPoint(index) = func_ctx.ty else {
1827 unreachable!();
1828 };
1829 writeln!(
1830 self.out,
1831 "{}if ({} == 0) {{",
1832 back::INDENT,
1833 local_invocation_index_name.as_ref().unwrap(),
1836 )?;
1837 self.write_workgroup_variables_initialization(
1838 func_ctx,
1839 module,
1840 module.entry_points[index as usize].stage,
1841 )?;
1842
1843 writeln!(self.out, "{}}}", back::INDENT)?;
1844 self.write_control_barrier(crate::Barrier::WORK_GROUP, back::Level(1))?;
1845 }
1846
1847 if let back::FunctionType::EntryPoint(index) = func_ctx.ty {
1848 self.write_ep_arguments_initialization(module, func, index)?;
1849 }
1850
1851 for (handle, local) in func.local_variables.iter() {
1853 write!(self.out, "{}", back::INDENT)?;
1855
1856 self.write_type(module, local.ty)?;
1859 write!(self.out, " {}", self.names[&func_ctx.name_key(handle)])?;
1860 if let TypeInner::Array { base, size, .. } = module.types[local.ty].inner {
1862 self.write_array_size(module, base, size)?;
1863 }
1864
1865 let is_ray_query = match module.types[local.ty].inner {
1866 TypeInner::RayQuery { .. } => true,
1868 _ => {
1869 write!(self.out, " = ")?;
1870 if let Some(init) = local.init {
1872 self.write_expr(module, init, func_ctx)?;
1873 } else {
1874 self.write_default_init(module, local.ty)?;
1876 }
1877 false
1878 }
1879 };
1880 writeln!(self.out, ";")?;
1882 if is_ray_query {
1884 write!(self.out, "{}", back::INDENT)?;
1885 self.write_value_type(module, &TypeInner::Scalar(Scalar::U32))?;
1886 writeln!(
1887 self.out,
1888 " {RAY_QUERY_TRACKER_VARIABLE_PREFIX}{} = 0;",
1889 self.names[&func_ctx.name_key(handle)]
1890 )?;
1891 }
1892 }
1893
1894 if !func.local_variables.is_empty() {
1895 writeln!(self.out)?;
1896 }
1897
1898 for sta in func.body.iter() {
1900 self.write_stmt(module, sta, func_ctx, back::Level(1))?;
1902 }
1903
1904 writeln!(self.out, "}}")?;
1905
1906 if nested {
1907 self.write_nested_function_outer(
1908 module,
1909 func_ctx,
1910 &header,
1911 name,
1912 need_workgroup_variables_initialization,
1913 &nested_name,
1914 ep.unwrap(),
1915 NestedEntryPointArgs {
1916 user_args: nested_wgsl_args,
1917 task_payload: nested_task_payload_name,
1918 local_invocation_index: local_invocation_index_name.unwrap(),
1920 },
1921 )?;
1922 }
1923
1924 self.named_expressions.clear();
1925
1926 Ok(())
1927 }
1928
1929 fn write_function_argument(
1930 &mut self,
1931 module: &Module,
1932 handle: Handle<crate::Function>,
1933 arg: &crate::FunctionArgument,
1934 index: usize,
1935 ) -> BackendResult {
1936 if let TypeInner::Image {
1939 class: crate::ImageClass::External,
1940 ..
1941 } = module.types[arg.ty].inner
1942 {
1943 return self.write_function_external_texture_argument(module, handle, index);
1944 }
1945
1946 let arg_ty = match module.types[arg.ty].inner {
1948 TypeInner::Pointer { base, .. } => {
1950 write!(self.out, "inout ")?;
1952 base
1953 }
1954 _ => arg.ty,
1955 };
1956 self.write_type(module, arg_ty)?;
1957
1958 let argument_name = &self.names[&NameKey::FunctionArgument(handle, index as u32)];
1959
1960 write!(self.out, " {argument_name}")?;
1962 if let TypeInner::Array { base, size, .. } = module.types[arg_ty].inner {
1963 self.write_array_size(module, base, size)?;
1964 }
1965
1966 Ok(())
1967 }
1968
1969 fn write_function_external_texture_argument(
1970 &mut self,
1971 module: &Module,
1972 handle: Handle<crate::Function>,
1973 index: usize,
1974 ) -> BackendResult {
1975 let plane_names = [0, 1, 2].map(|i| {
1976 &self.names[&NameKey::ExternalTextureFunctionArgument(
1977 handle,
1978 index as u32,
1979 ExternalTextureNameKey::Plane(i),
1980 )]
1981 });
1982 let params_name = &self.names[&NameKey::ExternalTextureFunctionArgument(
1983 handle,
1984 index as u32,
1985 ExternalTextureNameKey::Params,
1986 )];
1987 let params_ty_name =
1988 &self.names[&NameKey::Type(module.special_types.external_texture_params.unwrap())];
1989 write!(
1990 self.out,
1991 "Texture2D<float4> {}, Texture2D<float4> {}, Texture2D<float4> {}, {params_ty_name} {params_name}",
1992 plane_names[0], plane_names[1], plane_names[2],
1993 )?;
1994 Ok(())
1995 }
1996
1997 fn need_workgroup_variables_initialization(
1998 &mut self,
1999 func_ctx: &back::FunctionCtx,
2000 module: &Module,
2001 ) -> bool {
2002 self.options.zero_initialize_workgroup_memory
2003 && func_ctx.ty.is_compute_like_entry_point(module)
2004 && module.global_variables.iter().any(|(handle, var)| {
2005 !func_ctx.info[handle].is_empty() && var.space.is_workgroup_like()
2006 })
2007 }
2008
2009 pub(super) fn write_workgroup_variables_initialization(
2010 &mut self,
2011 func_ctx: &back::FunctionCtx,
2012 module: &Module,
2013 stage: ShaderStage,
2014 ) -> BackendResult {
2015 let vars = module.global_variables.iter().filter(|&(handle, var)| {
2016 let task_needs_zero =
2018 (var.space == crate::AddressSpace::TaskPayload) && stage == ShaderStage::Task;
2019 !func_ctx.info[handle].is_empty()
2020 && (var.space == crate::AddressSpace::WorkGroup || task_needs_zero)
2021 });
2022
2023 for (handle, var) in vars {
2024 let name = &self.names[&NameKey::GlobalVariable(handle)];
2025 write!(self.out, "{}{} = ", back::Level(2), name)?;
2026 self.write_default_init(module, var.ty)?;
2027 writeln!(self.out, ";")?;
2028 }
2029 Ok(())
2030 }
2031
2032 fn write_switch(
2034 &mut self,
2035 module: &Module,
2036 func_ctx: &back::FunctionCtx<'_>,
2037 level: back::Level,
2038 selector: Handle<crate::Expression>,
2039 cases: &[crate::SwitchCase],
2040 ) -> BackendResult {
2041 let indent_level_1 = level.next();
2043 let indent_level_2 = indent_level_1.next();
2044
2045 if let Some(variable) = self.continue_ctx.enter_switch(&mut self.namer) {
2047 writeln!(self.out, "{level}bool {variable} = false;",)?;
2048 };
2049
2050 let one_body = cases
2055 .iter()
2056 .rev()
2057 .skip(1)
2058 .all(|case| case.fall_through && case.body.is_empty());
2059 if one_body {
2060 writeln!(self.out, "{level}do {{")?;
2062 if let Some(case) = cases.last() {
2066 for sta in case.body.iter() {
2067 self.write_stmt(module, sta, func_ctx, indent_level_1)?;
2068 }
2069 }
2070 writeln!(self.out, "{level}}} while(false);")?;
2072 } else {
2073 write!(self.out, "{level}")?;
2075 write!(self.out, "switch(")?;
2076 self.write_expr(module, selector, func_ctx)?;
2077 writeln!(self.out, ") {{")?;
2078
2079 for (i, case) in cases.iter().enumerate() {
2080 match case.value {
2081 crate::SwitchValue::I32(value) => {
2082 write!(self.out, "{indent_level_1}case {value}:")?
2083 }
2084 crate::SwitchValue::U32(value) => {
2085 write!(self.out, "{indent_level_1}case {value}u:")?
2086 }
2087 crate::SwitchValue::Default => write!(self.out, "{indent_level_1}default:")?,
2088 }
2089
2090 let write_block_braces = !(case.fall_through && case.body.is_empty());
2097 if write_block_braces {
2098 writeln!(self.out, " {{")?;
2099 } else {
2100 writeln!(self.out)?;
2101 }
2102
2103 if case.fall_through && !case.body.is_empty() {
2121 let curr_len = i + 1;
2122 let end_case_idx = curr_len
2123 + cases
2124 .iter()
2125 .skip(curr_len)
2126 .position(|case| !case.fall_through)
2127 .unwrap();
2128 let indent_level_3 = indent_level_2.next();
2129 for case in &cases[i..=end_case_idx] {
2130 writeln!(self.out, "{indent_level_2}{{")?;
2131 let prev_len = self.named_expressions.len();
2132 for sta in case.body.iter() {
2133 self.write_stmt(module, sta, func_ctx, indent_level_3)?;
2134 }
2135 self.named_expressions.truncate(prev_len);
2137 writeln!(self.out, "{indent_level_2}}}")?;
2138 }
2139
2140 let last_case = &cases[end_case_idx];
2141 if last_case.body.last().is_none_or(|s| !s.is_terminator()) {
2142 writeln!(self.out, "{indent_level_2}break;")?;
2143 }
2144 } else {
2145 for sta in case.body.iter() {
2146 self.write_stmt(module, sta, func_ctx, indent_level_2)?;
2147 }
2148 if !case.fall_through && case.body.last().is_none_or(|s| !s.is_terminator()) {
2149 writeln!(self.out, "{indent_level_2}break;")?;
2150 }
2151 }
2152
2153 if write_block_braces {
2154 writeln!(self.out, "{indent_level_1}}}")?;
2155 }
2156 }
2157
2158 writeln!(self.out, "{level}}}")?;
2159 }
2160
2161 use back::continue_forward::ExitControlFlow;
2163 let op = match self.continue_ctx.exit_switch() {
2164 ExitControlFlow::None => None,
2165 ExitControlFlow::Continue { variable } => Some(("continue", variable)),
2166 ExitControlFlow::Break { variable } => Some(("break", variable)),
2167 };
2168 if let Some((control_flow, variable)) = op {
2169 writeln!(self.out, "{level}if ({variable}) {{")?;
2170 writeln!(self.out, "{indent_level_1}{control_flow};")?;
2171 writeln!(self.out, "{level}}}")?;
2172 }
2173
2174 Ok(())
2175 }
2176
2177 fn write_index(
2178 &mut self,
2179 module: &Module,
2180 index: Index,
2181 func_ctx: &back::FunctionCtx<'_>,
2182 ) -> BackendResult {
2183 match index {
2184 Index::Static(index) => {
2185 write!(self.out, "{index}")?;
2186 }
2187 Index::Expression(index) => {
2188 self.write_expr(module, index, func_ctx)?;
2189 }
2190 }
2191 Ok(())
2192 }
2193
2194 fn write_stmt(
2199 &mut self,
2200 module: &Module,
2201 stmt: &crate::Statement,
2202 func_ctx: &back::FunctionCtx<'_>,
2203 level: back::Level,
2204 ) -> BackendResult {
2205 use crate::Statement;
2206
2207 match *stmt {
2208 Statement::Emit(ref range) => {
2209 for handle in range.clone() {
2210 let ptr_class = func_ctx.resolve_type(handle, &module.types).pointer_space();
2211 let expr_name = if ptr_class.is_some() {
2212 None
2216 } else if let Some(name) = func_ctx.named_expressions.get(&handle) {
2217 Some(self.namer.call(name))
2222 } else if self.need_bake_expressions.contains(&handle) {
2223 Some(Baked(handle).to_string())
2224 } else {
2225 None
2226 };
2227
2228 if let Some(name) = expr_name {
2229 write!(self.out, "{level}")?;
2230 self.write_named_expr(module, handle, name, handle, func_ctx)?;
2231 }
2232 }
2233 }
2234 Statement::Block(ref block) => {
2236 write!(self.out, "{level}")?;
2237 writeln!(self.out, "{{")?;
2238 for sta in block.iter() {
2239 self.write_stmt(module, sta, func_ctx, level.next())?
2241 }
2242 writeln!(self.out, "{level}}}")?
2243 }
2244 Statement::If {
2246 condition,
2247 ref accept,
2248 ref reject,
2249 } => {
2250 write!(self.out, "{level}")?;
2251 write!(self.out, "if (")?;
2252 self.write_expr(module, condition, func_ctx)?;
2253 writeln!(self.out, ") {{")?;
2254
2255 let l2 = level.next();
2256 for sta in accept {
2257 self.write_stmt(module, sta, func_ctx, l2)?;
2259 }
2260
2261 if !reject.is_empty() {
2264 writeln!(self.out, "{level}}} else {{")?;
2265
2266 for sta in reject {
2267 self.write_stmt(module, sta, func_ctx, l2)?;
2269 }
2270 }
2271
2272 writeln!(self.out, "{level}}}")?
2273 }
2274 Statement::Kill => writeln!(self.out, "{level}discard;")?,
2276 Statement::Return { value: None } => {
2277 writeln!(self.out, "{level}return;")?;
2278 }
2279 Statement::Return { value: Some(expr) } => {
2280 let base_ty_res = &func_ctx.info[expr].ty;
2281 let mut resolved = base_ty_res.inner_with(&module.types);
2282 if let TypeInner::Pointer { base, space: _ } = *resolved {
2283 resolved = &module.types[base].inner;
2284 }
2285
2286 if let TypeInner::Struct { .. } = *resolved {
2287 let ty = base_ty_res.handle().unwrap();
2289 let struct_name = &self.names[&NameKey::Type(ty)];
2290 let variable_name = self.namer.call(&struct_name.to_lowercase());
2291 write!(self.out, "{level}const {struct_name} {variable_name} = ",)?;
2292 self.write_expr(module, expr, func_ctx)?;
2293 writeln!(self.out, ";")?;
2294
2295 let ep_output = match func_ctx.ty {
2297 back::FunctionType::Function(_) => None,
2298 back::FunctionType::EntryPoint(index) => self
2299 .entry_point_io
2300 .get(&(index as usize))
2301 .unwrap()
2302 .output
2303 .as_ref(),
2304 };
2305 let final_name = match ep_output {
2306 Some(ep_output) => {
2307 let final_name = self.namer.call(&variable_name);
2308 write!(
2309 self.out,
2310 "{}const {} {} = {{ ",
2311 level, ep_output.ty_name, final_name,
2312 )?;
2313 for (index, m) in ep_output.members.iter().enumerate() {
2314 if index != 0 {
2315 write!(self.out, ", ")?;
2316 }
2317 let member_name = &self.names[&NameKey::StructMember(ty, m.index)];
2318 write!(self.out, "{variable_name}.{member_name}")?;
2319 }
2320 writeln!(self.out, " }};")?;
2321 final_name
2322 }
2323 None => variable_name,
2324 };
2325 writeln!(self.out, "{level}return {final_name};")?;
2326 } else {
2327 write!(self.out, "{level}return ")?;
2328 self.write_expr(module, expr, func_ctx)?;
2329 writeln!(self.out, ";")?
2330 }
2331 }
2332 Statement::Store { pointer, value } => {
2333 let ty_inner = func_ctx.resolve_type(pointer, &module.types);
2334 if ty_inner.is_atomic_pointer(&module.types) {
2335 let pointer_space = ty_inner.pointer_space().unwrap();
2336 let dummy = self.namer.call("dummy");
2337 write!(self.out, "{level}{{ ")?;
2338 if let TypeInner::Pointer { base, .. } = *ty_inner {
2339 self.write_value_type(module, &module.types[base].inner)?;
2340 }
2341 write!(self.out, " {dummy} = 0; ")?;
2342 match pointer_space {
2343 crate::AddressSpace::WorkGroup => {
2344 write!(self.out, "InterlockedExchange(")?;
2345 self.write_expr(module, pointer, func_ctx)?;
2346 }
2347 crate::AddressSpace::Storage { .. } => {
2348 let var_handle = self.fill_access_chain(module, pointer, func_ctx)?;
2349 let var_name = &self.names[&NameKey::GlobalVariable(var_handle)];
2350 write!(self.out, "{var_name}.InterlockedExchange(")?;
2351 let chain = mem::take(&mut self.temp_access_chain);
2352 self.write_storage_address(module, &chain, func_ctx)?;
2353 self.temp_access_chain = chain;
2354 }
2355 _ => unreachable!(),
2356 }
2357 write!(self.out, ", ")?;
2358 self.write_expr(module, value, func_ctx)?;
2359 writeln!(self.out, ", {dummy}); }}")?;
2360 } else if let Some(crate::AddressSpace::Storage { .. }) = ty_inner.pointer_space() {
2361 let var_handle = self.fill_access_chain(module, pointer, func_ctx)?;
2362 self.write_storage_store(
2363 module,
2364 var_handle,
2365 StoreValue::Expression(value),
2366 func_ctx,
2367 level,
2368 None,
2369 )?;
2370 } else {
2371 enum MatrixAccess {
2377 Direct {
2378 base: Handle<crate::Expression>,
2379 index: u32,
2380 },
2381 Struct {
2382 columns: crate::VectorSize,
2383 width: u8,
2384 base: Handle<crate::Expression>,
2385 },
2386 }
2387
2388 let get_members = |expr: Handle<crate::Expression>| {
2389 let resolved = func_ctx.resolve_type(expr, &module.types);
2390 match *resolved {
2391 TypeInner::Pointer { base, .. } => match module.types[base].inner {
2392 TypeInner::Struct { ref members, .. } => Some(members),
2393 _ => None,
2394 },
2395 _ => None,
2396 }
2397 };
2398
2399 write!(self.out, "{level}")?;
2400
2401 let matrix_access_on_lhs =
2402 find_matrix_in_access_chain(module, pointer, func_ctx).and_then(
2403 |(matrix_expr, vector, scalar)| match (
2404 func_ctx.resolve_type(matrix_expr, &module.types),
2405 &func_ctx.expressions[matrix_expr],
2406 ) {
2407 (
2408 &TypeInner::Pointer { base: ty, .. },
2409 &crate::Expression::AccessIndex { base, index },
2410 ) if matches!(
2411 module.types[ty].inner,
2412 TypeInner::Matrix {
2413 rows: crate::VectorSize::Bi,
2414 ..
2415 }
2416 ) && get_members(base)
2417 .map(|members| members[index as usize].binding.is_none())
2418 == Some(true) =>
2419 {
2420 Some((MatrixAccess::Direct { base, index }, vector, scalar))
2421 }
2422 _ => {
2423 if let Some(MatrixType {
2424 columns,
2425 rows: crate::VectorSize::Bi,
2426 width,
2427 }) = get_inner_matrix_of_struct_array_member(
2428 module,
2429 matrix_expr,
2430 func_ctx,
2431 true,
2432 ) {
2433 Some((
2434 MatrixAccess::Struct {
2435 columns,
2436 width,
2437 base: matrix_expr,
2438 },
2439 vector,
2440 scalar,
2441 ))
2442 } else {
2443 None
2444 }
2445 }
2446 },
2447 );
2448
2449 match matrix_access_on_lhs {
2450 Some((MatrixAccess::Direct { index, base }, vector, scalar)) => {
2451 let base_ty_res = &func_ctx.info[base].ty;
2452 let resolved = base_ty_res.inner_with(&module.types);
2453 let ty = match *resolved {
2454 TypeInner::Pointer { base, .. } => base,
2455 _ => base_ty_res.handle().unwrap(),
2456 };
2457
2458 if let Some(Index::Static(vec_index)) = vector {
2459 self.write_expr(module, base, func_ctx)?;
2460 write!(
2461 self.out,
2462 ".{}_{}",
2463 &self.names[&NameKey::StructMember(ty, index)],
2464 vec_index
2465 )?;
2466
2467 if let Some(scalar_index) = scalar {
2468 write!(self.out, "[")?;
2469 self.write_index(module, scalar_index, func_ctx)?;
2470 write!(self.out, "]")?;
2471 }
2472
2473 write!(self.out, " = ")?;
2474 self.write_expr(module, value, func_ctx)?;
2475 writeln!(self.out, ";")?;
2476 } else {
2477 let access = WrappedStructMatrixAccess { ty, index };
2478 match (&vector, &scalar) {
2479 (&Some(_), &Some(_)) => {
2480 self.write_wrapped_struct_matrix_set_scalar_function_name(
2481 access,
2482 )?;
2483 }
2484 (&Some(_), &None) => {
2485 self.write_wrapped_struct_matrix_set_vec_function_name(
2486 access,
2487 )?;
2488 }
2489 (&None, _) => {
2490 self.write_wrapped_struct_matrix_set_function_name(access)?;
2491 }
2492 }
2493
2494 write!(self.out, "(")?;
2495 self.write_expr(module, base, func_ctx)?;
2496 write!(self.out, ", ")?;
2497 self.write_expr(module, value, func_ctx)?;
2498
2499 if let Some(Index::Expression(vec_index)) = vector {
2500 write!(self.out, ", ")?;
2501 self.write_expr(module, vec_index, func_ctx)?;
2502
2503 if let Some(scalar_index) = scalar {
2504 write!(self.out, ", ")?;
2505 self.write_index(module, scalar_index, func_ctx)?;
2506 }
2507 }
2508 writeln!(self.out, ");")?;
2509 }
2510 }
2511 Some((
2512 MatrixAccess::Struct {
2513 columns,
2514 width,
2515 base,
2516 },
2517 Some(Index::Expression(vec_index)),
2518 scalar,
2519 )) => {
2520 if scalar.is_some() {
2524 write!(
2525 self.out,
2526 "__set_el_of_mat{}x2_f{}",
2527 columns as u8,
2528 width * 8
2529 )?;
2530 } else {
2531 write!(
2532 self.out,
2533 "__set_col_of_mat{}x2_f{}",
2534 columns as u8,
2535 width * 8
2536 )?;
2537 }
2538 write!(self.out, "(")?;
2539 self.write_expr(module, base, func_ctx)?;
2540 write!(self.out, ", ")?;
2541 self.write_expr(module, vec_index, func_ctx)?;
2542
2543 if let Some(scalar_index) = scalar {
2544 write!(self.out, ", ")?;
2545 self.write_index(module, scalar_index, func_ctx)?;
2546 }
2547
2548 write!(self.out, ", ")?;
2549 self.write_expr(module, value, func_ctx)?;
2550
2551 writeln!(self.out, ");")?;
2552 }
2553 Some((MatrixAccess::Struct { .. }, Some(Index::Static(_)), _))
2554 | Some((MatrixAccess::Struct { .. }, None, _))
2555 | None => {
2556 self.write_expr(module, pointer, func_ctx)?;
2557 write!(self.out, " = ")?;
2558
2559 if let Some(MatrixType {
2564 columns,
2565 rows: crate::VectorSize::Bi,
2566 width,
2567 }) = get_inner_matrix_of_struct_array_member(
2568 module, pointer, func_ctx, false,
2569 ) {
2570 let mut resolved = func_ctx.resolve_type(pointer, &module.types);
2571 if let TypeInner::Pointer { base, .. } = *resolved {
2572 resolved = &module.types[base].inner;
2573 }
2574
2575 write!(self.out, "(__mat{}x2_f{}", columns as u8, width * 8)?;
2576 if let TypeInner::Array { base, size, .. } = *resolved {
2577 self.write_array_size(module, base, size)?;
2578 }
2579 write!(self.out, ")")?;
2580 }
2581
2582 self.write_expr(module, value, func_ctx)?;
2583 writeln!(self.out, ";")?
2584 }
2585 }
2586 }
2587 }
2588 Statement::Loop {
2589 ref body,
2590 ref continuing,
2591 break_if,
2592 } => {
2593 let force_loop_bound_statements = self.gen_force_bounded_loop_statements(level);
2594 let gate_name = (!continuing.is_empty() || break_if.is_some())
2595 .then(|| self.namer.call("loop_init"));
2596
2597 if let Some((ref decl, _)) = force_loop_bound_statements {
2598 writeln!(self.out, "{decl}")?;
2599 }
2600 if let Some(ref gate_name) = gate_name {
2601 writeln!(self.out, "{level}bool {gate_name} = true;")?;
2602 }
2603
2604 self.continue_ctx.enter_loop();
2605 writeln!(self.out, "{level}while(true) {{")?;
2606 if let Some((_, ref break_and_inc)) = force_loop_bound_statements {
2607 writeln!(self.out, "{break_and_inc}")?;
2608 }
2609 let l2 = level.next();
2610 if let Some(gate_name) = gate_name {
2611 writeln!(self.out, "{l2}if (!{gate_name}) {{")?;
2612 let l3 = l2.next();
2613 for sta in continuing.iter() {
2614 self.write_stmt(module, sta, func_ctx, l3)?;
2615 }
2616 if let Some(condition) = break_if {
2617 write!(self.out, "{l3}if (")?;
2618 self.write_expr(module, condition, func_ctx)?;
2619 writeln!(self.out, ") {{")?;
2620 writeln!(self.out, "{}break;", l3.next())?;
2621 writeln!(self.out, "{l3}}}")?;
2622 }
2623 writeln!(self.out, "{l2}}}")?;
2624 writeln!(self.out, "{l2}{gate_name} = false;")?;
2625 }
2626
2627 for sta in body.iter() {
2628 self.write_stmt(module, sta, func_ctx, l2)?;
2629 }
2630
2631 writeln!(self.out, "{level}}}")?;
2632 self.continue_ctx.exit_loop();
2633 }
2634 Statement::Break => writeln!(self.out, "{level}break;")?,
2635 Statement::Continue => {
2636 if let Some(variable) = self.continue_ctx.continue_encountered() {
2637 writeln!(self.out, "{level}{variable} = true;")?;
2638 writeln!(self.out, "{level}break;")?
2639 } else {
2640 writeln!(self.out, "{level}continue;")?
2641 }
2642 }
2643 Statement::ControlBarrier(barrier) => {
2644 self.write_control_barrier(barrier, level)?;
2645 }
2646 Statement::MemoryBarrier(barrier) => {
2647 self.write_memory_barrier(barrier, level)?;
2648 }
2649 Statement::ImageStore {
2650 image,
2651 coordinate,
2652 array_index,
2653 value,
2654 } => {
2655 write!(self.out, "{level}")?;
2656 self.write_expr(module, image, func_ctx)?;
2657
2658 write!(self.out, "[")?;
2659 if let Some(index) = array_index {
2660 write!(self.out, "int3(")?;
2662 self.write_expr(module, coordinate, func_ctx)?;
2663 write!(self.out, ", ")?;
2664 self.write_expr(module, index, func_ctx)?;
2665 write!(self.out, ")")?;
2666 } else {
2667 self.write_expr(module, coordinate, func_ctx)?;
2668 }
2669 write!(self.out, "]")?;
2670
2671 write!(self.out, " = ")?;
2672 self.write_expr(module, value, func_ctx)?;
2673 writeln!(self.out, ";")?;
2674 }
2675 Statement::Call {
2676 function,
2677 ref arguments,
2678 result,
2679 } => {
2680 write!(self.out, "{level}")?;
2681
2682 if let Some(expr) = result {
2683 write!(self.out, "const ")?;
2684 let name = Baked(expr).to_string();
2685 let expr_ty = &func_ctx.info[expr].ty;
2686 let ty_inner = match *expr_ty {
2687 proc::TypeResolution::Handle(handle) => {
2688 self.write_type(module, handle)?;
2689 &module.types[handle].inner
2690 }
2691 proc::TypeResolution::Value(ref value) => {
2692 self.write_value_type(module, value)?;
2693 value
2694 }
2695 };
2696 write!(self.out, " {name}")?;
2697 if let TypeInner::Array { base, size, .. } = *ty_inner {
2698 self.write_array_size(module, base, size)?;
2699 }
2700 write!(self.out, " = ")?;
2701 self.named_expressions.insert(expr, name);
2702 }
2703 let func_name = &self.names[&NameKey::Function(function)];
2704 write!(self.out, "{func_name}(")?;
2705 let mut any_args_written = false;
2706 let mut separator = || {
2707 if any_args_written {
2708 ", "
2709 } else {
2710 any_args_written = true;
2711 ""
2712 }
2713 };
2714 for argument in arguments {
2715 write!(self.out, "{}", separator())?;
2716 self.write_expr(module, *argument, func_ctx)?;
2717 }
2718 if let Some(&var) = self.function_task_payload_var.get(&function) {
2719 let name = &self.names[&NameKey::GlobalVariable(var)];
2720 write!(self.out, "{}{name}", separator())?;
2722 }
2723 writeln!(self.out, ");")?;
2724 }
2725 Statement::Atomic {
2726 pointer,
2727 ref fun,
2728 value,
2729 result,
2730 } => {
2731 write!(self.out, "{level}")?;
2732 let res_var_info = if let Some(res_handle) = result {
2733 let name = Baked(res_handle).to_string();
2734 match func_ctx.info[res_handle].ty {
2735 proc::TypeResolution::Handle(handle) => self.write_type(module, handle)?,
2736 proc::TypeResolution::Value(ref value) => {
2737 self.write_value_type(module, value)?
2738 }
2739 };
2740 write!(self.out, " {name}; ")?;
2741 self.named_expressions.insert(res_handle, name.clone());
2742 Some((res_handle, name))
2743 } else {
2744 None
2745 };
2746 let pointer_space = func_ctx
2747 .resolve_type(pointer, &module.types)
2748 .pointer_space()
2749 .unwrap();
2750 let fun_str = fun.to_hlsl_suffix();
2751 let compare_expr = match *fun {
2752 crate::AtomicFunction::Exchange { compare: Some(cmp) } => Some(cmp),
2753 _ => None,
2754 };
2755 match pointer_space {
2756 crate::AddressSpace::WorkGroup => {
2757 write!(self.out, "Interlocked{fun_str}(")?;
2758 self.write_expr(module, pointer, func_ctx)?;
2759 self.emit_hlsl_atomic_tail(
2760 module,
2761 func_ctx,
2762 fun,
2763 compare_expr,
2764 value,
2765 &res_var_info,
2766 )?;
2767 }
2768 crate::AddressSpace::Storage { .. } => {
2769 let var_handle = self.fill_access_chain(module, pointer, func_ctx)?;
2770 let var_name = &self.names[&NameKey::GlobalVariable(var_handle)];
2771 let width = match func_ctx.resolve_type(value, &module.types) {
2772 &TypeInner::Scalar(Scalar { width: 8, .. }) => "64",
2773 _ => "",
2774 };
2775 write!(self.out, "{var_name}.Interlocked{fun_str}{width}(")?;
2776 let chain = mem::take(&mut self.temp_access_chain);
2777 self.write_storage_address(module, &chain, func_ctx)?;
2778 self.temp_access_chain = chain;
2779 self.emit_hlsl_atomic_tail(
2780 module,
2781 func_ctx,
2782 fun,
2783 compare_expr,
2784 value,
2785 &res_var_info,
2786 )?;
2787 }
2788 ref other => {
2789 return Err(Error::Custom(format!(
2790 "invalid address space {other:?} for atomic statement"
2791 )))
2792 }
2793 }
2794 if let Some(cmp) = compare_expr {
2795 if let Some(&(_res_handle, ref res_name)) = res_var_info.as_ref() {
2796 write!(
2797 self.out,
2798 "{level}{res_name}.exchanged = ({res_name}.old_value == "
2799 )?;
2800 self.write_expr(module, cmp, func_ctx)?;
2801 writeln!(self.out, ");")?;
2802 }
2803 }
2804 }
2805 Statement::ImageAtomic {
2806 image,
2807 coordinate,
2808 array_index,
2809 fun,
2810 value,
2811 } => {
2812 write!(self.out, "{level}")?;
2813
2814 let fun_str = fun.to_hlsl_suffix();
2815 write!(self.out, "Interlocked{fun_str}(")?;
2816 self.write_expr(module, image, func_ctx)?;
2817 write!(self.out, "[")?;
2818 self.write_texture_coordinates(
2819 "int",
2820 coordinate,
2821 array_index,
2822 None,
2823 module,
2824 func_ctx,
2825 )?;
2826 write!(self.out, "],")?;
2827
2828 self.write_expr(module, value, func_ctx)?;
2829 writeln!(self.out, ");")?;
2830 }
2831 Statement::WorkGroupUniformLoad { pointer, result } => {
2832 self.write_control_barrier(crate::Barrier::WORK_GROUP, level)?;
2833 write!(self.out, "{level}")?;
2834 let name = Baked(result).to_string();
2835 self.write_named_expr(module, pointer, name, result, func_ctx)?;
2836
2837 self.write_control_barrier(crate::Barrier::WORK_GROUP, level)?;
2838 }
2839 Statement::Switch {
2840 selector,
2841 ref cases,
2842 } => {
2843 self.write_switch(module, func_ctx, level, selector, cases)?;
2844 }
2845 Statement::RayQuery { query, ref fun } => {
2846 let crate::Expression::LocalVariable(query_var) = func_ctx.expressions[query]
2858 else {
2859 unreachable!()
2860 };
2861
2862 let tracker_expr_name = format!(
2863 "{RAY_QUERY_TRACKER_VARIABLE_PREFIX}{}",
2864 self.names[&func_ctx.name_key(query_var)]
2865 );
2866
2867 match *fun {
2868 RayQueryFunction::Initialize {
2869 acceleration_structure,
2870 descriptor,
2871 } => {
2872 self.write_initialize_function(
2873 module,
2874 level,
2875 query,
2876 acceleration_structure,
2877 descriptor,
2878 &tracker_expr_name,
2879 func_ctx,
2880 )?;
2881 }
2882 RayQueryFunction::Proceed { result } => {
2883 self.write_proceed(
2884 module,
2885 level,
2886 query,
2887 result,
2888 &tracker_expr_name,
2889 func_ctx,
2890 )?;
2891 }
2892 RayQueryFunction::GenerateIntersection { hit_t } => {
2893 self.write_generate_intersection(
2894 module,
2895 level,
2896 query,
2897 hit_t,
2898 &tracker_expr_name,
2899 func_ctx,
2900 )?;
2901 }
2902 RayQueryFunction::ConfirmIntersection => {
2903 self.write_confirm_intersection(
2904 module,
2905 level,
2906 query,
2907 &tracker_expr_name,
2908 func_ctx,
2909 )?;
2910 }
2911 RayQueryFunction::Terminate => {
2912 self.write_terminate(module, level, query, &tracker_expr_name, func_ctx)?;
2913 }
2914 RayQueryFunction::Begin => {
2915 if self.options.ray_query_initialization_tracking {
2916 writeln!(self.out, "{level}{tracker_expr_name} = 0u;")?;
2917 }
2918 }
2919 }
2920 }
2921 Statement::SubgroupBallot { result, predicate } => {
2922 write!(self.out, "{level}")?;
2923 let name = Baked(result).to_string();
2924 write!(self.out, "const uint4 {name} = ")?;
2925 self.named_expressions.insert(result, name);
2926
2927 write!(self.out, "WaveActiveBallot(")?;
2928 match predicate {
2929 Some(predicate) => self.write_expr(module, predicate, func_ctx)?,
2930 None => write!(self.out, "true")?,
2931 }
2932 writeln!(self.out, ");")?;
2933 }
2934 Statement::SubgroupCollectiveOperation {
2935 op,
2936 collective_op,
2937 argument,
2938 result,
2939 } => {
2940 write!(self.out, "{level}")?;
2941 write!(self.out, "const ")?;
2942 let name = Baked(result).to_string();
2943 match func_ctx.info[result].ty {
2944 proc::TypeResolution::Handle(handle) => self.write_type(module, handle)?,
2945 proc::TypeResolution::Value(ref value) => {
2946 self.write_value_type(module, value)?
2947 }
2948 };
2949 write!(self.out, " {name} = ")?;
2950 self.named_expressions.insert(result, name);
2951
2952 match (collective_op, op) {
2953 (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::All) => {
2954 write!(self.out, "WaveActiveAllTrue(")?
2955 }
2956 (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::Any) => {
2957 write!(self.out, "WaveActiveAnyTrue(")?
2958 }
2959 (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::Add) => {
2960 write!(self.out, "WaveActiveSum(")?
2961 }
2962 (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::Mul) => {
2963 write!(self.out, "WaveActiveProduct(")?
2964 }
2965 (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::Max) => {
2966 write!(self.out, "WaveActiveMax(")?
2967 }
2968 (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::Min) => {
2969 write!(self.out, "WaveActiveMin(")?
2970 }
2971 (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::And) => {
2972 write!(self.out, "WaveActiveBitAnd(")?
2973 }
2974 (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::Or) => {
2975 write!(self.out, "WaveActiveBitOr(")?
2976 }
2977 (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::Xor) => {
2978 write!(self.out, "WaveActiveBitXor(")?
2979 }
2980 (crate::CollectiveOperation::ExclusiveScan, crate::SubgroupOperation::Add) => {
2981 write!(self.out, "WavePrefixSum(")?
2982 }
2983 (crate::CollectiveOperation::ExclusiveScan, crate::SubgroupOperation::Mul) => {
2984 write!(self.out, "WavePrefixProduct(")?
2985 }
2986 (crate::CollectiveOperation::InclusiveScan, crate::SubgroupOperation::Add) => {
2987 self.write_expr(module, argument, func_ctx)?;
2988 write!(self.out, " + WavePrefixSum(")?;
2989 }
2990 (crate::CollectiveOperation::InclusiveScan, crate::SubgroupOperation::Mul) => {
2991 self.write_expr(module, argument, func_ctx)?;
2992 write!(self.out, " * WavePrefixProduct(")?;
2993 }
2994 _ => unimplemented!(),
2995 }
2996 self.write_expr(module, argument, func_ctx)?;
2997 writeln!(self.out, ");")?;
2998 }
2999 Statement::SubgroupGather {
3000 mode,
3001 argument,
3002 result,
3003 } => {
3004 write!(self.out, "{level}")?;
3005 write!(self.out, "const ")?;
3006 let name = Baked(result).to_string();
3007 match func_ctx.info[result].ty {
3008 proc::TypeResolution::Handle(handle) => self.write_type(module, handle)?,
3009 proc::TypeResolution::Value(ref value) => {
3010 self.write_value_type(module, value)?
3011 }
3012 };
3013 write!(self.out, " {name} = ")?;
3014 self.named_expressions.insert(result, name);
3015 match mode {
3016 crate::GatherMode::BroadcastFirst => {
3017 write!(self.out, "WaveReadLaneFirst(")?;
3018 self.write_expr(module, argument, func_ctx)?;
3019 }
3020 crate::GatherMode::QuadBroadcast(index) => {
3021 write!(self.out, "QuadReadLaneAt(")?;
3022 self.write_expr(module, argument, func_ctx)?;
3023 write!(self.out, ", ")?;
3024 self.write_expr(module, index, func_ctx)?;
3025 }
3026 crate::GatherMode::QuadSwap(direction) => {
3027 match direction {
3028 crate::Direction::X => {
3029 write!(self.out, "QuadReadAcrossX(")?;
3030 }
3031 crate::Direction::Y => {
3032 write!(self.out, "QuadReadAcrossY(")?;
3033 }
3034 crate::Direction::Diagonal => {
3035 write!(self.out, "QuadReadAcrossDiagonal(")?;
3036 }
3037 }
3038 self.write_expr(module, argument, func_ctx)?;
3039 }
3040 _ => {
3041 write!(self.out, "WaveReadLaneAt(")?;
3042 self.write_expr(module, argument, func_ctx)?;
3043 write!(self.out, ", ")?;
3044 match mode {
3045 crate::GatherMode::BroadcastFirst => unreachable!(),
3046 crate::GatherMode::Broadcast(index)
3047 | crate::GatherMode::Shuffle(index) => {
3048 self.write_expr(module, index, func_ctx)?;
3049 }
3050 crate::GatherMode::ShuffleDown(index) => {
3051 write!(self.out, "WaveGetLaneIndex() + ")?;
3052 self.write_expr(module, index, func_ctx)?;
3053 }
3054 crate::GatherMode::ShuffleUp(index) => {
3055 write!(self.out, "WaveGetLaneIndex() - ")?;
3056 self.write_expr(module, index, func_ctx)?;
3057 }
3058 crate::GatherMode::ShuffleXor(index) => {
3059 write!(self.out, "WaveGetLaneIndex() ^ ")?;
3060 self.write_expr(module, index, func_ctx)?;
3061 }
3062 crate::GatherMode::QuadBroadcast(_) => unreachable!(),
3063 crate::GatherMode::QuadSwap(_) => unreachable!(),
3064 }
3065 }
3066 }
3067 writeln!(self.out, ");")?;
3068 }
3069 Statement::CooperativeStore { .. } => unimplemented!(),
3070 Statement::RayPipelineFunction(_) => unreachable!(),
3071 Statement::DebugPrintf { .. } => unimplemented!(),
3072 }
3073
3074 Ok(())
3075 }
3076
3077 #[allow(clippy::too_many_arguments)]
3078 fn write_math_expression(
3079 &mut self,
3080 module: &Module,
3081 fun: crate::MathFunction,
3082 arg: Handle<crate::Expression>,
3083 arg1: Option<Handle<crate::Expression>>,
3084 arg2: Option<Handle<crate::Expression>>,
3085 arg3: Option<Handle<crate::Expression>>,
3086 func_ctx: &back::FunctionCtx<'_>,
3087 ) -> BackendResult {
3088 use crate::MathFunction as Mf;
3089
3090 enum Function {
3091 Asincosh { is_sin: bool },
3092 Atanh,
3093 Pack2x16float,
3094 Pack2x16snorm,
3095 Pack2x16unorm,
3096 Pack4x8snorm,
3097 Pack4x8unorm,
3098 Pack4xI8,
3099 Pack4xU8,
3100 Pack4xI8Clamp,
3101 Pack4xU8Clamp,
3102 Unpack2x16float,
3103 Unpack2x16snorm,
3104 Unpack2x16unorm,
3105 Unpack4x8snorm,
3106 Unpack4x8unorm,
3107 Unpack4xI8,
3108 Unpack4xU8,
3109 Dot4I8Packed,
3110 Dot4U8Packed,
3111 QuantizeToF16,
3112 Regular(&'static str),
3113 MissingIntOverload(&'static str),
3114 MissingIntReturnType(&'static str),
3115 CountTrailingZeros,
3116 CountLeadingZeros,
3117 }
3118
3119 let fun = match fun {
3120 Mf::Abs => match func_ctx.resolve_type(arg, &module.types).scalar() {
3122 Some(Scalar::I32) => Function::Regular(ABS_FUNCTION),
3123 _ => Function::Regular("abs"),
3124 },
3125 Mf::Min => Function::Regular("min"),
3126 Mf::Max => Function::Regular("max"),
3127 Mf::Clamp => Function::Regular("clamp"),
3128 Mf::Saturate => Function::Regular("saturate"),
3129 Mf::Cos => Function::Regular("cos"),
3131 Mf::Cosh => Function::Regular("cosh"),
3132 Mf::Sin => Function::Regular("sin"),
3133 Mf::Sinh => Function::Regular("sinh"),
3134 Mf::Tan => Function::Regular("tan"),
3135 Mf::Tanh => Function::Regular("tanh"),
3136 Mf::Acos => Function::Regular("acos"),
3137 Mf::Asin => Function::Regular("asin"),
3138 Mf::Atan => Function::Regular("atan"),
3139 Mf::Atan2 => Function::Regular("atan2"),
3140 Mf::Asinh => Function::Asincosh { is_sin: true },
3141 Mf::Acosh => Function::Asincosh { is_sin: false },
3142 Mf::Atanh => Function::Atanh,
3143 Mf::Radians => Function::Regular("radians"),
3144 Mf::Degrees => Function::Regular("degrees"),
3145 Mf::Ceil => Function::Regular("ceil"),
3147 Mf::Floor => Function::Regular("floor"),
3148 Mf::Round => Function::Regular("round"),
3149 Mf::Fract => Function::Regular("frac"),
3150 Mf::Trunc => Function::Regular("trunc"),
3151 Mf::Modf => Function::Regular(MODF_FUNCTION),
3152 Mf::Frexp => Function::Regular(FREXP_FUNCTION),
3153 Mf::Ldexp => Function::Regular("ldexp"),
3154 Mf::Exp => Function::Regular("exp"),
3156 Mf::Exp2 => Function::Regular("exp2"),
3157 Mf::Log => Function::Regular("log"),
3158 Mf::Log2 => Function::Regular("log2"),
3159 Mf::Pow => Function::Regular("pow"),
3160 Mf::Dot => Function::Regular("dot"),
3162 Mf::Dot4I8Packed => Function::Dot4I8Packed,
3163 Mf::Dot4U8Packed => Function::Dot4U8Packed,
3164 Mf::Cross => Function::Regular("cross"),
3166 Mf::Distance => Function::Regular("distance"),
3167 Mf::Length => Function::Regular("length"),
3168 Mf::Normalize => Function::Regular("normalize"),
3169 Mf::FaceForward => Function::Regular("faceforward"),
3170 Mf::Reflect => Function::Regular("reflect"),
3171 Mf::Refract => Function::Regular("refract"),
3172 Mf::Sign => Function::Regular("sign"),
3174 Mf::Fma => Function::Regular("mad"),
3175 Mf::Mix => Function::Regular("lerp"),
3176 Mf::Step => Function::Regular("step"),
3177 Mf::SmoothStep => Function::Regular("smoothstep"),
3178 Mf::Sqrt => Function::Regular("sqrt"),
3179 Mf::InverseSqrt => Function::Regular("rsqrt"),
3180 Mf::Transpose => Function::Regular("transpose"),
3182 Mf::Determinant => Function::Regular("determinant"),
3183 Mf::QuantizeToF16 => Function::QuantizeToF16,
3184 Mf::CountTrailingZeros => Function::CountTrailingZeros,
3186 Mf::CountLeadingZeros => Function::CountLeadingZeros,
3187 Mf::CountOneBits => Function::MissingIntOverload("countbits"),
3188 Mf::ReverseBits => Function::MissingIntOverload("reversebits"),
3189 Mf::FirstTrailingBit => Function::MissingIntReturnType("firstbitlow"),
3190 Mf::FirstLeadingBit => Function::MissingIntReturnType("firstbithigh"),
3191 Mf::ExtractBits => Function::Regular(EXTRACT_BITS_FUNCTION),
3192 Mf::InsertBits => Function::Regular(INSERT_BITS_FUNCTION),
3193 Mf::Pack2x16float => Function::Pack2x16float,
3195 Mf::Pack2x16snorm => Function::Pack2x16snorm,
3196 Mf::Pack2x16unorm => Function::Pack2x16unorm,
3197 Mf::Pack4x8snorm => Function::Pack4x8snorm,
3198 Mf::Pack4x8unorm => Function::Pack4x8unorm,
3199 Mf::Pack4xI8 => Function::Pack4xI8,
3200 Mf::Pack4xU8 => Function::Pack4xU8,
3201 Mf::Pack4xI8Clamp => Function::Pack4xI8Clamp,
3202 Mf::Pack4xU8Clamp => Function::Pack4xU8Clamp,
3203 Mf::Unpack2x16float => Function::Unpack2x16float,
3205 Mf::Unpack2x16snorm => Function::Unpack2x16snorm,
3206 Mf::Unpack2x16unorm => Function::Unpack2x16unorm,
3207 Mf::Unpack4x8snorm => Function::Unpack4x8snorm,
3208 Mf::Unpack4x8unorm => Function::Unpack4x8unorm,
3209 Mf::Unpack4xI8 => Function::Unpack4xI8,
3210 Mf::Unpack4xU8 => Function::Unpack4xU8,
3211 _ => return Err(Error::Unimplemented(format!("write_expr_math {fun:?}"))),
3212 };
3213
3214 match fun {
3215 Function::Asincosh { is_sin } => {
3216 write!(self.out, "log(")?;
3217 self.write_expr(module, arg, func_ctx)?;
3218 write!(self.out, " + sqrt(")?;
3219 self.write_expr(module, arg, func_ctx)?;
3220 write!(self.out, " * ")?;
3221 self.write_expr(module, arg, func_ctx)?;
3222 match is_sin {
3223 true => write!(self.out, " + 1.0))")?,
3224 false => write!(self.out, " - 1.0))")?,
3225 }
3226 }
3227 Function::Atanh => {
3228 write!(self.out, "0.5 * log((1.0 + ")?;
3229 self.write_expr(module, arg, func_ctx)?;
3230 write!(self.out, ") / (1.0 - ")?;
3231 self.write_expr(module, arg, func_ctx)?;
3232 write!(self.out, "))")?;
3233 }
3234 Function::Pack2x16float => {
3235 write!(self.out, "(f32tof16(")?;
3236 self.write_expr(module, arg, func_ctx)?;
3237 write!(self.out, "[0]) | f32tof16(")?;
3238 self.write_expr(module, arg, func_ctx)?;
3239 write!(self.out, "[1]) << 16)")?;
3240 }
3241 Function::Pack2x16snorm => {
3242 let scale = 32767;
3243
3244 write!(self.out, "uint((int(round(clamp(")?;
3245 self.write_expr(module, arg, func_ctx)?;
3246 write!(
3247 self.out,
3248 "[0], -1.0, 1.0) * {scale}.0)) & 0xFFFF) | ((int(round(clamp("
3249 )?;
3250 self.write_expr(module, arg, func_ctx)?;
3251 write!(self.out, "[1], -1.0, 1.0) * {scale}.0)) & 0xFFFF) << 16))",)?;
3252 }
3253 Function::Pack2x16unorm => {
3254 let scale = 65535;
3255
3256 write!(self.out, "(uint(round(clamp(")?;
3257 self.write_expr(module, arg, func_ctx)?;
3258 write!(self.out, "[0], 0.0, 1.0) * {scale}.0)) | uint(round(clamp(")?;
3259 self.write_expr(module, arg, func_ctx)?;
3260 write!(self.out, "[1], 0.0, 1.0) * {scale}.0)) << 16)")?;
3261 }
3262 Function::Pack4x8snorm => {
3263 let scale = 127;
3264
3265 write!(self.out, "uint((int(round(clamp(")?;
3266 self.write_expr(module, arg, func_ctx)?;
3267 write!(
3268 self.out,
3269 "[0], -1.0, 1.0) * {scale}.0)) & 0xFF) | ((int(round(clamp("
3270 )?;
3271 self.write_expr(module, arg, func_ctx)?;
3272 write!(
3273 self.out,
3274 "[1], -1.0, 1.0) * {scale}.0)) & 0xFF) << 8) | ((int(round(clamp("
3275 )?;
3276 self.write_expr(module, arg, func_ctx)?;
3277 write!(
3278 self.out,
3279 "[2], -1.0, 1.0) * {scale}.0)) & 0xFF) << 16) | ((int(round(clamp("
3280 )?;
3281 self.write_expr(module, arg, func_ctx)?;
3282 write!(self.out, "[3], -1.0, 1.0) * {scale}.0)) & 0xFF) << 24))",)?;
3283 }
3284 Function::Pack4x8unorm => {
3285 let scale = 255;
3286
3287 write!(self.out, "(uint(round(clamp(")?;
3288 self.write_expr(module, arg, func_ctx)?;
3289 write!(self.out, "[0], 0.0, 1.0) * {scale}.0)) | uint(round(clamp(")?;
3290 self.write_expr(module, arg, func_ctx)?;
3291 write!(
3292 self.out,
3293 "[1], 0.0, 1.0) * {scale}.0)) << 8 | uint(round(clamp("
3294 )?;
3295 self.write_expr(module, arg, func_ctx)?;
3296 write!(
3297 self.out,
3298 "[2], 0.0, 1.0) * {scale}.0)) << 16 | uint(round(clamp("
3299 )?;
3300 self.write_expr(module, arg, func_ctx)?;
3301 write!(self.out, "[3], 0.0, 1.0) * {scale}.0)) << 24)")?;
3302 }
3303 fun @ (Function::Pack4xI8
3304 | Function::Pack4xU8
3305 | Function::Pack4xI8Clamp
3306 | Function::Pack4xU8Clamp) => {
3307 let was_signed = matches!(fun, Function::Pack4xI8 | Function::Pack4xI8Clamp);
3308 let clamp_bounds = match fun {
3309 Function::Pack4xI8Clamp => Some(("-128", "127")),
3310 Function::Pack4xU8Clamp => Some(("0", "255")),
3311 _ => None,
3312 };
3313 if was_signed {
3314 write!(self.out, "uint(")?;
3315 }
3316 let write_arg = |this: &mut Self| -> BackendResult {
3317 if let Some((min, max)) = clamp_bounds {
3318 write!(this.out, "clamp(")?;
3319 this.write_expr(module, arg, func_ctx)?;
3320 write!(this.out, ", {min}, {max})")?;
3321 } else {
3322 this.write_expr(module, arg, func_ctx)?;
3323 }
3324 Ok(())
3325 };
3326 write!(self.out, "(")?;
3327 write_arg(self)?;
3328 write!(self.out, "[0] & 0xFF) | ((")?;
3329 write_arg(self)?;
3330 write!(self.out, "[1] & 0xFF) << 8) | ((")?;
3331 write_arg(self)?;
3332 write!(self.out, "[2] & 0xFF) << 16) | ((")?;
3333 write_arg(self)?;
3334 write!(self.out, "[3] & 0xFF) << 24)")?;
3335 if was_signed {
3336 write!(self.out, ")")?;
3337 }
3338 }
3339
3340 Function::Unpack2x16float => {
3341 write!(self.out, "float2(f16tof32(")?;
3342 self.write_expr(module, arg, func_ctx)?;
3343 write!(self.out, "), f16tof32((")?;
3344 self.write_expr(module, arg, func_ctx)?;
3345 write!(self.out, ") >> 16))")?;
3346 }
3347 Function::Unpack2x16snorm => {
3348 let scale = 32767;
3349
3350 write!(self.out, "(float2(int2(")?;
3351 self.write_expr(module, arg, func_ctx)?;
3352 write!(self.out, " << 16, ")?;
3353 self.write_expr(module, arg, func_ctx)?;
3354 write!(self.out, ") >> 16) / {scale}.0)")?;
3355 }
3356 Function::Unpack2x16unorm => {
3357 let scale = 65535;
3358
3359 write!(self.out, "(float2(")?;
3360 self.write_expr(module, arg, func_ctx)?;
3361 write!(self.out, " & 0xFFFF, ")?;
3362 self.write_expr(module, arg, func_ctx)?;
3363 write!(self.out, " >> 16) / {scale}.0)")?;
3364 }
3365 Function::Unpack4x8snorm => {
3366 let scale = 127;
3367
3368 write!(self.out, "(float4(int4(")?;
3369 self.write_expr(module, arg, func_ctx)?;
3370 write!(self.out, " << 24, ")?;
3371 self.write_expr(module, arg, func_ctx)?;
3372 write!(self.out, " << 16, ")?;
3373 self.write_expr(module, arg, func_ctx)?;
3374 write!(self.out, " << 8, ")?;
3375 self.write_expr(module, arg, func_ctx)?;
3376 write!(self.out, ") >> 24) / {scale}.0)")?;
3377 }
3378 Function::Unpack4x8unorm => {
3379 let scale = 255;
3380
3381 write!(self.out, "(float4(")?;
3382 self.write_expr(module, arg, func_ctx)?;
3383 write!(self.out, " & 0xFF, ")?;
3384 self.write_expr(module, arg, func_ctx)?;
3385 write!(self.out, " >> 8 & 0xFF, ")?;
3386 self.write_expr(module, arg, func_ctx)?;
3387 write!(self.out, " >> 16 & 0xFF, ")?;
3388 self.write_expr(module, arg, func_ctx)?;
3389 write!(self.out, " >> 24) / {scale}.0)")?;
3390 }
3391 fun @ (Function::Unpack4xI8 | Function::Unpack4xU8) => {
3392 write!(self.out, "(")?;
3393 if matches!(fun, Function::Unpack4xU8) {
3394 write!(self.out, "u")?;
3395 }
3396 write!(self.out, "int4(")?;
3397 self.write_expr(module, arg, func_ctx)?;
3398 write!(self.out, ", ")?;
3399 self.write_expr(module, arg, func_ctx)?;
3400 write!(self.out, " >> 8, ")?;
3401 self.write_expr(module, arg, func_ctx)?;
3402 write!(self.out, " >> 16, ")?;
3403 self.write_expr(module, arg, func_ctx)?;
3404 write!(self.out, " >> 24) << 24 >> 24)")?;
3405 }
3406 fun @ (Function::Dot4I8Packed | Function::Dot4U8Packed) => {
3407 let arg1 = arg1.unwrap();
3408
3409 if self.options.shader_model >= ShaderModel::V6_4 {
3410 let function_name = match fun {
3412 Function::Dot4I8Packed => "dot4add_i8packed",
3413 Function::Dot4U8Packed => "dot4add_u8packed",
3414 _ => unreachable!(),
3415 };
3416 write!(self.out, "{function_name}(")?;
3417 self.write_expr(module, arg, func_ctx)?;
3418 write!(self.out, ", ")?;
3419 self.write_expr(module, arg1, func_ctx)?;
3420 write!(self.out, ", 0)")?;
3421 } else {
3422 write!(self.out, "dot(")?;
3424
3425 if matches!(fun, Function::Dot4U8Packed) {
3426 write!(self.out, "u")?;
3427 }
3428 write!(self.out, "int4(")?;
3429 self.write_expr(module, arg, func_ctx)?;
3430 write!(self.out, ", ")?;
3431 self.write_expr(module, arg, func_ctx)?;
3432 write!(self.out, " >> 8, ")?;
3433 self.write_expr(module, arg, func_ctx)?;
3434 write!(self.out, " >> 16, ")?;
3435 self.write_expr(module, arg, func_ctx)?;
3436 write!(self.out, " >> 24) << 24 >> 24, ")?;
3437
3438 if matches!(fun, Function::Dot4U8Packed) {
3439 write!(self.out, "u")?;
3440 }
3441 write!(self.out, "int4(")?;
3442 self.write_expr(module, arg1, func_ctx)?;
3443 write!(self.out, ", ")?;
3444 self.write_expr(module, arg1, func_ctx)?;
3445 write!(self.out, " >> 8, ")?;
3446 self.write_expr(module, arg1, func_ctx)?;
3447 write!(self.out, " >> 16, ")?;
3448 self.write_expr(module, arg1, func_ctx)?;
3449 write!(self.out, " >> 24) << 24 >> 24)")?;
3450 }
3451 }
3452 Function::QuantizeToF16 => {
3453 write!(self.out, "f16tof32(f32tof16(")?;
3454 self.write_expr(module, arg, func_ctx)?;
3455 write!(self.out, "))")?;
3456 }
3457 Function::Regular(fun_name) => {
3458 write!(self.out, "{fun_name}(")?;
3459 self.write_expr(module, arg, func_ctx)?;
3460 if let Some(arg) = arg1 {
3461 write!(self.out, ", ")?;
3462 self.write_expr(module, arg, func_ctx)?;
3463 }
3464 if let Some(arg) = arg2 {
3465 write!(self.out, ", ")?;
3466 self.write_expr(module, arg, func_ctx)?;
3467 }
3468 if let Some(arg) = arg3 {
3469 write!(self.out, ", ")?;
3470 self.write_expr(module, arg, func_ctx)?;
3471 }
3472 write!(self.out, ")")?
3473 }
3474 Function::MissingIntOverload(fun_name) => {
3477 let scalar_kind = func_ctx.resolve_type(arg, &module.types).scalar();
3478 if let Some(Scalar::I32) = scalar_kind {
3479 write!(self.out, "asint({fun_name}(asuint(")?;
3480 self.write_expr(module, arg, func_ctx)?;
3481 write!(self.out, ")))")?;
3482 } else {
3483 write!(self.out, "{fun_name}(")?;
3484 self.write_expr(module, arg, func_ctx)?;
3485 write!(self.out, ")")?;
3486 }
3487 }
3488 Function::MissingIntReturnType(fun_name) => {
3491 let scalar_kind = func_ctx.resolve_type(arg, &module.types).scalar();
3492 if let Some(Scalar::I32) = scalar_kind {
3493 write!(self.out, "asint({fun_name}(")?;
3494 self.write_expr(module, arg, func_ctx)?;
3495 write!(self.out, "))")?;
3496 } else {
3497 write!(self.out, "{fun_name}(")?;
3498 self.write_expr(module, arg, func_ctx)?;
3499 write!(self.out, ")")?;
3500 }
3501 }
3502 Function::CountTrailingZeros => {
3503 match *func_ctx.resolve_type(arg, &module.types) {
3504 TypeInner::Vector { size, scalar } => {
3505 let s = match size {
3506 crate::VectorSize::Bi => ".xx",
3507 crate::VectorSize::Tri => ".xxx",
3508 crate::VectorSize::Quad => ".xxxx",
3509 };
3510
3511 let scalar_width_bits = scalar.width * 8;
3512
3513 if scalar.kind == ScalarKind::Uint || scalar.width != 4 {
3514 write!(self.out, "min(({scalar_width_bits}u){s}, firstbitlow(")?;
3515 self.write_expr(module, arg, func_ctx)?;
3516 write!(self.out, "))")?;
3517 } else {
3518 write!(
3520 self.out,
3521 "asint(min(({scalar_width_bits}u){s}, firstbitlow("
3522 )?;
3523 self.write_expr(module, arg, func_ctx)?;
3524 write!(self.out, ")))")?;
3525 }
3526 }
3527 TypeInner::Scalar(scalar) => {
3528 let scalar_width_bits = scalar.width * 8;
3529
3530 if scalar.kind == ScalarKind::Uint || scalar.width != 4 {
3531 write!(self.out, "min({scalar_width_bits}u, firstbitlow(")?;
3532 self.write_expr(module, arg, func_ctx)?;
3533 write!(self.out, "))")?;
3534 } else {
3535 write!(self.out, "asint(min({scalar_width_bits}u, firstbitlow(")?;
3537 self.write_expr(module, arg, func_ctx)?;
3538 write!(self.out, ")))")?;
3539 }
3540 }
3541 _ => unreachable!(),
3542 }
3543
3544 return Ok(());
3545 }
3546 Function::CountLeadingZeros => {
3547 match *func_ctx.resolve_type(arg, &module.types) {
3548 TypeInner::Vector { size, scalar } => {
3549 let s = match size {
3550 crate::VectorSize::Bi => ".xx",
3551 crate::VectorSize::Tri => ".xxx",
3552 crate::VectorSize::Quad => ".xxxx",
3553 };
3554
3555 let constant = scalar.width * 8 - 1;
3557
3558 if scalar.kind == ScalarKind::Uint {
3559 write!(self.out, "(({constant}u){s} - firstbithigh(")?;
3560 self.write_expr(module, arg, func_ctx)?;
3561 write!(self.out, "))")?;
3562 } else {
3563 let conversion_func = match scalar.width {
3564 4 => "asint",
3565 _ => "",
3566 };
3567 write!(self.out, "(")?;
3568 self.write_expr(module, arg, func_ctx)?;
3569 write!(
3570 self.out,
3571 " < (0){s} ? (0){s} : ({constant}){s} - {conversion_func}(firstbithigh("
3572 )?;
3573 self.write_expr(module, arg, func_ctx)?;
3574 write!(self.out, ")))")?;
3575 }
3576 }
3577 TypeInner::Scalar(scalar) => {
3578 let constant = scalar.width * 8 - 1;
3580
3581 if let ScalarKind::Uint = scalar.kind {
3582 write!(self.out, "({constant}u - firstbithigh(")?;
3583 self.write_expr(module, arg, func_ctx)?;
3584 write!(self.out, "))")?;
3585 } else {
3586 let conversion_func = match scalar.width {
3587 4 => "asint",
3588 _ => "",
3589 };
3590 write!(self.out, "(")?;
3591 self.write_expr(module, arg, func_ctx)?;
3592 write!(
3593 self.out,
3594 " < 0 ? 0 : {constant} - {conversion_func}(firstbithigh("
3595 )?;
3596 self.write_expr(module, arg, func_ctx)?;
3597 write!(self.out, ")))")?;
3598 }
3599 }
3600 _ => unreachable!(),
3601 }
3602 }
3603 }
3604
3605 Ok(())
3606 }
3607
3608 fn write_const_expression(
3609 &mut self,
3610 module: &Module,
3611 expr: Handle<crate::Expression>,
3612 arena: &crate::Arena<crate::Expression>,
3613 ) -> BackendResult {
3614 self.write_possibly_const_expression(module, expr, arena, |writer, expr| {
3615 writer.write_const_expression(module, expr, arena)
3616 })
3617 }
3618
3619 pub(super) fn write_literal(&mut self, literal: crate::Literal) -> BackendResult {
3620 match literal {
3621 crate::Literal::F64(value) => write!(self.out, "{value:?}L")?,
3622 crate::Literal::F32(value) => write!(self.out, "{value:?}")?,
3623 crate::Literal::F16(value) => write!(self.out, "{value:?}h")?,
3624 crate::Literal::U16(value) => write!(self.out, "uint16_t({value})")?,
3625 crate::Literal::I16(value) => write!(self.out, "int16_t({value})")?,
3626 crate::Literal::U32(value) => write!(self.out, "{value}u")?,
3627 crate::Literal::I32(value) if value == i32::MIN => {
3633 write!(self.out, "int({} - 1)", value + 1)?
3634 }
3635 crate::Literal::I32(value) => write!(self.out, "int({value})")?,
3639 crate::Literal::U64(value) => write!(self.out, "{value}uL")?,
3640 crate::Literal::I64(value) if value == i64::MIN => {
3642 write!(self.out, "({}L - 1L)", value + 1)?;
3643 }
3644 crate::Literal::I64(value) => write!(self.out, "{value}L")?,
3645 crate::Literal::Bool(value) => write!(self.out, "{value}")?,
3646 crate::Literal::AbstractInt(_) | crate::Literal::AbstractFloat(_) => {
3647 return Err(Error::Custom(
3648 "Abstract types should not appear in IR presented to backends".into(),
3649 ));
3650 }
3651 }
3652 Ok(())
3653 }
3654
3655 fn write_possibly_const_expression<E>(
3656 &mut self,
3657 module: &Module,
3658 expr: Handle<crate::Expression>,
3659 expressions: &crate::Arena<crate::Expression>,
3660 write_expression: E,
3661 ) -> BackendResult
3662 where
3663 E: Fn(&mut Self, Handle<crate::Expression>) -> BackendResult,
3664 {
3665 use crate::Expression;
3666
3667 match expressions[expr] {
3668 Expression::Literal(literal) => {
3669 self.write_literal(literal)?;
3670 }
3671 Expression::Constant(handle) => {
3672 let constant = &module.constants[handle];
3673 if constant.name.is_some() {
3674 write!(self.out, "{}", self.names[&NameKey::Constant(handle)])?;
3675 } else {
3676 self.write_const_expression(module, constant.init, &module.global_expressions)?;
3677 }
3678 }
3679 Expression::ZeroValue(ty) => {
3680 self.write_wrapped_zero_value_function_name(module, WrappedZeroValue { ty })?;
3681 write!(self.out, "()")?;
3682 }
3683 Expression::Compose { ty, ref components } => {
3684 match module.types[ty].inner {
3685 TypeInner::Struct { .. } | TypeInner::Array { .. } => {
3686 self.write_wrapped_constructor_function_name(
3687 module,
3688 WrappedConstructor { ty },
3689 )?;
3690 }
3691 _ => {
3692 self.write_type(module, ty)?;
3693 }
3694 };
3695 write!(self.out, "(")?;
3696 for (index, component) in components.iter().enumerate() {
3697 if index != 0 {
3698 write!(self.out, ", ")?;
3699 }
3700 write_expression(self, *component)?;
3701 }
3702 write!(self.out, ")")?;
3703 }
3704 Expression::Splat { size, value } => {
3705 let number_of_components = match size {
3709 crate::VectorSize::Bi => "xx",
3710 crate::VectorSize::Tri => "xxx",
3711 crate::VectorSize::Quad => "xxxx",
3712 };
3713 write!(self.out, "(")?;
3714 write_expression(self, value)?;
3715 write!(self.out, ").{number_of_components}")?
3716 }
3717 _ => {
3718 return Err(Error::Override);
3719 }
3720 }
3721
3722 Ok(())
3723 }
3724
3725 pub(super) fn write_expr(
3730 &mut self,
3731 module: &Module,
3732 expr: Handle<crate::Expression>,
3733 func_ctx: &back::FunctionCtx<'_>,
3734 ) -> BackendResult {
3735 use crate::Expression;
3736
3737 let ff_input = if self.options.special_constants_binding.is_some() {
3739 func_ctx.is_fixed_function_input(expr, module)
3740 } else {
3741 None
3742 };
3743 let closing_bracket = match ff_input {
3744 Some(crate::BuiltIn::VertexIndex) => {
3745 write!(self.out, "({SPECIAL_CBUF_VAR}.{SPECIAL_FIRST_VERTEX} + ")?;
3746 ")"
3747 }
3748 Some(crate::BuiltIn::InstanceIndex) => {
3749 write!(self.out, "({SPECIAL_CBUF_VAR}.{SPECIAL_FIRST_INSTANCE} + ",)?;
3750 ")"
3751 }
3752 Some(crate::BuiltIn::NumWorkGroups) => {
3753 write!(
3757 self.out,
3758 "uint3({SPECIAL_CBUF_VAR}.{SPECIAL_FIRST_VERTEX}, {SPECIAL_CBUF_VAR}.{SPECIAL_FIRST_INSTANCE}, {SPECIAL_CBUF_VAR}.{SPECIAL_OTHER})",
3759 )?;
3760 return Ok(());
3761 }
3762 _ => "",
3763 };
3764
3765 if let Some(name) = self.named_expressions.get(&expr) {
3766 write!(self.out, "{name}{closing_bracket}")?;
3767 return Ok(());
3768 }
3769
3770 let expression = &func_ctx.expressions[expr];
3771
3772 match *expression {
3773 Expression::Literal(_)
3774 | Expression::Constant(_)
3775 | Expression::ZeroValue(_)
3776 | Expression::Compose { .. }
3777 | Expression::Splat { .. } => {
3778 self.write_possibly_const_expression(
3779 module,
3780 expr,
3781 func_ctx.expressions,
3782 |writer, expr| writer.write_expr(module, expr, func_ctx),
3783 )?;
3784 }
3785 Expression::Override(_) => return Err(Error::Override),
3786 Expression::Binary {
3793 op:
3794 op @ crate::BinaryOperator::Add
3795 | op @ crate::BinaryOperator::Subtract
3796 | op @ crate::BinaryOperator::Multiply,
3797 left,
3798 right,
3799 } if matches!(
3800 func_ctx.resolve_type(expr, &module.types).scalar(),
3801 Some(Scalar::I32)
3802 ) =>
3803 {
3804 write!(self.out, "asint(asuint(",)?;
3805 self.write_expr(module, left, func_ctx)?;
3806 write!(self.out, ") {} asuint(", back::binary_operation_str(op))?;
3807 self.write_expr(module, right, func_ctx)?;
3808 write!(self.out, "))")?;
3809 }
3810 Expression::Binary {
3813 op: crate::BinaryOperator::Multiply,
3814 left,
3815 right,
3816 } if func_ctx.resolve_type(left, &module.types).is_matrix()
3817 || func_ctx.resolve_type(right, &module.types).is_matrix() =>
3818 {
3819 write!(self.out, "mul(")?;
3821 self.write_expr(module, right, func_ctx)?;
3822 write!(self.out, ", ")?;
3823 self.write_expr(module, left, func_ctx)?;
3824 write!(self.out, ")")?;
3825 }
3826
3827 Expression::Binary {
3839 op: crate::BinaryOperator::Divide,
3840 left,
3841 right,
3842 } if matches!(
3843 func_ctx.resolve_type(expr, &module.types).scalar_kind(),
3844 Some(ScalarKind::Sint | ScalarKind::Uint)
3845 ) =>
3846 {
3847 write!(self.out, "{DIV_FUNCTION}(")?;
3848 self.write_expr(module, left, func_ctx)?;
3849 write!(self.out, ", ")?;
3850 self.write_expr(module, right, func_ctx)?;
3851 write!(self.out, ")")?;
3852 }
3853
3854 Expression::Binary {
3855 op: crate::BinaryOperator::Modulo,
3856 left,
3857 right,
3858 } if matches!(
3859 func_ctx.resolve_type(expr, &module.types).scalar_kind(),
3860 Some(ScalarKind::Sint | ScalarKind::Uint | ScalarKind::Float)
3861 ) =>
3862 {
3863 write!(self.out, "{MOD_FUNCTION}(")?;
3864 self.write_expr(module, left, func_ctx)?;
3865 write!(self.out, ", ")?;
3866 self.write_expr(module, right, func_ctx)?;
3867 write!(self.out, ")")?;
3868 }
3869
3870 Expression::Binary { op, left, right } => {
3871 write!(self.out, "(")?;
3872 self.write_expr(module, left, func_ctx)?;
3873 write!(self.out, " {} ", back::binary_operation_str(op))?;
3874 self.write_expr(module, right, func_ctx)?;
3875 write!(self.out, ")")?;
3876 }
3877 Expression::Access { base, index } => {
3878 if let Some(crate::AddressSpace::Storage { .. }) =
3879 func_ctx.resolve_type(expr, &module.types).pointer_space()
3880 {
3881 } else {
3883 if let Some(MatrixType {
3890 columns,
3891 rows: crate::VectorSize::Bi,
3892 width,
3893 }) = get_inner_matrix_of_struct_array_member(module, base, func_ctx, true)
3894 .or_else(|| {
3895 get_inner_matrix_of_global_uniform(module, base, func_ctx, true)
3896 })
3897 {
3898 write!(
3899 self.out,
3900 "__get_col_of_mat{}x2_f{}(",
3901 columns as u8,
3902 width * 8
3903 )?;
3904 self.write_expr(module, base, func_ctx)?;
3905 write!(self.out, ", ")?;
3906 self.write_expr(module, index, func_ctx)?;
3907 write!(self.out, ")")?;
3908 return Ok(());
3909 }
3910
3911 let resolved = func_ctx.resolve_type(base, &module.types);
3912
3913 let (indexing_binding_array, non_uniform_qualifier) = match *resolved {
3914 TypeInner::BindingArray { .. } => {
3915 let uniformity = &func_ctx.info[index].uniformity;
3916
3917 (true, uniformity.non_uniform_result.is_some())
3918 }
3919 _ => (false, false),
3920 };
3921
3922 self.write_expr(module, base, func_ctx)?;
3923
3924 let array_sampler_info = self.sampler_binding_array_info_from_expression(
3925 module, func_ctx, base, resolved,
3926 );
3927
3928 if let Some(ref info) = array_sampler_info {
3929 write!(self.out, "{}[", info.sampler_heap_name)?;
3930 } else {
3931 write!(self.out, "[")?;
3932 }
3933
3934 let needs_bound_check = self.options.restrict_indexing
3935 && !indexing_binding_array
3936 && match resolved.pointer_space() {
3937 Some(
3938 crate::AddressSpace::Function
3939 | crate::AddressSpace::Private
3940 | crate::AddressSpace::WorkGroup
3941 | crate::AddressSpace::Immediate
3942 | crate::AddressSpace::TaskPayload
3943 | crate::AddressSpace::RayPayload
3944 | crate::AddressSpace::IncomingRayPayload,
3945 )
3946 | None => true,
3947 Some(crate::AddressSpace::Uniform) => {
3948 let var_handle = self.fill_access_chain(module, base, func_ctx)?;
3950 let bind_target = self
3951 .options
3952 .resolve_resource_binding(
3953 module.global_variables[var_handle]
3954 .binding
3955 .as_ref()
3956 .unwrap(),
3957 )
3958 .unwrap();
3959 bind_target.restrict_indexing
3960 }
3961 Some(
3962 crate::AddressSpace::Handle | crate::AddressSpace::Storage { .. },
3963 ) => unreachable!(),
3964 };
3965 let restriction_needed = if needs_bound_check {
3967 index::access_needs_check(
3968 base,
3969 index::GuardedIndex::Expression(index),
3970 module,
3971 func_ctx.expressions,
3972 func_ctx.info,
3973 )
3974 } else {
3975 None
3976 };
3977 if let Some(limit) = restriction_needed {
3978 write!(self.out, "min(uint(")?;
3979 self.write_expr(module, index, func_ctx)?;
3980 write!(self.out, "), ")?;
3981 match limit {
3982 index::IndexableLength::Known(limit) => {
3983 write!(self.out, "{}u", limit - 1)?;
3984 }
3985 index::IndexableLength::Dynamic => unreachable!(),
3986 }
3987 write!(self.out, ")")?;
3988 } else {
3989 if non_uniform_qualifier {
3990 write!(self.out, "NonUniformResourceIndex(")?;
3991 }
3992 if let Some(ref info) = array_sampler_info {
3993 write!(
3994 self.out,
3995 "{}[{} + ",
3996 info.sampler_index_buffer_name, info.binding_array_base_index_name,
3997 )?;
3998 }
3999 self.write_expr(module, index, func_ctx)?;
4000 if array_sampler_info.is_some() {
4001 write!(self.out, "]")?;
4002 }
4003 if non_uniform_qualifier {
4004 write!(self.out, ")")?;
4005 }
4006 }
4007
4008 write!(self.out, "]")?;
4009 }
4010 }
4011 Expression::AccessIndex { base, index } => {
4012 if let Some(crate::AddressSpace::Storage { .. }) =
4013 func_ctx.resolve_type(expr, &module.types).pointer_space()
4014 {
4015 } else {
4017 if let Some(MatrixType {
4021 rows: crate::VectorSize::Bi,
4022 ..
4023 }) = get_inner_matrix_of_struct_array_member(module, base, func_ctx, true)
4024 .or_else(|| {
4025 get_inner_matrix_of_global_uniform(module, base, func_ctx, true)
4026 })
4027 {
4028 self.write_expr(module, base, func_ctx)?;
4029 write!(self.out, "._{index}")?;
4030 return Ok(());
4031 }
4032
4033 let base_ty_res = &func_ctx.info[base].ty;
4034 let mut resolved = base_ty_res.inner_with(&module.types);
4035 let base_ty_handle = match *resolved {
4036 TypeInner::Pointer { base, .. } => {
4037 resolved = &module.types[base].inner;
4038 Some(base)
4039 }
4040 _ => base_ty_res.handle(),
4041 };
4042
4043 if let TypeInner::Struct { ref members, .. } = *resolved {
4049 let member = &members[index as usize];
4050
4051 match module.types[member.ty].inner {
4052 TypeInner::Matrix {
4053 rows: crate::VectorSize::Bi,
4054 ..
4055 } if member.binding.is_none() => {
4056 let ty = base_ty_handle.unwrap();
4057 self.write_wrapped_struct_matrix_get_function_name(
4058 WrappedStructMatrixAccess { ty, index },
4059 )?;
4060 write!(self.out, "(")?;
4061 self.write_expr(module, base, func_ctx)?;
4062 write!(self.out, ")")?;
4063 return Ok(());
4064 }
4065 _ => {}
4066 }
4067 }
4068
4069 let array_sampler_info = self.sampler_binding_array_info_from_expression(
4070 module, func_ctx, base, resolved,
4071 );
4072
4073 if let Some(ref info) = array_sampler_info {
4074 write!(
4075 self.out,
4076 "{}[{}",
4077 info.sampler_heap_name, info.sampler_index_buffer_name
4078 )?;
4079 }
4080
4081 self.write_expr(module, base, func_ctx)?;
4082
4083 match *resolved {
4084 TypeInner::Vector { .. } | TypeInner::ValuePointer { .. } => {
4090 write!(self.out, ".{}", back::COMPONENTS[index as usize])?
4092 }
4093 TypeInner::Matrix { .. }
4094 | TypeInner::Array { .. }
4095 | TypeInner::BindingArray { .. } => {
4096 if let Some(ref info) = array_sampler_info {
4097 write!(
4098 self.out,
4099 "[{} + {index}]",
4100 info.binding_array_base_index_name
4101 )?;
4102 } else {
4103 write!(self.out, "[{index}]")?;
4104 }
4105 }
4106 TypeInner::Struct { .. } => {
4107 let ty = base_ty_handle.unwrap();
4110
4111 write!(
4112 self.out,
4113 ".{}",
4114 &self.names[&NameKey::StructMember(ty, index)]
4115 )?
4116 }
4117 ref other => return Err(Error::Custom(format!("Cannot index {other:?}"))),
4118 }
4119
4120 if array_sampler_info.is_some() {
4121 write!(self.out, "]")?;
4122 }
4123 }
4124 }
4125 Expression::FunctionArgument(pos) => {
4126 let ty = func_ctx.resolve_type(expr, &module.types);
4127
4128 if let TypeInner::Image {
4134 class: crate::ImageClass::External,
4135 ..
4136 } = *ty
4137 {
4138 let plane_names = [0, 1, 2].map(|i| {
4139 &self.names[&func_ctx
4140 .external_texture_argument_key(pos, ExternalTextureNameKey::Plane(i))]
4141 });
4142 let params_name = &self.names[&func_ctx
4143 .external_texture_argument_key(pos, ExternalTextureNameKey::Params)];
4144 write!(
4145 self.out,
4146 "{}, {}, {}, {}",
4147 plane_names[0], plane_names[1], plane_names[2], params_name
4148 )?;
4149 } else {
4150 let key = func_ctx.argument_key(pos);
4151 let name = &self.names[&key];
4152 write!(self.out, "{name}")?;
4153 }
4154 }
4155 Expression::ImageSample {
4156 coordinate,
4157 image,
4158 sampler,
4159 clamp_to_edge: true,
4160 gather: None,
4161 array_index: None,
4162 offset: None,
4163 level: crate::SampleLevel::Zero,
4164 depth_ref: None,
4165 } => {
4166 write!(self.out, "{IMAGE_SAMPLE_BASE_CLAMP_TO_EDGE_FUNCTION}(")?;
4167 self.write_expr(module, image, func_ctx)?;
4168 write!(self.out, ", ")?;
4169 self.write_expr(module, sampler, func_ctx)?;
4170 write!(self.out, ", ")?;
4171 self.write_expr(module, coordinate, func_ctx)?;
4172 write!(self.out, ")")?;
4173 }
4174 Expression::ImageSample {
4175 image,
4176 sampler,
4177 gather,
4178 coordinate,
4179 array_index,
4180 offset,
4181 level,
4182 depth_ref,
4183 clamp_to_edge,
4184 } => {
4185 if clamp_to_edge {
4186 return Err(Error::Custom(
4187 "ImageSample::clamp_to_edge should have been validated out".to_string(),
4188 ));
4189 }
4190
4191 use crate::SampleLevel as Sl;
4192 const COMPONENTS: [&str; 4] = ["", "Green", "Blue", "Alpha"];
4193
4194 let (base_str, component_str) = match gather {
4195 Some(component) => ("Gather", COMPONENTS[component as usize]),
4196 None => ("Sample", ""),
4197 };
4198 let cmp_str = match depth_ref {
4199 Some(_) => "Cmp",
4200 None => "",
4201 };
4202 let level_str = match level {
4203 Sl::Zero if gather.is_none() => "LevelZero",
4204 Sl::Auto | Sl::Zero => "",
4205 Sl::Exact(_) => "Level",
4206 Sl::Bias(_) => "Bias",
4207 Sl::Gradient { .. } => "Grad",
4208 };
4209
4210 self.write_expr(module, image, func_ctx)?;
4211 write!(self.out, ".{base_str}{cmp_str}{component_str}{level_str}(")?;
4212 self.write_expr(module, sampler, func_ctx)?;
4213 write!(self.out, ", ")?;
4214 self.write_texture_coordinates(
4215 "float",
4216 coordinate,
4217 array_index,
4218 None,
4219 module,
4220 func_ctx,
4221 )?;
4222
4223 if let Some(depth_ref) = depth_ref {
4224 write!(self.out, ", ")?;
4225 self.write_expr(module, depth_ref, func_ctx)?;
4226 }
4227
4228 match level {
4229 Sl::Auto | Sl::Zero => {}
4230 Sl::Exact(expr) => {
4231 write!(self.out, ", ")?;
4232 self.write_expr(module, expr, func_ctx)?;
4233 }
4234 Sl::Bias(expr) => {
4235 write!(self.out, ", ")?;
4236 self.write_expr(module, expr, func_ctx)?;
4237 }
4238 Sl::Gradient { x, y } => {
4239 write!(self.out, ", ")?;
4240 self.write_expr(module, x, func_ctx)?;
4241 write!(self.out, ", ")?;
4242 self.write_expr(module, y, func_ctx)?;
4243 }
4244 }
4245
4246 if let Some(offset) = offset {
4247 write!(self.out, ", ")?;
4248 let (size, scalar) = func_ctx
4250 .resolve_type(offset, &module.types)
4251 .vector_size_and_scalar()
4252 .unwrap();
4253 assert_eq!(scalar.kind, ScalarKind::Sint);
4254 write!(self.out, "{}", scalar.to_hlsl_str()?)?;
4255 if let Some(size) = size {
4256 write!(self.out, "{}", common::vector_size_str(size))?;
4257 }
4258 write!(self.out, "(")?;
4259 self.write_const_expression(module, offset, func_ctx.expressions)?;
4260 write!(self.out, ")")?;
4261 }
4262
4263 write!(self.out, ")")?;
4264 }
4265 Expression::ImageQuery { image, query } => {
4266 if let TypeInner::Image {
4268 dim,
4269 arrayed,
4270 class,
4271 } = *func_ctx.resolve_type(image, &module.types)
4272 {
4273 let wrapped_image_query = WrappedImageQuery {
4274 dim,
4275 arrayed,
4276 class,
4277 query: query.into(),
4278 };
4279
4280 self.write_wrapped_image_query_function_name(wrapped_image_query)?;
4281 write!(self.out, "(")?;
4282 self.write_expr(module, image, func_ctx)?;
4284 if let crate::ImageQuery::Size { level: Some(level) } = query {
4285 write!(self.out, ", ")?;
4286 self.write_expr(module, level, func_ctx)?;
4287 }
4288 write!(self.out, ")")?;
4289 }
4290 }
4291 Expression::ImageLoad {
4292 image,
4293 coordinate,
4294 array_index,
4295 sample,
4296 level,
4297 } => self.write_image_load(
4298 &module,
4299 expr,
4300 func_ctx,
4301 image,
4302 coordinate,
4303 array_index,
4304 sample,
4305 level,
4306 )?,
4307 Expression::GlobalVariable(handle) => {
4308 let global_variable = &module.global_variables[handle];
4309 let ty = &module.types[global_variable.ty].inner;
4310
4311 let is_binding_array_of_samplers = match *ty {
4316 TypeInner::BindingArray { base, .. } => {
4317 let base_ty = &module.types[base].inner;
4318 matches!(*base_ty, TypeInner::Sampler { .. })
4319 }
4320 _ => false,
4321 };
4322
4323 let is_storage_space =
4324 matches!(global_variable.space, crate::AddressSpace::Storage { .. });
4325
4326 if let TypeInner::Image {
4334 class: crate::ImageClass::External,
4335 ..
4336 } = *ty
4337 {
4338 let plane_names = [0, 1, 2].map(|i| {
4339 &self.names[&NameKey::ExternalTextureGlobalVariable(
4340 handle,
4341 ExternalTextureNameKey::Plane(i),
4342 )]
4343 });
4344 let params_name = &self.names[&NameKey::ExternalTextureGlobalVariable(
4345 handle,
4346 ExternalTextureNameKey::Params,
4347 )];
4348 write!(
4349 self.out,
4350 "{}, {}, {}, {}",
4351 plane_names[0], plane_names[1], plane_names[2], params_name
4352 )?;
4353 } else if !is_binding_array_of_samplers && !is_storage_space {
4354 let name = &self.names[&NameKey::GlobalVariable(handle)];
4355 write!(self.out, "{name}")?;
4356 }
4357 }
4358 Expression::LocalVariable(handle) => {
4359 write!(self.out, "{}", self.names[&func_ctx.name_key(handle)])?
4360 }
4361 Expression::Load { pointer } => {
4362 match func_ctx
4363 .resolve_type(pointer, &module.types)
4364 .pointer_space()
4365 {
4366 Some(crate::AddressSpace::Storage { .. }) => {
4367 let var_handle = self.fill_access_chain(module, pointer, func_ctx)?;
4368 let result_ty = func_ctx.info[expr].ty.clone();
4369 self.write_storage_load(module, var_handle, result_ty, func_ctx)?;
4370 }
4371 _ => {
4372 let mut close_paren = false;
4373
4374 if let Some(MatrixType {
4379 rows: crate::VectorSize::Bi,
4380 ..
4381 }) = get_inner_matrix_of_struct_array_member(
4382 module, pointer, func_ctx, false,
4383 )
4384 .or_else(|| {
4385 get_inner_matrix_of_global_uniform(module, pointer, func_ctx, false)
4386 }) {
4387 let mut resolved = func_ctx.resolve_type(pointer, &module.types);
4388 let ptr_tr = resolved.pointer_base_type();
4389 if let Some(ptr_ty) =
4390 ptr_tr.as_ref().map(|tr| tr.inner_with(&module.types))
4391 {
4392 resolved = ptr_ty;
4393 }
4394
4395 write!(self.out, "((")?;
4396 if let TypeInner::Array { base, size, .. } = *resolved {
4397 self.write_type(module, base)?;
4398 self.write_array_size(module, base, size)?;
4399 } else {
4400 self.write_value_type(module, resolved)?;
4401 }
4402 write!(self.out, ")")?;
4403 close_paren = true;
4404 }
4405
4406 self.write_expr(module, pointer, func_ctx)?;
4407
4408 if close_paren {
4409 write!(self.out, ")")?;
4410 }
4411 }
4412 }
4413 }
4414 Expression::Unary { op, expr } => {
4415 let op_str = match op {
4417 crate::UnaryOperator::Negate => {
4418 match func_ctx.resolve_type(expr, &module.types).scalar() {
4419 Some(Scalar::I32) => NEG_FUNCTION,
4420 _ => "-",
4421 }
4422 }
4423 crate::UnaryOperator::LogicalNot => "!",
4424 crate::UnaryOperator::BitwiseNot => "~",
4425 };
4426 write!(self.out, "{op_str}(")?;
4427 self.write_expr(module, expr, func_ctx)?;
4428 write!(self.out, ")")?;
4429 }
4430 Expression::As {
4431 expr,
4432 kind,
4433 convert,
4434 } => {
4435 let inner = func_ctx.resolve_type(expr, &module.types);
4436 if inner.scalar_kind() == Some(ScalarKind::Float)
4437 && (kind == ScalarKind::Sint || kind == ScalarKind::Uint)
4438 && convert.is_some()
4439 && matches!(convert, Some(4) | Some(8))
4440 {
4441 let fun_name = match (kind, convert) {
4445 (ScalarKind::Sint, Some(4)) => F2I32_FUNCTION,
4446 (ScalarKind::Uint, Some(4)) => F2U32_FUNCTION,
4447 (ScalarKind::Sint, Some(8)) => F2I64_FUNCTION,
4448 (ScalarKind::Uint, Some(8)) => F2U64_FUNCTION,
4449 _ => unreachable!(),
4450 };
4451 write!(self.out, "{fun_name}(")?;
4452 self.write_expr(module, expr, func_ctx)?;
4453 write!(self.out, ")")?;
4454 } else {
4455 let close_paren = match convert {
4456 Some(dst_width) => {
4457 let scalar = Scalar {
4458 kind,
4459 width: dst_width,
4460 };
4461 match *inner {
4462 TypeInner::Vector { size, .. } => {
4463 write!(
4464 self.out,
4465 "{}{}(",
4466 scalar.to_hlsl_str()?,
4467 common::vector_size_str(size)
4468 )?;
4469 }
4470 TypeInner::Scalar(_) => {
4471 write!(self.out, "{}(", scalar.to_hlsl_str()?,)?;
4472 }
4473 TypeInner::Matrix { columns, rows, .. } => {
4474 write!(
4475 self.out,
4476 "{}{}x{}(",
4477 scalar.to_hlsl_str()?,
4478 common::vector_size_str(columns),
4479 common::vector_size_str(rows)
4480 )?;
4481 }
4482 _ => {
4483 return Err(Error::Unimplemented(format!(
4484 "write_expr expression::as {inner:?}"
4485 )));
4486 }
4487 };
4488 true
4489 }
4490 None => {
4491 if inner.scalar_width() == Some(8) {
4492 false
4493 } else if inner.scalar_width() == Some(2) {
4494 let dst_scalar = Scalar { kind, width: 2 };
4497 match *inner {
4498 TypeInner::Vector { size, .. } => {
4499 write!(
4500 self.out,
4501 "{}{}(",
4502 dst_scalar.to_hlsl_str()?,
4503 common::vector_size_str(size)
4504 )?;
4505 }
4506 _ => {
4507 write!(self.out, "{}(", dst_scalar.to_hlsl_str()?)?;
4508 }
4509 };
4510 true
4511 } else {
4512 write!(self.out, "{}(", kind.to_hlsl_cast(),)?;
4513 true
4514 }
4515 }
4516 };
4517 self.write_expr(module, expr, func_ctx)?;
4518 if close_paren {
4519 write!(self.out, ")")?;
4520 }
4521 }
4522 }
4523 Expression::Math {
4524 fun,
4525 arg,
4526 arg1,
4527 arg2,
4528 arg3,
4529 } => {
4530 return self.write_math_expression(module, fun, arg, arg1, arg2, arg3, func_ctx);
4531 }
4532 Expression::Swizzle {
4533 size,
4534 vector,
4535 pattern,
4536 } => {
4537 self.write_expr(module, vector, func_ctx)?;
4538 write!(self.out, ".")?;
4539 for &sc in pattern[..size as usize].iter() {
4540 self.out.write_char(back::COMPONENTS[sc as usize])?;
4541 }
4542 }
4543 Expression::ArrayLength(expr) => {
4544 let var_handle = match func_ctx.expressions[expr] {
4545 Expression::AccessIndex { base, index: _ } => {
4546 match func_ctx.expressions[base] {
4547 Expression::GlobalVariable(handle) => handle,
4548 _ => unreachable!(),
4549 }
4550 }
4551 Expression::GlobalVariable(handle) => handle,
4552 _ => unreachable!(),
4553 };
4554
4555 let var = &module.global_variables[var_handle];
4556 let (offset, stride) = match module.types[var.ty].inner {
4557 TypeInner::Array { stride, .. } => (0, stride),
4558 TypeInner::Struct { ref members, .. } => {
4559 let last = members.last().unwrap();
4560 let stride = match module.types[last.ty].inner {
4561 TypeInner::Array { stride, .. } => stride,
4562 _ => unreachable!(),
4563 };
4564 (last.offset, stride)
4565 }
4566 _ => unreachable!(),
4567 };
4568
4569 let storage_access = match var.space {
4570 crate::AddressSpace::Storage { access } => access,
4571 _ => crate::StorageAccess::default(),
4572 };
4573 let wrapped_array_length = WrappedArrayLength {
4574 writable: storage_access.contains(crate::StorageAccess::STORE),
4575 };
4576
4577 write!(self.out, "((")?;
4578 self.write_wrapped_array_length_function_name(wrapped_array_length)?;
4579 let var_name = &self.names[&NameKey::GlobalVariable(var_handle)];
4580 write!(self.out, "({var_name}) - {offset}) / {stride})")?
4581 }
4582 Expression::Derivative { axis, ctrl, expr } => {
4583 use crate::{DerivativeAxis as Axis, DerivativeControl as Ctrl};
4584 if axis == Axis::Width && (ctrl == Ctrl::Coarse || ctrl == Ctrl::Fine) {
4585 let tail = match ctrl {
4586 Ctrl::Coarse => "coarse",
4587 Ctrl::Fine => "fine",
4588 Ctrl::None => unreachable!(),
4589 };
4590 write!(self.out, "abs(ddx_{tail}(")?;
4591 self.write_expr(module, expr, func_ctx)?;
4592 write!(self.out, ")) + abs(ddy_{tail}(")?;
4593 self.write_expr(module, expr, func_ctx)?;
4594 write!(self.out, "))")?
4595 } else {
4596 let fun_str = match (axis, ctrl) {
4597 (Axis::X, Ctrl::Coarse) => "ddx_coarse",
4598 (Axis::X, Ctrl::Fine) => "ddx_fine",
4599 (Axis::X, Ctrl::None) => "ddx",
4600 (Axis::Y, Ctrl::Coarse) => "ddy_coarse",
4601 (Axis::Y, Ctrl::Fine) => "ddy_fine",
4602 (Axis::Y, Ctrl::None) => "ddy",
4603 (Axis::Width, Ctrl::Coarse | Ctrl::Fine) => unreachable!(),
4604 (Axis::Width, Ctrl::None) => "fwidth",
4605 };
4606 write!(self.out, "{fun_str}(")?;
4607 self.write_expr(module, expr, func_ctx)?;
4608 write!(self.out, ")")?
4609 }
4610 }
4611 Expression::Relational { fun, argument } => {
4612 use crate::RelationalFunction as Rf;
4613
4614 let fun_str = match fun {
4615 Rf::All => "all",
4616 Rf::Any => "any",
4617 Rf::IsNan => "isnan",
4618 Rf::IsInf => "isinf",
4619 };
4620 write!(self.out, "{fun_str}(")?;
4621 self.write_expr(module, argument, func_ctx)?;
4622 write!(self.out, ")")?
4623 }
4624 Expression::Select {
4625 condition,
4626 accept,
4627 reject,
4628 } => {
4629 write!(self.out, "(")?;
4630 self.write_expr(module, condition, func_ctx)?;
4631 write!(self.out, " ? ")?;
4632 self.write_expr(module, accept, func_ctx)?;
4633 write!(self.out, " : ")?;
4634 self.write_expr(module, reject, func_ctx)?;
4635 write!(self.out, ")")?
4636 }
4637 Expression::RayQueryGetIntersection { query, committed } => {
4638 let Expression::LocalVariable(query_var) = func_ctx.expressions[query] else {
4640 unreachable!()
4641 };
4642
4643 let tracker_expr_name = format!(
4644 "{RAY_QUERY_TRACKER_VARIABLE_PREFIX}{}",
4645 self.names[&func_ctx.name_key(query_var)]
4646 );
4647
4648 if committed {
4649 write!(self.out, "GetCommittedIntersection(")?;
4650 self.write_expr(module, query, func_ctx)?;
4651 write!(self.out, ", {tracker_expr_name})")?;
4652 } else {
4653 write!(self.out, "GetCandidateIntersection(")?;
4654 self.write_expr(module, query, func_ctx)?;
4655 write!(self.out, ", {tracker_expr_name})")?;
4656 }
4657 }
4658 Expression::RayQueryVertexPositions { .. }
4660 | Expression::CooperativeLoad { .. }
4661 | Expression::CooperativeMultiplyAdd { .. } => {
4662 unreachable!()
4663 }
4664 Expression::CallResult(_)
4666 | Expression::AtomicResult { .. }
4667 | Expression::WorkGroupUniformLoadResult { .. }
4668 | Expression::RayQueryProceedResult
4669 | Expression::SubgroupBallotResult
4670 | Expression::SubgroupOperationResult { .. } => {}
4671 }
4672
4673 if !closing_bracket.is_empty() {
4674 write!(self.out, "{closing_bracket}")?;
4675 }
4676 Ok(())
4677 }
4678
4679 #[allow(clippy::too_many_arguments)]
4680 fn write_image_load(
4681 &mut self,
4682 module: &&Module,
4683 expr: Handle<crate::Expression>,
4684 func_ctx: &back::FunctionCtx,
4685 image: Handle<crate::Expression>,
4686 coordinate: Handle<crate::Expression>,
4687 array_index: Option<Handle<crate::Expression>>,
4688 sample: Option<Handle<crate::Expression>>,
4689 level: Option<Handle<crate::Expression>>,
4690 ) -> Result<(), Error> {
4691 let mut wrapping_type = None;
4692 match *func_ctx.resolve_type(image, &module.types) {
4693 TypeInner::Image {
4694 class: crate::ImageClass::External,
4695 ..
4696 } => {
4697 write!(self.out, "{IMAGE_LOAD_EXTERNAL_FUNCTION}(")?;
4698 self.write_expr(module, image, func_ctx)?;
4699 write!(self.out, ", ")?;
4700 self.write_expr(module, coordinate, func_ctx)?;
4701 write!(self.out, ")")?;
4702 return Ok(());
4703 }
4704 TypeInner::Image {
4705 class: crate::ImageClass::Storage { format, .. },
4706 ..
4707 } if format.single_component() => {
4708 wrapping_type = Some(Scalar::from(format));
4709 }
4710 _ => {}
4711 }
4712 if let Some(scalar) = wrapping_type {
4713 write!(
4714 self.out,
4715 "{}{}(",
4716 help::IMAGE_STORAGE_LOAD_SCALAR_WRAPPER,
4717 scalar.to_hlsl_str()?
4718 )?;
4719 }
4720 self.write_expr(module, image, func_ctx)?;
4722 write!(self.out, ".Load(")?;
4723
4724 self.write_texture_coordinates("int", coordinate, array_index, level, module, func_ctx)?;
4725
4726 if let Some(sample) = sample {
4727 write!(self.out, ", ")?;
4728 self.write_expr(module, sample, func_ctx)?;
4729 }
4730
4731 write!(self.out, ")")?;
4733
4734 if wrapping_type.is_some() {
4735 write!(self.out, ")")?;
4736 }
4737
4738 if let TypeInner::Scalar(_) = *func_ctx.resolve_type(expr, &module.types) {
4740 write!(self.out, ".x")?;
4741 }
4742 Ok(())
4743 }
4744
4745 fn sampler_binding_array_info_from_expression(
4748 &mut self,
4749 module: &Module,
4750 func_ctx: &back::FunctionCtx<'_>,
4751 base: Handle<crate::Expression>,
4752 resolved: &TypeInner,
4753 ) -> Option<BindingArraySamplerInfo> {
4754 if let TypeInner::BindingArray {
4755 base: base_ty_handle,
4756 ..
4757 } = *resolved
4758 {
4759 let base_ty = &module.types[base_ty_handle].inner;
4760 if let TypeInner::Sampler { comparison, .. } = *base_ty {
4761 let base = &func_ctx.expressions[base];
4762
4763 if let crate::Expression::GlobalVariable(handle) = *base {
4764 let variable = &module.global_variables[handle];
4765
4766 let sampler_heap_name = match comparison {
4767 true => COMPARISON_SAMPLER_HEAP_VAR,
4768 false => SAMPLER_HEAP_VAR,
4769 };
4770
4771 return Some(BindingArraySamplerInfo {
4772 sampler_heap_name,
4773 sampler_index_buffer_name: self
4774 .wrapped
4775 .sampler_index_buffers
4776 .get(&super::SamplerIndexBufferKey {
4777 group: variable.binding.unwrap().group,
4778 })
4779 .unwrap()
4780 .clone(),
4781 binding_array_base_index_name: self.names[&NameKey::GlobalVariable(handle)]
4782 .clone(),
4783 });
4784 }
4785 }
4786 }
4787
4788 None
4789 }
4790
4791 fn write_named_expr(
4792 &mut self,
4793 module: &Module,
4794 handle: Handle<crate::Expression>,
4795 name: String,
4796 expr: Handle<crate::Expression>,
4799 func_ctx: &back::FunctionCtx,
4800 ) -> BackendResult {
4801 if let crate::Expression::Load { pointer } = func_ctx.expressions[expr] {
4802 let ty_inner = func_ctx.resolve_type(pointer, &module.types);
4803 if ty_inner.is_atomic_pointer(&module.types) {
4804 let pointer_space = ty_inner.pointer_space().unwrap();
4805 self.write_value_type(module, func_ctx.info[handle].ty.inner_with(&module.types))?;
4806 write!(self.out, " {name}; ")?;
4807 match pointer_space {
4808 crate::AddressSpace::WorkGroup => {
4809 write!(self.out, "InterlockedOr(")?;
4810 self.write_expr(module, pointer, func_ctx)?;
4811 }
4812 crate::AddressSpace::Storage { .. } => {
4813 let var_handle = self.fill_access_chain(module, pointer, func_ctx)?;
4814 let var_name = &self.names[&NameKey::GlobalVariable(var_handle)];
4815 write!(self.out, "{var_name}.InterlockedOr(")?;
4816 let chain = mem::take(&mut self.temp_access_chain);
4817 self.write_storage_address(module, &chain, func_ctx)?;
4818 self.temp_access_chain = chain;
4819 }
4820 _ => unreachable!(),
4821 }
4822 writeln!(self.out, ", 0, {name});")?;
4823 self.named_expressions.insert(expr, name);
4824 return Ok(());
4825 }
4826 }
4827 match func_ctx.info[expr].ty {
4828 proc::TypeResolution::Handle(ty_handle) => match module.types[ty_handle].inner {
4829 TypeInner::Struct { .. } => {
4830 let ty_name = &self.names[&NameKey::Type(ty_handle)];
4831 write!(self.out, "{ty_name}")?;
4832 }
4833 _ => {
4834 self.write_type(module, ty_handle)?;
4835 }
4836 },
4837 proc::TypeResolution::Value(ref inner) => {
4838 self.write_value_type(module, inner)?;
4839 }
4840 }
4841
4842 let resolved = func_ctx.resolve_type(expr, &module.types);
4843
4844 write!(self.out, " {name}")?;
4845 if let TypeInner::Array { base, size, .. } = *resolved {
4847 self.write_array_size(module, base, size)?;
4848 }
4849 write!(self.out, " = ")?;
4850 self.write_expr(module, handle, func_ctx)?;
4851 writeln!(self.out, ";")?;
4852 self.named_expressions.insert(expr, name);
4853
4854 Ok(())
4855 }
4856
4857 pub(super) fn write_default_init(
4859 &mut self,
4860 module: &Module,
4861 ty: Handle<crate::Type>,
4862 ) -> BackendResult {
4863 write!(self.out, "(")?;
4864 self.write_type(module, ty)?;
4865 if let TypeInner::Array { base, size, .. } = module.types[ty].inner {
4866 self.write_array_size(module, base, size)?;
4867 }
4868 write!(self.out, ")0")?;
4869 Ok(())
4870 }
4871
4872 pub(super) fn write_control_barrier(
4873 &mut self,
4874 barrier: crate::Barrier,
4875 level: back::Level,
4876 ) -> BackendResult {
4877 if barrier.contains(crate::Barrier::STORAGE) {
4878 writeln!(self.out, "{level}DeviceMemoryBarrierWithGroupSync();")?;
4879 }
4880 if barrier.contains(crate::Barrier::WORK_GROUP) {
4881 writeln!(self.out, "{level}GroupMemoryBarrierWithGroupSync();")?;
4882 }
4883 if barrier.contains(crate::Barrier::SUB_GROUP) {
4884 }
4886 if barrier.contains(crate::Barrier::TEXTURE) {
4887 writeln!(self.out, "{level}DeviceMemoryBarrierWithGroupSync();")?;
4888 }
4889 Ok(())
4890 }
4891
4892 fn write_memory_barrier(
4893 &mut self,
4894 barrier: crate::Barrier,
4895 level: back::Level,
4896 ) -> BackendResult {
4897 if barrier.contains(crate::Barrier::STORAGE) {
4898 writeln!(self.out, "{level}DeviceMemoryBarrier();")?;
4899 }
4900 if barrier.contains(crate::Barrier::WORK_GROUP) {
4901 writeln!(self.out, "{level}GroupMemoryBarrier();")?;
4902 }
4903 if barrier.contains(crate::Barrier::SUB_GROUP) {
4904 }
4906 if barrier.contains(crate::Barrier::TEXTURE) {
4907 writeln!(self.out, "{level}DeviceMemoryBarrier();")?;
4908 }
4909 Ok(())
4910 }
4911
4912 fn emit_hlsl_atomic_tail(
4914 &mut self,
4915 module: &Module,
4916 func_ctx: &back::FunctionCtx<'_>,
4917 fun: &crate::AtomicFunction,
4918 compare_expr: Option<Handle<crate::Expression>>,
4919 value: Handle<crate::Expression>,
4920 res_var_info: &Option<(Handle<crate::Expression>, String)>,
4921 ) -> BackendResult {
4922 if let Some(cmp) = compare_expr {
4923 write!(self.out, ", ")?;
4924 self.write_expr(module, cmp, func_ctx)?;
4925 }
4926 write!(self.out, ", ")?;
4927 if let crate::AtomicFunction::Subtract = *fun {
4928 write!(self.out, "-")?;
4930 }
4931 self.write_expr(module, value, func_ctx)?;
4932 if let Some(&(_res_handle, ref res_name)) = res_var_info.as_ref() {
4933 write!(self.out, ", ")?;
4934 if compare_expr.is_some() {
4935 write!(self.out, "{res_name}.old_value")?;
4936 } else {
4937 write!(self.out, "{res_name}")?;
4938 }
4939 }
4940 writeln!(self.out, ");")?;
4941 Ok(())
4942 }
4943}
4944
4945pub(super) struct MatrixType {
4946 pub(super) columns: crate::VectorSize,
4947 pub(super) rows: crate::VectorSize,
4948 pub(super) width: crate::Bytes,
4949}
4950
4951pub(super) fn get_inner_matrix_data(
4952 module: &Module,
4953 handle: Handle<crate::Type>,
4954) -> Option<MatrixType> {
4955 match module.types[handle].inner {
4956 TypeInner::Matrix {
4957 columns,
4958 rows,
4959 scalar,
4960 } => Some(MatrixType {
4961 columns,
4962 rows,
4963 width: scalar.width,
4964 }),
4965 TypeInner::Array { base, .. } => get_inner_matrix_data(module, base),
4966 _ => None,
4967 }
4968}
4969
4970fn find_matrix_in_access_chain(
4974 module: &Module,
4975 base: Handle<crate::Expression>,
4976 func_ctx: &back::FunctionCtx<'_>,
4977) -> Option<(Handle<crate::Expression>, Option<Index>, Option<Index>)> {
4978 let mut current_base = base;
4979 let mut vector = None;
4980 let mut scalar = None;
4981 loop {
4982 let resolved_tr = func_ctx
4983 .resolve_type(current_base, &module.types)
4984 .pointer_base_type();
4985 let resolved = resolved_tr.as_ref()?.inner_with(&module.types);
4986
4987 match *resolved {
4988 TypeInner::Matrix { .. } => return Some((current_base, vector, scalar)),
4989 TypeInner::Scalar(_) | TypeInner::Vector { .. } => {}
4990 _ => return None,
4991 }
4992
4993 let index;
4994 (current_base, index) = match func_ctx.expressions[current_base] {
4995 crate::Expression::Access { base, index } => (base, Index::Expression(index)),
4996 crate::Expression::AccessIndex { base, index } => (base, Index::Static(index)),
4997 _ => return None,
4998 };
4999
5000 match *resolved {
5001 TypeInner::Scalar(_) => scalar = Some(index),
5002 TypeInner::Vector { .. } => vector = Some(index),
5003 _ => unreachable!(),
5004 }
5005 }
5006}
5007
5008pub(super) fn get_inner_matrix_of_struct_array_member(
5013 module: &Module,
5014 base: Handle<crate::Expression>,
5015 func_ctx: &back::FunctionCtx<'_>,
5016 direct: bool,
5017) -> Option<MatrixType> {
5018 let mut mat_data = None;
5019 let mut array_base = None;
5020
5021 let mut current_base = base;
5022 loop {
5023 let mut resolved = func_ctx.resolve_type(current_base, &module.types);
5024 if let TypeInner::Pointer { base, .. } = *resolved {
5025 resolved = &module.types[base].inner;
5026 };
5027
5028 match *resolved {
5029 TypeInner::Matrix {
5030 columns,
5031 rows,
5032 scalar,
5033 } => {
5034 mat_data = Some(MatrixType {
5035 columns,
5036 rows,
5037 width: scalar.width,
5038 })
5039 }
5040 TypeInner::Array { base, .. } => {
5041 array_base = Some(base);
5042 }
5043 TypeInner::Struct { .. } => {
5044 if let Some(array_base) = array_base {
5045 if direct {
5046 return mat_data;
5047 } else {
5048 return get_inner_matrix_data(module, array_base);
5049 }
5050 }
5051
5052 break;
5053 }
5054 _ => break,
5055 }
5056
5057 current_base = match func_ctx.expressions[current_base] {
5058 crate::Expression::Access { base, .. } => base,
5059 crate::Expression::AccessIndex { base, .. } => base,
5060 _ => break,
5061 };
5062 }
5063 None
5064}
5065
5066fn get_inner_matrix_of_global_uniform(
5071 module: &Module,
5072 base: Handle<crate::Expression>,
5073 func_ctx: &back::FunctionCtx<'_>,
5074 direct: bool,
5075) -> Option<MatrixType> {
5076 let mut mat_data = None;
5077 let mut array_base = None;
5078
5079 let mut current_base = base;
5080 loop {
5081 let mut resolved = func_ctx.resolve_type(current_base, &module.types);
5082 if let TypeInner::Pointer { base, .. } = *resolved {
5083 resolved = &module.types[base].inner;
5084 };
5085
5086 match *resolved {
5087 TypeInner::Matrix {
5088 columns,
5089 rows,
5090 scalar,
5091 } => {
5092 mat_data = Some(MatrixType {
5093 columns,
5094 rows,
5095 width: scalar.width,
5096 })
5097 }
5098 TypeInner::Array { base, .. } => {
5099 if !direct {
5100 array_base = Some(base);
5101 }
5102 }
5103 _ => break,
5104 }
5105
5106 current_base = match func_ctx.expressions[current_base] {
5107 crate::Expression::Access { base, .. } => base,
5108 crate::Expression::AccessIndex { base, .. } => base,
5109 crate::Expression::GlobalVariable(handle)
5110 if module.global_variables[handle].space == crate::AddressSpace::Uniform =>
5111 {
5112 return mat_data.or_else(|| {
5113 array_base.and_then(|array_base| get_inner_matrix_data(module, array_base))
5114 })
5115 }
5116 _ => break,
5117 };
5118 }
5119 None
5120}