Skip to main content

naga/back/hlsl/
writer.rs

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