concinnity-core 0.19.24

Runtime vocabulary for the Concinnity engine: GPU layouts, ECS components, registry, CPU kernels
Documentation
//! The camera blocks: what the forward pass and the G-buffer pre-pass need to
//! place a fragment, and the per-draw model pair the pre-pass differentiates for
//! motion vectors.

/// Per-frame view-projection uniforms, uploaded once per frame and shared across
/// every draw in it. `view` is the standalone view matrix the vertex shader uses
/// to compute view-space depth for cascade selection in the fragment shader.
///
/// The `.slang` source declares the same bytes with its own spelling
/// (`view_mat`, and `cam_x`/`cam_y`/`cam_z` in place of
/// `cam_pos`), which is why the two are checked as byte ranges rather than by
/// name.
#[derive(Copy, Clone, bytemuck::NoUninit)]
#[repr(C)]
pub struct ViewUniforms {
    /// Combined view-projection matrix (column-major).
    pub vp: [[f32; 4]; 4],
    /// Camera view matrix (column-major). Used to compute view-space depth
    /// in the vertex shader for shadow cascade selection.
    pub view: [[f32; 4]; 4],
    /// Elapsed seconds, available to shaders for animation.
    pub elapsed: f32,
    /// 1.0 when a screen-space / ray-traced reflection resolve composites this
    /// frame, else 0.0. The forward fragment shader uses it to yield the sharp
    /// specular for glossy surfaces to that resolve (whose miss-fallback samples
    /// the same probe set), so a glossy surface does not show both the
    /// parallax-approximate forward probe reflection and the exact resolved one.
    pub reflections_enabled: f32,
    /// World-space camera position (packed_float3 in the MSL contract, alignment 4).
    pub cam_pos: [f32; 3],
    /// Number of mip levels in the bound IBL prefilter cubemap. 0 means
    /// "no EnvironmentMap bound": the fragment shader uses this as the IBL
    /// enable flag and falls back to a flat ambient placeholder.
    pub prefilter_mip_count: f32,
    /// 1.0 while the unlit view mode is active: shade_surface returns the base
    /// color before lighting. Occupies what was pad space, so the offsets in
    /// the user-shader binding contract are unchanged.
    pub shade_mode: f32,
    /// End-padding: the shader rounds the block up to a multiple of float4x4's
    /// 16-byte alignment, so the upload rounds explicitly to match.
    pub _end_pad: f32,
    /// Rows of the rotation that takes a world-space direction into the
    /// environment cubemaps' baked frame: the inverse of the sky's current
    /// rotation (`SkyOrientation::sample_rows`), identity when the world
    /// declares no `SkyRotation`. Every cubemap sample helper applies it, so
    /// the sky, the ambient fill and the glossy reflections turn together.
    /// One `float4` per row; `w` is unused.
    pub sky_rot: [[f32; 4]; 3],
}

/// Per-frame view inputs to the unified G-buffer pre-pass. The jittered current
/// VP drives the rasterised position (matching the main pass); `view` takes the
/// normal + position into view space (where SSR / SSAO / SSGI / RT work); the
/// un-jittered cur/prev VPs derive a jitter-free motion vector. Matches `GbView`
/// in `shaders/gbuffer_prepass.slang`. 256 bytes (four float4x4, all naturally
/// 16-aligned, no padding).
#[derive(Copy, Clone, bytemuck::NoUninit)]
#[repr(C)]
pub struct GBufferView {
    /// View-projection with this frame's TAA jitter applied.
    pub jittered_vp: [[f32; 4]; 4],
    /// This frame's unjittered view-projection.
    pub cur_vp: [[f32; 4]; 4],
    /// The previous frame's unjittered view-projection.
    pub prev_vp: [[f32; 4]; 4],
    /// View matrix, column-major.
    pub view: [[f32; 4]; 4],
}

#[cfg(test)]
mod tests {
    use super::*;
    use core::mem::{offset_of, size_of};

    // Every offset here is a byte position the main pass reads through.
    // `sky_rot` lands past the block's own end padding, so nothing before it
    // moved.
    #[test]
    fn view_uniforms_layout_matches_msl() {
        assert_eq!(size_of::<ViewUniforms>(), 208);
        assert_eq!(offset_of!(ViewUniforms, vp), 0);
        assert_eq!(offset_of!(ViewUniforms, view), 64);
        assert_eq!(offset_of!(ViewUniforms, elapsed), 128);
        assert_eq!(offset_of!(ViewUniforms, reflections_enabled), 132);
        assert_eq!(offset_of!(ViewUniforms, cam_pos), 136);
        assert_eq!(offset_of!(ViewUniforms, prefilter_mip_count), 148);
        assert_eq!(offset_of!(ViewUniforms, shade_mode), 152);
        assert_eq!(offset_of!(ViewUniforms, _end_pad), 156);
        // A float4 array is 16-byte aligned on every target.
        assert_eq!(offset_of!(ViewUniforms, sky_rot), 160);
        assert_eq!(size_of::<ViewUniforms>() % 16, 0);
    }

    // Four float4x4s, all naturally aligned.
    #[test]
    fn gbuffer_view_layout_matches_the_shader() {
        assert_eq!(size_of::<GBufferView>(), 256);
        assert_eq!(offset_of!(GBufferView, jittered_vp), 0);
        assert_eq!(offset_of!(GBufferView, cur_vp), 64);
        assert_eq!(offset_of!(GBufferView, prev_vp), 128);
        assert_eq!(offset_of!(GBufferView, view), 192);
    }
}