naga/valid/
type.rs

1use super::Capabilities;
2use crate::{arena::Handle, proc::Alignment};
3
4bitflags::bitflags! {
5    /// Flags associated with [`Type`]s by [`Validator`].
6    ///
7    /// [`Type`]: crate::Type
8    /// [`Validator`]: crate::valid::Validator
9    #[cfg_attr(feature = "serialize", derive(serde::Serialize))]
10    #[cfg_attr(feature = "deserialize", derive(serde::Deserialize))]
11    #[repr(transparent)]
12    #[derive(Clone, Copy, Debug, Eq, PartialEq)]
13    pub struct TypeFlags: u8 {
14        /// Can be used for data variables.
15        ///
16        /// This flag is required on types of local variables, function
17        /// arguments, array elements, and struct members.
18        ///
19        /// This includes all types except [`Image`], [`Sampler`],
20        /// and some [`Pointer`] types.
21        ///
22        /// [`Image`]: crate::TypeInner::Image
23        /// [`Sampler`]: crate::TypeInner::Sampler
24        /// [`Pointer`]: crate::TypeInner::Pointer
25        const DATA = 0x1;
26
27        /// The data type has a size known by pipeline creation time.
28        ///
29        /// Unsized types are quite restricted. The only unsized types permitted
30        /// by Naga, other than the non-[`DATA`] types like [`Image`] and
31        /// [`Sampler`], are dynamically-sized [`Array`]s, and [`Struct`]s whose
32        /// last members are such arrays. See the documentation for those types
33        /// for details.
34        ///
35        /// [`DATA`]: TypeFlags::DATA
36        /// [`Image`]: crate::TypeInner::Image
37        /// [`Sampler`]: crate::TypeInner::Sampler
38        /// [`Array`]: crate::TypeInner::Array
39        /// [`Struct`]: crate::TypeInner::Struct
40        const SIZED = 0x2;
41
42        /// The data can be copied around.
43        const COPY = 0x4;
44
45        /// Can be be used for user-defined IO between pipeline stages.
46        ///
47        /// This covers anything that can be in [`Location`] binding:
48        /// non-bool scalars and vectors, matrices, and structs and
49        /// arrays containing only interface types.
50        ///
51        /// [`Location`]: crate::Binding::Location
52        const IO_SHAREABLE = 0x8;
53
54        /// Can be used for host-shareable structures.
55        const HOST_SHAREABLE = 0x10;
56
57        /// The set of types with a fixed size at shader-creation time (ie. everything
58        /// except arrays sized by an override-expression)
59        const CREATION_RESOLVED = 0x20;
60
61        /// This type can be passed as a function argument.
62        const ARGUMENT = 0x40;
63
64        /// A WGSL [constructible] type.
65        ///
66        /// The constructible types are scalars, vectors, matrices, fixed-size
67        /// arrays of constructible types, and structs whose members are all
68        /// constructible.
69        ///
70        /// [constructible]: https://gpuweb.github.io/gpuweb/wgsl/#constructible
71        const CONSTRUCTIBLE = 0x80;
72    }
73}
74
75#[derive(Clone, Copy, Debug, thiserror::Error)]
76#[cfg_attr(test, derive(PartialEq))]
77pub enum Disalignment {
78    #[error("The array stride {stride} is not a multiple of the required alignment {alignment}")]
79    ArrayStride { stride: u32, alignment: Alignment },
80    #[error("The struct span {span}, is not a multiple of the required alignment {alignment}")]
81    StructSpan { span: u32, alignment: Alignment },
82    #[error("The struct member[{index}] offset {offset} is not a multiple of the required alignment {alignment}")]
83    MemberOffset {
84        index: u32,
85        offset: u32,
86        alignment: Alignment,
87    },
88    #[error("The struct member[{index}] offset {offset} must be at least {expected}")]
89    MemberOffsetAfterStruct {
90        index: u32,
91        offset: u32,
92        expected: u32,
93    },
94    #[error("The struct member[{index}] is not statically sized")]
95    UnsizedMember { index: u32 },
96    #[error("The type is not host-shareable")]
97    NonHostShareable,
98}
99
100#[derive(Clone, Debug, thiserror::Error)]
101#[cfg_attr(test, derive(PartialEq))]
102pub enum TypeError {
103    #[error("Capability {0:?} is required")]
104    MissingCapability(Capabilities),
105    #[error("The {0:?} scalar width {1} is not supported for an atomic")]
106    InvalidAtomicWidth(crate::ScalarKind, crate::Bytes),
107    #[error("Invalid type for pointer target {0:?}")]
108    InvalidPointerBase(Handle<crate::Type>),
109    #[error("Unsized types like {base:?} must be in the `Storage` address space, not `{space:?}`")]
110    InvalidPointerToUnsized {
111        base: Handle<crate::Type>,
112        space: crate::AddressSpace,
113    },
114    #[error("Expected data type, found {0:?}")]
115    InvalidData(Handle<crate::Type>),
116    #[error("Base type {0:?} for the array is invalid")]
117    InvalidArrayBaseType(Handle<crate::Type>),
118    #[error("Matrix elements must always be floating-point types")]
119    MatrixElementNotFloat,
120    #[error("The constant {0:?} is specialized, and cannot be used as an array size")]
121    UnsupportedSpecializedArrayLength(Handle<crate::Constant>),
122    #[error("{} of dimensionality {dim:?} and class {class:?} are not supported", if *.arrayed {"Arrayed images"} else {"Images"})]
123    UnsupportedImageType {
124        dim: crate::ImageDimension,
125        arrayed: bool,
126        class: crate::ImageClass,
127    },
128    #[error("Array stride {stride} does not match the expected {expected}")]
129    InvalidArrayStride { stride: u32, expected: u32 },
130    #[error("Field '{0}' can't be dynamically-sized, has type {1:?}")]
131    InvalidDynamicArray(String, Handle<crate::Type>),
132    #[error("The base handle {0:?} has to be a struct")]
133    BindingArrayBaseTypeNotStruct(Handle<crate::Type>),
134    #[error("Structure member[{index}] at {offset} overlaps the previous member")]
135    MemberOverlap { index: u32, offset: u32 },
136    #[error(
137        "Structure member[{index}] at {offset} and size {size} crosses the structure boundary of size {span}"
138    )]
139    MemberOutOfBounds {
140        index: u32,
141        offset: u32,
142        size: u32,
143        span: u32,
144    },
145    #[error("Structure types must have at least one member")]
146    EmptyStruct,
147    #[error(transparent)]
148    WidthError(#[from] WidthError),
149    #[error(
150        "The base handle {0:?} has an override-expression that didn't get resolved to a constant"
151    )]
152    UnresolvedOverride(Handle<crate::Type>),
153}
154
155#[derive(Clone, Debug, thiserror::Error)]
156#[cfg_attr(test, derive(PartialEq))]
157pub enum WidthError {
158    #[error("The {0:?} scalar width {1} is not supported")]
159    Invalid(crate::ScalarKind, crate::Bytes),
160    #[error("Using `{name}` values requires the `naga::valid::Capabilities::{flag}` flag")]
161    MissingCapability {
162        name: &'static str,
163        flag: &'static str,
164    },
165
166    #[error("Abstract types may only appear in constant expressions")]
167    Abstract,
168}
169
170// Only makes sense if `flags.contains(HOST_SHAREABLE)`
171type LayoutCompatibility = Result<Alignment, (Handle<crate::Type>, Disalignment)>;
172
173fn check_member_layout(
174    accum: &mut LayoutCompatibility,
175    member: &crate::StructMember,
176    member_index: u32,
177    member_layout: LayoutCompatibility,
178    parent_handle: Handle<crate::Type>,
179) {
180    *accum = match (*accum, member_layout) {
181        (Ok(cur_alignment), Ok(alignment)) => {
182            if alignment.is_aligned(member.offset) {
183                Ok(cur_alignment.max(alignment))
184            } else {
185                Err((
186                    parent_handle,
187                    Disalignment::MemberOffset {
188                        index: member_index,
189                        offset: member.offset,
190                        alignment,
191                    },
192                ))
193            }
194        }
195        (Err(e), _) | (_, Err(e)) => Err(e),
196    };
197}
198
199/// Determine whether a pointer in `space` can be passed as an argument.
200///
201/// If a pointer in `space` is permitted to be passed as an argument to a
202/// user-defined function, return `TypeFlags::ARGUMENT`. Otherwise, return
203/// `TypeFlags::empty()`.
204///
205/// Pointers passed as arguments to user-defined functions must be in the
206/// `Function` or `Private` address space.
207const fn ptr_space_argument_flag(space: crate::AddressSpace) -> TypeFlags {
208    use crate::AddressSpace as As;
209    match space {
210        As::Function | As::Private => TypeFlags::ARGUMENT,
211        As::Uniform | As::Storage { .. } | As::Handle | As::PushConstant | As::WorkGroup => {
212            TypeFlags::empty()
213        }
214    }
215}
216
217#[derive(Clone, Debug)]
218pub(super) struct TypeInfo {
219    pub flags: TypeFlags,
220    pub uniform_layout: LayoutCompatibility,
221    pub storage_layout: LayoutCompatibility,
222}
223
224impl TypeInfo {
225    const fn dummy() -> Self {
226        TypeInfo {
227            flags: TypeFlags::empty(),
228            uniform_layout: Ok(Alignment::ONE),
229            storage_layout: Ok(Alignment::ONE),
230        }
231    }
232
233    const fn new(flags: TypeFlags, alignment: Alignment) -> Self {
234        TypeInfo {
235            flags,
236            uniform_layout: Ok(alignment),
237            storage_layout: Ok(alignment),
238        }
239    }
240}
241
242impl super::Validator {
243    const fn require_type_capability(&self, capability: Capabilities) -> Result<(), TypeError> {
244        if self.capabilities.contains(capability) {
245            Ok(())
246        } else {
247            Err(TypeError::MissingCapability(capability))
248        }
249    }
250
251    pub(super) const fn check_width(&self, scalar: crate::Scalar) -> Result<(), WidthError> {
252        let good = match scalar.kind {
253            crate::ScalarKind::Bool => scalar.width == crate::BOOL_WIDTH,
254            crate::ScalarKind::Float => {
255                if scalar.width == 8 {
256                    if !self.capabilities.contains(Capabilities::FLOAT64) {
257                        return Err(WidthError::MissingCapability {
258                            name: "f64",
259                            flag: "FLOAT64",
260                        });
261                    }
262                    true
263                } else {
264                    scalar.width == 4
265                }
266            }
267            crate::ScalarKind::Sint => {
268                if scalar.width == 8 {
269                    if !self.capabilities.contains(Capabilities::SHADER_INT64) {
270                        return Err(WidthError::MissingCapability {
271                            name: "i64",
272                            flag: "SHADER_INT64",
273                        });
274                    }
275                    true
276                } else {
277                    scalar.width == 4
278                }
279            }
280            crate::ScalarKind::Uint => {
281                if scalar.width == 8 {
282                    if !self.capabilities.contains(Capabilities::SHADER_INT64) {
283                        return Err(WidthError::MissingCapability {
284                            name: "u64",
285                            flag: "SHADER_INT64",
286                        });
287                    }
288                    true
289                } else {
290                    scalar.width == 4
291                }
292            }
293            crate::ScalarKind::AbstractInt | crate::ScalarKind::AbstractFloat => {
294                return Err(WidthError::Abstract);
295            }
296        };
297        if good {
298            Ok(())
299        } else {
300            Err(WidthError::Invalid(scalar.kind, scalar.width))
301        }
302    }
303
304    pub(super) fn reset_types(&mut self, size: usize) {
305        self.types.clear();
306        self.types.resize(size, TypeInfo::dummy());
307        self.layouter.clear();
308    }
309
310    pub(super) fn validate_type(
311        &self,
312        handle: Handle<crate::Type>,
313        gctx: crate::proc::GlobalCtx,
314    ) -> Result<TypeInfo, TypeError> {
315        use crate::TypeInner as Ti;
316        Ok(match gctx.types[handle].inner {
317            Ti::Scalar(scalar) => {
318                self.check_width(scalar)?;
319                let shareable = if scalar.kind.is_numeric() {
320                    TypeFlags::IO_SHAREABLE | TypeFlags::HOST_SHAREABLE
321                } else {
322                    TypeFlags::empty()
323                };
324                TypeInfo::new(
325                    TypeFlags::DATA
326                        | TypeFlags::SIZED
327                        | TypeFlags::COPY
328                        | TypeFlags::ARGUMENT
329                        | TypeFlags::CONSTRUCTIBLE
330                        | TypeFlags::CREATION_RESOLVED
331                        | shareable,
332                    Alignment::from_width(scalar.width),
333                )
334            }
335            Ti::Vector { size, scalar } => {
336                self.check_width(scalar)?;
337                let shareable = if scalar.kind.is_numeric() {
338                    TypeFlags::IO_SHAREABLE | TypeFlags::HOST_SHAREABLE
339                } else {
340                    TypeFlags::empty()
341                };
342                TypeInfo::new(
343                    TypeFlags::DATA
344                        | TypeFlags::SIZED
345                        | TypeFlags::COPY
346                        | TypeFlags::ARGUMENT
347                        | TypeFlags::CONSTRUCTIBLE
348                        | TypeFlags::CREATION_RESOLVED
349                        | shareable,
350                    Alignment::from(size) * Alignment::from_width(scalar.width),
351                )
352            }
353            Ti::Matrix {
354                columns: _,
355                rows,
356                scalar,
357            } => {
358                if scalar.kind != crate::ScalarKind::Float {
359                    return Err(TypeError::MatrixElementNotFloat);
360                }
361                self.check_width(scalar)?;
362                TypeInfo::new(
363                    TypeFlags::DATA
364                        | TypeFlags::SIZED
365                        | TypeFlags::COPY
366                        | TypeFlags::HOST_SHAREABLE
367                        | TypeFlags::ARGUMENT
368                        | TypeFlags::CONSTRUCTIBLE
369                        | TypeFlags::CREATION_RESOLVED,
370                    Alignment::from(rows) * Alignment::from_width(scalar.width),
371                )
372            }
373            Ti::Atomic(scalar) => {
374                match scalar {
375                    crate::Scalar {
376                        kind: crate::ScalarKind::Sint | crate::ScalarKind::Uint,
377                        width: _,
378                    } => {
379                        if scalar.width == 8
380                            && !self.capabilities.intersects(
381                                Capabilities::SHADER_INT64_ATOMIC_ALL_OPS
382                                    | Capabilities::SHADER_INT64_ATOMIC_MIN_MAX,
383                            )
384                        {
385                            return Err(TypeError::MissingCapability(
386                                Capabilities::SHADER_INT64_ATOMIC_ALL_OPS,
387                            ));
388                        }
389                    }
390                    crate::Scalar::F32 => {
391                        if !self
392                            .capabilities
393                            .contains(Capabilities::SHADER_FLOAT32_ATOMIC)
394                        {
395                            return Err(TypeError::MissingCapability(
396                                Capabilities::SHADER_FLOAT32_ATOMIC,
397                            ));
398                        }
399                    }
400                    _ => return Err(TypeError::InvalidAtomicWidth(scalar.kind, scalar.width)),
401                };
402                TypeInfo::new(
403                    TypeFlags::DATA
404                        | TypeFlags::SIZED
405                        | TypeFlags::HOST_SHAREABLE
406                        | TypeFlags::CREATION_RESOLVED,
407                    Alignment::from_width(scalar.width),
408                )
409            }
410            Ti::Pointer { base, space } => {
411                use crate::AddressSpace as As;
412
413                let base_info = &self.types[base.index()];
414                if !base_info.flags.contains(TypeFlags::DATA) {
415                    return Err(TypeError::InvalidPointerBase(base));
416                }
417
418                // Runtime-sized values can only live in the `Storage` address
419                // space, so it's useless to have a pointer to such a type in
420                // any other space.
421                //
422                // Detecting this problem here prevents the definition of
423                // functions like:
424                //
425                //     fn f(p: ptr<workgroup, UnsizedType>) -> ... { ... }
426                //
427                // which would otherwise be permitted, but uncallable. (They
428                // may also present difficulties in code generation).
429                if !base_info.flags.contains(TypeFlags::SIZED) {
430                    match space {
431                        As::Storage { .. } => {}
432                        _ => {
433                            return Err(TypeError::InvalidPointerToUnsized { base, space });
434                        }
435                    }
436                }
437
438                // `Validator::validate_function` actually checks the address
439                // space of pointer arguments explicitly before checking the
440                // `ARGUMENT` flag, to give better error messages. But it seems
441                // best to set `ARGUMENT` accurately anyway.
442                let argument_flag = ptr_space_argument_flag(space);
443
444                // Pointers cannot be stored in variables, structure members, or
445                // array elements, so we do not mark them as `DATA`.
446                TypeInfo::new(
447                    argument_flag
448                        | TypeFlags::SIZED
449                        | TypeFlags::COPY
450                        | TypeFlags::CREATION_RESOLVED,
451                    Alignment::ONE,
452                )
453            }
454            Ti::ValuePointer {
455                size: _,
456                scalar,
457                space,
458            } => {
459                // ValuePointer should be treated the same way as the equivalent
460                // Pointer / Scalar / Vector combination, so each step in those
461                // variants' match arms should have a counterpart here.
462                //
463                // However, some cases are trivial: All our implicit base types
464                // are DATA and SIZED, so we can never return
465                // `InvalidPointerBase` or `InvalidPointerToUnsized`.
466                self.check_width(scalar)?;
467
468                // `Validator::validate_function` actually checks the address
469                // space of pointer arguments explicitly before checking the
470                // `ARGUMENT` flag, to give better error messages. But it seems
471                // best to set `ARGUMENT` accurately anyway.
472                let argument_flag = ptr_space_argument_flag(space);
473
474                // Pointers cannot be stored in variables, structure members, or
475                // array elements, so we do not mark them as `DATA`.
476                TypeInfo::new(
477                    argument_flag
478                        | TypeFlags::SIZED
479                        | TypeFlags::COPY
480                        | TypeFlags::CREATION_RESOLVED,
481                    Alignment::ONE,
482                )
483            }
484            Ti::Array { base, size, stride } => {
485                let base_info = &self.types[base.index()];
486                if !base_info
487                    .flags
488                    .contains(TypeFlags::DATA | TypeFlags::SIZED | TypeFlags::CREATION_RESOLVED)
489                {
490                    return Err(TypeError::InvalidArrayBaseType(base));
491                }
492
493                let base_layout = self.layouter[base];
494                let general_alignment = base_layout.alignment;
495                let uniform_layout = match base_info.uniform_layout {
496                    Ok(base_alignment) => {
497                        let alignment = base_alignment
498                            .max(general_alignment)
499                            .max(Alignment::MIN_UNIFORM);
500                        if alignment.is_aligned(stride) {
501                            Ok(alignment)
502                        } else {
503                            Err((handle, Disalignment::ArrayStride { stride, alignment }))
504                        }
505                    }
506                    Err(e) => Err(e),
507                };
508                let storage_layout = match base_info.storage_layout {
509                    Ok(base_alignment) => {
510                        let alignment = base_alignment.max(general_alignment);
511                        if alignment.is_aligned(stride) {
512                            Ok(alignment)
513                        } else {
514                            Err((handle, Disalignment::ArrayStride { stride, alignment }))
515                        }
516                    }
517                    Err(e) => Err(e),
518                };
519
520                let type_info_mask = match size {
521                    crate::ArraySize::Constant(_) => {
522                        TypeFlags::DATA
523                            | TypeFlags::SIZED
524                            | TypeFlags::COPY
525                            | TypeFlags::HOST_SHAREABLE
526                            | TypeFlags::ARGUMENT
527                            | TypeFlags::CONSTRUCTIBLE
528                            | TypeFlags::CREATION_RESOLVED
529                    }
530                    crate::ArraySize::Pending(_) => {
531                        TypeFlags::DATA
532                            | TypeFlags::SIZED
533                            | TypeFlags::COPY
534                            | TypeFlags::HOST_SHAREABLE
535                            | TypeFlags::ARGUMENT
536                    }
537                    crate::ArraySize::Dynamic => {
538                        // Non-SIZED types may only appear as the last element of a structure.
539                        // This is enforced by checks for SIZED-ness for all compound types,
540                        // and a special case for structs.
541                        TypeFlags::DATA
542                            | TypeFlags::COPY
543                            | TypeFlags::HOST_SHAREABLE
544                            | TypeFlags::CREATION_RESOLVED
545                    }
546                };
547
548                TypeInfo {
549                    flags: base_info.flags & type_info_mask,
550                    uniform_layout,
551                    storage_layout,
552                }
553            }
554            Ti::Struct { ref members, span } => {
555                if members.is_empty() {
556                    return Err(TypeError::EmptyStruct);
557                }
558
559                let mut ti = TypeInfo::new(
560                    TypeFlags::DATA
561                        | TypeFlags::SIZED
562                        | TypeFlags::COPY
563                        | TypeFlags::HOST_SHAREABLE
564                        | TypeFlags::IO_SHAREABLE
565                        | TypeFlags::ARGUMENT
566                        | TypeFlags::CONSTRUCTIBLE
567                        | TypeFlags::CREATION_RESOLVED,
568                    Alignment::ONE,
569                );
570                ti.uniform_layout = Ok(Alignment::MIN_UNIFORM);
571
572                let mut min_offset = 0;
573                let mut prev_struct_data: Option<(u32, u32)> = None;
574
575                for (i, member) in members.iter().enumerate() {
576                    let base_info = &self.types[member.ty.index()];
577                    if !base_info
578                        .flags
579                        .contains(TypeFlags::DATA | TypeFlags::CREATION_RESOLVED)
580                    {
581                        return Err(TypeError::InvalidData(member.ty));
582                    }
583                    if !base_info.flags.contains(TypeFlags::HOST_SHAREABLE) {
584                        if ti.uniform_layout.is_ok() {
585                            ti.uniform_layout = Err((member.ty, Disalignment::NonHostShareable));
586                        }
587                        if ti.storage_layout.is_ok() {
588                            ti.storage_layout = Err((member.ty, Disalignment::NonHostShareable));
589                        }
590                    }
591                    ti.flags &= base_info.flags;
592
593                    if member.offset < min_offset {
594                        // HACK: this could be nicer. We want to allow some structures
595                        // to not bother with offsets/alignments if they are never
596                        // used for host sharing.
597                        if member.offset == 0 {
598                            ti.flags.set(TypeFlags::HOST_SHAREABLE, false);
599                        } else {
600                            return Err(TypeError::MemberOverlap {
601                                index: i as u32,
602                                offset: member.offset,
603                            });
604                        }
605                    }
606
607                    let base_size = gctx.types[member.ty].inner.size(gctx);
608                    min_offset = member.offset + base_size;
609                    if min_offset > span {
610                        return Err(TypeError::MemberOutOfBounds {
611                            index: i as u32,
612                            offset: member.offset,
613                            size: base_size,
614                            span,
615                        });
616                    }
617
618                    check_member_layout(
619                        &mut ti.uniform_layout,
620                        member,
621                        i as u32,
622                        base_info.uniform_layout,
623                        handle,
624                    );
625                    check_member_layout(
626                        &mut ti.storage_layout,
627                        member,
628                        i as u32,
629                        base_info.storage_layout,
630                        handle,
631                    );
632
633                    // Validate rule: If a structure member itself has a structure type S,
634                    // then the number of bytes between the start of that member and
635                    // the start of any following member must be at least roundUp(16, SizeOf(S)).
636                    if let Some((span, offset)) = prev_struct_data {
637                        let diff = member.offset - offset;
638                        let min = Alignment::MIN_UNIFORM.round_up(span);
639                        if diff < min {
640                            ti.uniform_layout = Err((
641                                handle,
642                                Disalignment::MemberOffsetAfterStruct {
643                                    index: i as u32,
644                                    offset: member.offset,
645                                    expected: offset + min,
646                                },
647                            ));
648                        }
649                    };
650
651                    prev_struct_data = match gctx.types[member.ty].inner {
652                        crate::TypeInner::Struct { span, .. } => Some((span, member.offset)),
653                        _ => None,
654                    };
655
656                    // The last field may be an unsized array.
657                    if !base_info.flags.contains(TypeFlags::SIZED) {
658                        let is_array = match gctx.types[member.ty].inner {
659                            crate::TypeInner::Array { .. } => true,
660                            _ => false,
661                        };
662                        if !is_array || i + 1 != members.len() {
663                            let name = member.name.clone().unwrap_or_default();
664                            return Err(TypeError::InvalidDynamicArray(name, member.ty));
665                        }
666                        if ti.uniform_layout.is_ok() {
667                            ti.uniform_layout =
668                                Err((handle, Disalignment::UnsizedMember { index: i as u32 }));
669                        }
670                    }
671                }
672
673                let alignment = self.layouter[handle].alignment;
674                if !alignment.is_aligned(span) {
675                    ti.uniform_layout = Err((handle, Disalignment::StructSpan { span, alignment }));
676                    ti.storage_layout = Err((handle, Disalignment::StructSpan { span, alignment }));
677                }
678
679                ti
680            }
681            Ti::Image {
682                dim,
683                arrayed,
684                class,
685            } => {
686                if arrayed && matches!(dim, crate::ImageDimension::D3) {
687                    return Err(TypeError::UnsupportedImageType {
688                        dim,
689                        arrayed,
690                        class,
691                    });
692                }
693                if arrayed && matches!(dim, crate::ImageDimension::Cube) {
694                    self.require_type_capability(Capabilities::CUBE_ARRAY_TEXTURES)?;
695                }
696                TypeInfo::new(
697                    TypeFlags::ARGUMENT | TypeFlags::CREATION_RESOLVED,
698                    Alignment::ONE,
699                )
700            }
701            Ti::Sampler { .. } => TypeInfo::new(
702                TypeFlags::ARGUMENT | TypeFlags::CREATION_RESOLVED,
703                Alignment::ONE,
704            ),
705            Ti::AccelerationStructure => {
706                self.require_type_capability(Capabilities::RAY_QUERY)?;
707                TypeInfo::new(
708                    TypeFlags::ARGUMENT | TypeFlags::CREATION_RESOLVED,
709                    Alignment::ONE,
710                )
711            }
712            Ti::RayQuery => {
713                self.require_type_capability(Capabilities::RAY_QUERY)?;
714                TypeInfo::new(
715                    TypeFlags::DATA
716                        | TypeFlags::CONSTRUCTIBLE
717                        | TypeFlags::SIZED
718                        | TypeFlags::CREATION_RESOLVED,
719                    Alignment::ONE,
720                )
721            }
722            Ti::BindingArray { base, size } => {
723                let type_info_mask = match size {
724                    crate::ArraySize::Constant(_) => {
725                        TypeFlags::SIZED | TypeFlags::HOST_SHAREABLE | TypeFlags::CREATION_RESOLVED
726                    }
727                    crate::ArraySize::Pending(_) => TypeFlags::SIZED | TypeFlags::HOST_SHAREABLE,
728                    crate::ArraySize::Dynamic => {
729                        // Final type is non-sized
730                        TypeFlags::HOST_SHAREABLE | TypeFlags::CREATION_RESOLVED
731                    }
732                };
733                let base_info = &self.types[base.index()];
734
735                if base_info.flags.contains(TypeFlags::DATA) {
736                    // Currently Naga only supports binding arrays of structs for non-handle types.
737                    match gctx.types[base].inner {
738                        crate::TypeInner::Struct { .. } => {}
739                        _ => return Err(TypeError::BindingArrayBaseTypeNotStruct(base)),
740                    };
741                }
742
743                if !base_info.flags.contains(TypeFlags::CREATION_RESOLVED) {
744                    return Err(TypeError::InvalidData(base));
745                }
746
747                TypeInfo::new(base_info.flags & type_info_mask, Alignment::ONE)
748            }
749        })
750    }
751}