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 WGSLmat3x4<f32>
, say, becomes an HLSLrow_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 tomul(v, m)
(note the reversal of the arguments), andmul(v, transpose(m))
is equivalent tomul(m, v)
, we can translate WGSLm * v
andv * m
to HLSL by simply reversing the arguments tomul
.
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
- A fragment entry point to be considered when generating HLSL for the output interface of vertex entry points.
- Configuration used in the
Writer
. - Reflection info for entry point names.
Enums
- A HLSL shader model version.