mirror of
https://github.com/gfx-rs/wgpu.git
synced 2026-04-22 03:02:01 -04:00
1093 lines
34 KiB
Rust
1093 lines
34 KiB
Rust
/*! Universal shader translator.
|
|
|
|
The central structure of the crate is [`Module`]. A `Module` contains:
|
|
|
|
- [`EntryPoint`]s, the main functions for pipeline stages like vertex shading or
|
|
fragment shading,
|
|
|
|
- [`Function`]s, representing functions used by `EntryPoint`s and other `Function`s,
|
|
|
|
- [`Constant`]s and [`GlobalVariable`]s used by `EntryPoint`s and `Function`s, and
|
|
|
|
- [`Type`]s used by the above.
|
|
|
|
The body of an `EntryPoint` or `Function` is represented using two types:
|
|
|
|
- An [`Expression`] produces a value, but has no side effects or control flow.
|
|
`Expressions` include variable references, unary and binary operators, and so
|
|
on.
|
|
|
|
- A [`Statement`] can have side effects and structured control flow.
|
|
`Statement`s do not produce a value, other than by storing one in some
|
|
designated place. `Statements` include blocks, conditionals, and loops, but also
|
|
operations that have side effects, like stores and function calls.
|
|
|
|
`Statement`s form a tree, with pointers into the DAG of `Expression`s.
|
|
|
|
Restricting side effects to statements simplifies analysis and code generation.
|
|
A Naga backend can generate code to evaluate an `Expression` however and
|
|
whenever it pleases, as long as it is certain to observe the side effects of all
|
|
previously executed `Statement`s.
|
|
|
|
Many `Statement` variants use the [`Block`] type, which is simply `Vec<Block>`,
|
|
representing a series of statements executed in order. The body of an
|
|
`EntryPoint`s or `Function` is a `Block`, and `Statement` has a
|
|
[`Block`][Statement::Block] variant.
|
|
|
|
To improve translator performance and reduce memory usage, most structures are
|
|
stored in an [`Arena`]. An `Arena<T>` stores a series of `T` values, indexed by
|
|
[`Handle<T>`](Handle) values, which are just wrappers around integer indexes.
|
|
For example, a `Function`'s expressions are stored in an `Arena<Expression>`,
|
|
and compound expressions refer to their sub-expressions via `Handle<Expression>`
|
|
values. (When examining the serialized form of a `Module`, note that the first
|
|
element of an `Arena` has an index of 1, not 0.)
|
|
|
|
## Function Calls
|
|
|
|
The Naga IR's representation of function calls is unusual. Most languages treat
|
|
function calls as expressions, but because calls may have side effects, Naga
|
|
represents them with [`Statement::Call`]. A call statement may designate a
|
|
particular `Expression` to represent its return value, if any, which can be used
|
|
by subsequent statements and their expressions.
|
|
|
|
## `Expression` evaluation time and scope
|
|
|
|
While the order of execution of [`Statement`]s is apparent from their structure,
|
|
it is not so obvious exactly when a given [`Expression`] should be evaluated,
|
|
since many `Statement`s and `Expression`s may refer to its value. But it is
|
|
essential to clearly specify an expression's evaluation time, since that
|
|
determines which statements' effects the expression should observe. It is also
|
|
helpful to backends to limit the visibility of an `Expression`'s value to a
|
|
portion of the statement tree.
|
|
|
|
An `Expression` may only be used, whether by a `Statement` or another
|
|
`Expression`, if one of the following is true:
|
|
|
|
- The expression is an [`Expression::Constant`], [`Expression::FunctionArgument`], or
|
|
[`Expression::GlobalVariable`].
|
|
|
|
- The expression is an [`Expression::LocalVariable`] that is either the
|
|
`pointer` (destination) of a [`Statement::Store`], or initialized by some
|
|
previously executed `Statement::Store`.
|
|
|
|
- The expression is the `result` of a [`Statement::Call`], representing the
|
|
call's return value. The call must be 'in scope' for the use (see below).
|
|
|
|
- The expression is included in the range of some [`Statement::Emit`] that is
|
|
'in scope' for the use (see below). The [`Expression::needs_pre_emit`] method
|
|
returns `true` if the given expression does *not* need to be covered by an
|
|
`Emit` statement.
|
|
|
|
The scope of an expression evaluated by an `Emit` statement covers the
|
|
subsequent expressions in that `Emit`, any following statements in the `Block`
|
|
to which that `Emit` belongs (if any) and their sub-statements (if any).
|
|
|
|
If a `Call` statement has a `result` expression, then it is in scope for any
|
|
statements following the `Call` in the `Block` to which that `Call` belongs (if
|
|
any) and their sub-statements (if any).
|
|
|
|
This means that, for example, an expression evaluated by some statement in a
|
|
nested `Block` is not available in the `Block`'s parents. Such a value would
|
|
need to be stored in a local variable to be carried upwards in the statement
|
|
tree.
|
|
|
|
## Variables
|
|
|
|
An [`Expression::LocalVariable`] or [`Expression::GlobalVariable`] produces a
|
|
pointer value referring to the variable's value. To retrieve the variable's
|
|
value, use an [`Expression::Load`], with the variable expression as its
|
|
`pointer` operand. To assign to a variable, use a [`Statement::Store`] with the
|
|
variable expression as its `pointer` operand.
|
|
|
|
As an exception, [`Expression::GlobalVariable`]s referring to
|
|
[`GlobalVariable`]s whose `class` is [`StorageClass::Handle`] produce the
|
|
variable's value directly; no `Load` is needed. These global variables refer to
|
|
opaque values like samplers and images.
|
|
|
|
!*/
|
|
|
|
// TODO: use `strip_prefix` instead when Rust 1.45 <= MSRV
|
|
#![allow(
|
|
renamed_and_removed_lints,
|
|
unknown_lints, // requires Rust 1.51
|
|
clippy::new_without_default,
|
|
clippy::unneeded_field_pattern,
|
|
clippy::match_like_matches_macro,
|
|
clippy::manual_strip,
|
|
clippy::unknown_clippy_lints,
|
|
)]
|
|
#![warn(
|
|
trivial_casts,
|
|
trivial_numeric_casts,
|
|
unused_extern_crates,
|
|
unused_qualifications,
|
|
clippy::pattern_type_mismatch
|
|
)]
|
|
#![deny(clippy::panic)]
|
|
|
|
mod arena;
|
|
pub mod back;
|
|
pub mod front;
|
|
pub mod proc;
|
|
pub mod valid;
|
|
|
|
pub use crate::arena::{Arena, Handle, Range};
|
|
|
|
use std::{
|
|
collections::{HashMap, HashSet},
|
|
hash::BuildHasherDefault,
|
|
};
|
|
|
|
#[cfg(feature = "deserialize")]
|
|
use serde::Deserialize;
|
|
#[cfg(feature = "serialize")]
|
|
use serde::Serialize;
|
|
|
|
/// Width of a boolean type, in bytes.
|
|
pub const BOOL_WIDTH: Bytes = 1;
|
|
|
|
/// Hash map that is faster but not resilient to DoS attacks.
|
|
pub type FastHashMap<K, T> = HashMap<K, T, BuildHasherDefault<fxhash::FxHasher>>;
|
|
/// Hash set that is faster but not resilient to DoS attacks.
|
|
pub type FastHashSet<K> = HashSet<K, BuildHasherDefault<fxhash::FxHasher>>;
|
|
|
|
/// Early fragment tests. In a standard situation if a driver determines that it is possible to
|
|
/// switch on early depth test it will. Typical situations when early depth test is switched off:
|
|
/// - Calling ```discard``` in a shader.
|
|
/// - Writing to the depth buffer, unless ConservativeDepth is enabled.
|
|
///
|
|
/// SPIR-V: ExecutionMode EarlyFragmentTests
|
|
/// In GLSL: layout(early_fragment_tests) in;
|
|
/// HLSL: Attribute earlydepthstencil
|
|
///
|
|
/// For more, see:
|
|
/// - https://www.khronos.org/opengl/wiki/Early_Fragment_Test#Explicit_specification
|
|
/// - https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/sm5-attributes-earlydepthstencil
|
|
#[derive(Clone, Copy, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]
|
|
#[cfg_attr(feature = "serialize", derive(Serialize))]
|
|
#[cfg_attr(feature = "deserialize", derive(Deserialize))]
|
|
pub struct EarlyDepthTest {
|
|
conservative: Option<ConservativeDepth>,
|
|
}
|
|
/// Enables adjusting depth without disabling early Z.
|
|
///
|
|
/// SPIR-V: ExecutionMode DepthGreater/DepthLess/DepthUnchanged
|
|
/// GLSL: layout (depth_<greater/less/unchanged/any>) out float gl_FragDepth;
|
|
/// - ```depth_any``` option behaves as if the layout qualifier was not present.
|
|
/// HLSL: SV_Depth/SV_DepthGreaterEqual/SV_DepthLessEqual
|
|
///
|
|
/// For more, see:
|
|
/// - https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_conservative_depth.txt
|
|
/// - https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-semantics#system-value-semantics
|
|
#[derive(Clone, Copy, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]
|
|
#[cfg_attr(feature = "serialize", derive(Serialize))]
|
|
#[cfg_attr(feature = "deserialize", derive(Deserialize))]
|
|
pub enum ConservativeDepth {
|
|
/// Shader may rewrite depth only with a value greater than calculated;
|
|
GreaterEqual,
|
|
|
|
/// Shader may rewrite depth smaller than one that would have been written without the modification.
|
|
LessEqual,
|
|
|
|
/// Shader may not rewrite depth value.
|
|
Unchanged,
|
|
}
|
|
|
|
/// Stage of the programmable pipeline.
|
|
#[derive(Clone, Copy, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]
|
|
#[cfg_attr(feature = "serialize", derive(Serialize))]
|
|
#[cfg_attr(feature = "deserialize", derive(Deserialize))]
|
|
#[allow(missing_docs)] // The names are self evident
|
|
pub enum ShaderStage {
|
|
Vertex,
|
|
Fragment,
|
|
Compute,
|
|
}
|
|
|
|
/// Class of storage for variables.
|
|
#[derive(Clone, Copy, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]
|
|
#[cfg_attr(feature = "serialize", derive(Serialize))]
|
|
#[cfg_attr(feature = "deserialize", derive(Deserialize))]
|
|
pub enum StorageClass {
|
|
/// Function locals.
|
|
Function,
|
|
/// Private data, per invocation, mutable.
|
|
Private,
|
|
/// Workgroup shared data, mutable.
|
|
WorkGroup,
|
|
/// Uniform buffer data.
|
|
Uniform,
|
|
/// Storage buffer data, potentially mutable.
|
|
Storage,
|
|
/// Opaque handles, such as samplers and images.
|
|
Handle,
|
|
/// Push constants.
|
|
PushConstant,
|
|
}
|
|
|
|
/// Built-in inputs and outputs.
|
|
#[derive(Clone, Copy, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]
|
|
#[cfg_attr(feature = "serialize", derive(Serialize))]
|
|
#[cfg_attr(feature = "deserialize", derive(Deserialize))]
|
|
pub enum BuiltIn {
|
|
Position,
|
|
// vertex
|
|
BaseInstance,
|
|
BaseVertex,
|
|
ClipDistance,
|
|
CullDistance,
|
|
InstanceIndex,
|
|
PointSize,
|
|
VertexIndex,
|
|
// fragment
|
|
FragDepth,
|
|
FrontFacing,
|
|
SampleIndex,
|
|
SampleMask,
|
|
// compute
|
|
GlobalInvocationId,
|
|
LocalInvocationId,
|
|
LocalInvocationIndex,
|
|
WorkGroupId,
|
|
WorkGroupSize,
|
|
}
|
|
|
|
/// Number of bytes per scalar.
|
|
pub type Bytes = u8;
|
|
|
|
/// Number of components in a vector.
|
|
#[repr(u8)]
|
|
#[derive(Clone, Copy, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]
|
|
#[cfg_attr(feature = "serialize", derive(Serialize))]
|
|
#[cfg_attr(feature = "deserialize", derive(Deserialize))]
|
|
pub enum VectorSize {
|
|
/// 2D vector
|
|
Bi = 2,
|
|
/// 3D vector
|
|
Tri = 3,
|
|
/// 4D vector
|
|
Quad = 4,
|
|
}
|
|
|
|
/// Primitive type for a scalar.
|
|
#[repr(u8)]
|
|
#[derive(Clone, Copy, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]
|
|
#[cfg_attr(feature = "serialize", derive(Serialize))]
|
|
#[cfg_attr(feature = "deserialize", derive(Deserialize))]
|
|
pub enum ScalarKind {
|
|
/// Signed integer type.
|
|
Sint,
|
|
/// Unsigned integer type.
|
|
Uint,
|
|
/// Floating point type.
|
|
Float,
|
|
/// Boolean type.
|
|
Bool,
|
|
}
|
|
|
|
/// Size of an array.
|
|
#[repr(u8)]
|
|
#[derive(Clone, Copy, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]
|
|
#[cfg_attr(feature = "serialize", derive(Serialize))]
|
|
#[cfg_attr(feature = "deserialize", derive(Deserialize))]
|
|
pub enum ArraySize {
|
|
/// The array size is constant.
|
|
Constant(Handle<Constant>),
|
|
/// The array size can change at runtime.
|
|
Dynamic,
|
|
}
|
|
|
|
/// The interpolation qualifier of a binding or struct field.
|
|
#[derive(Clone, Copy, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]
|
|
#[cfg_attr(feature = "serialize", derive(Serialize))]
|
|
#[cfg_attr(feature = "deserialize", derive(Deserialize))]
|
|
pub enum Interpolation {
|
|
/// The value will be interpolated in a perspective-correct fashion.
|
|
/// Also known as "smooth" in glsl.
|
|
Perspective,
|
|
/// Indicates that linear, non-perspective, correct
|
|
/// interpolation must be used.
|
|
/// Also known as "no_perspective" in glsl.
|
|
Linear,
|
|
/// Indicates that no interpolation will be performed.
|
|
Flat,
|
|
}
|
|
|
|
/// The sampling qualifiers of a binding or struct field.
|
|
#[derive(Clone, Copy, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]
|
|
#[cfg_attr(feature = "serialize", derive(Serialize))]
|
|
#[cfg_attr(feature = "deserialize", derive(Deserialize))]
|
|
pub enum Sampling {
|
|
/// Interpolate the value at the center of the pixel.
|
|
Center,
|
|
|
|
/// Interpolate the value at a point that lies within all samples covered by
|
|
/// the fragment within the current primitive. In multisampling, use a
|
|
/// single value for all samples in the primitive.
|
|
Centroid,
|
|
|
|
/// Interpolate the value at each sample location. In multisampling, invoke
|
|
/// the fragment shader once per sample.
|
|
Sample,
|
|
}
|
|
|
|
/// Member of a user-defined structure.
|
|
// Clone is used only for error reporting and is not intended for end users
|
|
#[derive(Clone, Debug, PartialEq)]
|
|
#[cfg_attr(feature = "serialize", derive(Serialize))]
|
|
#[cfg_attr(feature = "deserialize", derive(Deserialize))]
|
|
pub struct StructMember {
|
|
pub name: Option<String>,
|
|
/// Type of the field.
|
|
pub ty: Handle<Type>,
|
|
/// For I/O structs, defines the binding.
|
|
pub binding: Option<Binding>,
|
|
/// Offset from the beginning from the struct.
|
|
pub offset: u32,
|
|
}
|
|
|
|
/// The number of dimensions an image has.
|
|
#[derive(Clone, Copy, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]
|
|
#[cfg_attr(feature = "serialize", derive(Serialize))]
|
|
#[cfg_attr(feature = "deserialize", derive(Deserialize))]
|
|
pub enum ImageDimension {
|
|
/// 1D image
|
|
D1,
|
|
/// 2D image
|
|
D2,
|
|
/// 3D image
|
|
D3,
|
|
/// Cube map
|
|
Cube,
|
|
}
|
|
|
|
bitflags::bitflags! {
|
|
/// Flags describing an image.
|
|
#[cfg_attr(feature = "serialize", derive(Serialize))]
|
|
#[cfg_attr(feature = "deserialize", derive(Deserialize))]
|
|
#[derive(Default)]
|
|
pub struct StorageAccess: u32 {
|
|
/// Storage can be used as a source for load ops.
|
|
const LOAD = 0x1;
|
|
/// Storage can be used as a target for store ops.
|
|
const STORE = 0x2;
|
|
}
|
|
}
|
|
|
|
// Storage image format.
|
|
#[derive(Clone, Copy, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]
|
|
#[cfg_attr(feature = "serialize", derive(Serialize))]
|
|
#[cfg_attr(feature = "deserialize", derive(Deserialize))]
|
|
pub enum StorageFormat {
|
|
// 8-bit formats
|
|
R8Unorm,
|
|
R8Snorm,
|
|
R8Uint,
|
|
R8Sint,
|
|
|
|
// 16-bit formats
|
|
R16Uint,
|
|
R16Sint,
|
|
R16Float,
|
|
Rg8Unorm,
|
|
Rg8Snorm,
|
|
Rg8Uint,
|
|
Rg8Sint,
|
|
|
|
// 32-bit formats
|
|
R32Uint,
|
|
R32Sint,
|
|
R32Float,
|
|
Rg16Uint,
|
|
Rg16Sint,
|
|
Rg16Float,
|
|
Rgba8Unorm,
|
|
Rgba8Snorm,
|
|
Rgba8Uint,
|
|
Rgba8Sint,
|
|
|
|
// Packed 32-bit formats
|
|
Rgb10a2Unorm,
|
|
Rg11b10Float,
|
|
|
|
// 64-bit formats
|
|
Rg32Uint,
|
|
Rg32Sint,
|
|
Rg32Float,
|
|
Rgba16Uint,
|
|
Rgba16Sint,
|
|
Rgba16Float,
|
|
|
|
// 128-bit formats
|
|
Rgba32Uint,
|
|
Rgba32Sint,
|
|
Rgba32Float,
|
|
}
|
|
|
|
/// Sub-class of the image type.
|
|
#[derive(Clone, Copy, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]
|
|
#[cfg_attr(feature = "serialize", derive(Serialize))]
|
|
#[cfg_attr(feature = "deserialize", derive(Deserialize))]
|
|
pub enum ImageClass {
|
|
/// Regular sampled image.
|
|
Sampled {
|
|
/// Kind of values to sample.
|
|
kind: ScalarKind,
|
|
// Multi-sampled.
|
|
multi: bool,
|
|
},
|
|
/// Depth comparison image.
|
|
Depth,
|
|
/// Storage image.
|
|
Storage(StorageFormat),
|
|
}
|
|
|
|
/// A data type declared in the module.
|
|
#[derive(Debug, PartialEq)]
|
|
#[cfg_attr(feature = "serialize", derive(Serialize))]
|
|
#[cfg_attr(feature = "deserialize", derive(Deserialize))]
|
|
pub struct Type {
|
|
/// The name of the type, if any.
|
|
pub name: Option<String>,
|
|
/// Inner structure that depends on the kind of the type.
|
|
pub inner: TypeInner,
|
|
}
|
|
|
|
/// Enum with additional information, depending on the kind of type.
|
|
#[derive(Debug, PartialEq)]
|
|
#[cfg_attr(feature = "serialize", derive(Serialize))]
|
|
#[cfg_attr(feature = "deserialize", derive(Deserialize))]
|
|
pub enum TypeInner {
|
|
/// Number of integral or floating-point kind.
|
|
Scalar { kind: ScalarKind, width: Bytes },
|
|
/// Vector of numbers.
|
|
Vector {
|
|
size: VectorSize,
|
|
kind: ScalarKind,
|
|
width: Bytes,
|
|
},
|
|
/// Matrix of floats.
|
|
Matrix {
|
|
columns: VectorSize,
|
|
rows: VectorSize,
|
|
width: Bytes,
|
|
},
|
|
/// Pointer to another type.
|
|
Pointer {
|
|
base: Handle<Type>,
|
|
class: StorageClass,
|
|
},
|
|
/// Pointer to a value.
|
|
ValuePointer {
|
|
size: Option<VectorSize>,
|
|
kind: ScalarKind,
|
|
width: Bytes,
|
|
class: StorageClass,
|
|
},
|
|
/// Homogenous list of elements.
|
|
Array {
|
|
base: Handle<Type>,
|
|
size: ArraySize,
|
|
stride: u32,
|
|
},
|
|
/// User-defined structure.
|
|
Struct {
|
|
top_level: bool,
|
|
members: Vec<StructMember>,
|
|
//TODO: should this be unaligned?
|
|
span: u32,
|
|
},
|
|
/// Possibly multidimensional array of texels.
|
|
Image {
|
|
dim: ImageDimension,
|
|
arrayed: bool,
|
|
class: ImageClass,
|
|
},
|
|
/// Can be used to sample values from images.
|
|
Sampler { comparison: bool },
|
|
}
|
|
|
|
/// Constant value.
|
|
#[derive(Debug, PartialEq)]
|
|
#[cfg_attr(feature = "serialize", derive(Serialize))]
|
|
#[cfg_attr(feature = "deserialize", derive(Deserialize))]
|
|
pub struct Constant {
|
|
pub name: Option<String>,
|
|
pub specialization: Option<u32>,
|
|
pub inner: ConstantInner,
|
|
}
|
|
|
|
/// A literal scalar value, used in constants.
|
|
#[derive(Debug, Clone, Copy, PartialOrd)]
|
|
#[cfg_attr(feature = "serialize", derive(Serialize))]
|
|
#[cfg_attr(feature = "deserialize", derive(Deserialize))]
|
|
pub enum ScalarValue {
|
|
Sint(i64),
|
|
Uint(u64),
|
|
Float(f64),
|
|
Bool(bool),
|
|
}
|
|
|
|
/// Additional information, dependent on the kind of constant.
|
|
#[derive(Clone, Debug, PartialEq)]
|
|
#[cfg_attr(feature = "serialize", derive(Serialize))]
|
|
#[cfg_attr(feature = "deserialize", derive(Deserialize))]
|
|
pub enum ConstantInner {
|
|
Scalar {
|
|
width: Bytes,
|
|
value: ScalarValue,
|
|
},
|
|
Composite {
|
|
ty: Handle<Type>,
|
|
components: Vec<Handle<Constant>>,
|
|
},
|
|
}
|
|
|
|
/// Describes how an input/output variable is to be bound.
|
|
#[derive(Clone, Debug, PartialEq)]
|
|
#[cfg_attr(feature = "serialize", derive(Serialize))]
|
|
#[cfg_attr(feature = "deserialize", derive(Deserialize))]
|
|
pub enum Binding {
|
|
/// Built-in shader variable.
|
|
BuiltIn(BuiltIn),
|
|
/// Indexed location.
|
|
Location {
|
|
location: u32,
|
|
interpolation: Option<Interpolation>,
|
|
sampling: Option<Sampling>,
|
|
},
|
|
}
|
|
|
|
/// Pipeline binding information for global resources.
|
|
#[derive(Clone, Debug, PartialEq)]
|
|
#[cfg_attr(feature = "serialize", derive(Serialize))]
|
|
#[cfg_attr(feature = "deserialize", derive(Deserialize))]
|
|
pub struct ResourceBinding {
|
|
/// The bind group index.
|
|
pub group: u32,
|
|
/// Binding number within the group.
|
|
pub binding: u32,
|
|
}
|
|
|
|
/// Variable defined at module level.
|
|
#[derive(Clone, Debug, PartialEq)]
|
|
#[cfg_attr(feature = "serialize", derive(Serialize))]
|
|
#[cfg_attr(feature = "deserialize", derive(Deserialize))]
|
|
pub struct GlobalVariable {
|
|
/// Name of the variable, if any.
|
|
pub name: Option<String>,
|
|
/// How this variable is to be stored.
|
|
pub class: StorageClass,
|
|
/// For resources, defines the binding point.
|
|
pub binding: Option<ResourceBinding>,
|
|
/// The type of this variable.
|
|
pub ty: Handle<Type>,
|
|
/// Initial value for this variable.
|
|
pub init: Option<Handle<Constant>>,
|
|
/// Access bit for storage types of images and buffers.
|
|
pub storage_access: StorageAccess,
|
|
}
|
|
|
|
/// Variable defined at function level.
|
|
#[derive(Clone, Debug)]
|
|
#[cfg_attr(feature = "serialize", derive(Serialize))]
|
|
#[cfg_attr(feature = "deserialize", derive(Deserialize))]
|
|
pub struct LocalVariable {
|
|
/// Name of the variable, if any.
|
|
pub name: Option<String>,
|
|
/// The type of this variable.
|
|
pub ty: Handle<Type>,
|
|
/// Initial value for this variable.
|
|
pub init: Option<Handle<Constant>>,
|
|
}
|
|
|
|
/// Operation that can be applied on a single value.
|
|
#[derive(Clone, Copy, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]
|
|
#[cfg_attr(feature = "serialize", derive(Serialize))]
|
|
#[cfg_attr(feature = "deserialize", derive(Deserialize))]
|
|
pub enum UnaryOperator {
|
|
Negate,
|
|
Not,
|
|
}
|
|
|
|
/// Operation that can be applied on two values.
|
|
#[derive(Clone, Copy, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]
|
|
#[cfg_attr(feature = "serialize", derive(Serialize))]
|
|
#[cfg_attr(feature = "deserialize", derive(Deserialize))]
|
|
pub enum BinaryOperator {
|
|
Add,
|
|
Subtract,
|
|
Multiply,
|
|
Divide,
|
|
Modulo,
|
|
Equal,
|
|
NotEqual,
|
|
Less,
|
|
LessEqual,
|
|
Greater,
|
|
GreaterEqual,
|
|
And,
|
|
ExclusiveOr,
|
|
InclusiveOr,
|
|
LogicalAnd,
|
|
LogicalOr,
|
|
ShiftLeft,
|
|
/// Right shift carries the sign of signed integers only.
|
|
ShiftRight,
|
|
}
|
|
|
|
/// Axis on which to compute a derivative.
|
|
#[derive(Clone, Copy, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]
|
|
#[cfg_attr(feature = "serialize", derive(Serialize))]
|
|
#[cfg_attr(feature = "deserialize", derive(Deserialize))]
|
|
pub enum DerivativeAxis {
|
|
X,
|
|
Y,
|
|
Width,
|
|
}
|
|
|
|
/// Built-in shader function for testing relation between values.
|
|
#[derive(Clone, Copy, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]
|
|
#[cfg_attr(feature = "serialize", derive(Serialize))]
|
|
#[cfg_attr(feature = "deserialize", derive(Deserialize))]
|
|
pub enum RelationalFunction {
|
|
All,
|
|
Any,
|
|
IsNan,
|
|
IsInf,
|
|
IsFinite,
|
|
IsNormal,
|
|
}
|
|
|
|
/// Built-in shader function for math.
|
|
#[derive(Clone, Copy, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]
|
|
#[cfg_attr(feature = "serialize", derive(Serialize))]
|
|
#[cfg_attr(feature = "deserialize", derive(Deserialize))]
|
|
pub enum MathFunction {
|
|
// comparison
|
|
Abs,
|
|
Min,
|
|
Max,
|
|
Clamp,
|
|
// trigonometry
|
|
Cos,
|
|
Cosh,
|
|
Sin,
|
|
Sinh,
|
|
Tan,
|
|
Tanh,
|
|
Acos,
|
|
Asin,
|
|
Atan,
|
|
Atan2,
|
|
// decomposition
|
|
Ceil,
|
|
Floor,
|
|
Round,
|
|
Fract,
|
|
Trunc,
|
|
Modf,
|
|
Frexp,
|
|
Ldexp,
|
|
// exponent
|
|
Exp,
|
|
Exp2,
|
|
Log,
|
|
Log2,
|
|
Pow,
|
|
// geometry
|
|
Dot,
|
|
Outer,
|
|
Cross,
|
|
Distance,
|
|
Length,
|
|
Normalize,
|
|
FaceForward,
|
|
Reflect,
|
|
Refract,
|
|
// computational
|
|
Sign,
|
|
Fma,
|
|
Mix,
|
|
Step,
|
|
SmoothStep,
|
|
Sqrt,
|
|
InverseSqrt,
|
|
Inverse,
|
|
Transpose,
|
|
Determinant,
|
|
// bits
|
|
CountOneBits,
|
|
ReverseBits,
|
|
}
|
|
|
|
/// Sampling modifier to control the level of detail.
|
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
|
#[cfg_attr(feature = "serialize", derive(Serialize))]
|
|
#[cfg_attr(feature = "deserialize", derive(Deserialize))]
|
|
pub enum SampleLevel {
|
|
Auto,
|
|
Zero,
|
|
Exact(Handle<Expression>),
|
|
Bias(Handle<Expression>),
|
|
Gradient {
|
|
x: Handle<Expression>,
|
|
y: Handle<Expression>,
|
|
},
|
|
}
|
|
|
|
/// Type of an image query.
|
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
|
#[cfg_attr(feature = "serialize", derive(Serialize))]
|
|
#[cfg_attr(feature = "deserialize", derive(Deserialize))]
|
|
pub enum ImageQuery {
|
|
/// Get the size at the specified level.
|
|
Size {
|
|
/// If `None`, the base level is considered.
|
|
level: Option<Handle<Expression>>,
|
|
},
|
|
/// Get the number of mipmap levels.
|
|
NumLevels,
|
|
/// Get the number of array layers.
|
|
NumLayers,
|
|
/// Get the number of samples.
|
|
NumSamples,
|
|
}
|
|
|
|
/// Component selection for a vector swizzle.
|
|
#[repr(u8)]
|
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
|
#[cfg_attr(feature = "serialize", derive(Serialize))]
|
|
#[cfg_attr(feature = "deserialize", derive(Deserialize))]
|
|
pub enum SwizzleComponent {
|
|
///
|
|
X = 0,
|
|
///
|
|
Y = 1,
|
|
///
|
|
Z = 2,
|
|
///
|
|
W = 3,
|
|
}
|
|
|
|
bitflags::bitflags! {
|
|
/// Memory barrier flags.
|
|
#[cfg_attr(feature = "serialize", derive(Serialize))]
|
|
#[cfg_attr(feature = "deserialize", derive(Deserialize))]
|
|
#[derive(Default)]
|
|
pub struct Barrier: u32 {
|
|
/// Barrier affects all `StorageClass::Storage` accesses.
|
|
const STORAGE = 0x1;
|
|
/// Barrier affects all `StorageClass::WorkGroup` accesses.
|
|
const WORK_GROUP = 0x2;
|
|
}
|
|
}
|
|
|
|
/// An expression that can be evaluated to obtain a value.
|
|
///
|
|
/// This is a Single Static Assignment (SSA) scheme similar to SPIR-V.
|
|
#[derive(Clone, Debug)]
|
|
#[cfg_attr(test, derive(PartialEq))]
|
|
#[cfg_attr(feature = "serialize", derive(Serialize))]
|
|
#[cfg_attr(feature = "deserialize", derive(Deserialize))]
|
|
pub enum Expression {
|
|
/// Array access with a computed index.
|
|
Access {
|
|
base: Handle<Expression>,
|
|
index: Handle<Expression>, //int
|
|
},
|
|
/// Array access with a known index.
|
|
AccessIndex {
|
|
base: Handle<Expression>,
|
|
index: u32,
|
|
},
|
|
/// Constant value.
|
|
Constant(Handle<Constant>),
|
|
/// Splat scalar into a vector.
|
|
Splat {
|
|
size: VectorSize,
|
|
value: Handle<Expression>,
|
|
},
|
|
/// Vector swizzle.
|
|
Swizzle {
|
|
size: VectorSize,
|
|
vector: Handle<Expression>,
|
|
pattern: [SwizzleComponent; 4],
|
|
},
|
|
/// Composite expression.
|
|
Compose {
|
|
ty: Handle<Type>,
|
|
components: Vec<Handle<Expression>>,
|
|
},
|
|
/// Reference a function parameter, by its index.
|
|
FunctionArgument(u32),
|
|
/// Reference a global variable.
|
|
GlobalVariable(Handle<GlobalVariable>),
|
|
/// Reference a local variable.
|
|
LocalVariable(Handle<LocalVariable>),
|
|
/// Load a value indirectly.
|
|
Load { pointer: Handle<Expression> },
|
|
/// Sample a point from a sampled or a depth image.
|
|
ImageSample {
|
|
image: Handle<Expression>,
|
|
sampler: Handle<Expression>,
|
|
coordinate: Handle<Expression>,
|
|
array_index: Option<Handle<Expression>>,
|
|
offset: Option<Handle<Constant>>,
|
|
level: SampleLevel,
|
|
depth_ref: Option<Handle<Expression>>,
|
|
},
|
|
/// Load a texel from an image.
|
|
ImageLoad {
|
|
image: Handle<Expression>,
|
|
coordinate: Handle<Expression>,
|
|
array_index: Option<Handle<Expression>>,
|
|
/// For storage images, this is None.
|
|
/// For sampled images, this is the Some(Level).
|
|
/// For multisampled images, this is Some(Sample).
|
|
index: Option<Handle<Expression>>,
|
|
},
|
|
/// Query information from an image.
|
|
ImageQuery {
|
|
image: Handle<Expression>,
|
|
query: ImageQuery,
|
|
},
|
|
/// Apply an unary operator.
|
|
Unary {
|
|
op: UnaryOperator,
|
|
expr: Handle<Expression>,
|
|
},
|
|
/// Apply a binary operator.
|
|
Binary {
|
|
op: BinaryOperator,
|
|
left: Handle<Expression>,
|
|
right: Handle<Expression>,
|
|
},
|
|
/// Select between two values based on a condition.
|
|
///
|
|
/// Note that, because expressions have no side effects, it is unobservable
|
|
/// whether the non-selected branch is evaluated.
|
|
Select {
|
|
/// Boolean expression
|
|
condition: Handle<Expression>,
|
|
accept: Handle<Expression>,
|
|
reject: Handle<Expression>,
|
|
},
|
|
/// Compute the derivative on an axis.
|
|
Derivative {
|
|
axis: DerivativeAxis,
|
|
//modifier,
|
|
expr: Handle<Expression>,
|
|
},
|
|
/// Call a relational function.
|
|
Relational {
|
|
fun: RelationalFunction,
|
|
argument: Handle<Expression>,
|
|
},
|
|
/// Call a math function
|
|
Math {
|
|
fun: MathFunction,
|
|
arg: Handle<Expression>,
|
|
arg1: Option<Handle<Expression>>,
|
|
arg2: Option<Handle<Expression>>,
|
|
},
|
|
/// Cast a simply type to another kind.
|
|
As {
|
|
/// Source expression, which can only be a scalar or a vector.
|
|
expr: Handle<Expression>,
|
|
/// Target scalar kind.
|
|
kind: ScalarKind,
|
|
/// If provided, converts to the specified byte width.
|
|
/// Otherwise, bitcast.
|
|
convert: Option<Bytes>,
|
|
},
|
|
/// Result of calling another function.
|
|
Call(Handle<Function>),
|
|
/// Get the length of an array.
|
|
/// The expression must resolve to a pointer to an array with a dynamic size.
|
|
///
|
|
/// This doesn't match the semantics of spirv's `OpArrayLength`, which must be passed
|
|
/// a pointer to a structure containing a runtime array in its' last field.
|
|
ArrayLength(Handle<Expression>),
|
|
}
|
|
|
|
/// A code block is just a vector of statements.
|
|
pub type Block = Vec<Statement>;
|
|
|
|
/// A case for a switch statement.
|
|
// Clone is used only for error reporting and is not intended for end users
|
|
#[derive(Clone, Debug)]
|
|
#[cfg_attr(feature = "serialize", derive(Serialize))]
|
|
#[cfg_attr(feature = "deserialize", derive(Deserialize))]
|
|
pub struct SwitchCase {
|
|
/// Value, upon which the case is considered true.
|
|
pub value: i32,
|
|
/// Body of the cae.
|
|
pub body: Block,
|
|
/// If true, the control flow continues to the next case in the list,
|
|
/// or default.
|
|
pub fall_through: bool,
|
|
}
|
|
|
|
//TODO: consider removing `Clone`. It's not valid to clone `Statement::Emit` anyway.
|
|
/// Instructions which make up an executable block.
|
|
// Clone is used only for error reporting and is not intended for end users
|
|
#[derive(Clone, Debug)]
|
|
#[cfg_attr(feature = "serialize", derive(Serialize))]
|
|
#[cfg_attr(feature = "deserialize", derive(Deserialize))]
|
|
pub enum Statement {
|
|
/// Emit a range of expressions, visible to all statements that follow in this block.
|
|
///
|
|
/// See the [module-level documentation][emit] for details.
|
|
///
|
|
/// [emit]: index.html#expression-evaluation-time-and-scope
|
|
Emit(Range<Expression>),
|
|
/// A block containing more statements, to be executed sequentially.
|
|
Block(Block),
|
|
/// Conditionally executes one of two blocks, based on the value of the condition.
|
|
If {
|
|
condition: Handle<Expression>, //bool
|
|
accept: Block,
|
|
reject: Block,
|
|
},
|
|
/// Conditionally executes one of multiple blocks, based on the value of the selector.
|
|
Switch {
|
|
selector: Handle<Expression>, //int
|
|
cases: Vec<SwitchCase>,
|
|
default: Block,
|
|
},
|
|
/// Executes a block repeatedly.
|
|
Loop { body: Block, continuing: Block },
|
|
/// Exits the loop.
|
|
Break,
|
|
/// Skips execution to the next iteration of the loop.
|
|
Continue,
|
|
/// Returns from the function (possibly with a value).
|
|
Return { value: Option<Handle<Expression>> },
|
|
/// Aborts the current shader execution.
|
|
Kill,
|
|
/// Synchronize invocations within the work group.
|
|
/// The `Barrier` flags control which memory accesses should be synchronized.
|
|
/// If empty, this becomes purely an execution barrier.
|
|
Barrier(Barrier),
|
|
/// Stores a value at an address.
|
|
///
|
|
/// This statement is a barrier for any operations on the
|
|
/// `Expression::LocalVariable` or `Expression::GlobalVariable`
|
|
/// that is the destination of an access chain, started
|
|
/// from the `pointer`.
|
|
Store {
|
|
pointer: Handle<Expression>,
|
|
value: Handle<Expression>,
|
|
},
|
|
/// Stores a value to an image.
|
|
///
|
|
/// Image has to point into a global variable of type `TypeInner::Image`.
|
|
/// This statement is a barrier for any operations on the corresponding
|
|
/// `Expression::GlobalVariable` for this image.
|
|
ImageStore {
|
|
image: Handle<Expression>,
|
|
coordinate: Handle<Expression>,
|
|
array_index: Option<Handle<Expression>>,
|
|
value: Handle<Expression>,
|
|
},
|
|
/// Calls a function.
|
|
///
|
|
/// If the `result` is `Some`, the corresponding expression has to be
|
|
/// `Expression::Call`, and this statement serves as a barrier for any
|
|
/// operations on that expression.
|
|
Call {
|
|
function: Handle<Function>,
|
|
arguments: Vec<Handle<Expression>>,
|
|
result: Option<Handle<Expression>>,
|
|
},
|
|
}
|
|
|
|
/// A function argument.
|
|
#[derive(Clone, Debug)]
|
|
#[cfg_attr(feature = "serialize", derive(Serialize))]
|
|
#[cfg_attr(feature = "deserialize", derive(Deserialize))]
|
|
pub struct FunctionArgument {
|
|
/// Name of the argument, if any.
|
|
pub name: Option<String>,
|
|
/// Type of the argument.
|
|
pub ty: Handle<Type>,
|
|
/// For entry points, an argument has to have a binding
|
|
/// unless it's a structure.
|
|
pub binding: Option<Binding>,
|
|
}
|
|
|
|
#[derive(Clone, Debug)]
|
|
#[cfg_attr(feature = "serialize", derive(Serialize))]
|
|
#[cfg_attr(feature = "deserialize", derive(Deserialize))]
|
|
pub struct FunctionResult {
|
|
/// Type of the result.
|
|
pub ty: Handle<Type>,
|
|
/// For entry points, the result has to have a binding
|
|
/// unless it's a structure.
|
|
pub binding: Option<Binding>,
|
|
}
|
|
|
|
/// A function defined in the module.
|
|
#[derive(Debug, Default)]
|
|
#[cfg_attr(feature = "serialize", derive(Serialize))]
|
|
#[cfg_attr(feature = "deserialize", derive(Deserialize))]
|
|
pub struct Function {
|
|
/// Name of the function, if any.
|
|
pub name: Option<String>,
|
|
/// Information about function argument.
|
|
pub arguments: Vec<FunctionArgument>,
|
|
/// The result of this function, if any.
|
|
pub result: Option<FunctionResult>,
|
|
/// Local variables defined and used in the function.
|
|
pub local_variables: Arena<LocalVariable>,
|
|
/// Expressions used inside this function.
|
|
pub expressions: Arena<Expression>,
|
|
/// Block of instructions comprising the body of the function.
|
|
pub body: Block,
|
|
}
|
|
|
|
/// Exported function, to be run at a certain stage in the pipeline.
|
|
#[derive(Debug)]
|
|
#[cfg_attr(feature = "serialize", derive(Serialize))]
|
|
#[cfg_attr(feature = "deserialize", derive(Deserialize))]
|
|
pub struct EntryPoint {
|
|
/// Name of this entry point, visible externally.
|
|
pub name: String,
|
|
/// Shader stage.
|
|
pub stage: ShaderStage,
|
|
/// Early depth test for fragment stages.
|
|
pub early_depth_test: Option<EarlyDepthTest>,
|
|
/// Workgroup size for compute stages
|
|
pub workgroup_size: [u32; 3],
|
|
/// The entrance function.
|
|
pub function: Function,
|
|
}
|
|
|
|
/// Shader module.
|
|
///
|
|
/// A module is a set of constants, global variables and functions, as well as
|
|
/// the types required to define them.
|
|
///
|
|
/// Some functions are marked as entry points, to be used in a certain shader stage.
|
|
///
|
|
/// To create a new module, use the `Default` implementation.
|
|
/// Alternatively, you can load an existing shader using one of the [available front ends][front].
|
|
///
|
|
/// When finished, you can export modules using one of the [available backends][back].
|
|
#[derive(Debug, Default)]
|
|
#[cfg_attr(feature = "serialize", derive(Serialize))]
|
|
#[cfg_attr(feature = "deserialize", derive(Deserialize))]
|
|
pub struct Module {
|
|
/// Storage for the types defined in this module.
|
|
pub types: Arena<Type>,
|
|
/// Storage for the constants defined in this module.
|
|
pub constants: Arena<Constant>,
|
|
/// Storage for the global variables defined in this module.
|
|
pub global_variables: Arena<GlobalVariable>,
|
|
/// Storage for the functions defined in this module.
|
|
pub functions: Arena<Function>,
|
|
/// Entry points.
|
|
pub entry_points: Vec<EntryPoint>,
|
|
}
|