Skip to main content

naga/back/hlsl/
writer.rs

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