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