concinnity_core/render/uniforms/raymarch.rs
1//! The blocks the raymarched SDF volume pass binds.
2//!
3//! One declaration each, for all three backends: the three hosts bind the same
4//! bytes at different slots, and the slots are the only thing they disagree
5//! about. Before the pass was single-sourced these were three `#[repr(C)]`
6//! copies apiece, kept in step by hand.
7
8use crate::components::sdf_volume::SDF_PARAMS_LEN;
9
10/// Per-frame view inputs, 208 bytes. Every field after `inv_vp` is a `float4`
11/// lane or smaller, which is what keeps the three targets agreeing: a `float3`
12/// occupies 16 bytes in a Metal constant buffer and 12 on SPIR-V and DXIL.
13#[derive(Copy, Clone, bytemuck::NoUninit)]
14#[repr(C)]
15pub struct RaymarchView {
16 /// View-projection matrix, column-major.
17 pub vp: [[f32; 4]; 4],
18 /// Inverse view-projection matrix, column-major.
19 pub inv_vp: [[f32; 4]; 4],
20 /// World-space camera position in `xyz`; `w` is padding.
21 pub cam_pos: [f32; 4],
22 /// Render-target size in pixels.
23 pub viewport: [f32; 2],
24 /// Seconds since the world started, available to the authored field.
25 pub time: f32,
26 /// Mip count of the bound IBL prefilter cube. 0 means no `EnvironmentMap`
27 /// is bound and the ambient helper takes its hemispheric fallback.
28 pub prefilter_mip_count: f32,
29 /// Rows of the rotation taking a world direction into the environment
30 /// cubemap's baked frame, so a volume's ambient turns with the sky.
31 pub sky_rot: [[f32; 4]; 3],
32}
33
34/// Per-volume uniforms, 176 bytes. `centre` and `extent` each pair with the pad
35/// that completes their `float4` lane, for the reason above.
36#[derive(Copy, Clone, bytemuck::NoUninit)]
37#[repr(C)]
38pub struct RaymarchVolumeUniforms {
39 /// World-space centre of the bounding box.
40 pub centre: [f32; 3],
41 /// Padding completing the lane `centre` opens.
42 pub _pad0: f32,
43 /// Half-widths of the bounding box.
44 pub extent: [f32; 3],
45 /// Padding completing the lane `extent` opens.
46 pub _pad1: f32,
47 /// `1 / max_gradient`; the cone-step scale factor.
48 pub cone_ratio: f32,
49 /// Per-volume march far clip, in metres.
50 pub max_distance: f32,
51 /// Per-volume step cap, clamped at load.
52 pub max_steps: i32,
53 /// Non-zero when the volume samples the shadow maps.
54 pub receive_shadows: i32,
55 /// The authored parameter block the field interprets.
56 pub params: [f32; SDF_PARAMS_LEN],
57}
58
59/// Which cascade a shadow-caster draw targets, 16 bytes. A root constant on
60/// DirectX, a push constant on Vulkan, and a bound buffer on Metal.
61#[derive(Copy, Clone, bytemuck::NoUninit)]
62#[repr(C)]
63pub struct RaymarchShadowCascade {
64 /// Index into the cascade light view-projections.
65 pub cascade_idx: u32,
66 /// Padding to the 16-byte block the hosts allocate.
67 pub _pad: [u32; 3],
68}
69
70#[cfg(test)]
71mod tests {
72 use super::*;
73 use core::mem::size_of;
74
75 // The sizes the three hosts allocate for these blocks. The per-field
76 // offsets are checked against slangc's reflection in
77 // `concinnity-device/src/shader_layout/`, which is what catches a shader-side
78 // spelling that lays out differently on one target than on the others.
79 #[test]
80 fn the_blocks_are_the_sizes_the_hosts_bind() {
81 assert_eq!(size_of::<RaymarchView>(), 208);
82 assert_eq!(size_of::<RaymarchVolumeUniforms>(), 176);
83 assert_eq!(size_of::<RaymarchShadowCascade>(), 16);
84 }
85}