1use alloc::string::String;
2
3use super::Capabilities;
4use crate::{arena::Handle, ir, proc::Alignment};
5
6bitflags::bitflags! {
7 #[cfg_attr(feature = "serialize", derive(serde::Serialize))]
12 #[cfg_attr(feature = "deserialize", derive(serde::Deserialize))]
13 #[repr(transparent)]
14 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
15 pub struct TypeFlags: u8 {
16 const DATA = 0x1;
28
29 const SIZED = 0x2;
43
44 const COPY = 0x4;
46
47 const IO_SHAREABLE = 0x8;
60
61 const HOST_SHAREABLE = 0x10;
63
64 const CREATION_RESOLVED = 0x20;
67
68 const ARGUMENT = 0x40;
70
71 const CONSTRUCTIBLE = 0x80;
79 }
80}
81
82#[derive(Clone, Copy, Debug, thiserror::Error)]
83#[cfg_attr(test, derive(PartialEq))]
84pub enum Disalignment {
85 #[error("The array stride {stride} is not a multiple of the required alignment {alignment}")]
86 ArrayStride { stride: u32, alignment: Alignment },
87 #[error("The struct span {span}, is not a multiple of the required alignment {alignment}")]
88 StructSpan { span: u32, alignment: Alignment },
89 #[error("The struct member[{index}] offset {offset} is not a multiple of the required alignment {alignment}")]
90 MemberOffset {
91 index: u32,
92 offset: u32,
93 alignment: Alignment,
94 },
95 #[error("The struct member[{index}] offset {offset} must be at least {expected}")]
96 MemberOffsetAfterStruct {
97 index: u32,
98 offset: u32,
99 expected: u32,
100 },
101 #[error("The struct member[{index}] is not statically sized")]
102 UnsizedMember { index: u32 },
103 #[error("The type is not host-shareable")]
104 NonHostShareable,
105}
106
107#[derive(Clone, Debug, thiserror::Error)]
108#[cfg_attr(test, derive(PartialEq))]
109pub enum TypeError {
110 #[error("Capability {0:?} is required")]
111 MissingCapability(Capabilities),
112 #[error("The {0:?} scalar width {1} is not supported for an atomic")]
113 InvalidAtomicWidth(crate::ScalarKind, crate::Bytes),
114 #[error("Invalid type for pointer target {0:?}")]
115 InvalidPointerBase(Handle<crate::Type>),
116 #[error("Unsized types like {base:?} must be in the `Storage` address space, not `{space:?}`")]
117 InvalidPointerToUnsized {
118 base: Handle<crate::Type>,
119 space: crate::AddressSpace,
120 },
121 #[error("Expected data type, found {0:?}")]
122 InvalidData(Handle<crate::Type>),
123 #[error("Base type {0:?} for the array is invalid")]
124 InvalidArrayBaseType(Handle<crate::Type>),
125 #[error("Matrix elements must always be floating-point types")]
126 MatrixElementNotFloat,
127 #[error("The constant {0:?} is specialized, and cannot be used as an array size")]
128 UnsupportedSpecializedArrayLength(Handle<crate::Constant>),
129 #[error("{} of dimensionality {dim:?} and class {class:?} are not supported", if *.arrayed {"Arrayed images"} else {"Images"})]
130 UnsupportedImageType {
131 dim: crate::ImageDimension,
132 arrayed: bool,
133 class: crate::ImageClass,
134 },
135 #[error("Array stride {stride} does not match the expected {expected}")]
136 InvalidArrayStride { stride: u32, expected: u32 },
137 #[error("Field '{0}' can't be dynamically-sized, has type {1:?}")]
138 InvalidDynamicArray(String, Handle<crate::Type>),
139 #[error("The base handle {0:?} has to be a struct")]
140 BindingArrayBaseTypeNotStruct(Handle<crate::Type>),
141 #[error("Binding arrays of external textures are not yet supported")]
142 BindingArrayBaseExternalTextures,
143 #[error("Structure member[{index}] at {offset} overlaps the previous member")]
144 MemberOverlap { index: u32, offset: u32 },
145 #[error(
146 "Structure member[{index}] at {offset} and size {size} crosses the structure boundary of size {span}"
147 )]
148 MemberOutOfBounds {
149 index: u32,
150 offset: u32,
151 size: u32,
152 span: u32,
153 },
154 #[error("Structure types must have at least one member")]
155 EmptyStruct,
156 #[error("Invalid `@blend_src` structure: {0}")]
157 InvalidBlendSrc(super::VaryingError),
158 #[error(transparent)]
159 WidthError(#[from] WidthError),
160 #[error(
161 "The base handle {0:?} has an override-expression that didn't get resolved to a constant"
162 )]
163 UnresolvedOverride(Handle<crate::Type>),
164 #[error("Override-sized array type {0:?} does not have a positive size")]
165 InvalidArraySize(Handle<crate::Type>),
166}
167
168#[derive(Clone, Debug, thiserror::Error)]
169#[cfg_attr(test, derive(PartialEq))]
170pub enum WidthError {
171 #[error("The {0:?} scalar width {1} is not supported")]
172 Invalid(crate::ScalarKind, crate::Bytes),
173 #[error("Using `{name}` values requires the `naga::valid::Capabilities::{flag}` flag")]
174 MissingCapability {
175 name: &'static str,
176 flag: &'static str,
177 },
178
179 #[error("Abstract types may only appear in constant expressions")]
180 Abstract,
181}
182
183#[derive(Clone, Debug, thiserror::Error)]
184#[cfg_attr(test, derive(PartialEq))]
185pub enum ImmediateError {
186 #[error("The scalar type {0:?} is not supported in immediates")]
187 InvalidScalar(crate::Scalar),
188}
189
190type LayoutCompatibility = Result<Alignment, (Handle<crate::Type>, Disalignment)>;
192type ImmediateCompatibility = Result<(), ImmediateError>;
193
194fn check_member_layout(
195 accum: &mut LayoutCompatibility,
196 member: &crate::StructMember,
197 member_index: u32,
198 member_layout: LayoutCompatibility,
199 parent_handle: Handle<crate::Type>,
200) {
201 *accum = match (*accum, member_layout) {
202 (Ok(cur_alignment), Ok(alignment)) => {
203 if alignment.is_aligned(member.offset) {
204 Ok(cur_alignment.max(alignment))
205 } else {
206 Err((
207 parent_handle,
208 Disalignment::MemberOffset {
209 index: member_index,
210 offset: member.offset,
211 alignment,
212 },
213 ))
214 }
215 }
216 (Err(e), _) | (_, Err(e)) => Err(e),
217 };
218}
219
220const fn ptr_space_argument_flag(space: crate::AddressSpace) -> TypeFlags {
229 use crate::AddressSpace as As;
230 match space {
231 As::Function | As::Private | As::RayPayload | As::IncomingRayPayload => TypeFlags::ARGUMENT,
232 As::Uniform
233 | As::Storage { .. }
234 | As::Handle
235 | As::Immediate
236 | As::WorkGroup
237 | As::TaskPayload => TypeFlags::empty(),
238 }
239}
240
241#[derive(Clone, Debug)]
242pub(super) struct TypeInfo {
243 pub flags: TypeFlags,
244 pub uniform_layout: LayoutCompatibility,
245 pub storage_layout: LayoutCompatibility,
246 pub immediates_compatibility: ImmediateCompatibility,
247}
248
249impl TypeInfo {
250 const fn dummy() -> Self {
251 TypeInfo {
252 flags: TypeFlags::empty(),
253 uniform_layout: Ok(Alignment::ONE),
254 storage_layout: Ok(Alignment::ONE),
255 immediates_compatibility: Ok(()),
256 }
257 }
258
259 const fn new(flags: TypeFlags, alignment: Alignment) -> Self {
260 TypeInfo {
261 flags,
262 uniform_layout: Ok(alignment),
263 storage_layout: Ok(alignment),
264 immediates_compatibility: Ok(()),
265 }
266 }
267}
268
269impl super::Validator {
270 const fn require_type_capability(&self, capability: Capabilities) -> Result<(), TypeError> {
271 if self.capabilities.contains(capability) {
272 Ok(())
273 } else {
274 Err(TypeError::MissingCapability(capability))
275 }
276 }
277
278 pub(super) const fn check_width(
288 &self,
289 scalar: crate::Scalar,
290 ) -> Result<ImmediateCompatibility, WidthError> {
291 let mut immediates_compatibility = Ok(());
292 let good = match scalar.kind {
293 crate::ScalarKind::Bool => scalar.width == crate::BOOL_WIDTH,
294 crate::ScalarKind::Float => match scalar.width {
295 8 => {
296 if !self.capabilities.contains(Capabilities::FLOAT64) {
297 return Err(WidthError::MissingCapability {
298 name: "f64",
299 flag: "FLOAT64",
300 });
301 }
302 true
303 }
304 2 => {
305 if !self.capabilities.contains(Capabilities::SHADER_FLOAT16) {
306 return Err(WidthError::MissingCapability {
307 name: "f16",
308 flag: "FLOAT16",
309 });
310 }
311
312 immediates_compatibility = Err(ImmediateError::InvalidScalar(scalar));
313
314 true
315 }
316 _ => scalar.width == 4,
317 },
318 crate::ScalarKind::Sint => {
319 if scalar.width == 8 {
320 if !self.capabilities.contains(Capabilities::SHADER_INT64) {
321 return Err(WidthError::MissingCapability {
322 name: "i64",
323 flag: "SHADER_INT64",
324 });
325 }
326 true
327 } else {
328 scalar.width == 4
329 }
330 }
331 crate::ScalarKind::Uint => {
332 if scalar.width == 8 {
333 if !self.capabilities.contains(Capabilities::SHADER_INT64) {
334 return Err(WidthError::MissingCapability {
335 name: "u64",
336 flag: "SHADER_INT64",
337 });
338 }
339 true
340 } else {
341 scalar.width == 4
342 }
343 }
344 crate::ScalarKind::AbstractInt | crate::ScalarKind::AbstractFloat => {
345 return Err(WidthError::Abstract);
346 }
347 };
348 if good {
349 Ok(immediates_compatibility)
350 } else {
351 Err(WidthError::Invalid(scalar.kind, scalar.width))
352 }
353 }
354
355 pub(super) fn reset_types(&mut self, size: usize) {
356 self.types.clear();
357 self.types.resize(size, TypeInfo::dummy());
358 self.layouter.clear();
359 }
360
361 pub(super) fn validate_type(
362 &self,
363 handle: Handle<crate::Type>,
364 gctx: crate::proc::GlobalCtx,
365 ) -> Result<TypeInfo, TypeError> {
366 use crate::TypeInner as Ti;
367 Ok(match gctx.types[handle].inner {
368 Ti::Scalar(scalar) => {
369 let immediates_compatibility = self.check_width(scalar)?;
370 let shareable = if scalar.kind.is_numeric() {
371 TypeFlags::IO_SHAREABLE | TypeFlags::HOST_SHAREABLE
372 } else {
373 TypeFlags::empty()
374 };
375 let mut type_info = TypeInfo::new(
376 TypeFlags::DATA
377 | TypeFlags::SIZED
378 | TypeFlags::COPY
379 | TypeFlags::ARGUMENT
380 | TypeFlags::CONSTRUCTIBLE
381 | TypeFlags::CREATION_RESOLVED
382 | shareable,
383 Alignment::from_width(scalar.width),
384 );
385 type_info.immediates_compatibility = immediates_compatibility;
386 type_info
387 }
388 Ti::Vector { size, scalar } => {
389 let immediates_compatibility = self.check_width(scalar)?;
390 let shareable = if scalar.kind.is_numeric() {
391 TypeFlags::IO_SHAREABLE | TypeFlags::HOST_SHAREABLE
392 } else {
393 TypeFlags::empty()
394 };
395 let mut type_info = TypeInfo::new(
396 TypeFlags::DATA
397 | TypeFlags::SIZED
398 | TypeFlags::COPY
399 | TypeFlags::ARGUMENT
400 | TypeFlags::CONSTRUCTIBLE
401 | TypeFlags::CREATION_RESOLVED
402 | shareable,
403 Alignment::from(size) * Alignment::from_width(scalar.width),
404 );
405 type_info.immediates_compatibility = immediates_compatibility;
406 type_info
407 }
408 Ti::Matrix {
409 columns: _,
410 rows,
411 scalar,
412 } => {
413 if scalar.kind != crate::ScalarKind::Float {
414 return Err(TypeError::MatrixElementNotFloat);
415 }
416 let immediates_compatibility = self.check_width(scalar)?;
417 let mut type_info = TypeInfo::new(
418 TypeFlags::DATA
419 | TypeFlags::SIZED
420 | TypeFlags::COPY
421 | TypeFlags::HOST_SHAREABLE
422 | TypeFlags::ARGUMENT
423 | TypeFlags::CONSTRUCTIBLE
424 | TypeFlags::CREATION_RESOLVED,
425 Alignment::from(rows) * Alignment::from_width(scalar.width),
426 );
427 type_info.immediates_compatibility = immediates_compatibility;
428 type_info
429 }
430 Ti::CooperativeMatrix {
431 columns: _,
432 rows: _,
433 scalar,
434 role: _,
435 } => {
436 self.require_type_capability(Capabilities::COOPERATIVE_MATRIX)?;
437 if scalar.kind != crate::ScalarKind::Float
439 || (scalar.width != 2 && scalar.width != 4)
440 {
441 return Err(TypeError::MatrixElementNotFloat);
442 }
443 TypeInfo::new(
444 TypeFlags::DATA
445 | TypeFlags::SIZED
446 | TypeFlags::COPY
447 | TypeFlags::HOST_SHAREABLE
448 | TypeFlags::ARGUMENT
449 | TypeFlags::CONSTRUCTIBLE
450 | TypeFlags::CREATION_RESOLVED,
451 Alignment::from_width(scalar.width),
452 )
453 }
454 Ti::Atomic(scalar) => {
455 match scalar {
456 crate::Scalar {
457 kind: crate::ScalarKind::Sint | crate::ScalarKind::Uint,
458 width: _,
459 } => {
460 if scalar.width == 8
461 && !self.capabilities.intersects(
462 Capabilities::SHADER_INT64_ATOMIC_ALL_OPS
463 | Capabilities::SHADER_INT64_ATOMIC_MIN_MAX,
464 )
465 {
466 return Err(TypeError::MissingCapability(
467 Capabilities::SHADER_INT64_ATOMIC_ALL_OPS,
468 ));
469 }
470 }
471 crate::Scalar::F32 => {
472 if !self
473 .capabilities
474 .contains(Capabilities::SHADER_FLOAT32_ATOMIC)
475 {
476 return Err(TypeError::MissingCapability(
477 Capabilities::SHADER_FLOAT32_ATOMIC,
478 ));
479 }
480 }
481 _ => return Err(TypeError::InvalidAtomicWidth(scalar.kind, scalar.width)),
482 };
483 TypeInfo::new(
484 TypeFlags::DATA
485 | TypeFlags::SIZED
486 | TypeFlags::HOST_SHAREABLE
487 | TypeFlags::CREATION_RESOLVED,
488 Alignment::from_width(scalar.width),
489 )
490 }
491 Ti::Pointer { base, space } => {
492 use crate::AddressSpace as As;
493
494 let base_info = &self.types[base.index()];
495 if !base_info.flags.contains(TypeFlags::DATA) {
496 return Err(TypeError::InvalidPointerBase(base));
497 }
498
499 if !base_info.flags.contains(TypeFlags::SIZED) {
511 match space {
512 As::Storage { .. } => {}
513 _ => {
514 return Err(TypeError::InvalidPointerToUnsized { base, space });
515 }
516 }
517 }
518
519 let argument_flag = ptr_space_argument_flag(space);
524
525 TypeInfo::new(
528 argument_flag
529 | TypeFlags::SIZED
530 | TypeFlags::COPY
531 | TypeFlags::CREATION_RESOLVED,
532 Alignment::ONE,
533 )
534 }
535 Ti::ValuePointer {
536 size: _,
537 scalar,
538 space,
539 } => {
540 let _ = self.check_width(scalar)?;
548
549 let argument_flag = ptr_space_argument_flag(space);
554
555 TypeInfo::new(
558 argument_flag
559 | TypeFlags::SIZED
560 | TypeFlags::COPY
561 | TypeFlags::CREATION_RESOLVED,
562 Alignment::ONE,
563 )
564 }
565 Ti::Array { base, size, stride } => {
566 let base_info = &self.types[base.index()];
567 if !base_info
568 .flags
569 .contains(TypeFlags::DATA | TypeFlags::SIZED | TypeFlags::CREATION_RESOLVED)
570 {
571 return Err(TypeError::InvalidArrayBaseType(base));
572 }
573
574 if self.overrides_resolved {
575 if let crate::ArraySize::Pending(_) = size {
578 size.resolve(gctx)
579 .map_err(|_| TypeError::InvalidArraySize(handle))?;
580 }
581 }
582
583 let base_layout = self.layouter[base];
584 let general_alignment = base_layout.alignment;
585 let uniform_layout = match base_info.uniform_layout {
586 Ok(base_alignment) => {
587 let alignment = base_alignment
588 .max(general_alignment)
589 .max(Alignment::MIN_UNIFORM);
590 if alignment.is_aligned(stride) {
591 Ok(alignment)
592 } else {
593 Err((handle, Disalignment::ArrayStride { stride, alignment }))
594 }
595 }
596 Err(e) => Err(e),
597 };
598 let storage_layout = match base_info.storage_layout {
599 Ok(base_alignment) => {
600 let alignment = base_alignment.max(general_alignment);
601 if alignment.is_aligned(stride) {
602 Ok(alignment)
603 } else {
604 Err((handle, Disalignment::ArrayStride { stride, alignment }))
605 }
606 }
607 Err(e) => Err(e),
608 };
609
610 let type_info_mask = match size {
611 crate::ArraySize::Constant(_) => {
612 TypeFlags::DATA
613 | TypeFlags::SIZED
614 | TypeFlags::COPY
615 | TypeFlags::HOST_SHAREABLE
616 | TypeFlags::ARGUMENT
617 | TypeFlags::CONSTRUCTIBLE
618 | TypeFlags::CREATION_RESOLVED
619 }
620 crate::ArraySize::Pending(_) => {
621 TypeFlags::DATA
622 | TypeFlags::SIZED
623 | TypeFlags::COPY
624 | TypeFlags::HOST_SHAREABLE
625 | TypeFlags::ARGUMENT
626 }
627 crate::ArraySize::Dynamic => {
628 TypeFlags::DATA
632 | TypeFlags::COPY
633 | TypeFlags::HOST_SHAREABLE
634 | TypeFlags::CREATION_RESOLVED
635 }
636 };
637
638 TypeInfo {
639 flags: base_info.flags & type_info_mask,
640 uniform_layout,
641 storage_layout,
642 immediates_compatibility: base_info.immediates_compatibility.clone(),
643 }
644 }
645 Ti::Struct { ref members, span } => {
646 if members.is_empty() {
647 return Err(TypeError::EmptyStruct);
648 }
649
650 let mut blend_src_types = [None, None];
651 let mut non_blend_src_location = None;
652
653 let mut ti = TypeInfo::new(
654 TypeFlags::DATA
655 | TypeFlags::SIZED
656 | TypeFlags::COPY
657 | TypeFlags::HOST_SHAREABLE
658 | TypeFlags::ARGUMENT
659 | TypeFlags::CONSTRUCTIBLE
660 | TypeFlags::CREATION_RESOLVED,
661 Alignment::ONE,
662 );
663 ti.uniform_layout = Ok(Alignment::MIN_UNIFORM);
664
665 let mut min_offset = 0;
666 let mut prev_struct_data: Option<(u32, u32)> = None;
667
668 for (i, member) in members.iter().enumerate() {
669 let base_info = &self.types[member.ty.index()];
670 if !base_info
671 .flags
672 .contains(TypeFlags::DATA | TypeFlags::CREATION_RESOLVED)
673 {
674 return Err(TypeError::InvalidData(member.ty));
675 }
676 if !base_info.flags.contains(TypeFlags::HOST_SHAREABLE) {
677 if ti.uniform_layout.is_ok() {
678 ti.uniform_layout = Err((member.ty, Disalignment::NonHostShareable));
679 }
680 if ti.storage_layout.is_ok() {
681 ti.storage_layout = Err((member.ty, Disalignment::NonHostShareable));
682 }
683 }
684 ti.flags &= base_info.flags;
685
686 match member.binding {
687 Some(ir::Binding::Location {
688 location,
689 blend_src: Some(blend_src),
690 ..
691 }) => {
692 if !self
695 .capabilities
696 .contains(Capabilities::DUAL_SOURCE_BLENDING)
697 {
698 return Err(TypeError::MissingCapability(
699 Capabilities::DUAL_SOURCE_BLENDING,
700 ));
701 }
702 if !(location == 0 && (blend_src == 0 || blend_src == 1)) {
703 return Err(TypeError::InvalidBlendSrc(
704 super::VaryingError::InvalidBlendSrcIndex {
705 location,
706 blend_src,
707 },
708 ));
709 }
710 if blend_src_types[blend_src as usize]
711 .replace(member.ty)
712 .is_some()
713 {
714 return Err(TypeError::InvalidBlendSrc(
716 super::VaryingError::BindingCollisionBlendSrc { blend_src },
717 ));
718 }
719 }
720 Some(ir::Binding::Location {
721 location,
722 blend_src: None,
723 ..
724 }) => non_blend_src_location = Some(location),
725 _ => {}
726 }
727
728 if member.offset < min_offset {
729 if member.offset == 0 {
733 ti.flags.set(TypeFlags::HOST_SHAREABLE, false);
734 } else {
735 return Err(TypeError::MemberOverlap {
736 index: i as u32,
737 offset: member.offset,
738 });
739 }
740 }
741
742 let base_size = gctx.types[member.ty].inner.size(gctx);
743 min_offset = member.offset + base_size;
744 if min_offset > span {
745 return Err(TypeError::MemberOutOfBounds {
746 index: i as u32,
747 offset: member.offset,
748 size: base_size,
749 span,
750 });
751 }
752
753 check_member_layout(
754 &mut ti.uniform_layout,
755 member,
756 i as u32,
757 base_info.uniform_layout,
758 handle,
759 );
760 check_member_layout(
761 &mut ti.storage_layout,
762 member,
763 i as u32,
764 base_info.storage_layout,
765 handle,
766 );
767 if base_info.immediates_compatibility.is_err() {
768 ti.immediates_compatibility = base_info.immediates_compatibility.clone();
769 }
770
771 if let Some((span, offset)) = prev_struct_data {
775 let diff = member.offset - offset;
776 let min = Alignment::MIN_UNIFORM.round_up(span);
777 if diff < min {
778 ti.uniform_layout = Err((
779 handle,
780 Disalignment::MemberOffsetAfterStruct {
781 index: i as u32,
782 offset: member.offset,
783 expected: offset + min,
784 },
785 ));
786 }
787 };
788
789 prev_struct_data = match gctx.types[member.ty].inner {
790 crate::TypeInner::Struct { span, .. } => Some((span, member.offset)),
791 _ => None,
792 };
793
794 if !base_info.flags.contains(TypeFlags::SIZED) {
796 let is_array = match gctx.types[member.ty].inner {
797 crate::TypeInner::Array { .. } => true,
798 _ => false,
799 };
800 if !is_array || i + 1 != members.len() {
801 let name = member.name.clone().unwrap_or_default();
802 return Err(TypeError::InvalidDynamicArray(name, member.ty));
803 }
804 if ti.uniform_layout.is_ok() {
805 ti.uniform_layout =
806 Err((handle, Disalignment::UnsizedMember { index: i as u32 }));
807 }
808 }
809 }
810
811 match blend_src_types {
812 [None, None] => {}
813 [Some(ty0), Some(ty1)] => {
814 if let Some(location) = non_blend_src_location {
815 return Err(TypeError::InvalidBlendSrc(
818 super::VaryingError::InvalidBlendSrcWithOtherBindings { location },
819 ));
820 }
821 let ty0_inner = &gctx.types[ty0].inner;
822 let ty1_inner = &gctx.types[ty1].inner;
823 if !ty0_inner.non_struct_equivalent(ty1_inner, gctx.types) {
825 return Err(TypeError::InvalidBlendSrc(
826 super::VaryingError::BlendSrcOutputTypeMismatch {
827 blend_src_0_type: ty0,
828 blend_src_1_type: ty1,
829 },
830 ));
831 }
832 if !self.types[ty0.index()]
834 .flags
835 .contains(TypeFlags::IO_SHAREABLE)
836 {
837 return Err(TypeError::InvalidBlendSrc(
838 super::VaryingError::NotIOShareableType(ty0),
839 ));
840 }
841
842 ti.flags |= TypeFlags::IO_SHAREABLE;
847 }
848 [None, Some(_)] | [Some(_), None] => {
849 return Err(TypeError::InvalidBlendSrc(
851 super::VaryingError::IncompleteBlendSrcUsage {
852 present_blend_src: blend_src_types
853 .iter()
854 .position(|src| src.is_some())
855 .unwrap()
856 as u32,
857 },
858 ));
859 }
860 }
861
862 let alignment = self.layouter[handle].alignment;
863 if !alignment.is_aligned(span) {
864 ti.uniform_layout = Err((handle, Disalignment::StructSpan { span, alignment }));
865 ti.storage_layout = Err((handle, Disalignment::StructSpan { span, alignment }));
866 }
867
868 ti
869 }
870 Ti::Image {
871 dim,
872 arrayed,
873 class,
874 } => {
875 if arrayed && matches!(dim, crate::ImageDimension::D3) {
876 return Err(TypeError::UnsupportedImageType {
877 dim,
878 arrayed,
879 class,
880 });
881 }
882 if arrayed && matches!(dim, crate::ImageDimension::Cube) {
883 self.require_type_capability(Capabilities::CUBE_ARRAY_TEXTURES)?;
884 }
885 if matches!(class, crate::ImageClass::External) {
886 if dim != crate::ImageDimension::D2 || arrayed {
887 return Err(TypeError::UnsupportedImageType {
888 dim,
889 arrayed,
890 class,
891 });
892 }
893 self.require_type_capability(Capabilities::TEXTURE_EXTERNAL)?;
894 }
895 TypeInfo::new(
896 TypeFlags::ARGUMENT | TypeFlags::CREATION_RESOLVED,
897 Alignment::ONE,
898 )
899 }
900 Ti::Sampler { .. } => TypeInfo::new(
901 TypeFlags::ARGUMENT | TypeFlags::CREATION_RESOLVED,
902 Alignment::ONE,
903 ),
904 Ti::AccelerationStructure { vertex_return } => {
905 self.require_type_capability(Capabilities::RAY_TRACING_PIPELINE)
906 .or_else(|_| self.require_type_capability(Capabilities::RAY_QUERY))?;
907 if vertex_return {
908 self.require_type_capability(Capabilities::RAY_HIT_VERTEX_POSITION)?;
909 }
910 TypeInfo::new(
911 TypeFlags::ARGUMENT | TypeFlags::CREATION_RESOLVED,
912 Alignment::ONE,
913 )
914 }
915 Ti::RayQuery { vertex_return } => {
916 self.require_type_capability(Capabilities::RAY_QUERY)?;
917 if vertex_return {
918 self.require_type_capability(Capabilities::RAY_HIT_VERTEX_POSITION)?;
919 }
920 TypeInfo::new(
921 TypeFlags::DATA
922 | TypeFlags::CONSTRUCTIBLE
923 | TypeFlags::SIZED
924 | TypeFlags::CREATION_RESOLVED,
925 Alignment::ONE,
926 )
927 }
928 Ti::BindingArray { base, size } => {
929 let type_info_mask = match size {
930 crate::ArraySize::Constant(_) => {
931 TypeFlags::SIZED | TypeFlags::HOST_SHAREABLE | TypeFlags::CREATION_RESOLVED
932 }
933 crate::ArraySize::Pending(_) => TypeFlags::SIZED | TypeFlags::HOST_SHAREABLE,
934 crate::ArraySize::Dynamic => {
935 TypeFlags::HOST_SHAREABLE | TypeFlags::CREATION_RESOLVED
937 }
938 };
939 let base_info = &self.types[base.index()];
940
941 if base_info.flags.contains(TypeFlags::DATA) {
942 match gctx.types[base].inner {
945 crate::TypeInner::Struct { .. } => {}
946 _ => return Err(TypeError::BindingArrayBaseTypeNotStruct(base)),
947 };
948 }
949 if matches!(
950 gctx.types[base].inner,
951 crate::TypeInner::Image {
952 class: crate::ImageClass::External,
953 ..
954 }
955 ) {
956 return Err(TypeError::BindingArrayBaseExternalTextures);
960 }
961
962 if !base_info.flags.contains(TypeFlags::CREATION_RESOLVED) {
963 return Err(TypeError::InvalidData(base));
964 }
965
966 TypeInfo::new(base_info.flags & type_info_mask, Alignment::ONE)
967 }
968 })
969 }
970}