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}