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        let luminance = registry.allocate_image(
46            device,
47            &ResourceDescriptor {
48                label: Some("pyramid_luminance".to_string()),
49                kind: ResourceKind::Image {
50                    width: mips.last().map(|_| mip_w).unwrap_or(width),
51                    height: mips.last().map(|_| mip_h).unwrap_or(height),
52                    format: wgpu::TextureFormat::R8Unorm,
53                    mip_level_count: 1,
54                    usage: wgpu::TextureUsages::TEXTURE_BINDING
55                        | wgpu::TextureUsages::RENDER_ATTACHMENT,
56                },
57                lifetime: ResourceLifetime::Frame,
58            },
59        );
60
61        Self {
62            mips,
63            luminance,
64            width,
65            height,
66            levels,
67        }
68    }
69
70    pub fn sample_at_blur_radius(&self, radius: f32) -> ResourceId {
71        // Simple mapping: radius -> mip level.
72        // e.g., radius 0 -> mip 0
73        // radius 4 -> mip 1, etc.
74        let mip = (radius.log2().max(0.0) as usize).min(self.levels.saturating_sub(1) as usize);
75        self.mips[mip]
76    }
77}
78
79#[repr(C)]
80#[derive(Debug, Clone, Copy, bytemuck::Pod, bytemuck::Zeroable)]
81pub struct BlurUniforms {
82    pub params: [f32; 4], // xy = src_texture_size, z = mip_level, w = offset
83    pub mode: u32,        // 0=down, 1=up, 2=composite
84    pub _pad0: u32,
85    pub _pad1: u32,
86    pub _pad2: u32,
87}