Skip to main content

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