Skip to main content

concinnity_render/vulkan/
uniforms.rs

1//! repr(C) uniform / push-constant structs only the Vulkan frame encoders bind
2//! (std140 / std430 / push-constant layouts). Each is mirrored field-for-field
3//! in a `.glsl`/`.vert`/`.frag`/`.comp` shader under `vulkan/shaders/`, or in
4//! the one `.slang` block Vulkan alone declares.
5//!
6//! Blocks whose shader counterpart is a single-source `.slang` declaration are
7//! declared once for every backend in `crate::uniforms`; what is left here is
8//! what only this backend binds. Their layouts are checked by `shader_layout` in
9//! concinnity-device, which reads the expected offsets out of slangc's
10//! reflection per target. The hand-written asserts below are for the families
11//! whose shaders are still per backend -- the cull kernel, the skinning and
12//! morph kernels, the raymarch SDF templates, the legacy per-draw main and
13//! velocity passes, and Metal's water.
14
15use crate::components::sdf_volume::SDF_PARAMS_LEN;
16
17/// Byte size of the auto-exposure push-constant range. Pins the struct size to
18/// what the pipeline layout declares.
19pub const AUTO_EXPOSURE_PUSH_BYTES: u32 = 16;
20
21/// The main-pass push constant (std430): the model matrix, roughness/metallic
22/// with two pads, then tint and emissive vec3s each followed by a pad (112 B
23/// total). Matches ModelUniforms(64) + MaterialUniforms(48) packed together.
24#[derive(Copy, Clone)]
25#[repr(C)]
26pub struct MainPush {
27    /// Model matrix, column-major.
28    pub model: [[f32; 4]; 4],
29    /// Perceptual roughness in `[0, 1]`.
30    pub roughness: f32,
31    /// Metalness in `[0, 1]`.
32    pub metallic: f32,
33    /// Padding so the field layout matches the shader-side struct.
34    pub _mpad0: f32,
35    /// Padding so the field layout matches the shader-side struct.
36    pub _mpad1: f32,
37    /// Linear RGB base-colour tint.
38    pub tint: [f32; 3],
39    /// Padding so the field layout matches the shader-side struct.
40    pub _mpad2: f32,
41    /// Linear RGB emissive radiance.
42    pub emissive: [f32; 3],
43    /// Padding so the field layout matches the shader-side struct.
44    pub _mpad3: f32,
45}
46
47/// The GPU-cull push constant (cull.comp, std430): six already-normalised frustum
48/// planes (xyz = normal, w = d), the camera position sharing its 16-byte slot with
49/// the build-time object count, then the shader-bucket routing (120 B total).
50#[derive(Copy, Clone)]
51#[repr(C)]
52pub struct CullParams {
53    /// Frustum planes, each `(normal.xyz, d)`.
54    pub planes: [[f32; 4]; 6],
55    /// World-space camera position.
56    pub cam_pos: [f32; 3],
57    /// Draw records the kernel iterates.
58    pub object_count: u32,
59    /// Shader-bucket command regions in the indirect buffer. The kernel writes
60    /// every record's slot in all `bucket_count` regions (a draw in the record's
61    /// own bucket, a no-op everywhere else); region `b` starts at command
62    /// `b * bucket_stride`. `bucket_count = 1` degenerates to a single region.
63    pub bucket_count: u32,
64    /// `u32` slots one bucket occupies in the output list.
65    pub bucket_stride: u32,
66}
67
68/// Cull-side Hi-Z uniforms (cull.comp, std140, 80 bytes): the previous frame's
69/// un-jittered view-projection, the Hi-Z mip-0 dimensions, the mip count, and an
70/// enable flag. Mirrors the Metal / DirectX CullUniforms tail.
71#[derive(Copy, Clone)]
72#[repr(C)]
73pub struct CullHizParams {
74    /// Previous frame's un-jittered view-projection. Projects each AABB into the
75    /// depth space the Hi-Z pyramid was reduced from (`M * v`).
76    pub prev_view_proj: [[f32; 4]; 4],
77    /// Hi-Z mip-0 dimensions (in texels).
78    pub hiz_size: [f32; 2],
79    /// How many mip levels live in the bound texture.
80    pub hiz_mip_count: u32,
81    /// 0 skips the Hi-Z test entirely (first frame / after a resize, before a
82    /// valid pyramid exists).
83    pub hiz_enabled: u32,
84}
85
86/// The G-buffer pre-pass push constant (shared GLSL `PushBlock`): cur_model then
87/// prev_model (two column-major mat4) then roughness, plus a trailing pad to
88/// 16-byte alignment. The motion vector reads cur/prev model; the fragment reads
89/// roughness.
90#[derive(Copy, Clone)]
91#[repr(C)]
92pub struct GbModelPush {
93    /// This frame's model matrix, column-major.
94    pub cur_model: [[f32; 4]; 4],
95    /// The previous frame's model matrix, for velocity.
96    pub prev_model: [[f32; 4]; 4],
97    /// Perceptual roughness in `[0, 1]`.
98    pub roughness: f32,
99    /// Padding so the field layout matches the shader-side struct.
100    pub _pad: [f32; 3],
101}
102
103/// Byte size of the G-buffer pre-pass push-constant range (cur_model 64 +
104/// prev_model 64 + roughness 4 + 12 pad). Pins the struct size.
105pub const GBUFFER_PREPASS_PUSH_BYTES: u32 = 144;
106
107/// The raymarch pass per-frame view UBO (raymarch_helpers.glsl
108/// `RaymarchViewBlock`, std140, 160 bytes). Mirrors the DirectX / Metal
109/// `RaymarchView`.
110#[derive(Copy, Clone)]
111#[repr(C)]
112pub struct RaymarchView {
113    /// View-projection matrix, column-major.
114    pub vp: [[f32; 4]; 4],
115    /// Inverse view-projection matrix, column-major.
116    pub inv_vp: [[f32; 4]; 4],
117    /// World-space camera position.
118    pub cam_pos: [f32; 3],
119    /// Padding so the field layout matches the shader-side struct.
120    pub _pad0: f32,
121    /// Render-target size in pixels.
122    pub viewport: [f32; 2],
123    /// Seconds since the world started.
124    pub time: f32,
125    /// IBL cubemap mip count; 0 when there is no IBL.
126    pub prefilter_mip_count: f32,
127}
128
129/// The per-volume SDF raymarch UBO (`SdfVolumeBlock`, std140, 176 bytes). Mirrors
130/// the DirectX `RaymarchVolumeUniforms`.
131#[derive(Copy, Clone)]
132#[repr(C)]
133pub struct RaymarchVolumeUniforms {
134    /// World-space centre.
135    pub centre: [f32; 3],
136    /// Padding so the field layout matches the shader-side struct.
137    pub _pad0: f32,
138    /// Half-extents from the centre, in world units.
139    pub extent: [f32; 3],
140    /// Padding so the field layout matches the shader-side struct.
141    pub _pad1: f32,
142    /// Cone-tracing footprint growth per unit of march distance.
143    pub cone_ratio: f32,
144    /// Furthest world distance the march travels.
145    pub max_distance: f32,
146    /// Ray-march step cap.
147    pub max_steps: i32,
148    /// Non-zero when the volume samples the shadow maps.
149    pub receive_shadows: i32,
150    /// The volume's authored SDF parameters.
151    pub params: [f32; SDF_PARAMS_LEN],
152}
153
154#[cfg(test)]
155mod tests {
156    use super::*;
157    use core::mem::{offset_of, size_of};
158
159    // MainPush must match the `PushBlock` push constant in the main-pass shaders
160    // (std430): the model matrix, roughness/metallic with two pads, then tint and
161    // emissive vec3s each followed by a pad (112 B total).
162    #[test]
163    fn main_push_layout_matches_glsl() {
164        assert_eq!(size_of::<MainPush>(), 112);
165        assert_eq!(offset_of!(MainPush, model), 0);
166        assert_eq!(offset_of!(MainPush, roughness), 64);
167        assert_eq!(offset_of!(MainPush, metallic), 68);
168        assert_eq!(offset_of!(MainPush, _mpad0), 72);
169        assert_eq!(offset_of!(MainPush, _mpad1), 76);
170        assert_eq!(offset_of!(MainPush, tint), 80);
171        assert_eq!(offset_of!(MainPush, _mpad2), 92);
172        assert_eq!(offset_of!(MainPush, emissive), 96);
173        assert_eq!(offset_of!(MainPush, _mpad3), 108);
174    }
175
176    // CullParams must match the `CullParams` push-constant block in cull.comp
177    // (std430): six frustum planes, then cam_pos sharing its 16-byte slot with
178    // object_count (112 B total).
179    #[test]
180    fn cull_params_layout_matches_glsl() {
181        assert_eq!(size_of::<CullParams>(), 120);
182        assert_eq!(offset_of!(CullParams, planes), 0);
183        assert_eq!(offset_of!(CullParams, cam_pos), 96);
184        assert_eq!(offset_of!(CullParams, object_count), 108);
185        assert_eq!(offset_of!(CullParams, bucket_count), 112);
186        assert_eq!(offset_of!(CullParams, bucket_stride), 116);
187    }
188
189    // std140 CullHizParams in cull.comp: mat4 (64) + vec2 (8, 8-aligned) + two
190    // uints. Total 80 bytes, tightly packed after the mat4.
191    #[test]
192    fn cull_hiz_params_layout_matches_glsl() {
193        assert_eq!(size_of::<CullHizParams>(), 80);
194        assert_eq!(offset_of!(CullHizParams, prev_view_proj), 0);
195        assert_eq!(offset_of!(CullHizParams, hiz_size), 64);
196        assert_eq!(offset_of!(CullHizParams, hiz_mip_count), 72);
197        assert_eq!(offset_of!(CullHizParams, hiz_enabled), 76);
198    }
199
200    // The GLSL `RaymarchViewBlock` std140 layout is 160 bytes.
201    #[test]
202    fn raymarch_view_layout_matches_glsl() {
203        assert_eq!(size_of::<RaymarchView>(), 160);
204        assert_eq!(offset_of!(RaymarchView, vp), 0);
205        assert_eq!(offset_of!(RaymarchView, inv_vp), 64);
206        assert_eq!(offset_of!(RaymarchView, cam_pos), 128);
207        assert_eq!(offset_of!(RaymarchView, viewport), 144);
208        assert_eq!(offset_of!(RaymarchView, time), 152);
209        assert_eq!(offset_of!(RaymarchView, prefilter_mip_count), 156);
210    }
211
212    // The GLSL `SdfVolumeBlock` std140 layout is 176 bytes.
213    #[test]
214    fn sdf_volume_uniforms_layout_matches_glsl() {
215        assert_eq!(size_of::<RaymarchVolumeUniforms>(), 176);
216        assert_eq!(offset_of!(RaymarchVolumeUniforms, centre), 0);
217        assert_eq!(offset_of!(RaymarchVolumeUniforms, extent), 16);
218        assert_eq!(offset_of!(RaymarchVolumeUniforms, cone_ratio), 32);
219        assert_eq!(offset_of!(RaymarchVolumeUniforms, max_distance), 36);
220        assert_eq!(offset_of!(RaymarchVolumeUniforms, max_steps), 40);
221        assert_eq!(offset_of!(RaymarchVolumeUniforms, receive_shadows), 44);
222        assert_eq!(offset_of!(RaymarchVolumeUniforms, params), 48);
223    }
224}