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 == 2 {
320 if !self.capabilities.contains(Capabilities::SHADER_INT16) {
321 return Err(WidthError::MissingCapability {
322 name: "i16",
323 flag: "SHADER_INT16",
324 });
325 }
326
327 immediates_compatibility = Err(ImmediateError::InvalidScalar(scalar));
328
329 true
330 } else if scalar.width == 8 {
331 if !self.capabilities.contains(Capabilities::SHADER_INT64) {
332 return Err(WidthError::MissingCapability {
333 name: "i64",
334 flag: "SHADER_INT64",
335 });
336 }
337 true
338 } else {
339 scalar.width == 4
340 }
341 }
342 crate::ScalarKind::Uint => {
343 if scalar.width == 2 {
344 if !self.capabilities.contains(Capabilities::SHADER_INT16) {
345 return Err(WidthError::MissingCapability {
346 name: "u16",
347 flag: "SHADER_INT16",
348 });
349 }
350
351 immediates_compatibility = Err(ImmediateError::InvalidScalar(scalar));
352
353 true
354 } else if scalar.width == 8 {
355 if !self.capabilities.contains(Capabilities::SHADER_INT64) {
356 return Err(WidthError::MissingCapability {
357 name: "u64",
358 flag: "SHADER_INT64",
359 });
360 }
361 true
362 } else {
363 scalar.width == 4
364 }
365 }
366 crate::ScalarKind::AbstractInt | crate::ScalarKind::AbstractFloat => {
367 return Err(WidthError::Abstract);
368 }
369 };
370 if good {
371 Ok(immediates_compatibility)
372 } else {
373 Err(WidthError::Invalid(scalar.kind, scalar.width))
374 }
375 }
376
377 pub(super) fn reset_types(&mut self, size: usize) {
378 self.types.clear();
379 self.types.resize(size, TypeInfo::dummy());
380 self.layouter.clear();
381 }
382
383 pub(super) fn validate_type(
384 &self,
385 handle: Handle<crate::Type>,
386 gctx: crate::proc::GlobalCtx,
387 ) -> Result<TypeInfo, TypeError> {
388 use crate::TypeInner as Ti;
389 Ok(match gctx.types[handle].inner {
390 Ti::Scalar(scalar) => {
391 let immediates_compatibility = self.check_width(scalar)?;
392 let shareable = if scalar.kind.is_numeric() {
393 TypeFlags::IO_SHAREABLE | TypeFlags::HOST_SHAREABLE
394 } else {
395 TypeFlags::empty()
396 };
397 let mut type_info = TypeInfo::new(
398 TypeFlags::DATA
399 | TypeFlags::SIZED
400 | TypeFlags::COPY
401 | TypeFlags::ARGUMENT
402 | TypeFlags::CONSTRUCTIBLE
403 | TypeFlags::CREATION_RESOLVED
404 | shareable,
405 Alignment::from_width(scalar.width),
406 );
407 type_info.immediates_compatibility = immediates_compatibility;
408 type_info
409 }
410 Ti::Vector { size, scalar } => {
411 let immediates_compatibility = self.check_width(scalar)?;
412 let shareable = if scalar.kind.is_numeric() {
413 TypeFlags::IO_SHAREABLE | TypeFlags::HOST_SHAREABLE
414 } else {
415 TypeFlags::empty()
416 };
417 let mut type_info = TypeInfo::new(
418 TypeFlags::DATA
419 | TypeFlags::SIZED
420 | TypeFlags::COPY
421 | TypeFlags::ARGUMENT
422 | TypeFlags::CONSTRUCTIBLE
423 | TypeFlags::CREATION_RESOLVED
424 | shareable,
425 Alignment::from(size) * Alignment::from_width(scalar.width),
426 );
427 type_info.immediates_compatibility = immediates_compatibility;
428 type_info
429 }
430 Ti::Matrix {
431 columns: _,
432 rows,
433 scalar,
434 } => {
435 if scalar.kind != crate::ScalarKind::Float {
436 return Err(TypeError::MatrixElementNotFloat);
437 }
438 let immediates_compatibility = self.check_width(scalar)?;
439 let mut type_info = TypeInfo::new(
440 TypeFlags::DATA
441 | TypeFlags::SIZED
442 | TypeFlags::COPY
443 | TypeFlags::HOST_SHAREABLE
444 | TypeFlags::ARGUMENT
445 | TypeFlags::CONSTRUCTIBLE
446 | TypeFlags::CREATION_RESOLVED,
447 Alignment::from(rows) * Alignment::from_width(scalar.width),
448 );
449 type_info.immediates_compatibility = immediates_compatibility;
450 type_info
451 }
452 Ti::CooperativeMatrix {
453 columns: _,
454 rows: _,
455 scalar,
456 role: _,
457 } => {
458 self.require_type_capability(Capabilities::COOPERATIVE_MATRIX)?;
459 if scalar.kind != crate::ScalarKind::Float
461 || (scalar.width != 2 && scalar.width != 4)
462 {
463 return Err(TypeError::MatrixElementNotFloat);
464 }
465 TypeInfo::new(
466 TypeFlags::DATA
467 | TypeFlags::SIZED
468 | TypeFlags::COPY
469 | TypeFlags::HOST_SHAREABLE
470 | TypeFlags::ARGUMENT
471 | TypeFlags::CONSTRUCTIBLE
472 | TypeFlags::CREATION_RESOLVED,
473 Alignment::from_width(scalar.width),
474 )
475 }
476 Ti::Atomic(scalar) => {
477 match scalar {
478 crate::Scalar {
479 kind: crate::ScalarKind::Sint | crate::ScalarKind::Uint,
480 width: 4,
481 } => {}
482 crate::Scalar {
483 kind: crate::ScalarKind::Sint | crate::ScalarKind::Uint,
484 width: 8,
485 } => {
486 if !self.capabilities.intersects(
487 Capabilities::SHADER_INT64_ATOMIC_ALL_OPS
488 | Capabilities::SHADER_INT64_ATOMIC_MIN_MAX,
489 ) {
490 return Err(TypeError::MissingCapability(
491 Capabilities::SHADER_INT64_ATOMIC_ALL_OPS,
492 ));
493 }
494 }
495 crate::Scalar::F32 => {
496 if !self
497 .capabilities
498 .contains(Capabilities::SHADER_FLOAT32_ATOMIC)
499 {
500 return Err(TypeError::MissingCapability(
501 Capabilities::SHADER_FLOAT32_ATOMIC,
502 ));
503 }
504 }
505 _ => return Err(TypeError::InvalidAtomicWidth(scalar.kind, scalar.width)),
506 };
507 TypeInfo::new(
508 TypeFlags::DATA
509 | TypeFlags::SIZED
510 | TypeFlags::HOST_SHAREABLE
511 | TypeFlags::CREATION_RESOLVED,
512 Alignment::from_width(scalar.width),
513 )
514 }
515 Ti::Pointer { base, space } => {
516 use crate::AddressSpace as As;
517
518 let base_info = &self.types[base.index()];
519 if !base_info.flags.contains(TypeFlags::DATA) {
520 return Err(TypeError::InvalidPointerBase(base));
521 }
522
523 if !base_info.flags.contains(TypeFlags::SIZED) {
535 match space {
536 As::Storage { .. } => {}
537 _ => {
538 return Err(TypeError::InvalidPointerToUnsized { base, space });
539 }
540 }
541 }
542
543 let argument_flag = ptr_space_argument_flag(space);
548
549 TypeInfo::new(
552 argument_flag
553 | TypeFlags::SIZED
554 | TypeFlags::COPY
555 | TypeFlags::CREATION_RESOLVED,
556 Alignment::ONE,
557 )
558 }
559 Ti::ValuePointer {
560 size: _,
561 scalar,
562 space,
563 } => {
564 let _ = self.check_width(scalar)?;
572
573 let argument_flag = ptr_space_argument_flag(space);
578
579 TypeInfo::new(
582 argument_flag
583 | TypeFlags::SIZED
584 | TypeFlags::COPY
585 | TypeFlags::CREATION_RESOLVED,
586 Alignment::ONE,
587 )
588 }
589 Ti::Array { base, size, stride } => {
590 let base_info = &self.types[base.index()];
591 if !base_info
592 .flags
593 .contains(TypeFlags::DATA | TypeFlags::SIZED | TypeFlags::CREATION_RESOLVED)
594 {
595 return Err(TypeError::InvalidArrayBaseType(base));
596 }
597
598 if self.overrides_resolved {
599 if let crate::ArraySize::Pending(_) = size {
602 size.resolve(gctx)
603 .map_err(|_| TypeError::InvalidArraySize(handle))?;
604 }
605 }
606
607 let base_layout = self.layouter[base];
608 let general_alignment = base_layout.alignment;
609 let uniform_layout = match base_info.uniform_layout {
610 Ok(base_alignment) => {
611 let alignment = base_alignment
612 .max(general_alignment)
613 .max(Alignment::MIN_UNIFORM);
614 if alignment.is_aligned(stride) {
615 Ok(alignment)
616 } else {
617 Err((handle, Disalignment::ArrayStride { stride, alignment }))
618 }
619 }
620 Err(e) => Err(e),
621 };
622 let storage_layout = match base_info.storage_layout {
623 Ok(base_alignment) => {
624 let alignment = base_alignment.max(general_alignment);
625 if alignment.is_aligned(stride) {
626 Ok(alignment)
627 } else {
628 Err((handle, Disalignment::ArrayStride { stride, alignment }))
629 }
630 }
631 Err(e) => Err(e),
632 };
633
634 let type_info_mask = match size {
635 crate::ArraySize::Constant(_) => {
636 TypeFlags::DATA
637 | TypeFlags::SIZED
638 | TypeFlags::COPY
639 | TypeFlags::HOST_SHAREABLE
640 | TypeFlags::ARGUMENT
641 | TypeFlags::CONSTRUCTIBLE
642 | TypeFlags::CREATION_RESOLVED
643 }
644 crate::ArraySize::Pending(_) => {
645 TypeFlags::DATA
646 | TypeFlags::SIZED
647 | TypeFlags::COPY
648 | TypeFlags::HOST_SHAREABLE
649 | TypeFlags::ARGUMENT
650 }
651 crate::ArraySize::Dynamic => {
652 TypeFlags::DATA
656 | TypeFlags::COPY
657 | TypeFlags::HOST_SHAREABLE
658 | TypeFlags::CREATION_RESOLVED
659 }
660 };
661
662 TypeInfo {
663 flags: base_info.flags & type_info_mask,
664 uniform_layout,
665 storage_layout,
666 immediates_compatibility: base_info.immediates_compatibility.clone(),
667 }
668 }
669 Ti::Struct { ref members, span } => {
670 if members.is_empty() {
671 return Err(TypeError::EmptyStruct);
672 }
673
674 let mut blend_src_types = [None, None];
675 let mut non_blend_src_location = None;
676
677 let mut ti = TypeInfo::new(
678 TypeFlags::DATA
679 | TypeFlags::SIZED
680 | TypeFlags::COPY
681 | TypeFlags::HOST_SHAREABLE
682 | TypeFlags::ARGUMENT
683 | TypeFlags::CONSTRUCTIBLE
684 | TypeFlags::CREATION_RESOLVED,
685 Alignment::ONE,
686 );
687 ti.uniform_layout = Ok(Alignment::MIN_UNIFORM);
688
689 let mut min_offset = 0;
690 let mut prev_struct_data: Option<(u32, u32)> = None;
691
692 for (i, member) in members.iter().enumerate() {
693 let base_info = &self.types[member.ty.index()];
694 if !base_info
695 .flags
696 .contains(TypeFlags::DATA | TypeFlags::CREATION_RESOLVED)
697 {
698 return Err(TypeError::InvalidData(member.ty));
699 }
700 if !base_info.flags.contains(TypeFlags::HOST_SHAREABLE) {
701 if ti.uniform_layout.is_ok() {
702 ti.uniform_layout = Err((member.ty, Disalignment::NonHostShareable));
703 }
704 if ti.storage_layout.is_ok() {
705 ti.storage_layout = Err((member.ty, Disalignment::NonHostShareable));
706 }
707 }
708 ti.flags &= base_info.flags;
709
710 match member.binding {
711 Some(ir::Binding::Location {
712 location,
713 blend_src: Some(blend_src),
714 ..
715 }) => {
716 if !self
719 .capabilities
720 .contains(Capabilities::DUAL_SOURCE_BLENDING)
721 {
722 return Err(TypeError::MissingCapability(
723 Capabilities::DUAL_SOURCE_BLENDING,
724 ));
725 }
726 if !(location == 0 && (blend_src == 0 || blend_src == 1)) {
727 return Err(TypeError::InvalidBlendSrc(
728 super::VaryingError::InvalidBlendSrcIndex {
729 location,
730 blend_src,
731 },
732 ));
733 }
734 if blend_src_types[blend_src as usize]
735 .replace(member.ty)
736 .is_some()
737 {
738 return Err(TypeError::InvalidBlendSrc(
740 super::VaryingError::BindingCollisionBlendSrc { blend_src },
741 ));
742 }
743 }
744 Some(ir::Binding::Location {
745 location,
746 blend_src: None,
747 ..
748 }) => non_blend_src_location = Some(location),
749 _ => {}
750 }
751
752 if member.offset < min_offset {
753 if member.offset == 0 {
757 ti.flags.set(TypeFlags::HOST_SHAREABLE, false);
758 } else {
759 return Err(TypeError::MemberOverlap {
760 index: i as u32,
761 offset: member.offset,
762 });
763 }
764 }
765
766 let base_size = gctx.types[member.ty].inner.size(gctx);
767 min_offset = member.offset + base_size;
768 if min_offset > span {
769 return Err(TypeError::MemberOutOfBounds {
770 index: i as u32,
771 offset: member.offset,
772 size: base_size,
773 span,
774 });
775 }
776
777 check_member_layout(
778 &mut ti.uniform_layout,
779 member,
780 i as u32,
781 base_info.uniform_layout,
782 handle,
783 );
784 check_member_layout(
785 &mut ti.storage_layout,
786 member,
787 i as u32,
788 base_info.storage_layout,
789 handle,
790 );
791 if base_info.immediates_compatibility.is_err() {
792 ti.immediates_compatibility = base_info.immediates_compatibility.clone();
793 }
794
795 if let Some((span, offset)) = prev_struct_data {
799 let diff = member.offset - offset;
800 let min = Alignment::MIN_UNIFORM.round_up(span);
801 if diff < min {
802 ti.uniform_layout = Err((
803 handle,
804 Disalignment::MemberOffsetAfterStruct {
805 index: i as u32,
806 offset: member.offset,
807 expected: offset + min,
808 },
809 ));
810 }
811 };
812
813 prev_struct_data = match gctx.types[member.ty].inner {
814 crate::TypeInner::Struct { span, .. } => Some((span, member.offset)),
815 _ => None,
816 };
817
818 if !base_info.flags.contains(TypeFlags::SIZED) {
820 let is_array = match gctx.types[member.ty].inner {
821 crate::TypeInner::Array { .. } => true,
822 _ => false,
823 };
824 if !is_array || i + 1 != members.len() {
825 let name = member.name.clone().unwrap_or_default();
826 return Err(TypeError::InvalidDynamicArray(name, member.ty));
827 }
828 if ti.uniform_layout.is_ok() {
829 ti.uniform_layout =
830 Err((handle, Disalignment::UnsizedMember { index: i as u32 }));
831 }
832 }
833 }
834
835 match blend_src_types {
836 [None, None] => {}
837 [Some(ty0), Some(ty1)] => {
838 if let Some(location) = non_blend_src_location {
839 return Err(TypeError::InvalidBlendSrc(
842 super::VaryingError::InvalidBlendSrcWithOtherBindings { location },
843 ));
844 }
845 let ty0_inner = &gctx.types[ty0].inner;
846 let ty1_inner = &gctx.types[ty1].inner;
847 if !ty0_inner.non_struct_equivalent(ty1_inner, gctx.types) {
849 return Err(TypeError::InvalidBlendSrc(
850 super::VaryingError::BlendSrcOutputTypeMismatch {
851 blend_src_0_type: ty0,
852 blend_src_1_type: ty1,
853 },
854 ));
855 }
856 if !self.types[ty0.index()]
858 .flags
859 .contains(TypeFlags::IO_SHAREABLE)
860 {
861 return Err(TypeError::InvalidBlendSrc(
862 super::VaryingError::NotIOShareableType(ty0),
863 ));
864 }
865
866 ti.flags |= TypeFlags::IO_SHAREABLE;
871 }
872 [None, Some(_)] | [Some(_), None] => {
873 return Err(TypeError::InvalidBlendSrc(
875 super::VaryingError::IncompleteBlendSrcUsage {
876 present_blend_src: blend_src_types
877 .iter()
878 .position(|src| src.is_some())
879 .unwrap()
880 as u32,
881 },
882 ));
883 }
884 }
885
886 let alignment = self.layouter[handle].alignment;
887 if !alignment.is_aligned(span) {
888 ti.uniform_layout = Err((handle, Disalignment::StructSpan { span, alignment }));
889 ti.storage_layout = Err((handle, Disalignment::StructSpan { span, alignment }));
890 }
891
892 ti
893 }
894 Ti::Image {
895 dim,
896 arrayed,
897 class,
898 } => {
899 if arrayed && matches!(dim, crate::ImageDimension::D3) {
900 return Err(TypeError::UnsupportedImageType {
901 dim,
902 arrayed,
903 class,
904 });
905 }
906 if arrayed && matches!(dim, crate::ImageDimension::Cube) {
907 self.require_type_capability(Capabilities::CUBE_ARRAY_TEXTURES)?;
908 }
909 if matches!(class, crate::ImageClass::External) {
910 if dim != crate::ImageDimension::D2 || arrayed {
911 return Err(TypeError::UnsupportedImageType {
912 dim,
913 arrayed,
914 class,
915 });
916 }
917 self.require_type_capability(Capabilities::TEXTURE_EXTERNAL)?;
918 }
919 TypeInfo::new(
920 TypeFlags::ARGUMENT | TypeFlags::CREATION_RESOLVED,
921 Alignment::ONE,
922 )
923 }
924 Ti::Sampler { .. } => TypeInfo::new(
925 TypeFlags::ARGUMENT | TypeFlags::CREATION_RESOLVED,
926 Alignment::ONE,
927 ),
928 Ti::AccelerationStructure { vertex_return } => {
929 self.require_type_capability(Capabilities::RAY_TRACING_PIPELINE)
930 .or_else(|_| self.require_type_capability(Capabilities::RAY_QUERY))?;
931 if vertex_return {
932 self.require_type_capability(Capabilities::RAY_HIT_VERTEX_POSITION)?;
933 }
934 TypeInfo::new(
935 TypeFlags::ARGUMENT | TypeFlags::CREATION_RESOLVED,
936 Alignment::ONE,
937 )
938 }
939 Ti::RayQuery { vertex_return } => {
940 self.require_type_capability(Capabilities::RAY_QUERY)?;
941 if vertex_return {
942 self.require_type_capability(Capabilities::RAY_HIT_VERTEX_POSITION)?;
943 }
944 TypeInfo::new(
945 TypeFlags::DATA
946 | TypeFlags::CONSTRUCTIBLE
947 | TypeFlags::SIZED
948 | TypeFlags::CREATION_RESOLVED,
949 Alignment::ONE,
950 )
951 }
952 Ti::BindingArray { base, size } => {
953 let type_info_mask = match size {
954 crate::ArraySize::Constant(_) => {
955 TypeFlags::SIZED | TypeFlags::HOST_SHAREABLE | TypeFlags::CREATION_RESOLVED
956 }
957 crate::ArraySize::Pending(_) => TypeFlags::SIZED | TypeFlags::HOST_SHAREABLE,
958 crate::ArraySize::Dynamic => {
959 TypeFlags::HOST_SHAREABLE | TypeFlags::CREATION_RESOLVED
961 }
962 };
963 let base_info = &self.types[base.index()];
964
965 if base_info.flags.contains(TypeFlags::DATA) {
966 match gctx.types[base].inner {
969 crate::TypeInner::Struct { .. } => {}
970 _ => return Err(TypeError::BindingArrayBaseTypeNotStruct(base)),
971 };
972 }
973 if matches!(
974 gctx.types[base].inner,
975 crate::TypeInner::Image {
976 class: crate::ImageClass::External,
977 ..
978 }
979 ) {
980 return Err(TypeError::BindingArrayBaseExternalTextures);
984 }
985
986 if !base_info.flags.contains(TypeFlags::CREATION_RESOLVED) {
987 return Err(TypeError::InvalidData(base));
988 }
989
990 TypeInfo::new(base_info.flags & type_info_mask, Alignment::ONE)
991 }
992 })
993 }
994}