Name Description Size Coverage
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 -