usesuper::{
analyzer::{FunctionInfo, GlobalUse},
Capabilities, Disalignment, FunctionError, ImmediateError, ModuleInfo,
}; usecrate::arena::{Handle, UniqueArena}; usecrate::span::{AddSpan as _, MapErrWithSpan as _, SpanProvider as _, WithSpan};
const MAX_WORKGROUP_SIZE: u32 = 0x4000;
#[derive(Clone, Debug, thiserror::Error)] #[cfg_attr(test, derive(PartialEq))] pubenum GlobalVariableError { #[error("Usage isn't compatible with address space {0:?}")]
InvalidUsage(crate::AddressSpace), #[error("Type isn't compatible with address space {0:?}")]
InvalidType(crate::AddressSpace), #[error("Type {0:?} isn't compatible with binding arrays")]
InvalidBindingArray(Handle<crate::Type>), #[error("Type flags {seen:?} do not meet the required {required:?}")]
MissingTypeFlags {
required: super::TypeFlags,
seen: super::TypeFlags,
}, #[error("Capability {0:?} is not supported")]
UnsupportedCapability(Capabilities), #[error("Binding decoration is missing or not applicable")]
InvalidBinding, #[error("Alignment requirements for address space {0:?} are not met by {1:?}")]
Alignment( crate::AddressSpace,
Handle<crate::Type>, #[source] Disalignment,
), #[error("Initializer must be an override-expression")]
InitializerExprType, #[error("Initializer doesn't match the variable type")]
InitializerType, #[error("Initializer can't be used with address space {0:?}")]
InitializerNotAllowed(crate::AddressSpace), #[error("Storage address space doesn't support write-only access")]
StorageAddressSpaceWriteOnlyNotSupported, #[error("Type is not valid for use as a immediate data")]
InvalidImmediateType(#[source] ImmediateError), #[error("Task payload must not be zero-sized")]
ZeroSizedTaskPayload, #[error("Memory decorations (`@coherent`, `@volatile`) are only valid for variables in the `storage` address space")]
InvalidMemoryDecorationsAddressSpace, #[error("`@coherent` requires the MEMORY_DECORATION_COHERENT capability")]
CoherentNotSupported, #[error("`@volatile` requires the MEMORY_DECORATION_VOLATILE capability")]
VolatileNotSupported,
}
#[derive(Clone, Debug, thiserror::Error)] #[cfg_attr(test, derive(PartialEq))] pubenum VaryingError { #[error("The type {0:?} does not match the varying")]
InvalidType(Handle<crate::Type>), #[error( "The type {0:?} cannot be used for user-defined entry point inputs or outputs. \
Only numeric scalars and vectors are allowed."
)]
NotIOShareableType(Handle<crate::Type>), #[error("Interpolation {0:?} is only valid for stage {1:?}")]
InvalidInterpolationInStage(crate::Interpolation, crate::ShaderStage), #[error("Cannot combine {interpolation:?} interpolation with the {sampling:?} sample type")]
InvalidInterpolationSamplingCombination {
interpolation: crate::Interpolation,
sampling: crate::Sampling,
}, #[error("`@interpolate(flat) must be explicitly specified for integer I/O")]
InvalidInterpolationForInteger, #[error("Interpolation must be specified on vertex shader outputs and fragment shader inputs")]
MissingInterpolation, #[error("Built-in {0:?} is not available at this stage")]
InvalidBuiltInStage(crate::BuiltIn), #[error("Built-in type for {0:?} is invalid. Found {1:?}")]
InvalidBuiltInType(crate::BuiltIn, crate::TypeInner), #[error("Entry point arguments and return values must all have bindings")]
MissingBinding, #[error("Struct member {0} is missing a binding")]
MemberMissingBinding(u32), #[error("Multiple bindings at location {location} are present")]
BindingCollision { location: u32 }, #[error("Multiple bindings use the same `blend_src` {blend_src}")]
BindingCollisionBlendSrc { blend_src: u32 }, #[error("Built-in {0:?} is present more than once")]
DuplicateBuiltIn(crate::BuiltIn), #[error("Capability {0:?} is not supported")]
UnsupportedCapability(Capabilities), #[error("The attribute {0:?} is only valid as an output for stage {1:?}")]
InvalidInputAttributeInStage(&'static str, crate::ShaderStage), #[error("The attribute {0:?} is not valid for stage {1:?}")]
InvalidAttributeInStage(&'static str, crate::ShaderStage), #[error("`@blend_src` can only be used at location 0, indices 0 and 1. Found `@location({location}) @blend_src({blend_src})`.")]
InvalidBlendSrcIndex { location: u32, blend_src: u32 }, #[error( "`@blend_src` structure must specify two sources. \
Found `@blend_src({present_blend_src})` but not `@blend_src({absent_blend_src})`.",
absent_blend_src = if *present_blend_src == 0 { 1 } else { 0 },
)]
IncompleteBlendSrcUsage { present_blend_src: u32 }, #[error("Structure using `@blend_src` may not specify `@location` on any other members. Found a binding at `@location({location})`.")]
InvalidBlendSrcWithOtherBindings { location: u32 }, #[error("Both `@blend_src` structure members must have the same type. `blend_src(0)` has type {blend_src_0_type:?} and `blend_src(1)` has type {blend_src_1_type:?}.")]
BlendSrcOutputTypeMismatch {
blend_src_0_type: Handle<crate::Type>,
blend_src_1_type: Handle<crate::Type>,
}, #[error("`@blend_src` can only be used on struct members, not directly on entry point I/O")]
BlendSrcNotOnStructMember, #[error("Workgroup size is multi dimensional, `@builtin(subgroup_id)` and `@builtin(subgroup_invocation_id)` are not supported.")]
InvalidMultiDimensionalSubgroupBuiltIn, #[error("The `@per_primitive` attribute can only be used in fragment shader inputs or mesh shader primitive outputs")]
InvalidPerPrimitive, #[error("Non-builtin members of a mesh primitive output struct must be decorated with `@per_primitive`")]
MissingPerPrimitive, #[error("Per vertex fragment inputs must be an array of length 3.")]
PerVertexNotArrayOfThree, #[error("Per vertex can only have Center sampling or no sampling modifier")]
InvalidPerVertexSampling,
}
#[derive(Clone, Debug, thiserror::Error)] #[cfg_attr(test, derive(PartialEq))] pubenum EntryPointError { #[error("Multiple conflicting entry points")]
Conflict, #[error("Vertex shaders must return a `@builtin(position)` output value")]
MissingVertexOutputPosition, #[error("Early depth test is not applicable")]
UnexpectedEarlyDepthTest, #[error("Workgroup size is not applicable")]
UnexpectedWorkgroupSize, #[error("Workgroup size is out of range")]
OutOfRangeWorkgroupSize, #[error("Uses operations forbidden at this stage")]
ForbiddenStageOperations, #[error("Global variable {0:?} is used incorrectly as {1:?}")]
InvalidGlobalUsage(Handle<crate::GlobalVariable>, GlobalUse), #[error("More than 1 immediate data variable is used")]
MoreThanOneImmediateUsed, #[error("Bindings for {0:?} conflict with other resource")]
BindingCollision(Handle<crate::GlobalVariable>), #[error("Argument {0} varying error")]
Argument(u32, #[source] VaryingError), #[error(transparent)]
Result(#[from] VaryingError), #[error(transparent)]
Function(#[from] FunctionError), #[error("Capability {0:?} is not supported")]
UnsupportedCapability(Capabilities),
#[error("mesh shader entry point missing mesh shader attributes")]
ExpectedMeshShaderAttributes, #[error("Non mesh shader entry point cannot have mesh shader attributes")]
UnexpectedMeshShaderAttributes, #[error("Non mesh/task shader entry point cannot have task payload attribute")]
UnexpectedTaskPayload, #[error("Task payload must be declared with `var<task_payload>`")]
TaskPayloadWrongAddressSpace, #[error("For a task payload to be used, it must be declared with @payload")]
WrongTaskPayloadUsed, #[error("Task shader entry point must return @builtin(mesh_task_size) vec3<u32>")]
WrongTaskShaderEntryResult, #[error("Task shaders must declare a task payload output")]
ExpectedTaskPayload, #[error( "Mesh shader output variable must be a struct with fields that are all allowed builtins"
)]
BadMeshOutputVariableType, #[error("Mesh shader output variable fields must have types that are in accordance with the mesh shader spec")]
BadMeshOutputVariableField, #[error("Mesh shader entry point cannot have a return type")]
UnexpectedMeshShaderEntryResult, #[error( "Mesh output type must be a user-defined struct with fields in alignment with the mesh shader spec"
)]
InvalidMeshOutputType, #[error("Mesh primitive outputs must have exactly one of `@builtin(triangle_indices)`, `@builtin(line_indices)`, or `@builtin(point_index)`")]
InvalidMeshPrimitiveOutputType, #[error("Mesh output global variable must live in the workgroup address space")]
WrongMeshOutputAddressSpace, #[error("Task payload must be at least 4 bytes, but is {0} bytes")]
TaskPayloadTooSmall(u32), #[error("Only the `ray_generation`, `closest_hit`, and `any_hit` shader stages can access a global variable in the `ray_payload` address space")]
RayPayloadInInvalidStage(crate::ShaderStage), #[error("Only the `closest_hit`, `any_hit`, and `miss` shader stages can access a global variable in the `incoming_ray_payload` address space")]
IncomingRayPayloadInInvalidStage(crate::ShaderStage),
}
impl VaryingContext<'_> { fn validate_impl(
&mutself,
ep: &crate::EntryPoint,
ty: Handle<crate::Type>,
binding: &crate::Binding,
) -> Result<(), VaryingError> { usecrate::{BuiltIn as Bi, ShaderStage as St, TypeInner as Ti, VectorSize as Vs};
let ty_inner = &self.types[ty].inner; match *binding { crate::Binding::BuiltIn(built_in) => { // Ignore the `invariant` field for the sake of duplicate checks, // but use the original in error messages. let canonical = match built_in { crate::BuiltIn::Position { .. } => { crate::BuiltIn::Position { invariant: false }
} crate::BuiltIn::Barycentric { .. } => { crate::BuiltIn::Barycentric { perspective: false }
}
x => x,
};
if !visible { return Err(VaryingError::InvalidBuiltInStage(built_in));
} if !type_good { return Err(VaryingError::InvalidBuiltInType(built_in, ty_inner.clone()));
}
} crate::Binding::Location {
location,
interpolation,
sampling,
blend_src,
per_primitive,
} => { if per_primitive && !self.capabilities.contains(Capabilities::MESH_SHADER) { return Err(VaryingError::UnsupportedCapability(
Capabilities::MESH_SHADER,
));
} if interpolation == Some(crate::Interpolation::PerVertex) { ifself.stage != crate::ShaderStage::Fragment { return Err(VaryingError::InvalidInterpolationInStage( crate::Interpolation::PerVertex, crate::ShaderStage::Fragment,
));
} if !self.capabilities.contains(Capabilities::PER_VERTEX) { return Err(VaryingError::UnsupportedCapability(
Capabilities::PER_VERTEX,
));
} if sampling.is_some_and(|e| e != crate::Sampling::Center) { return Err(VaryingError::InvalidPerVertexSampling);
}
} // If this is per-vertex, we change the type we validate to the inner type, otherwise we leave it be. // This lets all validation be done on the inner type once we've ensured the per-vertex is array<T, 3> let (ty, ty_inner) = if interpolation == Some(crate::Interpolation::PerVertex) { let three = crate::ArraySize::Constant(core::num::NonZeroU32::new(3).unwrap()); match ty_inner {
&Ti::Array { base, size, .. } if size == three => {
(base, &self.types[base].inner)
}
_ => return Err(VaryingError::PerVertexNotArrayOfThree),
}
} else {
(ty, ty_inner)
};
// Only IO-shareable types may be stored in locations. if !self.type_info[ty.index()]
.flags
.contains(super::TypeFlags::IO_SHAREABLE)
{ return Err(VaryingError::NotIOShareableType(ty));
}
// Check whether `per_primitive` is appropriate for this stage and direction. ifself.mesh_output_type == MeshOutputType::PrimitiveOutput { // All mesh shader `Location` outputs must be `per_primitive`. if !per_primitive { return Err(VaryingError::MissingPerPrimitive);
}
} elseifself.stage == crate::ShaderStage::Fragment && !self.output { // Fragment stage inputs may be `per_primitive`. We'll only // know if these are correct when the whole mesh pipeline is // created and we're paired with a specific mesh or vertex // shader.
} elseif per_primitive { // All other `Location` bindings must not be `per_primitive`. return Err(VaryingError::InvalidPerPrimitive);
}
if blend_src.is_some() { return Err(VaryingError::BlendSrcNotOnStructMember);
} elseif !self.location_mask.insert(location as usize)
&& self.flags.contains(super::ValidationFlags::BINDINGS)
{ return Err(VaryingError::BindingCollision { location });
}
// It doesn't make sense to specify a sampling when `interpolation` is `Flat`, but // SPIR-V and GLSL both explicitly tolerate such combinations of decorators / // qualifiers, so we won't complain about that here. let _ = sampling;
let required = match sampling {
Some(crate::Sampling::Sample) => Capabilities::MULTISAMPLED_SHADING,
_ => Capabilities::empty(),
}; if !self.capabilities.contains(required) { return Err(VaryingError::UnsupportedCapability(required));
}
if interpolation != Some(crate::Interpolation::PerVertex) { match ty_inner.scalar_kind() {
Some(crate::ScalarKind::Float) => { // Default interpolation is applied in the front end. if needs_interpolation && interpolation.is_none() { return Err(VaryingError::MissingInterpolation);
}
}
Some(crate::ScalarKind::Sint | crate::ScalarKind::Uint) => { // Integers do not have a default interpolation; `flat` must be // specified explicitly. if needs_interpolation
&& interpolation != Some(crate::Interpolation::Flat)
{ return Err(VaryingError::InvalidInterpolationForInteger);
}
}
Some(_) | None => return Err(VaryingError::InvalidType(ty)),
}
}
}
}
ifself.type_info[ty.index()]
.flags
.contains(super::TypeFlags::IO_SHAREABLE)
{ // `@blend_src` is the only case where `IO_SHAREABLE` is set on a struct (as // opposed to members of a struct). The struct definition is validated during // type validation. ifself.stage != crate::ShaderStage::Fragment { return Err(
VaryingError::InvalidAttributeInStage("blend_src", self.stage)
.with_span(),
);
} if !self.output { return Err(VaryingError::InvalidInputAttributeInStage( "blend_src", self.stage,
)
.with_span());
} // Dual blend sources must always be at location 0. if !self.location_mask.insert(0)
&& self.flags.contains(super::ValidationFlags::BINDINGS)
{ return Err(VaryingError::BindingCollision { location: 0 }.with_span());
}
log::debug!("var {var:?}"); let inner_ty = match gctx.types[var.ty].inner { // A binding array is (mostly) supposed to behave the same as a // series of individually bound resources, so we can (mostly) // validate a `binding_array<T>` as if it were just a plain `T`. crate::TypeInner::BindingArray { base, .. } => match var.space { crate::AddressSpace::Storage { .. } => { if !self
.capabilities
.contains(Capabilities::STORAGE_BUFFER_BINDING_ARRAY)
{ return Err(GlobalVariableError::UnsupportedCapability(
Capabilities::STORAGE_BUFFER_BINDING_ARRAY,
));
}
base
} crate::AddressSpace::Uniform => { if !self
.capabilities
.contains(Capabilities::BUFFER_BINDING_ARRAY)
{ return Err(GlobalVariableError::UnsupportedCapability(
Capabilities::BUFFER_BINDING_ARRAY,
));
}
base
} crate::AddressSpace::Handle => { match gctx.types[base].inner { crate::TypeInner::Image { class, .. } => match class { crate::ImageClass::Storage { .. } => { if !self
.capabilities
.contains(Capabilities::STORAGE_TEXTURE_BINDING_ARRAY)
{ return Err(GlobalVariableError::UnsupportedCapability(
Capabilities::STORAGE_TEXTURE_BINDING_ARRAY,
));
}
} crate::ImageClass::Sampled { .. } | crate::ImageClass::Depth { .. } => { if !self
.capabilities
.contains(Capabilities::TEXTURE_AND_SAMPLER_BINDING_ARRAY)
{ return Err(GlobalVariableError::UnsupportedCapability(
Capabilities::TEXTURE_AND_SAMPLER_BINDING_ARRAY,
));
}
} crate::ImageClass::External => { // This should have been rejected in `validate_type`.
unreachable!("binding arrays of external images are not supported");
}
}, crate::TypeInner::Sampler { .. } => { if !self
.capabilities
.contains(Capabilities::TEXTURE_AND_SAMPLER_BINDING_ARRAY)
{ return Err(GlobalVariableError::UnsupportedCapability(
Capabilities::TEXTURE_AND_SAMPLER_BINDING_ARRAY,
));
}
} crate::TypeInner::AccelerationStructure { .. } => { if !self
.capabilities
.contains(Capabilities::ACCELERATION_STRUCTURE_BINDING_ARRAY)
{ return Err(GlobalVariableError::UnsupportedCapability(
Capabilities::ACCELERATION_STRUCTURE_BINDING_ARRAY,
));
}
} crate::TypeInner::RayQuery { .. } => { // This should have been rejected in `validate_type`.
unreachable!("binding arrays of ray queries are not supported");
}
_ => { // Fall through to the regular validation, which will reject `base` // as invalid in `AddressSpace::Handle`.
}
}
base
}
_ => return Err(GlobalVariableError::InvalidUsage(var.space)),
},
_ => var.ty,
}; let type_info = &self.types[inner_ty.index()];
if var.space == crate::AddressSpace::TaskPayload { let ty = &gctx.types[var.ty].inner; // HLSL doesn't allow zero sized payloads. if ty.try_size(gctx) == Some(0) { return Err(GlobalVariableError::ZeroSizedTaskPayload);
}
}
if !var.memory_decorations.is_empty()
&& !matches!(var.space, crate::AddressSpace::Storage { .. })
{ return Err(GlobalVariableError::InvalidMemoryDecorationsAddressSpace);
} if var
.memory_decorations
.contains(crate::MemoryDecorations::COHERENT)
&& !self
.capabilities
.contains(Capabilities::MEMORY_DECORATION_COHERENT)
{ return Err(GlobalVariableError::CoherentNotSupported);
} if var
.memory_decorations
.contains(crate::MemoryDecorations::VOLATILE)
&& !self
.capabilities
.contains(Capabilities::MEMORY_DECORATION_VOLATILE)
{ return Err(GlobalVariableError::VolatileNotSupported);
}
// Other stages must not have a payload.
_ => { iflet Some(handle) = ep.task_payload { return Err(EntryPointError::UnexpectedTaskPayload
.with_span_handle(handle, &module.global_variables));
}
}
}
{ letmut used_immediates = module
.global_variables
.iter()
.filter(|&(_, var)| var.space == crate::AddressSpace::Immediate)
.map(|(handle, _)| handle)
.filter(|&handle| !info[handle].is_empty()); // Check if there is more than one immediate data, and error if so. // Use a loop for when returning multiple errors is supported. iflet Some(handle) = used_immediates.nth(1) { return Err(EntryPointError::MoreThanOneImmediateUsed
.with_span_handle(handle, &module.global_variables));
}
}
self.ep_resource_bindings.clear(); for (var_handle, var) in module.global_variables.iter() { let usage = info[var_handle]; if usage.is_empty() { continue;
}
if var.space == crate::AddressSpace::TaskPayload { if ep.task_payload != Some(var_handle) { return Err(EntryPointError::WrongTaskPayloadUsed
.with_span_handle(var_handle, &module.global_variables));
} let size = module.types[var.ty].inner.size(module.to_ctx()); if size < 4 { return Err(EntryPointError::TaskPayloadTooSmall(size)
.with_span_handle(var_handle, &module.global_variables));
}
}
// If this is a `Mesh` entry point, check its vertex and primitive output types. // We verified previously that only mesh shaders can have `mesh_info`. iflet &Some(ref mesh_info) = &ep.mesh_info { if module.global_variables[mesh_info.output_variable].space
!= crate::AddressSpace::WorkGroup
{ return Err(EntryPointError::WrongMeshOutputAddressSpace.with_span());
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.