Expand description
Backend for SPIR-V (Standard Portable Intermediate Representation).
§Layout of values in uniform buffers
WGSL’s “Internal Layout of Values” rules specify the memory layout of
each WGSL type. The memory layout is important for data stored in uniform and
storage buffers, especially when exchanging data with CPU code.
Both WGSL and Vulkan specify some conditions that a type’s memory layout
must satisfy in order to use that type in a uniform or storage buffer.
For storage buffers, the WGSL and Vulkan restrictions are compatible, but
for uniform buffers, WGSL allows some types that Vulkan does not, requiring
adjustments when emitting SPIR-V for uniform buffers.
§Padding in two-row matrices
SPIR-V provides detailed control over the layout of matrix types, and is capable of describing the WGSL memory layout. However, Vulkan imposes additional restrictions.
Vulkan’s “extended layout” (also known as std140) rules
apply to types used in uniform buffers. Under these rules, matrices are
defined in terms of arrays of their vector type, and arrays are defined to have
an alignment equal to the alignment of their element type rounded up to a
multiple of 16. This means that each column of the matrix has a minimum
alignment of 16. WGSL, and consequently Naga IR, on the other hand specifies
column alignment equal to the alignment of the vector type, without being
rounded up to 16.
To compensate for this, for any struct used as a uniform buffer which
contains a two-row matrix, we declare an additional “std140 compatible” type
in which each column of the matrix has been decomposed into the containing
struct. For example, the following WGSL struct type:
struct Baz {
m: mat3x2<f32>,
}is rendered as the SPIR-V struct type:
OpTypeStruct %v2float %v2float %v2floatThis has the effect that struct indices in Naga IR for such types do not
correspond to the struct indices used in SPIR-V. A mapping of struct indices
for these types is maintained in Std140CompatTypeInfo.
Additionally, any two-row matrices that are declared directly as uniform buffers without being wrapped in a struct are declared as a struct containing a vector member for each column. Any array of a two-row matrix in a uniform buffer is declared as an array of a struct containing a vector member for each column. Any struct or array within a uniform buffer which contains a member or whose base type requires a std140 compatible type declaration, itself requires a std140 compatible type declaration.
Whenever a value of such a type is loaded we insert code to convert the
loaded value from the std140 compatible type to the regular type. This occurs
in BlockContext::write_checked_load, making use of the wrapper function
defined by Writer::write_wrapped_convert_from_std140_compat_type. For matrices
that have been decomposed as separate columns in the containing struct, we load
each column separately then composite the matrix type in
BlockContext::maybe_write_load_uniform_matcx2_struct_member.
Whenever a column of a matrix that has been decomposed into its containing
struct is accessed with a constant index we adjust the emitted access chain
to access from the containing struct instead, in BlockContext::write_access_chain.
Whenever a column of a uniform buffer two-row matrix is dynamically accessed
we must first load the matrix type, converting it from its std140 compatible
type as described above, then access the column using the wrapper function
defined by Writer::write_wrapped_matcx2_get_column. This is handled by
BlockContext::maybe_write_uniform_matcx2_dynamic_access.
Note that this approach differs somewhat from the equivalent code in the HLSL backend. For HLSL all structs containing two-row matrices (or arrays of such) have their declarations modified, not just those used as uniform buffers. Two-row matrices and arrays of such only use modified type declarations when used as uniform buffers, or additionally when used as struct member in any context. This avoids the need to convert struct values when loading from uniform buffers, but when loading arrays and matrices from uniform buffers or from any struct the conversion is still required. In contrast, the approach used here always requires converting any affected type when loading from a uniform buffer, but consistently only when loading from a uniform buffer. As a result this also means we only have to handle loads and not stores, as uniform buffers are read-only.
Modules§
- block 🔒
- Implementations for
BlockContextmethods. - f16_
polyfill 🔒 - This module provides functionality for polyfilling
f16input/output variables when theStorageInputOutput16capability is not available or disabled. - helpers 🔒
- image 🔒
- Generating SPIR-V for image operations.
- index 🔒
- Bounds-checking for SPIR-V output.
- instructions 🔒
- layout 🔒
- mesh_
shader 🔒 - ray 🔒
- Generating SPIR-V for ray query operations.
- recyclable 🔒
- Reusing collections’ previous allocations.
- selection 🔒
- Generate SPIR-V conditional structures.
- subgroup 🔒
- writer 🔒
Structs§
- Binding
Info - Block 🔒
- A SPIR-V block to which we are still adding instructions.
- Block
Context 🔒 - General information needed to emit SPIR-V for Naga statements.
- Cached
Expressions 🔒 - A map from evaluated
Expressions to their SPIR-V ids. - Debug
Info - Entry
Point 🔒Context - Expression
Constness 🔒Tracker - Tracks the expressions for which the backend emits the following instructions:
- Function 🔒
- Function
Argument 🔒 - Global
Variable 🔒 - The SPIR-V representation of a
crate::GlobalVariable. - IdGenerator 🔒
- Image
Type Flags - Flags corresponding to the boolean(-ish) parameters to OpTypeImage.
- Instruction 🔒
- Local
Image 🔒Type - Characteristics of a SPIR-V
OpTypeImagetype. - Local
Variable 🔒 - Logical
Layout 🔒 - Lookup
Function 🔒Type - Mesh
Return Info - Mesh
Return Member - Options
- Physical
Layout 🔒 - Pipeline
Options - RayQuery
Trackers 🔒 - Result
Member 🔒 - Std140
Compat Type Info - Information about a type for which we have declared a std140 layout
compatible variant, because the type is used in a uniform but does not
adhere to std140 requirements. The uniform will be declared using the
type
type_id, and the result of anyLoadwill be immediately converted to the base type. This is used for matrices with 2 rows, as well as any arrays or structs containing such matrices. - Terminated
Block 🔒 - A SPIR-V block that ends with a termination instruction.
- Writer
- Writer
Flags
Enums§
- Cached
Constant 🔒 - Capability
- SPIR-V operand kind: Capability
- Cooperative
Type 🔒 - A cooperative type, for use in
LocalType. - Dimension 🔒
- Error
- Local
Type 🔒 - A SPIR-V type constructed during code generation.
- Lookup
RayQuery 🔒Function - Lookup
Type 🔒 - A type encountered during SPIR-V generation.
- Numeric
Type 🔒 - A numeric type, for use in
LocalType. - Source
Language - SPIR-V operand kind: SourceLanguage
- Wrapped
Function 🔒 - Key used to look up an operation which we have wrapped in a helper
function, which should be called instead of directly emitting code
for the expression. See
Writer::wrapped_functions. - Zero
Initialize Workgroup Memory Mode