| block.rs |
!
Implementations for `BlockContext` methods.
|
188224 |
- |
| f16_polyfill.rs |
!
This module provides functionality for polyfilling `f16` input/output variables
when the `StorageInputOutput16` capability is not available or disabled.
It works by:
1. Declaring `f16` I/O variables as `f32` in SPIR-V
2. Converting between `f16` and `f32` at runtime using `OpFConvert`
3. Maintaining mappings to track which variables need conversion
|
3179 |
- |
| helpers.rs |
|
6962 |
- |
| image.rs |
!
Generating SPIR-V for image operations.
|
51707 |
- |
| index.rs |
!
Bounds-checking for SPIR-V output.
|
25093 |
- |
| instructions.rs |
|
41457 |
- |
| layout.rs |
|
7275 |
- |
| mesh_shader.rs |
|
35305 |
- |
| mod.rs |
!
Backend for [SPIR-V][spv] (Standard Portable Intermediate Representation).
# Layout of values in `uniform` buffers
WGSL's ["Internal Layout of Values"][ilov] 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"][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:
```ignore
struct Baz {
m: mat3x2<f32>,
}
```
is rendered as the SPIR-V struct type:
```ignore
OpTypeStruct %v2float %v2float %v2float
```
This 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.
[spv]: https://www.khronos.org/registry/SPIR-V/
[ilov]: https://gpuweb.github.io/gpuweb/wgsl/#internal-value-layout
[extended-layout]: https://docs.vulkan.org/spec/latest/chapters/interfaces.html#interfaces-resources-layout
[`loaded`]: crate::Expression::Load
[`accessed`]: crate::Expression::AccessIndex
[`dynamically accessed`]: crate::Expression::Access
|
40478 |
- |
| ray |
|
|
- |
| recyclable.rs |
!
Reusing collections' previous allocations.
|
2405 |
- |
| selection.rs |
!
Generate SPIR-V conditional structures.
Builders for `if` structures with `and`s.
The types in this module track the information needed to emit SPIR-V code
for complex conditional structures, like those whose conditions involve
short-circuiting 'and' and 'or' structures. These track labels and can emit
`OpPhi` instructions to merge values produced along different paths.
This currently only supports exactly the forms Naga uses, so it doesn't
support `or` or `else`, and only supports zero or one merged values.
Naga needs to emit code roughly like this:
```ignore
value = DEFAULT;
if COND1 && COND2 {
value = THEN_VALUE;
}
// use value
```
Assuming `ctx` and `block` are a mutable references to a [`BlockContext`]
and the current [`Block`], and `merge_type` is the SPIR-V type for the
merged value `value`, we can build SPIR-V for the code above like so:
```ignore
let cond = Selection::start(block, merge_type);
// ... compute `cond1` ...
cond.if_true(ctx, cond1, DEFAULT);
// ... compute `cond2` ...
cond.if_true(ctx, cond2, DEFAULT);
// ... compute THEN_VALUE
let merged_value = cond.finish(ctx, THEN_VALUE);
```
After this, `merged_value` is either `DEFAULT` or `THEN_VALUE`, depending on
the path by which the merged block was reached.
This takes care of writing all branch instructions, including an
`OpSelectionMerge` annotation in the header block; starting new blocks and
assigning them labels; and emitting the `OpPhi` that gathers together the
right sources for the merged values, for every path through the selection
construct.
When there is no merged value to produce, you can pass `()` for `merge_type`
and the merge values. In this case no `OpPhi` instructions are produced, and
the `finish` method returns `()`.
To enforce proper nesting, a `Selection` takes ownership of the `&mut Block`
pointer for the duration of its lifetime. To obtain the block for generating
code in the selection's body, call the `Selection::block` method.
|
9972 |
- |
| subgroup.rs |
|
9211 |
- |
| writer.rs |
|
161627 |
- |