naga/back/hlsl/
help.rs

1/*!
2Helpers for the hlsl backend
3
4Important note about `Expression::ImageQuery`/`Expression::ArrayLength` and hlsl backend:
5
6Due to implementation of `GetDimensions` function in hlsl (<https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-to-getdimensions>)
7backend can't work with it as an expression.
8Instead, it generates a unique wrapped function per `Expression::ImageQuery`, based on texture info and query function.
9See `WrappedImageQuery` struct that represents a unique function and will be generated before writing all statements and expressions.
10This allowed to works with `Expression::ImageQuery` as expression and write wrapped function.
11
12For example:
13```wgsl
14let dim_1d = textureDimensions(image_1d);
15```
16
17```hlsl
18int NagaDimensions1D(Texture1D<float4>)
19{
20   uint4 ret;
21   image_1d.GetDimensions(ret.x);
22   return ret.x;
23}
24
25int dim_1d = NagaDimensions1D(image_1d);
26```
27*/
28
29use alloc::format;
30use core::fmt::Write;
31
32use super::{
33    super::FunctionCtx,
34    writer::{
35        ABS_FUNCTION, DIV_FUNCTION, EXTRACT_BITS_FUNCTION, F2I32_FUNCTION, F2I64_FUNCTION,
36        F2U32_FUNCTION, F2U64_FUNCTION, IMAGE_LOAD_EXTERNAL_FUNCTION,
37        IMAGE_SAMPLE_BASE_CLAMP_TO_EDGE_FUNCTION, INSERT_BITS_FUNCTION, MOD_FUNCTION, NEG_FUNCTION,
38    },
39    BackendResult, WrappedType,
40};
41use crate::{arena::Handle, proc::NameKey, ScalarKind};
42
43#[derive(Clone, Copy, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]
44pub(super) struct WrappedArrayLength {
45    pub(super) writable: bool,
46}
47
48#[derive(Clone, Copy, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]
49pub(super) struct WrappedImageLoad {
50    pub(super) class: crate::ImageClass,
51}
52
53#[derive(Clone, Copy, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]
54pub(super) struct WrappedImageSample {
55    pub(super) class: crate::ImageClass,
56    pub(super) clamp_to_edge: bool,
57}
58
59#[derive(Clone, Copy, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]
60pub(super) struct WrappedImageQuery {
61    pub(super) dim: crate::ImageDimension,
62    pub(super) arrayed: bool,
63    pub(super) class: crate::ImageClass,
64    pub(super) query: ImageQuery,
65}
66
67#[derive(Clone, Copy, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]
68pub(super) struct WrappedConstructor {
69    pub(super) ty: Handle<crate::Type>,
70}
71
72#[derive(Clone, Copy, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]
73pub(super) struct WrappedStructMatrixAccess {
74    pub(super) ty: Handle<crate::Type>,
75    pub(super) index: u32,
76}
77
78#[derive(Clone, Copy, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]
79pub(super) struct WrappedMatCx2 {
80    pub(super) columns: crate::VectorSize,
81    pub(super) width: u8,
82}
83
84#[derive(Clone, Copy, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]
85pub(super) struct WrappedMath {
86    pub(super) fun: crate::MathFunction,
87    pub(super) scalar: crate::Scalar,
88    pub(super) components: Option<u32>,
89}
90
91#[derive(Clone, Copy, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]
92pub(super) struct WrappedZeroValue {
93    pub(super) ty: Handle<crate::Type>,
94}
95
96#[derive(Clone, Copy, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]
97pub(super) struct WrappedUnaryOp {
98    pub(super) op: crate::UnaryOperator,
99    // This can only represent scalar or vector types. If we ever need to wrap
100    // unary ops with other types, we'll need a better representation.
101    pub(super) ty: (Option<crate::VectorSize>, crate::Scalar),
102}
103
104#[derive(Clone, Copy, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]
105pub(super) struct WrappedBinaryOp {
106    pub(super) op: crate::BinaryOperator,
107    // This can only represent scalar or vector types. If we ever need to wrap
108    // binary ops with other types, we'll need a better representation.
109    pub(super) left_ty: (Option<crate::VectorSize>, crate::Scalar),
110    pub(super) right_ty: (Option<crate::VectorSize>, crate::Scalar),
111}
112
113#[derive(Clone, Copy, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]
114pub(super) struct WrappedCast {
115    // This can only represent scalar or vector types. If we ever need to wrap
116    // casts with other types, we'll need a better representation.
117    pub(super) vector_size: Option<crate::VectorSize>,
118    pub(super) src_scalar: crate::Scalar,
119    pub(super) dst_scalar: crate::Scalar,
120}
121
122/// HLSL backend requires its own `ImageQuery` enum.
123///
124/// It is used inside `WrappedImageQuery` and should be unique per ImageQuery function.
125/// IR version can't be unique per function, because it's store mipmap level as an expression.
126///
127/// For example:
128/// ```wgsl
129/// let dim_cube_array_lod = textureDimensions(image_cube_array, 1);
130/// let dim_cube_array_lod2 = textureDimensions(image_cube_array, 1);
131/// ```
132///
133/// ```ir
134/// ImageQuery {
135///  image: [1],
136///  query: Size {
137///      level: Some(
138///          [1],
139///      ),
140///  },
141/// },
142/// ImageQuery {
143///  image: [1],
144///  query: Size {
145///      level: Some(
146///          [2],
147///      ),
148///  },
149/// },
150/// ```
151///
152/// HLSL should generate only 1 function for this case.
153#[derive(Clone, Copy, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]
154pub(super) enum ImageQuery {
155    Size,
156    SizeLevel,
157    NumLevels,
158    NumLayers,
159    NumSamples,
160}
161
162impl From<crate::ImageQuery> for ImageQuery {
163    fn from(q: crate::ImageQuery) -> Self {
164        use crate::ImageQuery as Iq;
165        match q {
166            Iq::Size { level: Some(_) } => ImageQuery::SizeLevel,
167            Iq::Size { level: None } => ImageQuery::Size,
168            Iq::NumLevels => ImageQuery::NumLevels,
169            Iq::NumLayers => ImageQuery::NumLayers,
170            Iq::NumSamples => ImageQuery::NumSamples,
171        }
172    }
173}
174
175pub(super) const IMAGE_STORAGE_LOAD_SCALAR_WRAPPER: &str = "LoadedStorageValueFrom";
176
177impl<W: Write> super::Writer<'_, W> {
178    pub(super) fn write_image_type(
179        &mut self,
180        dim: crate::ImageDimension,
181        arrayed: bool,
182        class: crate::ImageClass,
183    ) -> BackendResult {
184        let access_str = match class {
185            crate::ImageClass::Storage { .. } => "RW",
186            _ => "",
187        };
188        let dim_str = dim.to_hlsl_str();
189        let arrayed_str = if arrayed { "Array" } else { "" };
190        write!(self.out, "{access_str}Texture{dim_str}{arrayed_str}")?;
191        match class {
192            crate::ImageClass::Depth { multi } => {
193                let multi_str = if multi { "MS" } else { "" };
194                write!(self.out, "{multi_str}<float>")?
195            }
196            crate::ImageClass::Sampled { kind, multi } => {
197                let multi_str = if multi { "MS" } else { "" };
198                let scalar_kind_str = crate::Scalar { kind, width: 4 }.to_hlsl_str()?;
199                write!(self.out, "{multi_str}<{scalar_kind_str}4>")?
200            }
201            crate::ImageClass::Storage { format, .. } => {
202                let storage_format_str = format.to_hlsl_str();
203                write!(self.out, "<{storage_format_str}>")?
204            }
205            crate::ImageClass::External => {
206                unreachable!(
207                    "external images should be handled by `write_global_external_texture`"
208                );
209            }
210        }
211        Ok(())
212    }
213
214    pub(super) fn write_wrapped_array_length_function_name(
215        &mut self,
216        query: WrappedArrayLength,
217    ) -> BackendResult {
218        let access_str = if query.writable { "RW" } else { "" };
219        write!(self.out, "NagaBufferLength{access_str}",)?;
220
221        Ok(())
222    }
223
224    /// Helper function that write wrapped function for `Expression::ArrayLength`
225    ///
226    /// <https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/sm5-object-rwbyteaddressbuffer-getdimensions>
227    pub(super) fn write_wrapped_array_length_function(
228        &mut self,
229        wal: WrappedArrayLength,
230    ) -> BackendResult {
231        use crate::back::INDENT;
232
233        const ARGUMENT_VARIABLE_NAME: &str = "buffer";
234        const RETURN_VARIABLE_NAME: &str = "ret";
235
236        // Write function return type and name
237        write!(self.out, "uint ")?;
238        self.write_wrapped_array_length_function_name(wal)?;
239
240        // Write function parameters
241        write!(self.out, "(")?;
242        let access_str = if wal.writable { "RW" } else { "" };
243        writeln!(
244            self.out,
245            "{access_str}ByteAddressBuffer {ARGUMENT_VARIABLE_NAME})"
246        )?;
247        // Write function body
248        writeln!(self.out, "{{")?;
249
250        // Write `GetDimensions` function.
251        writeln!(self.out, "{INDENT}uint {RETURN_VARIABLE_NAME};")?;
252        writeln!(
253            self.out,
254            "{INDENT}{ARGUMENT_VARIABLE_NAME}.GetDimensions({RETURN_VARIABLE_NAME});"
255        )?;
256
257        // Write return value
258        writeln!(self.out, "{INDENT}return {RETURN_VARIABLE_NAME};")?;
259
260        // End of function body
261        writeln!(self.out, "}}")?;
262        // Write extra new line
263        writeln!(self.out)?;
264
265        Ok(())
266    }
267
268    /// Helper function used by [`Self::write_wrapped_image_load_function`] and
269    /// [`Self::write_wrapped_image_sample_function`] to write the shared YUV
270    /// to RGB conversion code for external textures. Expects the preceding
271    /// code to declare the Y component as a `float` variable of name `y`, the
272    /// UV components as a `float2` variable of name `uv`, and the external
273    /// texture params as a variable of name `params`. The emitted code will
274    /// return the result.
275    fn write_convert_yuv_to_rgb_and_return(
276        &mut self,
277        level: crate::back::Level,
278        y: &str,
279        uv: &str,
280        params: &str,
281    ) -> BackendResult {
282        let l1 = level;
283        let l2 = l1.next();
284
285        // Convert from YUV to non-linear RGB in the source color space. We
286        // declare our matrices as row_major in HLSL, therefore we must reverse
287        // the order of this multiplication
288        writeln!(
289            self.out,
290            "{l1}float3 srcGammaRgb = mul(float4({y}, {uv}, 1.0), {params}.yuv_conversion_matrix).rgb;"
291        )?;
292
293        // Apply the inverse of the source transfer function to convert to
294        // linear RGB in the source color space.
295        writeln!(
296            self.out,
297            "{l1}float3 srcLinearRgb = srcGammaRgb < {params}.src_tf.k * {params}.src_tf.b ?"
298        )?;
299        writeln!(self.out, "{l2}srcGammaRgb / {params}.src_tf.k :")?;
300        writeln!(self.out, "{l2}pow((srcGammaRgb + {params}.src_tf.a - 1.0) / {params}.src_tf.a, {params}.src_tf.g);")?;
301
302        // Multiply by the gamut conversion matrix to convert to linear RGB in
303        // the destination color space. We declare our matrices as row_major in
304        // HLSL, therefore we must reverse the order of this multiplication.
305        writeln!(
306            self.out,
307            "{l1}float3 dstLinearRgb = mul(srcLinearRgb, {params}.gamut_conversion_matrix);"
308        )?;
309
310        // Finally, apply the dest transfer function to convert to non-linear
311        // RGB in the destination color space, and return the result.
312        writeln!(
313            self.out,
314            "{l1}float3 dstGammaRgb = dstLinearRgb < {params}.dst_tf.b ?"
315        )?;
316        writeln!(self.out, "{l2}{params}.dst_tf.k * dstLinearRgb :")?;
317        writeln!(self.out, "{l2}{params}.dst_tf.a * pow(dstLinearRgb, 1.0 / {params}.dst_tf.g) - ({params}.dst_tf.a - 1);")?;
318
319        writeln!(self.out, "{l1}return float4(dstGammaRgb, 1.0);")?;
320        Ok(())
321    }
322
323    pub(super) fn write_wrapped_image_load_function(
324        &mut self,
325        module: &crate::Module,
326        load: WrappedImageLoad,
327    ) -> BackendResult {
328        match load {
329            WrappedImageLoad {
330                class: crate::ImageClass::External,
331            } => {
332                let l1 = crate::back::Level(1);
333                let l2 = l1.next();
334                let l3 = l2.next();
335                let params_ty_name = &self.names
336                    [&NameKey::Type(module.special_types.external_texture_params.unwrap())];
337                writeln!(self.out, "float4 {IMAGE_LOAD_EXTERNAL_FUNCTION}(")?;
338                writeln!(self.out, "{l1}Texture2D<float4> plane0,")?;
339                writeln!(self.out, "{l1}Texture2D<float4> plane1,")?;
340                writeln!(self.out, "{l1}Texture2D<float4> plane2,")?;
341                writeln!(self.out, "{l1}{params_ty_name} params,")?;
342                writeln!(self.out, "{l1}uint2 coords)")?;
343                writeln!(self.out, "{{")?;
344                writeln!(self.out, "{l1}uint2 plane0_size;")?;
345                writeln!(
346                    self.out,
347                    "{l1}plane0.GetDimensions(plane0_size.x, plane0_size.y);"
348                )?;
349                // Clamp coords to provided size of external texture to prevent OOB read.
350                // If params.size is zero then clamp to the actual size of the texture.
351                writeln!(
352                    self.out,
353                    "{l1}uint2 cropped_size = any(params.size) ? params.size : plane0_size;"
354                )?;
355                writeln!(self.out, "{l1}coords = min(coords, cropped_size - 1);")?;
356
357                // Apply load transformation. We declare our matrices as row_major in
358                // HLSL, therefore we must reverse the order of this multiplication
359                writeln!(self.out, "{l1}float3x2 load_transform = float3x2(")?;
360                writeln!(self.out, "{l2}params.load_transform_0,")?;
361                writeln!(self.out, "{l2}params.load_transform_1,")?;
362                writeln!(self.out, "{l2}params.load_transform_2")?;
363                writeln!(self.out, "{l1});")?;
364                writeln!(self.out, "{l1}uint2 plane0_coords = uint2(round(mul(float3(coords, 1.0), load_transform)));")?;
365                writeln!(self.out, "{l1}if (params.num_planes == 1u) {{")?;
366                // For single plane, simply read from plane0
367                writeln!(
368                    self.out,
369                    "{l2}return plane0.Load(uint3(plane0_coords, 0u));"
370                )?;
371                writeln!(self.out, "{l1}}} else {{")?;
372
373                // Chroma planes may be subsampled so we must scale the coords accordingly.
374                writeln!(self.out, "{l2}uint2 plane1_size;")?;
375                writeln!(
376                    self.out,
377                    "{l2}plane1.GetDimensions(plane1_size.x, plane1_size.y);"
378                )?;
379                writeln!(self.out, "{l2}uint2 plane1_coords = uint2(floor(float2(plane0_coords) * float2(plane1_size) / float2(plane0_size)));")?;
380
381                // For multi-plane, read the Y value from plane 0
382                writeln!(
383                    self.out,
384                    "{l2}float y = plane0.Load(uint3(plane0_coords, 0u)).x;"
385                )?;
386
387                writeln!(self.out, "{l2}float2 uv;")?;
388                writeln!(self.out, "{l2}if (params.num_planes == 2u) {{")?;
389                // Read UV from interleaved plane 1
390                writeln!(
391                    self.out,
392                    "{l3}uv = plane1.Load(uint3(plane1_coords, 0u)).xy;"
393                )?;
394                writeln!(self.out, "{l2}}} else {{")?;
395                // Read U and V from planes 1 and 2 respectively
396                writeln!(self.out, "{l3}uint2 plane2_size;")?;
397                writeln!(
398                    self.out,
399                    "{l3}plane2.GetDimensions(plane2_size.x, plane2_size.y);"
400                )?;
401                writeln!(self.out, "{l3}uint2 plane2_coords = uint2(floor(float2(plane0_coords) * float2(plane2_size) / float2(plane0_size)));")?;
402                writeln!(self.out, "{l3}uv = float2(plane1.Load(uint3(plane1_coords, 0u)).x, plane2.Load(uint3(plane2_coords, 0u)).x);")?;
403                writeln!(self.out, "{l2}}}")?;
404
405                self.write_convert_yuv_to_rgb_and_return(l2, "y", "uv", "params")?;
406
407                writeln!(self.out, "{l1}}}")?;
408                writeln!(self.out, "}}")?;
409                writeln!(self.out)?;
410            }
411            _ => {}
412        }
413
414        Ok(())
415    }
416
417    pub(super) fn write_wrapped_image_sample_function(
418        &mut self,
419        module: &crate::Module,
420        sample: WrappedImageSample,
421    ) -> BackendResult {
422        match sample {
423            WrappedImageSample {
424                class: crate::ImageClass::External,
425                clamp_to_edge: true,
426            } => {
427                let l1 = crate::back::Level(1);
428                let l2 = l1.next();
429                let l3 = l2.next();
430                let params_ty_name = &self.names
431                    [&NameKey::Type(module.special_types.external_texture_params.unwrap())];
432                writeln!(
433                    self.out,
434                    "float4 {IMAGE_SAMPLE_BASE_CLAMP_TO_EDGE_FUNCTION}("
435                )?;
436                writeln!(self.out, "{l1}Texture2D<float4> plane0,")?;
437                writeln!(self.out, "{l1}Texture2D<float4> plane1,")?;
438                writeln!(self.out, "{l1}Texture2D<float4> plane2,")?;
439                writeln!(self.out, "{l1}{params_ty_name} params,")?;
440                writeln!(self.out, "{l1}SamplerState samp,")?;
441                writeln!(self.out, "{l1}float2 coords)")?;
442                writeln!(self.out, "{{")?;
443                writeln!(self.out, "{l1}float2 plane0_size;")?;
444                writeln!(
445                    self.out,
446                    "{l1}plane0.GetDimensions(plane0_size.x, plane0_size.y);"
447                )?;
448                writeln!(self.out, "{l1}float3x2 sample_transform = float3x2(")?;
449                writeln!(self.out, "{l2}params.sample_transform_0,")?;
450                writeln!(self.out, "{l2}params.sample_transform_1,")?;
451                writeln!(self.out, "{l2}params.sample_transform_2")?;
452                writeln!(self.out, "{l1});")?;
453                // Apply sample transformation. We declare our matrices as row_major in
454                // HLSL, therefore we must reverse the order of this multiplication
455                writeln!(
456                    self.out,
457                    "{l1}coords = mul(float3(coords, 1.0), sample_transform);"
458                )?;
459                // Calculate the sample bounds. The purported size of the texture
460                // (params.size) is irrelevant here as we are dealing with normalized
461                // coordinates. Usually we would clamp to (0,0)..(1,1). However, we must
462                // apply the sample transformation to that, also bearing in mind that it
463                // may contain a flip on either axis. We calculate and adjust for the
464                // half-texel separately for each plane as it depends on the actual
465                // texture size which may vary between planes.
466                writeln!(
467                    self.out,
468                    "{l1}float2 bounds_min = mul(float3(0.0, 0.0, 1.0), sample_transform);"
469                )?;
470                writeln!(
471                    self.out,
472                    "{l1}float2 bounds_max = mul(float3(1.0, 1.0, 1.0), sample_transform);"
473                )?;
474                writeln!(self.out, "{l1}float4 bounds = float4(min(bounds_min, bounds_max), max(bounds_min, bounds_max));")?;
475                writeln!(
476                    self.out,
477                    "{l1}float2 plane0_half_texel = float2(0.5, 0.5) / plane0_size;"
478                )?;
479                writeln!(
480                    self.out,
481                    "{l1}float2 plane0_coords = clamp(coords, bounds.xy + plane0_half_texel, bounds.zw - plane0_half_texel);"
482                )?;
483                writeln!(self.out, "{l1}if (params.num_planes == 1u) {{")?;
484                // For single plane, simply sample from plane0
485                writeln!(
486                    self.out,
487                    "{l2}return plane0.SampleLevel(samp, plane0_coords, 0.0f);"
488                )?;
489                writeln!(self.out, "{l1}}} else {{")?;
490
491                writeln!(self.out, "{l2}float2 plane1_size;")?;
492                writeln!(
493                    self.out,
494                    "{l2}plane1.GetDimensions(plane1_size.x, plane1_size.y);"
495                )?;
496                writeln!(
497                    self.out,
498                    "{l2}float2 plane1_half_texel = float2(0.5, 0.5) / plane1_size;"
499                )?;
500                writeln!(
501                    self.out,
502                    "{l2}float2 plane1_coords = clamp(coords, bounds.xy + plane1_half_texel, bounds.zw - plane1_half_texel);"
503                )?;
504
505                // For multi-plane, sample the Y value from plane 0
506                writeln!(
507                    self.out,
508                    "{l2}float y = plane0.SampleLevel(samp, plane0_coords, 0.0f).x;"
509                )?;
510                writeln!(self.out, "{l2}float2 uv;")?;
511                writeln!(self.out, "{l2}if (params.num_planes == 2u) {{")?;
512                // Sample UV from interleaved plane 1
513                writeln!(
514                    self.out,
515                    "{l3}uv = plane1.SampleLevel(samp, plane1_coords, 0.0f).xy;"
516                )?;
517                writeln!(self.out, "{l2}}} else {{")?;
518                // Sample U and V from planes 1 and 2 respectively
519                writeln!(self.out, "{l3}float2 plane2_size;")?;
520                writeln!(
521                    self.out,
522                    "{l3}plane2.GetDimensions(plane2_size.x, plane2_size.y);"
523                )?;
524                writeln!(
525                    self.out,
526                    "{l3}float2 plane2_half_texel = float2(0.5, 0.5) / plane2_size;"
527                )?;
528                writeln!(self.out, "{l3}float2 plane2_coords = clamp(coords, bounds.xy + plane2_half_texel, bounds.zw - plane2_half_texel);")?;
529                writeln!(self.out, "{l3}uv = float2(plane1.SampleLevel(samp, plane1_coords, 0.0f).x, plane2.SampleLevel(samp, plane2_coords, 0.0f).x);")?;
530                writeln!(self.out, "{l2}}}")?;
531
532                self.write_convert_yuv_to_rgb_and_return(l2, "y", "uv", "params")?;
533
534                writeln!(self.out, "{l1}}}")?;
535                writeln!(self.out, "}}")?;
536                writeln!(self.out)?;
537            }
538            WrappedImageSample {
539                class:
540                    crate::ImageClass::Sampled {
541                        kind: ScalarKind::Float,
542                        multi: false,
543                    },
544                clamp_to_edge: true,
545            } => {
546                writeln!(self.out, "float4 {IMAGE_SAMPLE_BASE_CLAMP_TO_EDGE_FUNCTION}(Texture2D<float4> tex, SamplerState samp, float2 coords) {{")?;
547                let l1 = crate::back::Level(1);
548                writeln!(self.out, "{l1}float2 size;")?;
549                writeln!(self.out, "{l1}tex.GetDimensions(size.x, size.y);")?;
550                writeln!(self.out, "{l1}float2 half_texel = float2(0.5, 0.5) / size;")?;
551                writeln!(
552                    self.out,
553                    "{l1}return tex.SampleLevel(samp, clamp(coords, half_texel, 1.0 - half_texel), 0.0);"
554                )?;
555                writeln!(self.out, "}}")?;
556                writeln!(self.out)?;
557            }
558            _ => {}
559        }
560
561        Ok(())
562    }
563
564    pub(super) fn write_wrapped_image_query_function_name(
565        &mut self,
566        query: WrappedImageQuery,
567    ) -> BackendResult {
568        let dim_str = query.dim.to_hlsl_str();
569        let class_str = match query.class {
570            crate::ImageClass::Sampled { multi: true, .. } => "MS",
571            crate::ImageClass::Depth { multi: true } => "DepthMS",
572            crate::ImageClass::Depth { multi: false } => "Depth",
573            crate::ImageClass::Sampled { multi: false, .. } => "",
574            crate::ImageClass::Storage { .. } => "RW",
575            crate::ImageClass::External => "External",
576        };
577        let arrayed_str = if query.arrayed { "Array" } else { "" };
578        let query_str = match query.query {
579            ImageQuery::Size => "Dimensions",
580            ImageQuery::SizeLevel => "MipDimensions",
581            ImageQuery::NumLevels => "NumLevels",
582            ImageQuery::NumLayers => "NumLayers",
583            ImageQuery::NumSamples => "NumSamples",
584        };
585
586        write!(self.out, "Naga{class_str}{query_str}{dim_str}{arrayed_str}")?;
587
588        Ok(())
589    }
590
591    /// Helper function that write wrapped function for `Expression::ImageQuery`
592    ///
593    /// <https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-to-getdimensions>
594    pub(super) fn write_wrapped_image_query_function(
595        &mut self,
596        module: &crate::Module,
597        wiq: WrappedImageQuery,
598        expr_handle: Handle<crate::Expression>,
599        func_ctx: &FunctionCtx,
600    ) -> BackendResult {
601        use crate::{
602            back::{COMPONENTS, INDENT},
603            ImageDimension as IDim,
604        };
605
606        match wiq.class {
607            crate::ImageClass::External => {
608                if wiq.query != ImageQuery::Size {
609                    return Err(super::Error::Custom(
610                        "External images only support `Size` queries".into(),
611                    ));
612                }
613
614                write!(self.out, "uint2 ")?;
615                self.write_wrapped_image_query_function_name(wiq)?;
616                let params_name = &self.names
617                    [&NameKey::Type(module.special_types.external_texture_params.unwrap())];
618                // Only plane0 and params are used by this implementation, but it's easier to
619                // always take all of them as arguments so that we can unconditionally expand an
620                // external texture expression each of its parts.
621                writeln!(self.out, "(Texture2D<float4> plane0, Texture2D<float4> plane1, Texture2D<float4> plane2, {params_name} params) {{")?;
622                let l1 = crate::back::Level(1);
623                let l2 = l1.next();
624                writeln!(self.out, "{l1}if (any(params.size)) {{")?;
625                writeln!(self.out, "{l2}return params.size;")?;
626                writeln!(self.out, "{l1}}} else {{")?;
627                // params.size == (0, 0) indicates to query and return plane 0's actual size
628                writeln!(self.out, "{l2}uint2 ret;")?;
629                writeln!(self.out, "{l2}plane0.GetDimensions(ret.x, ret.y);")?;
630                writeln!(self.out, "{l2}return ret;")?;
631                writeln!(self.out, "{l1}}}")?;
632                writeln!(self.out, "}}")?;
633                writeln!(self.out)?;
634            }
635            _ => {
636                const ARGUMENT_VARIABLE_NAME: &str = "tex";
637                const RETURN_VARIABLE_NAME: &str = "ret";
638                const MIP_LEVEL_PARAM: &str = "mip_level";
639
640                // Write function return type and name
641                let ret_ty = func_ctx.resolve_type(expr_handle, &module.types);
642                self.write_value_type(module, ret_ty)?;
643                write!(self.out, " ")?;
644                self.write_wrapped_image_query_function_name(wiq)?;
645
646                // Write function parameters
647                write!(self.out, "(")?;
648                // Texture always first parameter
649                self.write_image_type(wiq.dim, wiq.arrayed, wiq.class)?;
650                write!(self.out, " {ARGUMENT_VARIABLE_NAME}")?;
651                // Mipmap is a second parameter if exists
652                if let ImageQuery::SizeLevel = wiq.query {
653                    write!(self.out, ", uint {MIP_LEVEL_PARAM}")?;
654                }
655                writeln!(self.out, ")")?;
656
657                // Write function body
658                writeln!(self.out, "{{")?;
659
660                let array_coords = usize::from(wiq.arrayed);
661                // extra parameter is the mip level count or the sample count
662                let extra_coords = match wiq.class {
663                    crate::ImageClass::Storage { .. } => 0,
664                    crate::ImageClass::Sampled { .. } | crate::ImageClass::Depth { .. } => 1,
665                    crate::ImageClass::External => unreachable!(),
666                };
667
668                // GetDimensions Overloaded Methods
669                // https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-to-getdimensions#overloaded-methods
670                // We rely on the validator to reject invalid queries.
671                let (ret_swizzle, number_of_out_params) = match wiq.query {
672                    ImageQuery::Size | ImageQuery::SizeLevel => {
673                        let ret = match wiq.dim {
674                            IDim::D1 => "x",
675                            IDim::D2 => "xy",
676                            IDim::D3 => "xyz",
677                            IDim::Cube => "xy",
678                        };
679                        (ret, ret.len() + array_coords + extra_coords)
680                    }
681                    ImageQuery::NumLevels | ImageQuery::NumSamples => {
682                        // We want `NumberOfLevels` or `Samples`
683                        match wiq.dim {
684                            IDim::D1 => ("y", 2),
685                            IDim::D3 => ("w", 4),
686                            IDim::D2 | IDim::Cube => {
687                                if wiq.arrayed {
688                                    ("w", 4)
689                                } else {
690                                    ("z", 3)
691                                }
692                            }
693                        }
694                    }
695                    // We want `Elements`
696                    ImageQuery::NumLayers => ("z", 3 + extra_coords),
697                };
698
699                // Write `GetDimensions` function.
700                writeln!(self.out, "{INDENT}uint4 {RETURN_VARIABLE_NAME};")?;
701                write!(self.out, "{INDENT}{ARGUMENT_VARIABLE_NAME}.GetDimensions(")?;
702                match wiq.query {
703                    ImageQuery::SizeLevel => {
704                        write!(self.out, "{MIP_LEVEL_PARAM}, ")?;
705                    }
706                    _ => match wiq.class {
707                        crate::ImageClass::Sampled { multi: true, .. }
708                        | crate::ImageClass::Depth { multi: true }
709                        | crate::ImageClass::Storage { .. } => {}
710                        _ => {
711                            // Write zero mipmap level for supported types
712                            write!(self.out, "0, ")?;
713                        }
714                    },
715                }
716
717                for component in COMPONENTS[..number_of_out_params - 1].iter() {
718                    write!(self.out, "{RETURN_VARIABLE_NAME}.{component}, ")?;
719                }
720
721                // write last parameter without comma and space for last parameter
722                write!(
723                    self.out,
724                    "{}.{}",
725                    RETURN_VARIABLE_NAME,
726                    COMPONENTS[number_of_out_params - 1]
727                )?;
728
729                writeln!(self.out, ");")?;
730
731                // Write return value
732                writeln!(
733                    self.out,
734                    "{INDENT}return {RETURN_VARIABLE_NAME}.{ret_swizzle};"
735                )?;
736
737                // End of function body
738                writeln!(self.out, "}}")?;
739                // Write extra new line
740                writeln!(self.out)?;
741            }
742        }
743        Ok(())
744    }
745
746    pub(super) fn write_wrapped_constructor_function_name(
747        &mut self,
748        module: &crate::Module,
749        constructor: WrappedConstructor,
750    ) -> BackendResult {
751        let name = crate::TypeInner::hlsl_type_id(constructor.ty, module.to_ctx(), &self.names)?;
752        write!(self.out, "Construct{name}")?;
753        Ok(())
754    }
755
756    /// Helper function that write wrapped function for `Expression::Compose` for structures.
757    fn write_wrapped_constructor_function(
758        &mut self,
759        module: &crate::Module,
760        constructor: WrappedConstructor,
761    ) -> BackendResult {
762        use crate::back::INDENT;
763
764        const ARGUMENT_VARIABLE_NAME: &str = "arg";
765        const RETURN_VARIABLE_NAME: &str = "ret";
766
767        // Write function return type and name
768        if let crate::TypeInner::Array { base, size, .. } = module.types[constructor.ty].inner {
769            write!(self.out, "typedef ")?;
770            self.write_type(module, constructor.ty)?;
771            write!(self.out, " ret_")?;
772            self.write_wrapped_constructor_function_name(module, constructor)?;
773            self.write_array_size(module, base, size)?;
774            writeln!(self.out, ";")?;
775
776            write!(self.out, "ret_")?;
777            self.write_wrapped_constructor_function_name(module, constructor)?;
778        } else {
779            self.write_type(module, constructor.ty)?;
780        }
781        write!(self.out, " ")?;
782        self.write_wrapped_constructor_function_name(module, constructor)?;
783
784        // Write function parameters
785        write!(self.out, "(")?;
786
787        let mut write_arg = |i, ty| -> BackendResult {
788            if i != 0 {
789                write!(self.out, ", ")?;
790            }
791            self.write_type(module, ty)?;
792            write!(self.out, " {ARGUMENT_VARIABLE_NAME}{i}")?;
793            if let crate::TypeInner::Array { base, size, .. } = module.types[ty].inner {
794                self.write_array_size(module, base, size)?;
795            }
796            Ok(())
797        };
798
799        match module.types[constructor.ty].inner {
800            crate::TypeInner::Struct { ref members, .. } => {
801                for (i, member) in members.iter().enumerate() {
802                    write_arg(i, member.ty)?;
803                }
804            }
805            crate::TypeInner::Array {
806                base,
807                size: crate::ArraySize::Constant(size),
808                ..
809            } => {
810                for i in 0..size.get() as usize {
811                    write_arg(i, base)?;
812                }
813            }
814            _ => unreachable!(),
815        };
816
817        write!(self.out, ")")?;
818
819        // Write function body
820        writeln!(self.out, " {{")?;
821
822        match module.types[constructor.ty].inner {
823            crate::TypeInner::Struct { ref members, .. } => {
824                let struct_name = &self.names[&NameKey::Type(constructor.ty)];
825                writeln!(
826                    self.out,
827                    "{INDENT}{struct_name} {RETURN_VARIABLE_NAME} = ({struct_name})0;"
828                )?;
829                for (i, member) in members.iter().enumerate() {
830                    let field_name = &self.names[&NameKey::StructMember(constructor.ty, i as u32)];
831
832                    match module.types[member.ty].inner {
833                        crate::TypeInner::Matrix {
834                            columns,
835                            rows: crate::VectorSize::Bi,
836                            ..
837                        } if member.binding.is_none() => {
838                            for j in 0..columns as u8 {
839                                writeln!(
840                                    self.out,
841                                    "{INDENT}{RETURN_VARIABLE_NAME}.{field_name}_{j} = {ARGUMENT_VARIABLE_NAME}{i}[{j}];"
842                                )?;
843                            }
844                        }
845                        ref other => {
846                            // We cast arrays of native HLSL `floatCx2`s to arrays of `matCx2`s
847                            // (where the inner matrix is represented by a struct with C `float2` members).
848                            // See the module-level block comment in mod.rs for details.
849                            if let Some(super::writer::MatrixType {
850                                columns,
851                                rows: crate::VectorSize::Bi,
852                                width,
853                            }) = super::writer::get_inner_matrix_data(module, member.ty)
854                            {
855                                write!(
856                                    self.out,
857                                    "{}{}.{} = (__mat{}x2_f{}",
858                                    INDENT,
859                                    RETURN_VARIABLE_NAME,
860                                    field_name,
861                                    columns as u8,
862                                    width * 8
863                                )?;
864                                if let crate::TypeInner::Array { base, size, .. } = *other {
865                                    self.write_array_size(module, base, size)?;
866                                }
867                                writeln!(self.out, "){ARGUMENT_VARIABLE_NAME}{i};",)?;
868                            } else {
869                                writeln!(
870                                    self.out,
871                                    "{INDENT}{RETURN_VARIABLE_NAME}.{field_name} = {ARGUMENT_VARIABLE_NAME}{i};",
872                                )?;
873                            }
874                        }
875                    }
876                }
877            }
878            crate::TypeInner::Array {
879                base,
880                size: crate::ArraySize::Constant(size),
881                ..
882            } => {
883                write!(self.out, "{INDENT}")?;
884                self.write_type(module, base)?;
885                write!(self.out, " {RETURN_VARIABLE_NAME}")?;
886                self.write_array_size(module, base, crate::ArraySize::Constant(size))?;
887                write!(self.out, " = {{ ")?;
888                for i in 0..size.get() {
889                    if i != 0 {
890                        write!(self.out, ", ")?;
891                    }
892                    write!(self.out, "{ARGUMENT_VARIABLE_NAME}{i}")?;
893                }
894                writeln!(self.out, " }};",)?;
895            }
896            _ => unreachable!(),
897        }
898
899        // Write return value
900        writeln!(self.out, "{INDENT}return {RETURN_VARIABLE_NAME};")?;
901
902        // End of function body
903        writeln!(self.out, "}}")?;
904        // Write extra new line
905        writeln!(self.out)?;
906
907        Ok(())
908    }
909
910    /// Writes the conversion from a single length storage texture load to a vec4 with the loaded
911    /// scalar in its `x` component, 1 in its `a` component and 0 everywhere else.
912    fn write_loaded_scalar_to_storage_loaded_value(
913        &mut self,
914        scalar_type: crate::Scalar,
915    ) -> BackendResult {
916        const ARGUMENT_VARIABLE_NAME: &str = "arg";
917        const RETURN_VARIABLE_NAME: &str = "ret";
918
919        let zero;
920        let one;
921        match scalar_type.kind {
922            ScalarKind::Sint => {
923                assert_eq!(
924                    scalar_type.width, 4,
925                    "Scalar {scalar_type:?} is not a result from any storage format"
926                );
927                zero = "0";
928                one = "1";
929            }
930            ScalarKind::Uint => match scalar_type.width {
931                4 => {
932                    zero = "0u";
933                    one = "1u";
934                }
935                8 => {
936                    zero = "0uL";
937                    one = "1uL"
938                }
939                _ => unreachable!("Scalar {scalar_type:?} is not a result from any storage format"),
940            },
941            ScalarKind::Float => {
942                assert_eq!(
943                    scalar_type.width, 4,
944                    "Scalar {scalar_type:?} is not a result from any storage format"
945                );
946                zero = "0.0";
947                one = "1.0";
948            }
949            _ => unreachable!("Scalar {scalar_type:?} is not a result from any storage format"),
950        }
951
952        let ty = scalar_type.to_hlsl_str()?;
953        writeln!(
954            self.out,
955            "{ty}4 {IMAGE_STORAGE_LOAD_SCALAR_WRAPPER}{ty}({ty} {ARGUMENT_VARIABLE_NAME}) {{\
956    {ty}4 {RETURN_VARIABLE_NAME} = {ty}4({ARGUMENT_VARIABLE_NAME}, {zero}, {zero}, {one});\
957    return {RETURN_VARIABLE_NAME};\
958}}"
959        )?;
960
961        Ok(())
962    }
963
964    pub(super) fn write_wrapped_struct_matrix_get_function_name(
965        &mut self,
966        access: WrappedStructMatrixAccess,
967    ) -> BackendResult {
968        let name = &self.names[&NameKey::Type(access.ty)];
969        let field_name = &self.names[&NameKey::StructMember(access.ty, access.index)];
970        write!(self.out, "GetMat{field_name}On{name}")?;
971        Ok(())
972    }
973
974    /// Writes a function used to get a matCx2 from within a structure.
975    pub(super) fn write_wrapped_struct_matrix_get_function(
976        &mut self,
977        module: &crate::Module,
978        access: WrappedStructMatrixAccess,
979    ) -> BackendResult {
980        use crate::back::INDENT;
981
982        const STRUCT_ARGUMENT_VARIABLE_NAME: &str = "obj";
983
984        // Write function return type and name
985        let member = match module.types[access.ty].inner {
986            crate::TypeInner::Struct { ref members, .. } => &members[access.index as usize],
987            _ => unreachable!(),
988        };
989        let ret_ty = &module.types[member.ty].inner;
990        self.write_value_type(module, ret_ty)?;
991        write!(self.out, " ")?;
992        self.write_wrapped_struct_matrix_get_function_name(access)?;
993
994        // Write function parameters
995        write!(self.out, "(")?;
996        let struct_name = &self.names[&NameKey::Type(access.ty)];
997        write!(self.out, "{struct_name} {STRUCT_ARGUMENT_VARIABLE_NAME}")?;
998
999        // Write function body
1000        writeln!(self.out, ") {{")?;
1001
1002        // Write return value
1003        write!(self.out, "{INDENT}return ")?;
1004        self.write_value_type(module, ret_ty)?;
1005        write!(self.out, "(")?;
1006        let field_name = &self.names[&NameKey::StructMember(access.ty, access.index)];
1007        match module.types[member.ty].inner {
1008            crate::TypeInner::Matrix { columns, .. } => {
1009                for i in 0..columns as u8 {
1010                    if i != 0 {
1011                        write!(self.out, ", ")?;
1012                    }
1013                    write!(self.out, "{STRUCT_ARGUMENT_VARIABLE_NAME}.{field_name}_{i}")?;
1014                }
1015            }
1016            _ => unreachable!(),
1017        }
1018        writeln!(self.out, ");")?;
1019
1020        // End of function body
1021        writeln!(self.out, "}}")?;
1022        // Write extra new line
1023        writeln!(self.out)?;
1024
1025        Ok(())
1026    }
1027
1028    pub(super) fn write_wrapped_struct_matrix_set_function_name(
1029        &mut self,
1030        access: WrappedStructMatrixAccess,
1031    ) -> BackendResult {
1032        let name = &self.names[&NameKey::Type(access.ty)];
1033        let field_name = &self.names[&NameKey::StructMember(access.ty, access.index)];
1034        write!(self.out, "SetMat{field_name}On{name}")?;
1035        Ok(())
1036    }
1037
1038    /// Writes a function used to set a matCx2 from within a structure.
1039    pub(super) fn write_wrapped_struct_matrix_set_function(
1040        &mut self,
1041        module: &crate::Module,
1042        access: WrappedStructMatrixAccess,
1043    ) -> BackendResult {
1044        use crate::back::INDENT;
1045
1046        const STRUCT_ARGUMENT_VARIABLE_NAME: &str = "obj";
1047        const MATRIX_ARGUMENT_VARIABLE_NAME: &str = "mat";
1048
1049        // Write function return type and name
1050        write!(self.out, "void ")?;
1051        self.write_wrapped_struct_matrix_set_function_name(access)?;
1052
1053        // Write function parameters
1054        write!(self.out, "(")?;
1055        let struct_name = &self.names[&NameKey::Type(access.ty)];
1056        write!(self.out, "{struct_name} {STRUCT_ARGUMENT_VARIABLE_NAME}, ")?;
1057        let member = match module.types[access.ty].inner {
1058            crate::TypeInner::Struct { ref members, .. } => &members[access.index as usize],
1059            _ => unreachable!(),
1060        };
1061        self.write_type(module, member.ty)?;
1062        write!(self.out, " {MATRIX_ARGUMENT_VARIABLE_NAME}")?;
1063        // Write function body
1064        writeln!(self.out, ") {{")?;
1065
1066        let field_name = &self.names[&NameKey::StructMember(access.ty, access.index)];
1067
1068        match module.types[member.ty].inner {
1069            crate::TypeInner::Matrix { columns, .. } => {
1070                for i in 0..columns as u8 {
1071                    writeln!(
1072                        self.out,
1073                        "{INDENT}{STRUCT_ARGUMENT_VARIABLE_NAME}.{field_name}_{i} = {MATRIX_ARGUMENT_VARIABLE_NAME}[{i}];"
1074                    )?;
1075                }
1076            }
1077            _ => unreachable!(),
1078        }
1079
1080        // End of function body
1081        writeln!(self.out, "}}")?;
1082        // Write extra new line
1083        writeln!(self.out)?;
1084
1085        Ok(())
1086    }
1087
1088    pub(super) fn write_wrapped_struct_matrix_set_vec_function_name(
1089        &mut self,
1090        access: WrappedStructMatrixAccess,
1091    ) -> BackendResult {
1092        let name = &self.names[&NameKey::Type(access.ty)];
1093        let field_name = &self.names[&NameKey::StructMember(access.ty, access.index)];
1094        write!(self.out, "SetMatVec{field_name}On{name}")?;
1095        Ok(())
1096    }
1097
1098    /// Writes a function used to set a vec2 on a matCx2 from within a structure.
1099    pub(super) fn write_wrapped_struct_matrix_set_vec_function(
1100        &mut self,
1101        module: &crate::Module,
1102        access: WrappedStructMatrixAccess,
1103    ) -> BackendResult {
1104        use crate::back::INDENT;
1105
1106        const STRUCT_ARGUMENT_VARIABLE_NAME: &str = "obj";
1107        const VECTOR_ARGUMENT_VARIABLE_NAME: &str = "vec";
1108        const MATRIX_INDEX_ARGUMENT_VARIABLE_NAME: &str = "mat_idx";
1109
1110        // Write function return type and name
1111        write!(self.out, "void ")?;
1112        self.write_wrapped_struct_matrix_set_vec_function_name(access)?;
1113
1114        // Write function parameters
1115        write!(self.out, "(")?;
1116        let struct_name = &self.names[&NameKey::Type(access.ty)];
1117        write!(self.out, "{struct_name} {STRUCT_ARGUMENT_VARIABLE_NAME}, ")?;
1118        let member = match module.types[access.ty].inner {
1119            crate::TypeInner::Struct { ref members, .. } => &members[access.index as usize],
1120            _ => unreachable!(),
1121        };
1122        let vec_ty = match module.types[member.ty].inner {
1123            crate::TypeInner::Matrix { rows, scalar, .. } => {
1124                crate::TypeInner::Vector { size: rows, scalar }
1125            }
1126            _ => unreachable!(),
1127        };
1128        self.write_value_type(module, &vec_ty)?;
1129        write!(
1130            self.out,
1131            " {VECTOR_ARGUMENT_VARIABLE_NAME}, uint {MATRIX_INDEX_ARGUMENT_VARIABLE_NAME}"
1132        )?;
1133
1134        // Write function body
1135        writeln!(self.out, ") {{")?;
1136
1137        writeln!(
1138            self.out,
1139            "{INDENT}switch({MATRIX_INDEX_ARGUMENT_VARIABLE_NAME}) {{"
1140        )?;
1141
1142        let field_name = &self.names[&NameKey::StructMember(access.ty, access.index)];
1143
1144        match module.types[member.ty].inner {
1145            crate::TypeInner::Matrix { columns, .. } => {
1146                for i in 0..columns as u8 {
1147                    writeln!(
1148                        self.out,
1149                        "{INDENT}case {i}: {{ {STRUCT_ARGUMENT_VARIABLE_NAME}.{field_name}_{i} = {VECTOR_ARGUMENT_VARIABLE_NAME}; break; }}"
1150                    )?;
1151                }
1152            }
1153            _ => unreachable!(),
1154        }
1155
1156        writeln!(self.out, "{INDENT}}}")?;
1157
1158        // End of function body
1159        writeln!(self.out, "}}")?;
1160        // Write extra new line
1161        writeln!(self.out)?;
1162
1163        Ok(())
1164    }
1165
1166    pub(super) fn write_wrapped_struct_matrix_set_scalar_function_name(
1167        &mut self,
1168        access: WrappedStructMatrixAccess,
1169    ) -> BackendResult {
1170        let name = &self.names[&NameKey::Type(access.ty)];
1171        let field_name = &self.names[&NameKey::StructMember(access.ty, access.index)];
1172        write!(self.out, "SetMatScalar{field_name}On{name}")?;
1173        Ok(())
1174    }
1175
1176    /// Writes a function used to set a float on a matCx2 from within a structure.
1177    pub(super) fn write_wrapped_struct_matrix_set_scalar_function(
1178        &mut self,
1179        module: &crate::Module,
1180        access: WrappedStructMatrixAccess,
1181    ) -> BackendResult {
1182        use crate::back::INDENT;
1183
1184        const STRUCT_ARGUMENT_VARIABLE_NAME: &str = "obj";
1185        const SCALAR_ARGUMENT_VARIABLE_NAME: &str = "scalar";
1186        const MATRIX_INDEX_ARGUMENT_VARIABLE_NAME: &str = "mat_idx";
1187        const VECTOR_INDEX_ARGUMENT_VARIABLE_NAME: &str = "vec_idx";
1188
1189        // Write function return type and name
1190        write!(self.out, "void ")?;
1191        self.write_wrapped_struct_matrix_set_scalar_function_name(access)?;
1192
1193        // Write function parameters
1194        write!(self.out, "(")?;
1195        let struct_name = &self.names[&NameKey::Type(access.ty)];
1196        write!(self.out, "{struct_name} {STRUCT_ARGUMENT_VARIABLE_NAME}, ")?;
1197        let member = match module.types[access.ty].inner {
1198            crate::TypeInner::Struct { ref members, .. } => &members[access.index as usize],
1199            _ => unreachable!(),
1200        };
1201        let scalar_ty = match module.types[member.ty].inner {
1202            crate::TypeInner::Matrix { scalar, .. } => crate::TypeInner::Scalar(scalar),
1203            _ => unreachable!(),
1204        };
1205        self.write_value_type(module, &scalar_ty)?;
1206        write!(
1207            self.out,
1208            " {SCALAR_ARGUMENT_VARIABLE_NAME}, uint {MATRIX_INDEX_ARGUMENT_VARIABLE_NAME}, uint {VECTOR_INDEX_ARGUMENT_VARIABLE_NAME}"
1209        )?;
1210
1211        // Write function body
1212        writeln!(self.out, ") {{")?;
1213
1214        writeln!(
1215            self.out,
1216            "{INDENT}switch({MATRIX_INDEX_ARGUMENT_VARIABLE_NAME}) {{"
1217        )?;
1218
1219        let field_name = &self.names[&NameKey::StructMember(access.ty, access.index)];
1220
1221        match module.types[member.ty].inner {
1222            crate::TypeInner::Matrix { columns, .. } => {
1223                for i in 0..columns as u8 {
1224                    writeln!(
1225                        self.out,
1226                        "{INDENT}case {i}: {{ {STRUCT_ARGUMENT_VARIABLE_NAME}.{field_name}_{i}[{VECTOR_INDEX_ARGUMENT_VARIABLE_NAME}] = {SCALAR_ARGUMENT_VARIABLE_NAME}; break; }}"
1227                    )?;
1228                }
1229            }
1230            _ => unreachable!(),
1231        }
1232
1233        writeln!(self.out, "{INDENT}}}")?;
1234
1235        // End of function body
1236        writeln!(self.out, "}}")?;
1237        // Write extra new line
1238        writeln!(self.out)?;
1239
1240        Ok(())
1241    }
1242
1243    /// Write functions to create special types.
1244    pub(super) fn write_special_functions(&mut self, module: &crate::Module) -> BackendResult {
1245        for (type_key, struct_ty) in module.special_types.predeclared_types.iter() {
1246            match type_key {
1247                &crate::PredeclaredType::ModfResult { size, scalar }
1248                | &crate::PredeclaredType::FrexpResult { size, scalar } => {
1249                    let arg_type_name_owner;
1250                    let arg_type_name = if let Some(size) = size {
1251                        arg_type_name_owner = format!(
1252                            "{}{}",
1253                            if scalar.width == 8 { "double" } else { "float" },
1254                            size as u8
1255                        );
1256                        &arg_type_name_owner
1257                    } else if scalar.width == 8 {
1258                        "double"
1259                    } else {
1260                        "float"
1261                    };
1262
1263                    let (defined_func_name, called_func_name, second_field_name, sign_multiplier) =
1264                        if matches!(type_key, &crate::PredeclaredType::ModfResult { .. }) {
1265                            (super::writer::MODF_FUNCTION, "modf", "whole", "")
1266                        } else {
1267                            (
1268                                super::writer::FREXP_FUNCTION,
1269                                "frexp",
1270                                "exp_",
1271                                "sign(arg) * ",
1272                            )
1273                        };
1274
1275                    let struct_name = &self.names[&NameKey::Type(*struct_ty)];
1276
1277                    writeln!(
1278                        self.out,
1279                        "{struct_name} {defined_func_name}({arg_type_name} arg) {{
1280    {arg_type_name} other;
1281    {struct_name} result;
1282    result.fract = {sign_multiplier}{called_func_name}(arg, other);
1283    result.{second_field_name} = other;
1284    return result;
1285}}"
1286                    )?;
1287                    writeln!(self.out)?;
1288                }
1289                &crate::PredeclaredType::AtomicCompareExchangeWeakResult { .. } => {}
1290            }
1291        }
1292        if module.special_types.ray_desc.is_some() {
1293            self.write_ray_desc_from_ray_desc_constructor_function(module)?;
1294        }
1295
1296        Ok(())
1297    }
1298
1299    /// Helper function that writes wrapped functions for expressions in a function
1300    pub(super) fn write_wrapped_expression_functions(
1301        &mut self,
1302        module: &crate::Module,
1303        expressions: &crate::Arena<crate::Expression>,
1304        context: Option<&FunctionCtx>,
1305    ) -> BackendResult {
1306        for (handle, _) in expressions.iter() {
1307            match expressions[handle] {
1308                crate::Expression::Compose { ty, .. } => {
1309                    match module.types[ty].inner {
1310                        crate::TypeInner::Struct { .. } | crate::TypeInner::Array { .. } => {
1311                            let constructor = WrappedConstructor { ty };
1312                            if self.wrapped.insert(WrappedType::Constructor(constructor)) {
1313                                self.write_wrapped_constructor_function(module, constructor)?;
1314                            }
1315                        }
1316                        _ => {}
1317                    };
1318                }
1319                crate::Expression::ImageLoad { image, .. } => {
1320                    // This can only happen in a function as this is not a valid const expression
1321                    match *context.as_ref().unwrap().resolve_type(image, &module.types) {
1322                        crate::TypeInner::Image {
1323                            class: crate::ImageClass::Storage { format, .. },
1324                            ..
1325                        } => {
1326                            if format.single_component() {
1327                                let scalar: crate::Scalar = format.into();
1328                                if self.wrapped.insert(WrappedType::ImageLoadScalar(scalar)) {
1329                                    self.write_loaded_scalar_to_storage_loaded_value(scalar)?;
1330                                }
1331                            }
1332                        }
1333                        _ => {}
1334                    }
1335                }
1336                crate::Expression::RayQueryGetIntersection { committed, .. } => {
1337                    if committed {
1338                        if !self.written_committed_intersection {
1339                            self.write_committed_intersection_function(module)?;
1340                            self.written_committed_intersection = true;
1341                        }
1342                    } else if !self.written_candidate_intersection {
1343                        self.write_candidate_intersection_function(module)?;
1344                        self.written_candidate_intersection = true;
1345                    }
1346                }
1347                _ => {}
1348            }
1349        }
1350        Ok(())
1351    }
1352
1353    // TODO: we could merge this with iteration in write_wrapped_expression_functions...
1354    //
1355    /// Helper function that writes zero value wrapped functions
1356    pub(super) fn write_wrapped_zero_value_functions(
1357        &mut self,
1358        module: &crate::Module,
1359        expressions: &crate::Arena<crate::Expression>,
1360    ) -> BackendResult {
1361        for (handle, _) in expressions.iter() {
1362            if let crate::Expression::ZeroValue(ty) = expressions[handle] {
1363                let zero_value = WrappedZeroValue { ty };
1364                if self.wrapped.insert(WrappedType::ZeroValue(zero_value)) {
1365                    self.write_wrapped_zero_value_function(module, zero_value)?;
1366                }
1367            }
1368        }
1369        Ok(())
1370    }
1371
1372    pub(super) fn write_wrapped_math_functions(
1373        &mut self,
1374        module: &crate::Module,
1375        func_ctx: &FunctionCtx,
1376    ) -> BackendResult {
1377        for (_, expression) in func_ctx.expressions.iter() {
1378            if let crate::Expression::Math {
1379                fun,
1380                arg,
1381                arg1: _arg1,
1382                arg2: _arg2,
1383                arg3: _arg3,
1384            } = *expression
1385            {
1386                let arg_ty = func_ctx.resolve_type(arg, &module.types);
1387
1388                match fun {
1389                    crate::MathFunction::ExtractBits => {
1390                        // The behavior of our extractBits polyfill is undefined if offset + count > bit_width. We need
1391                        // to first sanitize the offset and count first. If we don't do this, we will get out-of-spec
1392                        // values if the extracted range is not within the bit width.
1393                        //
1394                        // This encodes the exact formula specified by the wgsl spec:
1395                        // https://gpuweb.github.io/gpuweb/wgsl/#extractBits-unsigned-builtin
1396                        //
1397                        // w = sizeof(x) * 8
1398                        // o = min(offset, w)
1399                        // c = min(count, w - o)
1400                        //
1401                        // bitfieldExtract(x, o, c)
1402                        let scalar = arg_ty.scalar().unwrap();
1403                        let components = arg_ty.components();
1404
1405                        let wrapped = WrappedMath {
1406                            fun,
1407                            scalar,
1408                            components,
1409                        };
1410
1411                        if !self.wrapped.insert(WrappedType::Math(wrapped)) {
1412                            continue;
1413                        }
1414
1415                        // Write return type
1416                        self.write_value_type(module, arg_ty)?;
1417
1418                        let scalar_width: u8 = scalar.width * 8;
1419
1420                        // Write function name and parameters
1421                        writeln!(self.out, " {EXTRACT_BITS_FUNCTION}(")?;
1422                        write!(self.out, "    ")?;
1423                        self.write_value_type(module, arg_ty)?;
1424                        writeln!(self.out, " e,")?;
1425                        writeln!(self.out, "    uint offset,")?;
1426                        writeln!(self.out, "    uint count")?;
1427                        writeln!(self.out, ") {{")?;
1428
1429                        // Write function body
1430                        writeln!(self.out, "    uint w = {scalar_width};")?;
1431                        writeln!(self.out, "    uint o = min(offset, w);")?;
1432                        writeln!(self.out, "    uint c = min(count, w - o);")?;
1433                        writeln!(
1434                            self.out,
1435                            "    return (c == 0 ? 0 : (e << (w - c - o)) >> (w - c));"
1436                        )?;
1437
1438                        // End of function body
1439                        writeln!(self.out, "}}")?;
1440                    }
1441                    crate::MathFunction::InsertBits => {
1442                        // The behavior of our insertBits polyfill has the same constraints as the extractBits polyfill.
1443
1444                        let scalar = arg_ty.scalar().unwrap();
1445                        let components = arg_ty.components();
1446
1447                        let wrapped = WrappedMath {
1448                            fun,
1449                            scalar,
1450                            components,
1451                        };
1452
1453                        if !self.wrapped.insert(WrappedType::Math(wrapped)) {
1454                            continue;
1455                        }
1456
1457                        // Write return type
1458                        self.write_value_type(module, arg_ty)?;
1459
1460                        let scalar_width: u8 = scalar.width * 8;
1461                        let scalar_max: u64 = match scalar.width {
1462                            1 => 0xFF,
1463                            2 => 0xFFFF,
1464                            4 => 0xFFFFFFFF,
1465                            8 => 0xFFFFFFFFFFFFFFFF,
1466                            _ => unreachable!(),
1467                        };
1468
1469                        // Write function name and parameters
1470                        writeln!(self.out, " {INSERT_BITS_FUNCTION}(")?;
1471                        write!(self.out, "    ")?;
1472                        self.write_value_type(module, arg_ty)?;
1473                        writeln!(self.out, " e,")?;
1474                        write!(self.out, "    ")?;
1475                        self.write_value_type(module, arg_ty)?;
1476                        writeln!(self.out, " newbits,")?;
1477                        writeln!(self.out, "    uint offset,")?;
1478                        writeln!(self.out, "    uint count")?;
1479                        writeln!(self.out, ") {{")?;
1480
1481                        // Write function body
1482                        writeln!(self.out, "    uint w = {scalar_width}u;")?;
1483                        writeln!(self.out, "    uint o = min(offset, w);")?;
1484                        writeln!(self.out, "    uint c = min(count, w - o);")?;
1485
1486                        // The `u` suffix on the literals is _extremely_ important. Otherwise it will use
1487                        // i32 shifting instead of the intended u32 shifting.
1488                        writeln!(
1489                            self.out,
1490                            "    uint mask = (({scalar_max}u >> ({scalar_width}u - c)) << o);"
1491                        )?;
1492                        writeln!(
1493                            self.out,
1494                            "    return (c == 0 ? e : ((e & ~mask) | ((newbits << o) & mask)));"
1495                        )?;
1496
1497                        // End of function body
1498                        writeln!(self.out, "}}")?;
1499                    }
1500                    // Taking the absolute value of the minimum value of a two's
1501                    // complement signed integer type causes overflow, which is
1502                    // undefined behaviour in HLSL. To avoid this, when the value is
1503                    // negative we bitcast the value to unsigned and negate it, then
1504                    // bitcast back to signed.
1505                    // This adheres to the WGSL spec in that the absolute of the type's
1506                    // minimum value should equal to the minimum value.
1507                    //
1508                    // TODO(#7109): asint()/asuint() only support 32-bit integers, so we
1509                    // must find another solution for different bit-widths.
1510                    crate::MathFunction::Abs
1511                        if matches!(arg_ty.scalar(), Some(crate::Scalar::I32)) =>
1512                    {
1513                        let scalar = arg_ty.scalar().unwrap();
1514                        let components = arg_ty.components();
1515
1516                        let wrapped = WrappedMath {
1517                            fun,
1518                            scalar,
1519                            components,
1520                        };
1521
1522                        if !self.wrapped.insert(WrappedType::Math(wrapped)) {
1523                            continue;
1524                        }
1525
1526                        self.write_value_type(module, arg_ty)?;
1527                        write!(self.out, " {ABS_FUNCTION}(")?;
1528                        self.write_value_type(module, arg_ty)?;
1529                        writeln!(self.out, " val) {{")?;
1530
1531                        let level = crate::back::Level(1);
1532                        writeln!(
1533                            self.out,
1534                            "{level}return val >= 0 ? val : asint(-asuint(val));"
1535                        )?;
1536                        writeln!(self.out, "}}")?;
1537                        writeln!(self.out)?;
1538                    }
1539                    _ => {}
1540                }
1541            }
1542        }
1543
1544        Ok(())
1545    }
1546
1547    pub(super) fn write_wrapped_unary_ops(
1548        &mut self,
1549        module: &crate::Module,
1550        func_ctx: &FunctionCtx,
1551    ) -> BackendResult {
1552        for (_, expression) in func_ctx.expressions.iter() {
1553            if let crate::Expression::Unary { op, expr } = *expression {
1554                let expr_ty = func_ctx.resolve_type(expr, &module.types);
1555                let Some((vector_size, scalar)) = expr_ty.vector_size_and_scalar() else {
1556                    continue;
1557                };
1558                let wrapped = WrappedUnaryOp {
1559                    op,
1560                    ty: (vector_size, scalar),
1561                };
1562
1563                // Negating the minimum value of a two's complement signed integer type
1564                // causes overflow, which is undefined behaviour in HLSL. To avoid this
1565                // we bitcast the value to unsigned and negate it, then bitcast back to
1566                // signed. This adheres to the WGSL spec in that the negative of the
1567                // type's minimum value should equal to the minimum value.
1568                //
1569                // TODO(#7109): asint()/asuint() only support 32-bit integers, so we must
1570                // find another solution for different bit-widths.
1571                match (op, scalar) {
1572                    (crate::UnaryOperator::Negate, crate::Scalar::I32) => {
1573                        if !self.wrapped.insert(WrappedType::UnaryOp(wrapped)) {
1574                            continue;
1575                        }
1576
1577                        self.write_value_type(module, expr_ty)?;
1578                        write!(self.out, " {NEG_FUNCTION}(")?;
1579                        self.write_value_type(module, expr_ty)?;
1580                        writeln!(self.out, " val) {{")?;
1581
1582                        let level = crate::back::Level(1);
1583                        writeln!(self.out, "{level}return asint(-asuint(val));",)?;
1584                        writeln!(self.out, "}}")?;
1585                        writeln!(self.out)?;
1586                    }
1587                    _ => {}
1588                }
1589            }
1590        }
1591
1592        Ok(())
1593    }
1594
1595    pub(super) fn write_wrapped_binary_ops(
1596        &mut self,
1597        module: &crate::Module,
1598        func_ctx: &FunctionCtx,
1599    ) -> BackendResult {
1600        for (expr_handle, expression) in func_ctx.expressions.iter() {
1601            if let crate::Expression::Binary { op, left, right } = *expression {
1602                let expr_ty = func_ctx.resolve_type(expr_handle, &module.types);
1603                let left_ty = func_ctx.resolve_type(left, &module.types);
1604                let right_ty = func_ctx.resolve_type(right, &module.types);
1605
1606                match (op, expr_ty.scalar()) {
1607                    // Signed integer division of the type's minimum representable value
1608                    // divided by -1, or signed or unsigned division by zero, is
1609                    // undefined behaviour in HLSL. We override the divisor to 1 in these
1610                    // cases.
1611                    // This adheres to the WGSL spec in that:
1612                    // * TYPE_MIN / -1 == TYPE_MIN
1613                    // * x / 0 == x
1614                    (
1615                        crate::BinaryOperator::Divide,
1616                        Some(
1617                            scalar @ crate::Scalar {
1618                                kind: ScalarKind::Sint | ScalarKind::Uint,
1619                                ..
1620                            },
1621                        ),
1622                    ) => {
1623                        let Some(left_wrapped_ty) = left_ty.vector_size_and_scalar() else {
1624                            continue;
1625                        };
1626                        let Some(right_wrapped_ty) = right_ty.vector_size_and_scalar() else {
1627                            continue;
1628                        };
1629                        let wrapped = WrappedBinaryOp {
1630                            op,
1631                            left_ty: left_wrapped_ty,
1632                            right_ty: right_wrapped_ty,
1633                        };
1634                        if !self.wrapped.insert(WrappedType::BinaryOp(wrapped)) {
1635                            continue;
1636                        }
1637
1638                        self.write_value_type(module, expr_ty)?;
1639                        write!(self.out, " {DIV_FUNCTION}(")?;
1640                        self.write_value_type(module, left_ty)?;
1641                        write!(self.out, " lhs, ")?;
1642                        self.write_value_type(module, right_ty)?;
1643                        writeln!(self.out, " rhs) {{")?;
1644                        let level = crate::back::Level(1);
1645                        match scalar.kind {
1646                            ScalarKind::Sint => {
1647                                let min_val = match scalar.width {
1648                                    2 => crate::Literal::I16(i16::MIN),
1649                                    4 => crate::Literal::I32(i32::MIN),
1650                                    8 => crate::Literal::I64(i64::MIN),
1651                                    _ => {
1652                                        return Err(super::Error::UnsupportedScalar(scalar));
1653                                    }
1654                                };
1655                                write!(self.out, "{level}return lhs / (((lhs == ")?;
1656                                self.write_literal(min_val)?;
1657                                writeln!(self.out, " & rhs == -1) | (rhs == 0)) ? 1 : rhs);")?
1658                            }
1659                            ScalarKind::Uint => {
1660                                writeln!(self.out, "{level}return lhs / (rhs == 0u ? 1u : rhs);")?
1661                            }
1662                            _ => unreachable!(),
1663                        }
1664                        writeln!(self.out, "}}")?;
1665                        writeln!(self.out)?;
1666                    }
1667                    // The modulus operator is only defined for integers in HLSL when
1668                    // either both sides are positive or both sides are negative. To
1669                    // avoid this undefined behaviour we use the following equation:
1670                    //
1671                    // dividend - (dividend / divisor) * divisor
1672                    //
1673                    // overriding the divisor to 1 if either it is 0, or it is -1
1674                    // and the dividend is the minimum representable value.
1675                    //
1676                    // This adheres to the WGSL spec in that:
1677                    // * min_value % -1 == 0
1678                    // * x % 0 == 0
1679                    (
1680                        crate::BinaryOperator::Modulo,
1681                        Some(
1682                            scalar @ crate::Scalar {
1683                                kind: ScalarKind::Sint | ScalarKind::Uint | ScalarKind::Float,
1684                                ..
1685                            },
1686                        ),
1687                    ) => {
1688                        let Some(left_wrapped_ty) = left_ty.vector_size_and_scalar() else {
1689                            continue;
1690                        };
1691                        let Some(right_wrapped_ty) = right_ty.vector_size_and_scalar() else {
1692                            continue;
1693                        };
1694                        let wrapped = WrappedBinaryOp {
1695                            op,
1696                            left_ty: left_wrapped_ty,
1697                            right_ty: right_wrapped_ty,
1698                        };
1699                        if !self.wrapped.insert(WrappedType::BinaryOp(wrapped)) {
1700                            continue;
1701                        }
1702
1703                        self.write_value_type(module, expr_ty)?;
1704                        write!(self.out, " {MOD_FUNCTION}(")?;
1705                        self.write_value_type(module, left_ty)?;
1706                        write!(self.out, " lhs, ")?;
1707                        self.write_value_type(module, right_ty)?;
1708                        writeln!(self.out, " rhs) {{")?;
1709                        let level = crate::back::Level(1);
1710                        match scalar.kind {
1711                            ScalarKind::Sint => {
1712                                let min_val = match scalar.width {
1713                                    2 => crate::Literal::I16(i16::MIN),
1714                                    4 => crate::Literal::I32(i32::MIN),
1715                                    8 => crate::Literal::I64(i64::MIN),
1716                                    _ => {
1717                                        return Err(super::Error::UnsupportedScalar(scalar));
1718                                    }
1719                                };
1720                                write!(self.out, "{level}")?;
1721                                self.write_value_type(module, right_ty)?;
1722                                write!(self.out, " divisor = ((lhs == ")?;
1723                                self.write_literal(min_val)?;
1724                                writeln!(self.out, " & rhs == -1) | (rhs == 0)) ? 1 : rhs;")?;
1725                                writeln!(
1726                                    self.out,
1727                                    "{level}return lhs - (lhs / divisor) * divisor;"
1728                                )?
1729                            }
1730                            ScalarKind::Uint => {
1731                                writeln!(self.out, "{level}return lhs % (rhs == 0u ? 1u : rhs);")?
1732                            }
1733                            // HLSL's fmod has the same definition as WGSL's % operator but due
1734                            // to its implementation in DXC it is not as accurate as the WGSL spec
1735                            // requires it to be. See:
1736                            // - https://shader-playground.timjones.io/0c8572816dbb6fc4435cc5d016a978a7
1737                            // - https://github.com/llvm/llvm-project/blob/50f9b8acafdca48e87e6b8e393c1f116a2d193ee/clang/lib/Headers/hlsl/hlsl_intrinsic_helpers.h#L78-L81
1738                            ScalarKind::Float => {
1739                                writeln!(self.out, "{level}return lhs - rhs * trunc(lhs / rhs);")?
1740                            }
1741                            _ => unreachable!(),
1742                        }
1743                        writeln!(self.out, "}}")?;
1744                        writeln!(self.out)?;
1745                    }
1746                    _ => {}
1747                }
1748            }
1749        }
1750
1751        Ok(())
1752    }
1753
1754    fn write_wrapped_cast_functions(
1755        &mut self,
1756        module: &crate::Module,
1757        func_ctx: &FunctionCtx,
1758    ) -> BackendResult {
1759        for (_, expression) in func_ctx.expressions.iter() {
1760            if let crate::Expression::As {
1761                expr,
1762                kind,
1763                convert: Some(width),
1764            } = *expression
1765            {
1766                // Avoid undefined behaviour when casting from a float to integer
1767                // when the value is out of range for the target type. Additionally
1768                // ensure we clamp to the correct value as per the WGSL spec.
1769                //
1770                // https://www.w3.org/TR/WGSL/#floating-point-conversion:
1771                // * If X is exactly representable in the target type T, then the
1772                //   result is that value.
1773                // * Otherwise, the result is the value in T closest to
1774                //   truncate(X) and also exactly representable in the original
1775                //   floating point type.
1776                let src_ty = func_ctx.resolve_type(expr, &module.types);
1777                let Some((vector_size, src_scalar)) = src_ty.vector_size_and_scalar() else {
1778                    continue;
1779                };
1780                let dst_scalar = crate::Scalar { kind, width };
1781                if src_scalar.kind != ScalarKind::Float
1782                    || (dst_scalar.kind != ScalarKind::Sint && dst_scalar.kind != ScalarKind::Uint)
1783                {
1784                    continue;
1785                }
1786
1787                let wrapped = WrappedCast {
1788                    src_scalar,
1789                    vector_size,
1790                    dst_scalar,
1791                };
1792                if !self.wrapped.insert(WrappedType::Cast(wrapped)) {
1793                    continue;
1794                }
1795
1796                let (src_ty, dst_ty) = match vector_size {
1797                    None => (
1798                        crate::TypeInner::Scalar(src_scalar),
1799                        crate::TypeInner::Scalar(dst_scalar),
1800                    ),
1801                    Some(vector_size) => (
1802                        crate::TypeInner::Vector {
1803                            scalar: src_scalar,
1804                            size: vector_size,
1805                        },
1806                        crate::TypeInner::Vector {
1807                            scalar: dst_scalar,
1808                            size: vector_size,
1809                        },
1810                    ),
1811                };
1812                let (min, max) =
1813                    crate::proc::min_max_float_representable_by(src_scalar, dst_scalar);
1814                let cast_str = format!(
1815                    "{}{}",
1816                    dst_scalar.to_hlsl_str()?,
1817                    vector_size
1818                        .map(crate::common::vector_size_str)
1819                        .unwrap_or(""),
1820                );
1821                let fun_name = match dst_scalar {
1822                    crate::Scalar::I32 => F2I32_FUNCTION,
1823                    crate::Scalar::U32 => F2U32_FUNCTION,
1824                    crate::Scalar::I64 => F2I64_FUNCTION,
1825                    crate::Scalar::U64 => F2U64_FUNCTION,
1826                    _ => unreachable!(),
1827                };
1828                self.write_value_type(module, &dst_ty)?;
1829                write!(self.out, " {fun_name}(")?;
1830                self.write_value_type(module, &src_ty)?;
1831                writeln!(self.out, " value) {{")?;
1832                let level = crate::back::Level(1);
1833                write!(self.out, "{level}return {cast_str}(clamp(value, ")?;
1834                self.write_literal(min)?;
1835                write!(self.out, ", ")?;
1836                self.write_literal(max)?;
1837                writeln!(self.out, "));",)?;
1838                writeln!(self.out, "}}")?;
1839                writeln!(self.out)?;
1840            }
1841        }
1842        Ok(())
1843    }
1844
1845    /// Helper function that writes various wrapped functions
1846    pub(super) fn write_wrapped_functions(
1847        &mut self,
1848        module: &crate::Module,
1849        func_ctx: &FunctionCtx,
1850    ) -> BackendResult {
1851        self.write_wrapped_math_functions(module, func_ctx)?;
1852        self.write_wrapped_unary_ops(module, func_ctx)?;
1853        self.write_wrapped_binary_ops(module, func_ctx)?;
1854        self.write_wrapped_expression_functions(module, func_ctx.expressions, Some(func_ctx))?;
1855        self.write_wrapped_zero_value_functions(module, func_ctx.expressions)?;
1856        self.write_wrapped_cast_functions(module, func_ctx)?;
1857
1858        for (handle, _) in func_ctx.expressions.iter() {
1859            match func_ctx.expressions[handle] {
1860                crate::Expression::ArrayLength(expr) => {
1861                    let global_expr = match func_ctx.expressions[expr] {
1862                        crate::Expression::GlobalVariable(_) => expr,
1863                        crate::Expression::AccessIndex { base, index: _ } => base,
1864                        ref other => unreachable!("Array length of {:?}", other),
1865                    };
1866                    let global_var = match func_ctx.expressions[global_expr] {
1867                        crate::Expression::GlobalVariable(var_handle) => {
1868                            &module.global_variables[var_handle]
1869                        }
1870                        ref other => {
1871                            return Err(super::Error::Unimplemented(format!(
1872                                "Array length of base {other:?}"
1873                            )))
1874                        }
1875                    };
1876                    let storage_access = match global_var.space {
1877                        crate::AddressSpace::Storage { access } => access,
1878                        _ => crate::StorageAccess::default(),
1879                    };
1880                    let wal = WrappedArrayLength {
1881                        writable: storage_access.contains(crate::StorageAccess::STORE),
1882                    };
1883
1884                    if self.wrapped.insert(WrappedType::ArrayLength(wal)) {
1885                        self.write_wrapped_array_length_function(wal)?;
1886                    }
1887                }
1888                crate::Expression::ImageLoad { image, .. } => {
1889                    let class = match *func_ctx.resolve_type(image, &module.types) {
1890                        crate::TypeInner::Image { class, .. } => class,
1891                        _ => unreachable!(),
1892                    };
1893                    let wrapped = WrappedImageLoad { class };
1894                    if self.wrapped.insert(WrappedType::ImageLoad(wrapped)) {
1895                        self.write_wrapped_image_load_function(module, wrapped)?;
1896                    }
1897                }
1898                crate::Expression::ImageSample {
1899                    image,
1900                    clamp_to_edge,
1901                    ..
1902                } => {
1903                    let class = match *func_ctx.resolve_type(image, &module.types) {
1904                        crate::TypeInner::Image { class, .. } => class,
1905                        _ => unreachable!(),
1906                    };
1907                    let wrapped = WrappedImageSample {
1908                        class,
1909                        clamp_to_edge,
1910                    };
1911                    if self.wrapped.insert(WrappedType::ImageSample(wrapped)) {
1912                        self.write_wrapped_image_sample_function(module, wrapped)?;
1913                    }
1914                }
1915                crate::Expression::ImageQuery { image, query } => {
1916                    let wiq = match *func_ctx.resolve_type(image, &module.types) {
1917                        crate::TypeInner::Image {
1918                            dim,
1919                            arrayed,
1920                            class,
1921                        } => WrappedImageQuery {
1922                            dim,
1923                            arrayed,
1924                            class,
1925                            query: query.into(),
1926                        },
1927                        _ => unreachable!("we only query images"),
1928                    };
1929
1930                    if self.wrapped.insert(WrappedType::ImageQuery(wiq)) {
1931                        self.write_wrapped_image_query_function(module, wiq, handle, func_ctx)?;
1932                    }
1933                }
1934                // Write `WrappedConstructor` for structs that are loaded from `AddressSpace::Storage`
1935                // since they will later be used by the fn `write_storage_load`
1936                crate::Expression::Load { pointer } => {
1937                    let pointer_space = func_ctx
1938                        .resolve_type(pointer, &module.types)
1939                        .pointer_space();
1940
1941                    if let Some(crate::AddressSpace::Storage { .. }) = pointer_space {
1942                        if let Some(ty) = func_ctx.info[handle].ty.handle() {
1943                            write_wrapped_constructor(self, ty, module)?;
1944                        }
1945                    }
1946
1947                    fn write_wrapped_constructor<W: Write>(
1948                        writer: &mut super::Writer<'_, W>,
1949                        ty: Handle<crate::Type>,
1950                        module: &crate::Module,
1951                    ) -> BackendResult {
1952                        match module.types[ty].inner {
1953                            crate::TypeInner::Struct { ref members, .. } => {
1954                                for member in members {
1955                                    write_wrapped_constructor(writer, member.ty, module)?;
1956                                }
1957
1958                                let constructor = WrappedConstructor { ty };
1959                                if writer.wrapped.insert(WrappedType::Constructor(constructor)) {
1960                                    writer
1961                                        .write_wrapped_constructor_function(module, constructor)?;
1962                                }
1963                            }
1964                            crate::TypeInner::Array { base, .. } => {
1965                                write_wrapped_constructor(writer, base, module)?;
1966
1967                                let constructor = WrappedConstructor { ty };
1968                                if writer.wrapped.insert(WrappedType::Constructor(constructor)) {
1969                                    writer
1970                                        .write_wrapped_constructor_function(module, constructor)?;
1971                                }
1972                            }
1973                            _ => {}
1974                        };
1975
1976                        Ok(())
1977                    }
1978                }
1979                // We treat matrices of the form `matCx2` as a sequence of C `vec2`s
1980                // (see top level module docs for details).
1981                //
1982                // The functions injected here are required to get the matrix accesses working.
1983                crate::Expression::AccessIndex { base, index } => {
1984                    let base_ty_res = &func_ctx.info[base].ty;
1985                    let mut resolved = base_ty_res.inner_with(&module.types);
1986                    let base_ty_handle = match *resolved {
1987                        crate::TypeInner::Pointer { base, .. } => {
1988                            resolved = &module.types[base].inner;
1989                            Some(base)
1990                        }
1991                        _ => base_ty_res.handle(),
1992                    };
1993                    if let crate::TypeInner::Struct { ref members, .. } = *resolved {
1994                        let member = &members[index as usize];
1995
1996                        match module.types[member.ty].inner {
1997                            crate::TypeInner::Matrix {
1998                                rows: crate::VectorSize::Bi,
1999                                ..
2000                            } if member.binding.is_none() => {
2001                                let ty = base_ty_handle.unwrap();
2002                                let access = WrappedStructMatrixAccess { ty, index };
2003
2004                                if self.wrapped.insert(WrappedType::StructMatrixAccess(access)) {
2005                                    self.write_wrapped_struct_matrix_get_function(module, access)?;
2006                                    self.write_wrapped_struct_matrix_set_function(module, access)?;
2007                                    self.write_wrapped_struct_matrix_set_vec_function(
2008                                        module, access,
2009                                    )?;
2010                                    self.write_wrapped_struct_matrix_set_scalar_function(
2011                                        module, access,
2012                                    )?;
2013                                }
2014                            }
2015                            _ => {}
2016                        }
2017                    }
2018                }
2019                _ => {}
2020            };
2021        }
2022
2023        Ok(())
2024    }
2025
2026    /// Writes out the sampler heap declarations if they haven't been written yet.
2027    pub(super) fn write_sampler_heaps(&mut self) -> BackendResult {
2028        if self.wrapped.sampler_heaps {
2029            return Ok(());
2030        }
2031
2032        writeln!(
2033            self.out,
2034            "SamplerState {}[2048]: register(s{}, space{});",
2035            super::writer::SAMPLER_HEAP_VAR,
2036            self.options.sampler_heap_target.standard_samplers.register,
2037            self.options.sampler_heap_target.standard_samplers.space
2038        )?;
2039        writeln!(
2040            self.out,
2041            "SamplerComparisonState {}[2048]: register(s{}, space{});",
2042            super::writer::COMPARISON_SAMPLER_HEAP_VAR,
2043            self.options
2044                .sampler_heap_target
2045                .comparison_samplers
2046                .register,
2047            self.options.sampler_heap_target.comparison_samplers.space
2048        )?;
2049
2050        self.wrapped.sampler_heaps = true;
2051
2052        Ok(())
2053    }
2054
2055    /// Writes out the sampler index buffer declaration if it hasn't been written yet.
2056    pub(super) fn write_wrapped_sampler_buffer(
2057        &mut self,
2058        key: super::SamplerIndexBufferKey,
2059    ) -> BackendResult {
2060        // The astute will notice that we do a double hash lookup, but we do this to avoid
2061        // holding a mutable reference to `self` while trying to call `write_sampler_heaps`.
2062        //
2063        // We only pay this double lookup cost when we actually need to write out the sampler
2064        // buffer, which should be not be common.
2065
2066        if self.wrapped.sampler_index_buffers.contains_key(&key) {
2067            return Ok(());
2068        };
2069
2070        self.write_sampler_heaps()?;
2071
2072        // Because the group number can be arbitrary, we use the namer to generate a unique name
2073        // instead of adding it to the reserved name list.
2074        let sampler_array_name = self
2075            .namer
2076            .call(&format!("nagaGroup{}SamplerIndexArray", key.group));
2077
2078        let bind_target = match self.options.sampler_buffer_binding_map.get(&key) {
2079            Some(&bind_target) => bind_target,
2080            None if self.options.fake_missing_bindings => super::BindTarget {
2081                space: u8::MAX,
2082                register: key.group,
2083                binding_array_size: None,
2084                dynamic_storage_buffer_offsets_index: None,
2085                restrict_indexing: false,
2086            },
2087            None => {
2088                unreachable!("Sampler buffer of group {key:?} not bound to a register");
2089            }
2090        };
2091
2092        writeln!(
2093            self.out,
2094            "StructuredBuffer<uint> {sampler_array_name} : register(t{}, space{});",
2095            bind_target.register, bind_target.space
2096        )?;
2097
2098        self.wrapped
2099            .sampler_index_buffers
2100            .insert(key, sampler_array_name);
2101
2102        Ok(())
2103    }
2104
2105    pub(super) fn write_texture_coordinates(
2106        &mut self,
2107        kind: &str,
2108        coordinate: Handle<crate::Expression>,
2109        array_index: Option<Handle<crate::Expression>>,
2110        mip_level: Option<Handle<crate::Expression>>,
2111        module: &crate::Module,
2112        func_ctx: &FunctionCtx,
2113    ) -> BackendResult {
2114        // HLSL expects the array index to be merged with the coordinate
2115        let extra = array_index.is_some() as usize + (mip_level.is_some()) as usize;
2116        if extra == 0 {
2117            self.write_expr(module, coordinate, func_ctx)?;
2118        } else {
2119            let num_coords = match *func_ctx.resolve_type(coordinate, &module.types) {
2120                crate::TypeInner::Scalar { .. } => 1,
2121                crate::TypeInner::Vector { size, .. } => size as usize,
2122                _ => unreachable!(),
2123            };
2124            write!(self.out, "{}{}(", kind, num_coords + extra)?;
2125            self.write_expr(module, coordinate, func_ctx)?;
2126            if let Some(expr) = array_index {
2127                write!(self.out, ", ")?;
2128                self.write_expr(module, expr, func_ctx)?;
2129            }
2130            if let Some(expr) = mip_level {
2131                // Explicit cast if needed
2132                let cast_to_int = matches!(
2133                    *func_ctx.resolve_type(expr, &module.types),
2134                    crate::TypeInner::Scalar(crate::Scalar {
2135                        kind: ScalarKind::Uint,
2136                        ..
2137                    })
2138                );
2139
2140                write!(self.out, ", ")?;
2141
2142                if cast_to_int {
2143                    write!(self.out, "int(")?;
2144                }
2145
2146                self.write_expr(module, expr, func_ctx)?;
2147
2148                if cast_to_int {
2149                    write!(self.out, ")")?;
2150                }
2151            }
2152            write!(self.out, ")")?;
2153        }
2154        Ok(())
2155    }
2156
2157    pub(super) fn write_mat_cx2_typedef_and_functions(
2158        &mut self,
2159        WrappedMatCx2 { columns, width }: WrappedMatCx2,
2160    ) -> BackendResult {
2161        use crate::back::INDENT;
2162
2163        let bit_width = width * 8;
2164        let type_name = crate::Scalar {
2165            kind: ScalarKind::Float,
2166            width,
2167        }
2168        .to_hlsl_str()?;
2169
2170        // typedef
2171        write!(self.out, "typedef struct {{ ")?;
2172        for i in 0..columns as u8 {
2173            write!(self.out, "{type_name}2 _{i}; ")?;
2174        }
2175        writeln!(self.out, "}} __mat{}x2_f{bit_width};", columns as u8)?;
2176
2177        // __get_col_of_mat
2178        writeln!(
2179            self.out,
2180            "{type_name}2 __get_col_of_mat{}x2_f{bit_width}(__mat{}x2_f{bit_width} mat, uint idx) {{",
2181            columns as u8, columns as u8
2182        )?;
2183        writeln!(self.out, "{INDENT}switch(idx) {{")?;
2184        for i in 0..columns as u8 {
2185            writeln!(self.out, "{INDENT}case {i}: {{ return mat._{i}; }}")?;
2186        }
2187        writeln!(self.out, "{INDENT}default: {{ return ({type_name}2)0; }}")?;
2188        writeln!(self.out, "{INDENT}}}")?;
2189        writeln!(self.out, "}}")?;
2190
2191        // __set_col_of_mat
2192        writeln!(
2193            self.out,
2194            "void __set_col_of_mat{}x2_f{bit_width}(__mat{}x2_f{bit_width} mat, uint idx, {type_name}2 value) {{",
2195            columns as u8, columns as u8
2196        )?;
2197        writeln!(self.out, "{INDENT}switch(idx) {{")?;
2198        for i in 0..columns as u8 {
2199            writeln!(self.out, "{INDENT}case {i}: {{ mat._{i} = value; break; }}")?;
2200        }
2201        writeln!(self.out, "{INDENT}}}")?;
2202        writeln!(self.out, "}}")?;
2203
2204        // __set_el_of_mat
2205        writeln!(
2206            self.out,
2207            "void __set_el_of_mat{}x2_f{bit_width}(__mat{}x2_f{bit_width} mat, uint idx, uint vec_idx, {type_name} value) {{",
2208            columns as u8, columns as u8
2209        )?;
2210        writeln!(self.out, "{INDENT}switch(idx) {{")?;
2211        for i in 0..columns as u8 {
2212            writeln!(
2213                self.out,
2214                "{INDENT}case {i}: {{ mat._{i}[vec_idx] = value; break; }}"
2215            )?;
2216        }
2217        writeln!(self.out, "{INDENT}}}")?;
2218        writeln!(self.out, "}}")?;
2219
2220        writeln!(self.out)?;
2221
2222        Ok(())
2223    }
2224
2225    pub(super) fn write_all_mat_cx2_typedefs_and_functions(
2226        &mut self,
2227        module: &crate::Module,
2228    ) -> BackendResult {
2229        for (handle, _) in module.global_variables.iter() {
2230            let global = &module.global_variables[handle];
2231
2232            if global.space == crate::AddressSpace::Uniform {
2233                if let Some(super::writer::MatrixType {
2234                    columns,
2235                    rows: crate::VectorSize::Bi,
2236                    width,
2237                }) = super::writer::get_inner_matrix_data(module, global.ty)
2238                {
2239                    let entry = WrappedMatCx2 { columns, width };
2240                    if self.wrapped.insert(WrappedType::MatCx2(entry)) {
2241                        self.write_mat_cx2_typedef_and_functions(entry)?;
2242                    }
2243                }
2244            }
2245        }
2246
2247        for (_, ty) in module.types.iter() {
2248            if let crate::TypeInner::Struct { ref members, .. } = ty.inner {
2249                for member in members.iter() {
2250                    if let crate::TypeInner::Array { .. } = module.types[member.ty].inner {
2251                        if let Some(super::writer::MatrixType {
2252                            columns,
2253                            rows: crate::VectorSize::Bi,
2254                            width,
2255                        }) = super::writer::get_inner_matrix_data(module, member.ty)
2256                        {
2257                            let entry = WrappedMatCx2 { columns, width };
2258                            if self.wrapped.insert(WrappedType::MatCx2(entry)) {
2259                                self.write_mat_cx2_typedef_and_functions(entry)?;
2260                            }
2261                        }
2262                    }
2263                }
2264            }
2265        }
2266
2267        Ok(())
2268    }
2269
2270    pub(super) fn write_wrapped_zero_value_function_name(
2271        &mut self,
2272        module: &crate::Module,
2273        zero_value: WrappedZeroValue,
2274    ) -> BackendResult {
2275        let name = crate::TypeInner::hlsl_type_id(zero_value.ty, module.to_ctx(), &self.names)?;
2276        write!(self.out, "ZeroValue{name}")?;
2277        Ok(())
2278    }
2279
2280    /// Helper function that write wrapped function for `Expression::ZeroValue`
2281    ///
2282    /// This is necessary since we might have a member access after the zero value expression, e.g.
2283    /// `.y` (in practice this can come up when consuming SPIRV that's been produced by glslc).
2284    ///
2285    /// So we can't just write `(float4)0` since `(float4)0.y` won't parse correctly.
2286    ///
2287    /// Parenthesizing the expression like `((float4)0).y` would work... except DXC can't handle
2288    /// cases like:
2289    ///
2290    /// ```text
2291    /// tests\out\hlsl\access.hlsl:183:41: error: cannot compile this l-value expression yet
2292    ///     t_1.am = (__mat4x2_f32[2])((float4x2[2])0);
2293    ///                                         ^
2294    /// ```
2295    fn write_wrapped_zero_value_function(
2296        &mut self,
2297        module: &crate::Module,
2298        zero_value: WrappedZeroValue,
2299    ) -> BackendResult {
2300        use crate::back::INDENT;
2301
2302        // Write function return type and name
2303        if let crate::TypeInner::Array { base, size, .. } = module.types[zero_value.ty].inner {
2304            write!(self.out, "typedef ")?;
2305            self.write_type(module, zero_value.ty)?;
2306            write!(self.out, " ret_")?;
2307            self.write_wrapped_zero_value_function_name(module, zero_value)?;
2308            self.write_array_size(module, base, size)?;
2309            writeln!(self.out, ";")?;
2310
2311            write!(self.out, "ret_")?;
2312            self.write_wrapped_zero_value_function_name(module, zero_value)?;
2313        } else {
2314            self.write_type(module, zero_value.ty)?;
2315        }
2316        write!(self.out, " ")?;
2317        self.write_wrapped_zero_value_function_name(module, zero_value)?;
2318
2319        // Write function parameters (none) and start function body
2320        writeln!(self.out, "() {{")?;
2321
2322        // Write `ZeroValue` function.
2323        write!(self.out, "{INDENT}return ")?;
2324        self.write_default_init(module, zero_value.ty)?;
2325        writeln!(self.out, ";")?;
2326
2327        // End of function body
2328        writeln!(self.out, "}}")?;
2329        // Write extra new line
2330        writeln!(self.out)?;
2331
2332        Ok(())
2333    }
2334}
2335
2336impl crate::StorageFormat {
2337    /// Returns `true` if there is just one component, otherwise `false`
2338    pub(super) const fn single_component(&self) -> bool {
2339        match *self {
2340            crate::StorageFormat::R16Float
2341            | crate::StorageFormat::R32Float
2342            | crate::StorageFormat::R8Unorm
2343            | crate::StorageFormat::R16Unorm
2344            | crate::StorageFormat::R8Snorm
2345            | crate::StorageFormat::R16Snorm
2346            | crate::StorageFormat::R8Uint
2347            | crate::StorageFormat::R16Uint
2348            | crate::StorageFormat::R32Uint
2349            | crate::StorageFormat::R8Sint
2350            | crate::StorageFormat::R16Sint
2351            | crate::StorageFormat::R32Sint
2352            | crate::StorageFormat::R64Uint => true,
2353            _ => false,
2354        }
2355    }
2356}