Skip to main content

dear_imgui_wgpu/
uniforms.rs

1//! Uniform buffer management for the WGPU renderer
2//!
3//! This module handles the uniform data structure and buffer management,
4//! corresponding to the Uniforms struct in imgui_impl_wgpu.cpp
5
6use bytemuck::{Pod, Zeroable};
7use wgpu::*;
8
9/// Memory alignment function (equivalent to MEMALIGN macro in C++)
10/// Aligns size to the specified alignment boundary
11fn align_size(size: usize, alignment: usize) -> usize {
12    (size + alignment - 1) & !(alignment - 1)
13}
14
15/// Uniform data structure
16///
17/// This corresponds to the Uniforms struct in the C++ implementation.
18/// Contains the MVP matrix and gamma correction value.
19#[repr(C)]
20#[derive(Debug, Copy, Clone, Pod, Zeroable)]
21pub struct Uniforms {
22    /// Model-View-Projection matrix (4x4 f32 matrix)
23    pub mvp: [[f32; 4]; 4],
24    /// Gamma correction value
25    pub gamma: f32,
26    /// Padding to ensure proper alignment
27    pub _padding: [f32; 3],
28}
29
30impl Uniforms {
31    /// Create new uniforms with identity matrix and default gamma
32    pub fn new() -> Self {
33        Self {
34            mvp: [
35                [1.0, 0.0, 0.0, 0.0],
36                [0.0, 1.0, 0.0, 0.0],
37                [0.0, 0.0, 1.0, 0.0],
38                [0.0, 0.0, 0.0, 1.0],
39            ],
40            gamma: 1.0,
41            _padding: [0.0; 3],
42        }
43    }
44
45    /// Create orthographic projection matrix for Dear ImGui
46    ///
47    /// This matches the matrix calculation in ImGui_ImplWGPU_SetupRenderState
48    pub fn create_orthographic_matrix(
49        display_pos: [f32; 2],
50        display_size: [f32; 2],
51    ) -> [[f32; 4]; 4] {
52        let l = display_pos[0];
53        let r = display_pos[0] + display_size[0];
54        let t = display_pos[1];
55        let b = display_pos[1] + display_size[1];
56
57        [
58            [2.0 / (r - l), 0.0, 0.0, 0.0],
59            [0.0, 2.0 / (t - b), 0.0, 0.0],
60            [0.0, 0.0, 0.5, 0.0],
61            [(r + l) / (l - r), (t + b) / (b - t), 0.5, 1.0],
62        ]
63    }
64
65    /// Determine gamma value based on texture format
66    ///
67    /// This matches the gamma detection logic in ImGui_ImplWGPU_SetupRenderState
68    /// from the official C++ implementation, supporting all sRGB texture formats.
69    pub fn gamma_for_format(format: TextureFormat) -> f32 {
70        match format {
71            // sRGB formats need gamma correction (gamma = 2.2)
72            // This matches the complete list from imgui_impl_wgpu.cpp
73
74            // ASTC sRGB formats
75            TextureFormat::Astc {
76                block: AstcBlock::B4x4,
77                channel: AstcChannel::UnormSrgb,
78            }
79            | TextureFormat::Astc {
80                block: AstcBlock::B5x4,
81                channel: AstcChannel::UnormSrgb,
82            }
83            | TextureFormat::Astc {
84                block: AstcBlock::B5x5,
85                channel: AstcChannel::UnormSrgb,
86            }
87            | TextureFormat::Astc {
88                block: AstcBlock::B6x5,
89                channel: AstcChannel::UnormSrgb,
90            }
91            | TextureFormat::Astc {
92                block: AstcBlock::B6x6,
93                channel: AstcChannel::UnormSrgb,
94            }
95            | TextureFormat::Astc {
96                block: AstcBlock::B8x5,
97                channel: AstcChannel::UnormSrgb,
98            }
99            | TextureFormat::Astc {
100                block: AstcBlock::B8x6,
101                channel: AstcChannel::UnormSrgb,
102            }
103            | TextureFormat::Astc {
104                block: AstcBlock::B8x8,
105                channel: AstcChannel::UnormSrgb,
106            }
107            | TextureFormat::Astc {
108                block: AstcBlock::B10x5,
109                channel: AstcChannel::UnormSrgb,
110            }
111            | TextureFormat::Astc {
112                block: AstcBlock::B10x6,
113                channel: AstcChannel::UnormSrgb,
114            }
115            | TextureFormat::Astc {
116                block: AstcBlock::B10x8,
117                channel: AstcChannel::UnormSrgb,
118            }
119            | TextureFormat::Astc {
120                block: AstcBlock::B10x10,
121                channel: AstcChannel::UnormSrgb,
122            }
123            | TextureFormat::Astc {
124                block: AstcBlock::B12x10,
125                channel: AstcChannel::UnormSrgb,
126            }
127            | TextureFormat::Astc {
128                block: AstcBlock::B12x12,
129                channel: AstcChannel::UnormSrgb,
130            }
131            // BC (Block Compression) sRGB formats
132            | TextureFormat::Bc1RgbaUnormSrgb
133            | TextureFormat::Bc2RgbaUnormSrgb
134            | TextureFormat::Bc3RgbaUnormSrgb
135            | TextureFormat::Bc7RgbaUnormSrgb
136            // Standard sRGB formats
137            | TextureFormat::Rgba8UnormSrgb
138            | TextureFormat::Bgra8UnormSrgb
139            // ETC2 sRGB formats
140            | TextureFormat::Etc2Rgb8UnormSrgb
141            | TextureFormat::Etc2Rgb8A1UnormSrgb
142            | TextureFormat::Etc2Rgba8UnormSrgb => 2.2,
143            // Linear formats don't need gamma correction (gamma = 1.0)
144            _ => 1.0,
145        }
146    }
147
148    /// Update the MVP matrix
149    pub fn set_mvp(&mut self, mvp: [[f32; 4]; 4]) {
150        self.mvp = mvp;
151    }
152
153    /// Update the gamma value
154    pub fn set_gamma(&mut self, gamma: f32) {
155        self.gamma = gamma;
156    }
157
158    /// Update both MVP and gamma
159    pub fn update(&mut self, mvp: [[f32; 4]; 4], gamma: f32) {
160        self.mvp = mvp;
161        self.gamma = gamma;
162    }
163}
164
165impl Default for Uniforms {
166    fn default() -> Self {
167        Self::new()
168    }
169}
170
171/// Uniform buffer manager
172pub struct UniformBuffer {
173    buffer: Buffer,
174    bind_group: BindGroup,
175    bind_group_layout: BindGroupLayout,
176}
177
178impl UniformBuffer {
179    /// Create a new uniform buffer
180    pub fn new(device: &Device, sampler: &Sampler) -> Self {
181        // Create the uniform buffer with proper alignment (16 bytes for uniforms)
182        let buffer_size = align_size(std::mem::size_of::<Uniforms>(), 16);
183        let buffer = device.create_buffer(&BufferDescriptor {
184            label: Some("Dear ImGui Uniform Buffer"),
185            size: buffer_size as u64,
186            usage: BufferUsages::UNIFORM | BufferUsages::COPY_DST,
187            mapped_at_creation: false,
188        });
189
190        // Create bind group layout (uniform buffer + sampler)
191        let bind_group_layout = device.create_bind_group_layout(&BindGroupLayoutDescriptor {
192            label: Some("Dear ImGui Common Bind Group Layout"),
193            entries: &[
194                BindGroupLayoutEntry {
195                    binding: 0,
196                    visibility: ShaderStages::VERTEX | ShaderStages::FRAGMENT,
197                    ty: BindingType::Buffer {
198                        ty: BufferBindingType::Uniform,
199                        has_dynamic_offset: false,
200                        min_binding_size: None,
201                    },
202                    count: None,
203                },
204                BindGroupLayoutEntry {
205                    binding: 1,
206                    visibility: ShaderStages::FRAGMENT,
207                    ty: BindingType::Sampler(SamplerBindingType::Filtering),
208                    count: None,
209                },
210            ],
211        });
212
213        // Create bind group
214        let bind_group = device.create_bind_group(&BindGroupDescriptor {
215            label: Some("Dear ImGui Common Bind Group"),
216            layout: &bind_group_layout,
217            entries: &[
218                BindGroupEntry {
219                    binding: 0,
220                    resource: buffer.as_entire_binding(),
221                },
222                BindGroupEntry {
223                    binding: 1,
224                    resource: BindingResource::Sampler(sampler),
225                },
226            ],
227        });
228
229        Self {
230            buffer,
231            bind_group,
232            bind_group_layout,
233        }
234    }
235
236    /// Update the uniform buffer with new data
237    pub fn update(&self, queue: &Queue, uniforms: &Uniforms) {
238        queue.write_buffer(&self.buffer, 0, bytemuck::bytes_of(uniforms));
239    }
240
241    /// Get the bind group for rendering
242    pub fn bind_group(&self) -> &BindGroup {
243        &self.bind_group
244    }
245
246    /// Get the bind group layout
247    pub fn bind_group_layout(&self) -> &BindGroupLayout {
248        &self.bind_group_layout
249    }
250
251    /// Get the buffer
252    pub fn buffer(&self) -> &Buffer {
253        &self.buffer
254    }
255}