mod constant_evaluator; mod emitter; pubmod index; mod keyword_set; mod layouter; mod namer; mod overloads; mod terminator; mod type_methods; mod typifier;
implcrate::Expression { /// Returns true if the expression is considered emitted at the start of a function. pubconstfn needs_pre_emit(&self) -> bool { match *self { Self::Literal(_)
| Self::Constant(_)
| Self::Override(_)
| Self::ZeroValue(_)
| Self::FunctionArgument(_)
| Self::GlobalVariable(_)
| Self::LocalVariable(_) => true,
_ => false,
}
}
/// Return true if this expression is a dynamic array/vector/matrix index, /// for [`Access`]. /// /// This method returns true if this expression is a dynamically computed /// index, and as such can only be used to index matrices when they appear /// behind a pointer. See the documentation for [`Access`] for details. /// /// Note, this does not check the _type_ of the given expression. It's up to /// the caller to establish that the `Access` expression is well-typed /// through other means, like [`ResolveContext`]. /// /// [`Access`]: crate::Expression::Access /// [`ResolveContext`]: crate::proc::ResolveContext pubconstfn is_dynamic_index(&self) -> bool { match *self { Self::Literal(_) | Self::ZeroValue(_) | Self::Constant(_) => false,
_ => true,
}
}
}
implcrate::Function { /// Return the global variable being accessed by the expression `pointer`. /// /// Assuming that `pointer` is a series of `Access` and `AccessIndex` /// expressions that ultimately access some part of a `GlobalVariable`, /// return a handle for that global. /// /// If the expression does not ultimately access a global variable, return /// `None`. pubfn originating_global(
&self, mut pointer: crate::Handle<crate::Expression>,
) -> Option<crate::Handle<crate::GlobalVariable>> { loop {
pointer = matchself.expressions[pointer] { crate::Expression::Access { base, .. } => base, crate::Expression::AccessIndex { base, .. } => base, crate::Expression::GlobalVariable(handle) => return Some(handle), crate::Expression::LocalVariable(_) => return None, crate::Expression::FunctionArgument(_) => return None, // There are no other expressions that produce pointer values.
_ => unreachable!(),
}
}
}
}
impl GlobalCtx<'_> { /// Try to evaluate the expression in `self.global_expressions` using its `handle` /// and return it as a `T: TryFrom<ir::Literal>`. /// /// This currently only evaluates scalar expressions. If adding support for vectors, /// consider changing `valid::expression::validate_constant_shift_amounts` to use that /// support. #[cfg_attr(
not(any(
feature = "glsl-in",
feature = "spv-in",
feature = "wgsl-in",
glsl_out,
hlsl_out,
msl_out,
wgsl_out
)),
allow(dead_code)
)] pub(super) fn get_const_val<T, E>(
&self,
handle: crate::Handle<crate::Expression>,
) -> Result<T, ConstValueError> where
T: TryFrom<crate::Literal, Error = E>,
E: Into<ConstValueError>,
{ self.get_const_val_from(handle, self.global_expressions)
}
#[derive(Error, Debug, Clone, Copy, PartialEq)] pubenum ResolveArraySizeError { #[error("array element count must be positive (> 0)")]
ExpectedPositiveArrayLength, #[error("internal: array size override has not been resolved")]
NonConstArrayLength,
}
implcrate::ArraySize { /// Return the number of elements that `size` represents, if known at code generation time. /// /// If `size` is override-based, return an error unless the override's /// initializer is a fully evaluated constant expression. You can call /// [`pipeline_constants::process_overrides`] to supply values for a /// module's overrides and ensure their initializers are fully evaluated, as /// this function expects. /// /// [`pipeline_constants::process_overrides`]: crate::back::pipeline_constants::process_overrides pubfn resolve(&self, gctx: GlobalCtx) -> Result<IndexableLength, ResolveArraySizeError> { match *self { crate::ArraySize::Constant(length) => Ok(IndexableLength::Known(length.get())), crate::ArraySize::Pending(handle) => { let Some(expr) = gctx.overrides[handle].init else { return Err(ResolveArraySizeError::NonConstArrayLength);
}; let length = gctx.get_const_val(expr).map_err(|err| match err {
ConstValueError::NonConst => ResolveArraySizeError::NonConstArrayLength,
ConstValueError::Negative | ConstValueError::InvalidType => {
ResolveArraySizeError::ExpectedPositiveArrayLength
}
})?;
if length == 0 { return Err(ResolveArraySizeError::ExpectedPositiveArrayLength);
}
/// Return an iterator over the individual components assembled by a /// `Compose` expression. /// /// Given `ty` and `components` from an `Expression::Compose`, return an /// iterator over the components of the resulting value. /// /// Normally, this would just be an iterator over `components`. However, /// `Compose` expressions can concatenate vectors, in which case the i'th /// value being composed is not generally the i'th element of `components`. /// This function consults `ty` to decide if this concatenation is occurring, /// and returns an iterator that produces the components of the result of /// the `Compose` expression in either case. pubfn flatten_compose<'arenas>(
ty: crate::Handle<crate::Type>,
components: &'arenas [crate::Handle<crate::Expression>],
expressions: &'arenas crate::Arena<crate::Expression>,
types: &'arenas crate::UniqueArena<crate::Type>,
) -> impl Iterator<Item = crate::Handle<crate::Expression>> + 'arenas { // Returning `impl Iterator` is a bit tricky. We may or may not // want to flatten the components, but we have to settle on a // single concrete type to return. This function returns a single // iterator chain that handles both the flattening and // non-flattening cases. let (size, is_vector) = ifletcrate::TypeInner::Vector { size, .. } = types[ty].inner {
(size as usize, true)
} else {
(components.len(), false)
};
// Expressions like `vec4(vec3(vec2(6, 7), 8), 9)` require us to // flatten up to two levels of `Compose` expressions. // // Expressions like `vec4(vec3(1.0), 1.0)` require us to flatten // `Splat` expressions. Fortunately, the operand of a `Splat` must // be a scalar, so we can stop there.
components
.iter()
.flat_map(move |component| flatten_compose(component, is_vector, expressions))
.flat_map(move |component| flatten_compose(component, is_vector, expressions))
.flat_map(move |component| flatten_splat(component, is_vector, expressions))
.take(size)
}
implcrate::Module { /// Extracts mesh shader info from a mesh output global variable. Used in frontends /// and by validators. This only validates the output variable itself, and not the /// vertex and primitive output types. /// /// The output contains the extracted mesh stage info, with overrides unset, /// and then the overrides separately. This is because the overrides should be /// treated as expressions elsewhere, but that requires mutably modifying the /// module and the expressions should only be created at parse time, not validation /// time. #[allow(clippy::type_complexity)] pubfn analyze_mesh_shader_info(
&self,
gv: crate::Handle<crate::GlobalVariable>,
) -> ( crate::MeshStageInfo,
[Option<crate::Handle<crate::Override>>; 2],
Option<crate::WithSpan<crate::valid::EntryPointError>>,
) { usecrate::span::AddSpan; usecrate::valid::EntryPointError; #[derive(Default)] struct OutError { pub inner: Option<EntryPointError>,
} impl OutError { pubfn set(&mutself, err: EntryPointError) { ifself.inner.is_none() { self.inner = Some(err);
}
}
}
// Used to temporarily initialize stuff let null_type = crate::Handle::new(NonMaxU32::new(0).unwrap()); letmut output = crate::MeshStageInfo {
topology: crate::MeshOutputTopology::Triangles,
max_vertices: 0,
max_vertices_override: None,
max_primitives: 0,
max_primitives_override: None,
vertex_output_type: null_type,
primitive_output_type: null_type,
output_variable: gv,
}; // Stores the error to output, if any. letmut error = OutError::default(); let r#type = &self.types[self.global_variables[gv].ty].inner;
match r#type {
&crate::TypeInner::Struct { ref members, .. } => { letmut builtins = crate::FastHashSet::default(); for member in members { match member.binding {
Some(crate::Binding::BuiltIn(crate::BuiltIn::VertexCount)) => { // Must have type u32 ifself.types[member.ty].inner.scalar() != Some(crate::Scalar::U32) {
error.set(EntryPointError::BadMeshOutputVariableField);
} // Each builtin should only occur once if builtins.contains(&crate::BuiltIn::VertexCount) {
error.set(EntryPointError::BadMeshOutputVariableType);
}
builtins.insert(crate::BuiltIn::VertexCount);
}
Some(crate::Binding::BuiltIn(crate::BuiltIn::PrimitiveCount)) => { // Must have type u32 ifself.types[member.ty].inner.scalar() != Some(crate::Scalar::U32) {
error.set(EntryPointError::BadMeshOutputVariableField);
} // Each builtin should only occur once if builtins.contains(&crate::BuiltIn::PrimitiveCount) {
error.set(EntryPointError::BadMeshOutputVariableType);
}
builtins.insert(crate::BuiltIn::PrimitiveCount);
}
Some(crate::Binding::BuiltIn( crate::BuiltIn::Vertices | crate::BuiltIn::Primitives,
)) => { let ty = &self.types[member.ty].inner; // Analyze the array type to determine size and vertex/primitive type let (a, b, c) = match ty {
&crate::TypeInner::Array { base, size, .. } => { let ty = base; let (max, max_override) = match size { crate::ArraySize::Constant(a) => (a.get(), None), crate::ArraySize::Pending(o) => (0, Some(o)), crate::ArraySize::Dynamic => {
error.set(EntryPointError::BadMeshOutputVariableField);
(0, None)
}
};
(max, max_override, ty)
}
_ => {
error.set(EntryPointError::BadMeshOutputVariableField);
(0, None, null_type)
}
}; if matches!(
member.binding,
Some(crate::Binding::BuiltIn(crate::BuiltIn::Primitives))
) { // Primitives require special analysis to determine topology
primitive_info = (a, b, c); matchself.types[c].inner { crate::TypeInner::Struct { ref members, .. } => { for member in members { match member.binding {
Some(crate::Binding::BuiltIn( crate::BuiltIn::PointIndex,
)) => {
topology = crate::MeshOutputTopology::Points;
}
Some(crate::Binding::BuiltIn( crate::BuiltIn::LineIndices,
)) => {
topology = crate::MeshOutputTopology::Lines;
}
Some(crate::Binding::BuiltIn( crate::BuiltIn::TriangleIndices,
)) => {
topology = crate::MeshOutputTopology::Triangles;
}
_ => (),
}
}
}
_ => (),
} // Each builtin should only occur once if builtins.contains(&crate::BuiltIn::Primitives) {
error.set(EntryPointError::BadMeshOutputVariableType);
}
builtins.insert(crate::BuiltIn::Primitives);
} else {
vertex_info = (a, b, c); // Each builtin should only occur once if builtins.contains(&crate::BuiltIn::Vertices) {
error.set(EntryPointError::BadMeshOutputVariableType);
}
builtins.insert(crate::BuiltIn::Vertices);
}
}
_ => error.set(EntryPointError::BadMeshOutputVariableType),
}
}
output = crate::MeshStageInfo {
topology,
max_vertices: vertex_info.0,
max_vertices_override: None,
vertex_output_type: vertex_info.2,
max_primitives: primitive_info.0,
max_primitives_override: None,
primitive_output_type: primitive_info.2,
..output
}
}
_ => error.set(EntryPointError::BadMeshOutputVariableType),
}
(
output,
[vertex_info.1, primitive_info.1],
error
.inner
.map(|a| a.with_span_handle(self.global_variables[gv].ty, &self.types)),
)
}
pubfn uses_mesh_shaders(&self) -> bool { let binding_uses_mesh = |b: &crate::Binding| {
matches!(
b, crate::Binding::BuiltIn( crate::BuiltIn::MeshTaskSize
| crate::BuiltIn::CullPrimitive
| crate::BuiltIn::PointIndex
| crate::BuiltIn::LineIndices
| crate::BuiltIn::TriangleIndices
| crate::BuiltIn::VertexCount
| crate::BuiltIn::Vertices
| crate::BuiltIn::PrimitiveCount
| crate::BuiltIn::Primitives,
) | crate::Binding::Location {
per_primitive: true,
..
}
)
}; for (_, ty) inself.types.iter() { match ty.inner { crate::TypeInner::Struct { ref members, .. } => { for binding in members.iter().filter_map(|m| m.binding.as_ref()) { if binding_uses_mesh(binding) { returntrue;
}
}
}
_ => (),
}
} for ep in &self.entry_points { if matches!(
ep.stage, crate::ShaderStage::Mesh | crate::ShaderStage::Task
) { returntrue;
} for binding in ep
.function
.arguments
.iter()
.filter_map(|arg| arg.binding.as_ref())
.chain(
ep.function
.result
.iter()
.filter_map(|res| res.binding.as_ref()),
)
{ if binding_uses_mesh(binding) { returntrue;
}
}
} ifself
.global_variables
.iter()
.any(|gv| gv.1.space == crate::AddressSpace::TaskPayload)
{ returntrue;
} false
}
pubfn uses_ray_tracing(&self, ep_index: Option<usize>) -> RayTracingUses { letmut uses = RayTracingUses::default(); // Whether this uses ray tracing (unknown whether the usage is pipelines or ray queries). letmut uses_ray_tracing = self.special_types.ray_desc.is_some();
for (_, &crate::Type { ref inner, .. }) inself.types.iter() { // Backends do not know whether these have vertex return - that is done by us match *inner { crate::TypeInner::AccelerationStructure { .. } => {
uses_ray_tracing = true;
} crate::TypeInner::RayQuery { .. } => uses.queries = true,
_ => {}
}
}
for (index, ep) inself.entry_points.iter().enumerate() { if ep_index.is_some() && ep_index != Some(index) { continue;
}
// if we have a ray tracing pipeline shader we are definitely using // pipelines, otherwise, if we have a ray tracing type, we might // be using it in the shader (which would require ray queries), // so we should use queries. if matches!(
ep.stage, crate::ShaderStage::RayGeneration
| crate::ShaderStage::AnyHit
| crate::ShaderStage::ClosestHit
| crate::ShaderStage::Miss
) {
uses.pipelines = true;
} else {
uses.queries |= uses_ray_tracing;
}
}
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.