Skip to main content

cvkg_render_gpu/
pyramid.rs

1use crate::kvasir::registry::ResourceRegistry;
2use crate::kvasir::resource::{ResourceDescriptor, ResourceId, ResourceKind, ResourceLifetime};
3use wgpu;
4
5pub struct ImagePyramid {
6    pub mips: Vec<ResourceId>,
7    pub luminance: ResourceId,
8    pub width: u32,
9    pub height: u32,
10    pub levels: u32,
11}
12
13impl ImagePyramid {
14    pub fn new(
15        registry: &mut ResourceRegistry,
16        device: &wgpu::Device,
17        width: u32,
18        height: u32,
19        levels: u32,
20    ) -> Self {
21        let mut mips = Vec::new();
22        let mut mip_w = width;
23        let mut mip_h = height;
24
25        for i in 0..levels {
26            let desc = ResourceDescriptor {
27                label: Some(format!("pyramid_mip_{}", i)),
28                kind: ResourceKind::Image {
29                    width: mip_w,
30                    height: mip_h,
31                    format: wgpu::TextureFormat::Rgba16Float,
32                    mip_level_count: 1,
33                    usage: wgpu::TextureUsages::TEXTURE_BINDING
34                        | wgpu::TextureUsages::RENDER_ATTACHMENT
35                        | wgpu::TextureUsages::COPY_DST,
36                },
37                lifetime: ResourceLifetime::Frame,
38            };
39            mips.push(registry.allocate_image(device, &desc));
40
41            mip_w = (mip_w / 2).max(1);
42            mip_h = (mip_h / 2).max(1);
43        }
44
45        // Luminance texture matches the last allocated mip level dimensions
46        let last_mip_w = (mip_w * 2).min(width);
47        let last_mip_h = (mip_h * 2).min(height);
48        let luminance = registry.allocate_image(
49            device,
50            &ResourceDescriptor {
51                label: Some("pyramid_luminance".to_string()),
52                kind: ResourceKind::Image {
53                    width: last_mip_w,
54                    height: last_mip_h,
55                    format: wgpu::TextureFormat::R8Unorm,
56                    mip_level_count: 1,
57                    usage: wgpu::TextureUsages::TEXTURE_BINDING
58                        | wgpu::TextureUsages::RENDER_ATTACHMENT,
59                },
60                lifetime: ResourceLifetime::Frame,
61            },
62        );
63
64        Self {
65            mips,
66            luminance,
67            width,
68            height,
69            levels,
70        }
71    }
72
73    pub fn sample_at_blur_radius(&self, radius: f32) -> ResourceId {
74        // Simple mapping: radius -> mip level.
75        // e.g., radius 0 -> mip 0
76        // radius 4 -> mip 1, etc.
77        let mip = (radius.log2().max(0.0) as usize).min(self.levels.saturating_sub(1) as usize);
78        self.mips[mip]
79    }
80}
81
82#[repr(C)]
83#[derive(Debug, Clone, Copy, bytemuck::Pod, bytemuck::Zeroable)]
84pub struct BlurUniforms {
85    pub params: [f32; 4], // xy = src_texture_size, z = mip_level, w = offset
86    pub mode: u32,        // 0=down, 1=up, 2=composite
87    pub _pad0: u32,
88    pub _pad1: u32,
89    pub _pad2: u32,
90}