Document the full HLSL target and its source transformations

design.md states the generated swizzle and mixed-constructor surface, writable
matrix rows, HLSL intrinsic types and NaN behavior, the deferred matrix items
(float4x4, transpose, determinant, _11 names), the exact HLSL-to-C#
transformations the compatibility test applies, and the dxc check.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Meta Vens 2026-09-14 13:57:03 +02:00
commit 7a1bd1ebb1
2 changed files with 53 additions and 8 deletions

View file

@ -16,7 +16,9 @@ shader semantics.
- `float2`, `float3`, `float4`
- component-wise arithmetic
- scalar-to-vector splats
- swizzles for common lanes
- every HLSL swizzle (`xyzw` and `rgba`, repeats readable, non-repeating
masks writable) and every HLSL mixed constructor, generated by
`tools/generate-swizzles.ps1`
- conversions to and from `System.Numerics`
- `Random`, a compact deterministic XORShift stream with scalar and vector draws
- `BatchMath` SoA kernels over `Span<float>` / `ReadOnlySpan<float>` using

View file

@ -12,20 +12,63 @@ rest of the spelling should survive the move.
That makes these HLSL rules, not options:
- Components and swizzles are writable. `v.xz = f2;` and `v[1] = s;` work.
Swizzle setters exist only for non-repeating masks, because HLSL rejects
`v.xx = ...`.
- Every swizzle exists. `float2/3/4`, `int2/3/4`, and `bool2/3/4` expose every
read swizzle of length 2 to 4, repeats included (`v.wwxy`), in both `xyzw` and
`rgba` naming (never mixed), plus single-component `r/g/b/a`. `double2/3` get
lengths 2 and 3 only, because there is no `double4`. Components and swizzles are
writable: `v.xz = f2;`, `c.rgb *= 0.5f;`, and `v[1] = s;` work. Setters exist
only for non-repeating masks, because HLSL rejects `v.xx = ...`.
- Every HLSL mixed constructor exists, as a struct constructor and as a `math`
function: `float4(float2, float2)`, `float4(float, float2, float)`,
`float3(float, float2)`, the copy form `float3(float3)`, and the rest, for the
float, int, and bool families (double gets constructors only; a `math.double3`
function would hide the `double3` type name).
- Swizzles and mixed constructors are generated. `tools/generate-swizzles.ps1`
writes the checked-in `src/CultMath/Swizzles.g.cs` (partial structs plus a
partial `math`). Edit the generator, rerun it, and commit both; never edit the
output by hand.
- Comparison operators (`<`, `>`, `<=`, `>=`, `==`, `!=`) are component-wise and
return `bool2`/`bool3`/`bool4`. Reduce them with `any` and `all`.
`Equals`/`GetHashCode` stay value-based so vectors still work as dictionary
keys.
- `select(condition, whenTrue, whenFalse)` uses HLSL's argument order.
- Matrices are row-major: constructors take rows, `m[i]` is row i, `_mRC` is
row R column C, and products go through `mul` (`mul(m, v)` treats `v` as a
column, `mul(v, m)` as a row). `*` on matrices would be component-wise in HLSL,
so CultMath does not define it.
- Matrices are row-major: constructors take rows, `m[i]` returns a reference to
row i (so `m[1][2] = s;` writes one element), `_mRC` is row R column C, and
products go through `mul` (`mul(m, v)` treats `v` as a column, `mul(v, m)` as a
row). `*` on matrices would be component-wise in HLSL, so CultMath does not
define it.
- Scalar/vector arithmetic and comparisons have explicit overloads on both
sides rather than leaning on implicit scalar splat.
- Intrinsics return HLSL's types and edge cases: `sign` returns `int`/`intN`
(NaN gives 0); `step(y, x)` is `x >= y ? 1 : 0` (NaN gives 0), in C# and in the
HLSL mirror; `any`/`all` accept numeric vectors (a component is true when it is
not 0); `min`, `max`, and `abs` on int vectors return int vectors.
Deferred until a consumer needs them: `float4x4`, `transpose`, `determinant`,
and HLSL's one-based `_11`..`_33` element names.
### HLSL Target: Source Transformations
`HlslSourceCompatibilityTests` compiles the real `shaders/CultMath.hlsl` with
Roslyn against CultMath. These transformations are the complete list; if a body
needs anything else, the test fails:
1. Include guards (`#ifndef`/`#define`/`#endif`) are dropped.
2. Functions taking `Texture2D`/`SamplerState` are dropped; GPU resources have
no CultMath analog.
3. File-scope `static const` becomes `const`.
4. Floating literals gain `f` (`0.5` becomes `0.5f`).
5. The file body is wrapped in a class, since C# has no free functions; HLSL
functions become private instance methods.
HLSL constructs that stay outside the target and must not appear in the mirror:
`const` locals of vector type (C# `const` is primitives only; write a plain
local) and writes through a swizzle component (`v.xy.x = s`, which C# rejects as
modifying a temporary).
The HLSL side is checked too: `tools/get-dxc.ps1` fetches Microsoft's
DirectXShaderCompiler, and
`dxc -T lib_6_3 -HV 2021 shaders/CultMath.hlsl` must compile cleanly.
Where HLSL is silent, CultMath keeps its own decisions and does not defer to
Unity.Mathematics: `normalize` is safe (divides by `max(length, 1e-20)`),