Module naga::back::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.

The rules described here only apply to WGSL 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.

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 variable, whether directly as the variable’s type 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.

Structs

Enums

Type Aliases