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