Module hlsl

Source
Expand description

Backend for HLSL (High-Level Shading Language).

§Supported shader model versions:

  • 5.0
  • 5.1
  • 6.0

§Layout of values in uniform buffers

WGSL’s “Internal Layout of Values” rules specify how each WGSL type should be stored in uniform and storage buffers. The HLSL we generate must access values in that form, even when it is not what HLSL would use normally.

Matching the WGSL memory layout is a concern only for uniform variables. WGSL storage buffers are translated as HLSL ByteAddressBuffers, for which we generate Load and Store method calls with explicit byte offsets. WGSL pipeline inputs must be scalars or vectors; they cannot be matrices, which is where the interesting problems arise. However, when an affected type appears in a struct definition, the transformations described here are applied without consideration of where the struct is used.

Access to storage buffers is implemented in storage.rs. Access to uniform buffers is implemented where applicable in writer.rs.

§Row- and column-major ordering for matrices

WGSL specifies that matrices in uniform buffers are stored in column-major order. This matches HLSL’s default, so one might expect things to be straightforward. Unfortunately, WGSL and HLSL disagree on what indexing a matrix means: in WGSL, m[i] retrieves the i’th column of m, whereas in HLSL it retrieves the i’th row. We want to avoid translating m[i] into some complicated reassembly of a vector from individually fetched components, so this is a problem.

However, with a bit of trickery, it is possible to use HLSL’s m[i] as the translation of WGSL’s m[i]:

  • We declare all matrices in uniform buffers in HLSL with the row_major qualifier, and transpose the row and column counts: a WGSL mat3x4<f32>, say, becomes an HLSL row_major float3x4. (Note that WGSL and HLSL type names put the row and column in reverse order.) Since the HLSL type is the transpose of how WebGPU directs the user to store the data, HLSL will load all matrices transposed.

  • Since matrices are transposed, an HLSL indexing expression retrieves the “columns” of the intended WGSL value, as desired.

  • For vector-matrix multiplication, since mul(transpose(m), v) is equivalent to mul(v, m) (note the reversal of the arguments), and mul(v, transpose(m)) is equivalent to mul(m, v), we can translate WGSL m * v and v * m to HLSL by simply reversing the arguments to mul.

§Padding in two-row matrices

An HLSL row_major floatKx2 matrix has padding between its rows that the WGSL matKx2<f32> matrix it represents does not. HLSL stores all matrix rows aligned on 16-byte boundaries, whereas WGSL says that the columns of a matKx2<f32> need only be aligned as required for vec2<f32>, which is eight-byte alignment.

To compensate for this, any time a matKx2<f32> appears in a WGSL uniform value or as part of a struct/array, we actually emit K separate float2 members, and assemble/disassemble the matrix from its columns (in WGSL; rows in HLSL) upon load and store.

For example, the following WGSL struct type:

struct Baz {
        m: mat3x2<f32>,
}

is rendered as the HLSL struct type:

struct Baz {
    float2 m_0; float2 m_1; float2 m_2;
};

The wrapped_struct_matrix functions in help.rs generate HLSL helper functions to access such members, converting between the stored form and the HLSL matrix types appropriately. For example, for reading the member m of the Baz struct above, we emit:

float3x2 GetMatmOnBaz(Baz obj) {
    return float3x2(obj.m_0, obj.m_1, obj.m_2);
}

We also emit an analogous Set function, as well as functions for accessing individual columns by dynamic index.

§Sampler Handling

Due to limitations in how sampler heaps work in D3D12, we need to access samplers through a layer of indirection. Instead of directly binding samplers, we bind the entire sampler heap as both a standard and a comparison sampler heap. We then use a sampler index buffer for each bind group. This buffer is accessed in the shader to get the actual sampler index within the heap. See the wgpu_hal dx12 backend documentation for more information.

§External textures

Support for crate::ImageClass::External textures is implemented by lowering each external texture global variable to 3 Texture2D<float4>s, and a cbuffer of type NagaExternalTextureParams. This provides up to 3 planes of texture data (for example single planar RGBA, or separate Y, Cb, and Cr planes), and the parameters buffer containing information describing how to handle these correctly. The bind target to use for each of these globals is specified via Options::external_texture_binding_map.

External textures are supported by WGSL’s textureDimensions(), textureLoad(), and textureSampleBaseClampToEdge() built-in functions. These are implemented using helper functions. See the following functions for how these are generated:

  • Writer::write_wrapped_image_query_function
  • Writer::write_wrapped_image_load_function
  • Writer::write_wrapped_image_sample_function

Ideally the set of global variables could be wrapped in a single struct that could conveniently be passed around. But, alas, HLSL does not allow structs to have Texture2D members. Fortunately, however, external textures can only be used as arguments to either built-in or user-defined functions. We therefore expand any external texture function argument to four consecutive arguments (3 textures and the params struct) when declaring user-defined functions, and ensure our built-in function implementations take the same arguments. Then, whenever we need to emit an external texture in Writer::write_expr, which fortunately can only ever be for a global variable or function argument, we simply emit the variable name of each of the three textures and the parameters struct in a comma-separated list. This won’t win any awards for elegance, but it works for our purposes.

Modules§

conv 🔒
help 🔒
Helpers for the hlsl backend
keywords 🔒
ray 🔒
storage 🔒
Generating accesses to ByteAddressBuffer contents.
writer 🔒

Structs§

BindTarget
Direct3D 12 binding information for a global variable.
BindingMapSerialization 🔒
DynamicStorageBufferOffsetTargetSerialization 🔒
ExternalTextureBindTarget
HLSL binding information for a Naga External image global variable.
ExternalTextureBindingMapSerialization 🔒
FragmentEntryPoint
A fragment entry point to be considered when generating HLSL for the output interface of vertex entry points.
OffsetsBindTarget
BindTarget for dynamic storage buffer offsets
Options
Configuration used in the Writer.
PipelineOptions
A subset of options that are meant to be changed per pipeline.
ReflectionInfo
Reflection info for entry point names.
SamplerHeapBindTargets
SamplerIndexBufferBindingSerialization 🔒
SamplerIndexBufferKey
Wrapped 🔒
Writer

Enums§

EntryPointError
Error
ShaderModel
A HLSL shader model version.
WrappedType 🔒

Functions§

deserialize_binding_map 🔒
deserialize_external_texture_binding_map 🔒
deserialize_sampler_index_buffer_bindings 🔒
deserialize_storage_buffer_offsets 🔒

Type Aliases§

BackendResult 🔒
Shorthand result used internally by the backend
BindingMap
DynamicStorageBufferOffsetsTargets
ExternalTextureBindingMap
SamplerIndexBufferBindingMap