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