Skip to main content

concinnity_core/render/directx/
uniforms.rs

1//! repr(C) uniform / root-constant structs only the DirectX frame encoders bind
2//! (cbuffer / root-constant layouts). Each is mirrored field-for-field in an
3//! `.hlsl` shader under `directx/shaders/`.
4//!
5//! Blocks whose shader counterpart is a single-source `.slang` declaration are
6//! declared once for every backend in `crate::render::uniforms`; what is left here is
7//! what only this backend binds. Their layouts are checked by `shader_layout` in
8//! concinnity-device, which reads the expected offsets out of slangc's
9//! reflection per target. The hand-written asserts below are for the families
10//! whose shaders are still per backend -- the cull kernel, the skinning and
11//! morph kernels, the raymarch SDF templates, the legacy per-draw main and
12//! velocity passes, and Metal's water.
13
14use crate::components::sdf_volume::SDF_PARAMS_LEN;
15
16/// The GPU-cull `CullParams` cbuffer (b0, 208 bytes): six already-normalised
17/// frustum planes, the camera position sharing its row with the object count, the
18/// previous frame's view-projection, the Hi-Z metadata (dims, mip count, enable
19/// flag), then the shader-bucket routing. DirectX fuses the cull + Hi-Z uniforms
20/// into one cbuffer.
21#[derive(Copy, Clone)]
22#[repr(C)]
23pub struct CullParams {
24    /// Frustum planes, each `(normal.xyz, d)`.
25    pub planes: [[f32; 4]; 6],
26    /// World-space camera position.
27    pub cam_pos: [f32; 3],
28    /// Draw records the kernel iterates.
29    pub object_count: u32,
30    /// The previous frame's view-projection, for velocity and Hi-Z reprojection.
31    pub prev_view_proj: [[f32; 4]; 4],
32    /// Hi-Z pyramid base size in pixels.
33    pub hiz_size: [f32; 2],
34    /// Mip levels in the Hi-Z pyramid.
35    pub hiz_mip_count: u32,
36    /// Non-zero when the Hi-Z occlusion test runs.
37    pub hiz_enabled: u32,
38    /// Shader-bucket command regions in the indirect buffer. The kernel writes
39    /// every record's slot in all `bucket_count` regions (a draw in the record's
40    /// own bucket, a no-op everywhere else); region `b` starts at command
41    /// `b * bucket_stride`. `bucket_count = 1` degenerates to a single region.
42    pub bucket_count: u32,
43    /// `u32` slots one bucket occupies in the output list.
44    pub bucket_stride: u32,
45    /// Padding so the field layout matches the shader-side struct.
46    pub _pad: [u32; 2],
47}
48
49/// The raymarch pass per-frame `RaymarchView` cbuffer (b0, 160 bytes; aligned to
50/// 256 for the D3D12 cbuffer). Mirrors the Metal `RaymarchView`.
51#[derive(Copy, Clone)]
52#[repr(C)]
53pub struct RaymarchView {
54    /// View-projection matrix, column-major.
55    pub vp: [[f32; 4]; 4],
56    /// Inverse view-projection matrix, column-major.
57    pub inv_vp: [[f32; 4]; 4],
58    /// World-space camera position.
59    pub cam_pos: [f32; 3],
60    /// Padding so the field layout matches the shader-side struct.
61    pub _pad0: f32,
62    /// Render-target size in pixels.
63    pub viewport: [f32; 2],
64    /// Seconds since the world started.
65    pub time: f32,
66    /// IBL cubemap mip count; 0 when there is no IBL.
67    pub prefilter_mip_count: f32,
68}
69
70/// The per-volume `SdfVolumeUniforms` cbuffer (b1, 176 bytes; aligned to 256 in
71/// the cbuffer allocation).
72#[derive(Copy, Clone)]
73#[repr(C)]
74pub struct RaymarchVolumeUniforms {
75    /// World-space centre.
76    pub centre: [f32; 3],
77    /// Padding so the field layout matches the shader-side struct.
78    pub _pad0: f32,
79    /// Half-extents from the centre, in world units.
80    pub extent: [f32; 3],
81    /// Padding so the field layout matches the shader-side struct.
82    pub _pad1: f32,
83    /// Cone-tracing footprint growth per unit of march distance.
84    pub cone_ratio: f32,
85    /// Furthest world distance the march travels.
86    pub max_distance: f32,
87    /// Ray-march step cap.
88    pub max_steps: i32,
89    /// Non-zero when the volume samples the shadow maps.
90    pub receive_shadows: i32,
91    /// The volume's authored SDF parameters.
92    pub params: [f32; SDF_PARAMS_LEN],
93}
94
95#[cfg(test)]
96mod tests {
97    use super::*;
98    use core::mem::{offset_of, size_of};
99
100    // CullParams must match the `CullParams` cbuffer (b0) in cull.hlsl: six
101    // frustum planes, cam_pos sharing its row with object_count, the previous
102    // view-projection, the Hi-Z metadata, then the bucket routing pair opening
103    // a fresh 16-byte row (208 B total).
104    #[test]
105    fn cull_params_layout_matches_hlsl() {
106        assert_eq!(size_of::<CullParams>(), 208);
107        assert_eq!(offset_of!(CullParams, planes), 0);
108        assert_eq!(offset_of!(CullParams, cam_pos), 96);
109        assert_eq!(offset_of!(CullParams, object_count), 108);
110        assert_eq!(offset_of!(CullParams, prev_view_proj), 112);
111        assert_eq!(offset_of!(CullParams, hiz_size), 176);
112        assert_eq!(offset_of!(CullParams, hiz_mip_count), 184);
113        assert_eq!(offset_of!(CullParams, hiz_enabled), 188);
114        assert_eq!(offset_of!(CullParams, bucket_count), 192);
115        assert_eq!(offset_of!(CullParams, bucket_stride), 196);
116    }
117
118    // RaymarchView must match the `RaymarchView` cbuffer (b0) in
119    // raymarch_helpers.hlsl: two column-major float4x4 then the packed
120    // cam_pos/pad/viewport/time/prefilter scalars (160 B total).
121    #[test]
122    fn raymarch_view_layout_matches_hlsl() {
123        assert_eq!(size_of::<RaymarchView>(), 160);
124        assert_eq!(offset_of!(RaymarchView, vp), 0);
125        assert_eq!(offset_of!(RaymarchView, inv_vp), 64);
126        assert_eq!(offset_of!(RaymarchView, cam_pos), 128);
127        assert_eq!(offset_of!(RaymarchView, _pad0), 140);
128        assert_eq!(offset_of!(RaymarchView, viewport), 144);
129        assert_eq!(offset_of!(RaymarchView, time), 152);
130        assert_eq!(offset_of!(RaymarchView, prefilter_mip_count), 156);
131    }
132
133    // RaymarchVolumeUniforms must match the `SdfVolumeUniforms` cbuffer (b1):
134    // centre/pad, extent/pad, the four scalars, then 32 floats of params packed
135    // as 8 float4 rows (176 B total).
136    #[test]
137    fn raymarch_volume_uniforms_layout_matches_hlsl() {
138        assert_eq!(size_of::<RaymarchVolumeUniforms>(), 176);
139        assert_eq!(offset_of!(RaymarchVolumeUniforms, centre), 0);
140        assert_eq!(offset_of!(RaymarchVolumeUniforms, _pad0), 12);
141        assert_eq!(offset_of!(RaymarchVolumeUniforms, extent), 16);
142        assert_eq!(offset_of!(RaymarchVolumeUniforms, _pad1), 28);
143        assert_eq!(offset_of!(RaymarchVolumeUniforms, cone_ratio), 32);
144        assert_eq!(offset_of!(RaymarchVolumeUniforms, max_distance), 36);
145        assert_eq!(offset_of!(RaymarchVolumeUniforms, max_steps), 40);
146        assert_eq!(offset_of!(RaymarchVolumeUniforms, receive_shadows), 44);
147        assert_eq!(offset_of!(RaymarchVolumeUniforms, params), 48);
148    }
149}