Re-export of our
Universal shader translator.
The central structure of the crate is
Functions, which have arguments, a return type, local variables, and a body,
EntryPoints, which are specialized functions that can serve as the entry point for pipeline stages like vertex shading or fragment shading,
Types used by the above.
The body of an
Function is represented using two types:
Expressionproduces a value, but has no side effects or control flow.
Expressionsinclude variable references, unary and binary operators, and so on.
Statementcan have side effects and structured control flow.
Statements do not produce a value, other than by storing one in some designated place.
Statementsinclude blocks, conditionals, and loops, but also operations that have side effects, like stores and function calls.
Statements form a tree, with pointers into the DAG of
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
Statement variants use the
Block type, which is
with optional span info, representing a series of statements executed in order. The body of an
Function is a
Statement has a
To improve translator performance and reduce memory usage, most structures are
stored in an
Arena<T> stores a series of
T values, indexed by
Handle<T> values, which are just wrappers around integer indexes.
For example, a
Function’s expressions are stored in an
and compound expressions refer to their sub-expressions via
values. (When examining the serialized form of a
Module, note that the first
element of an
Arena has an index of 1, not 0.)
UniqueArena is just like an
Arena, except that it stores only a single
instance of each value. The value type must implement
Hash. Like an
Arena, inserting a value into a
UniqueArena returns a
Handle which can be
used to efficiently access the value, without a hash lookup. Inserting a value
multiple times returns the same
span feature is enabled, both
UniqueArena can associate a
source code span with each element.
Naga’s representation of function calls is unusual. Most languages treat
function calls as expressions, but because calls may have side effects, Naga
represents them as a kind of statement,
Statement::Call. If the function
returns a value, a call statement designates a particular
expression to represent its return value, for use by subsequent statements and
It is essential to know when an
Expression should be evaluated, because its
value may depend on previous
Statements’ effects. But whereas the order of
execution for a tree of
Statements is apparent from its structure, it is not
so clear for
Expressions, since an expression may be referred to by any number
Statements and other
Naga’s rules for when
Expressions are evaluated are as follows:
LocalVariableexpressions are considered implicitly evaluated upon entry to the function to which they belong. Function arguments cannot be assigned to, and
LocalVariableexpressions produce a pointer to the variable’s value (for use with
Store). Neither varies while the function executes, so it suffices to consider these expressions evaluated once on entry.
GlobalVariableexpressions are considered implicitly evaluated before execution begins, since their value does not change while code executes, for one of two reasons:
GlobalVariableexpressions produce a pointer to the variable’s value, for use with
LocalVariableexpressions do. Although the variable’s value may change, its address does not.
GlobalVariableexpression referring to a global in the
AddressSpace::Handleaddress space produces the value directly, not a pointer. Such global variables hold opaque types like shaders or images, and cannot be assigned to.
All other expressions are evaluated when the (unique)
Statement::Emitstatement that covers them is executed.
Now, strictly speaking, not all
Expression variants actually care when they’re
evaluated. For example, you can evaluate a
any time you like, as long as you give it the right operands. It’s really only a
very small set of expressions that are affected by timing:
Derivativeexpressions are sensitive to control flow uniformity: they must not be moved out of an area of uniform control flow into a non-uniform area.
More generally, any expression that’s used by more than one other expression or statement should probably be evaluated only once, and then stored in a variable to be cited at each point of use.
Naga tries to help back ends handle all these cases correctly in a somewhat
circuitous way. The
ModuleInfo structure returned by
provides a reference count for each expression in each function in the module.
Naturally, any expression with a reference count of two or more deserves to be
evaluated and stored in a temporary variable at the point that the
statement covering it is executed. But if we selectively lower the reference
count threshold to one for the sensitive expression types listed above, so
that we always generate a temporary variable and save their value, then the
same code that manages multiply referenced expressions will take care of
introducing temporaries for time-sensitive expressions as well. The
Expression::bake_ref_count method (private to the back ends) is meant to help
Expression has a scope, which is the region of the function within
which it can be used by
Statements and other
Expressions. It is a validation
error to use an
Expression outside its scope.
An expression’s scope is defined as follows:
The scope of an expression evaluated by an
Emitstatement covers the subsequent expressions in that
Emit, the subsequent statements in the
Blockto which that
Emitbelongs (if any) and their sub-statements (if any).
For example, this implies that an expression evaluated by some statement in a
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
A Naga constant expression is one of the following
variants, whose operands (if any) are also constant expressions:
ZeroValue, for fixed-size types
A constant expression can be evaluated at module translation time.
An override expression can be evaluated at pipeline creation time.
- Backend functions that export shader
Modules into binary and text formats.
- Frontend parsers that consume binary and text shaders and load them into
- Shader validator.
- An arena holding some kind of component (e.g., type, constant, instruction, etc.) that can be referenced.
- Memory barrier flags.
- A code block is a vector of statements, with maybe a vector of spans.
- Constant value.
- Early fragment tests.
- The main function for a pipeline stage.
- A function defined in the module.
- A function argument.
- A function result.
- Variable defined at module level.
- A strongly typed reference to an arena item.
- Variable defined at function level.
- Shader module.
- A strongly typed range of handles.
- Pipeline binding information for global resources.
- Characteristics of a scalar type.
- A human-readable representation for a span, tailored for text source.
- A source code span, used for error reporting.
- Set of special types that can be optionally generated by the frontends.
- Flags describing an image.
- Member of a user-defined structure.
- A case for a switch statement.
- A data type declared in the module.
- An arena whose elements are guaranteed to be unique.
- Addressing space of variables.
- Size of an array.
- Function on an atomic value.
- Operation that can be applied on two values.
- Describes how an input/output variable is to be bound.
- Built-in inputs and outputs.
- Enables adjusting depth without disabling early Z.
- Axis on which to compute a derivative.
- Hint at which precision to compute a derivative.
- An expression that can be evaluated to obtain a value.
- Sub-class of the image type.
- The number of dimensions an image has.
- Type of an image query.
- The interpolation qualifier of a binding or struct field.
- Built-in shader function for math.
- Return types predeclared for the frexp, modf, and atomicCompareExchangeWeak built-in functions.
- Built-in shader function for testing relation between values.
- Sampling modifier to control the level of detail.
- The sampling qualifiers of a binding or struct field.
- Primitive type for a scalar.
- Stage of the programmable pipeline.
- Instructions which make up an executable block.
- Image storage format.
- The value of the switch case.
- Component selection for a vector swizzle.
- Enum with additional information, depending on the kind of type.
- Operation that can be applied on a single value.
- Number of components in a vector.
- Width of abstract types, in bytes.
- Width of a boolean type, in bytes.
- Number of bytes per scalar.
- Hash map that is faster but not resilient to DoS attacks.
- Hash set that is faster but not resilient to DoS attacks.
- Insertion-order-preserving hash map (
IndexMap<K, V>), but with the same hasher as
FastHashMap<K, V>(faster but not resilient to DoS attacks).
- Insertion-order-preserving hash set (
IndexSet<K>), but with the same hasher as
FastHashSet<K>(faster but not resilient to DoS attacks).
- A source code span together with “context”, a user-readable description of what part of the error it refers to.