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