1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
|
# WGSL Uniform Buffer Guidelines
This document outlines the rules and best practices for defining and using uniform buffers in WGSL shaders within this project, focusing on alignment, size, and consistency.
## WGSL Alignment Rules
Understanding WGSL's memory layout rules is crucial for correct uniform buffer implementation. The following are the general alignment requirements for common WGSL types:
- `f32`: 4-byte alignment.
- `vec2<f32>`: 8-byte alignment (4 bytes per component * 2 components = 8 bytes).
- `vec3<f32>`: 16-byte alignment (4 bytes per component * 3 components = 12 bytes, padded to 16).
- `vec4<f32>`: 16-byte alignment (4 bytes per component * 4 components = 16 bytes).
- `array<T, N>`: The alignment of an array is typically the alignment of its base type `T`.
Structs are padded to the alignment of their largest member. Any trailing space in a struct is also padded to match the maximum alignment of any member within the struct.
## Standard Uniform Buffer Pattern
To maintain consistency and facilitate efficient rendering, standard patterns for uniform buffer usage are established:
### Post-Process Effects
- **Binding 0 & 1:** Reserved for Sampler and Texture access (handled by `pp_update_bind_group`).
- **Binding 2:** **Common Uniforms** (`CommonPostProcessUniforms`). Contains resolution, aspect ratio, physical time, beat time, beat phase, audio intensity.
- **Binding 3:** **Effect-Specific Parameters**. Unique per-effect data (e.g., `strength`, `speed`, `fade_amount`).
### SDF/Raymarching Effects
- **Binding 0:** **Common Uniforms** (`CommonPostProcessUniforms`). Same as above.
- **Binding 1:** **Camera Parameters** (`CameraParams`). Camera transform and projection data for raymarching.
- **Binding 2+:** **Effect-Specific Parameters** (optional).
This pattern ensures that common data is shared efficiently across effects, while effect-specific data remains isolated.
## Defining Uniform Structs
### WGSL Definitions
When defining uniform structs in WGSL, adhere to the following:
- **Explicit Padding:** Use padding fields (`_pad0`, `_pad1`, etc.) where necessary to ensure correct alignment, especially when mixing types of different alignment requirements (e.g., `vec2<f32>` followed by `f32`s).
- **Use `vec2<f32>` for 8-byte padding:** If you need 8 bytes of padding, use `_pad0: vec2<f32>` instead of `_pad0: f32, _pad1: f32` for potentially better clarity and to leverage WGSL's type system.
- **Minimize Padding:** Only add padding where required by alignment rules to reduce memory usage.
**Example (CommonPostProcessUniforms):**
```wgsl
struct CommonUniforms {
resolution: vec2<f32>, // Screen dimensions (8 bytes)
aspect_ratio: f32, // Width/height ratio (4 bytes)
time: f32, // Physical seconds, unaffected by tempo (4 bytes)
beat_time: f32, // Musical time in beats (4 bytes)
beat_phase: f32, // Fractional beat 0.0-1.0 (4 bytes)
audio_intensity: f32, // Audio peak for beat sync (4 bytes)
_pad: f32, // Alignment padding (4 bytes)
};
// Total size: 32 bytes (8 f32 values)
```
**Use cases:**
- `time`: Constant-speed physics animation
- `beat_time`: Musical bar/beat synchronization
- `beat_phase`: Smooth per-beat oscillation
**Example (EffectParams with f32 members):**
```wgsl
struct EffectParams {
parameter1: f32,
parameter2: f32,
// ... more parameters ...
};
// Expected size: 8 bytes (if only two f32s)
```
### C++ Definitions and Validation
For every WGSL uniform struct, a corresponding C++ struct must exist. This C++ struct must include a `static_assert` to verify its size and alignment matches the WGSL definition.
- **Mirror WGSL Structure:** The C++ struct should mirror the WGSL struct's member order and types as closely as possible to ensure accurate size calculation.
- **`static_assert`:** Always include `static_assert(sizeof(MyStruct) == EXPECTED_SIZE, "MyStruct must be EXPECTED_SIZE bytes for WGSL alignment");`.
- **Use `float` for `f32`:** Use `float` for `f32` in C++.
- **Use `vec2<f32>` mapping:** If WGSL uses `vec2<f32>`, map it to an equivalent C++ type that occupies 8 bytes, typically `float[2]` or a `struct Vec2 { float x, y; }` if more complex type handling is needed.
- **Padding:** C++ padding rules can differ from WGSL. Pay close attention to `static_assert` for validation.
**Example (C++ CommonPostProcessUniforms):**
```cpp
struct CommonPostProcessUniforms {
vec2 resolution; // 8 bytes - screen dimensions
float aspect_ratio; // 4 bytes - width/height ratio
float time; // 4 bytes - physical seconds
float beat_time; // 4 bytes - musical beats
float beat_phase; // 4 bytes - fractional beat 0-1
float audio_intensity; // 4 bytes - audio peak
float _pad; // 4 bytes - alignment padding
};
static_assert(sizeof(CommonPostProcessUniforms) == 32,
"CommonPostProcessUniforms must be 32 bytes for WGSL alignment");
```
**Example (C++ GaussianBlurParams):**
```cpp
struct GaussianBlurParams {
float strength = 2.0f;
float _pad = 0.0f;
};
static_assert(sizeof(GaussianBlurParams) == 8,
"GaussianBlurParams must be 8 bytes for WGSL alignment");
```
**Example (C++ CameraParams):**
```cpp
struct CameraParams {
mat4 inv_view; // 64 bytes - inverse view matrix (screen→world)
float fov; // 4 bytes - vertical field of view (radians)
float near_plane; // 4 bytes - near clipping plane
float far_plane; // 4 bytes - far clipping plane
float aspect_ratio; // 4 bytes - width/height ratio
};
static_assert(sizeof(CameraParams) == 80,
"CameraParams must be 80 bytes for WGSL alignment");
```
**Corresponding WGSL:**
```wgsl
struct CameraParams {
inv_view: mat4x4<f32>, // 64 bytes
fov: f32, // 4 bytes
near_plane: f32, // 4 bytes
far_plane: f32, // 4 bytes
aspect_ratio: f32, // 4 bytes
}
```
## Handling Common Pitfalls
- **`vec3<f32>` Padding:** Avoid using `vec3<f32>` for padding in WGSL, as it has a 16-byte alignment. If padding is needed, use `vec2<f32>` for 8 bytes or individual `f32`s for 4-byte alignment.
- **C++ vs. WGSL Alignment:** Always rely on `static_assert` in C++ and verify against WGSL alignment rules. C++ padding rules might differ, and the `static_assert` is the ultimate arbiter.
- **Unmatched Structs:** Ensure every WGSL uniform struct has a corresponding C++ struct with a matching `static_assert`.
## Validation Tool
The `tools/validate_uniforms.py` script is integrated into the build system. It automatically checks for inconsistencies between WGSL and C++ uniform struct definitions and reports any size mismatches. Ensure this script passes for all new or modified uniform definitions.
|