Skip to main content

dear_imgui_wgpu/
frame_resources.rs

1//! Frame resources management for the WGPU renderer
2//!
3//! This module handles per-frame resources like vertex and index buffers,
4//! corresponding to the FrameResources struct in imgui_impl_wgpu.cpp
5
6use crate::{RendererError, RendererResult};
7use dear_imgui_rs::render::{DrawIdx, DrawVert};
8use wgpu::*;
9
10/// Memory alignment function (equivalent to MEMALIGN macro in C++)
11/// Aligns size to the specified alignment boundary
12fn align_size(size: usize, alignment: usize) -> usize {
13    (size + alignment - 1) & !(alignment - 1)
14}
15
16/// Per-frame resources
17///
18/// This corresponds to the FrameResources struct in the C++ implementation.
19/// Each frame in flight has its own set of vertex and index buffers.
20pub struct FrameResources {
21    /// GPU vertex buffer
22    pub vertex_buffer: Option<Buffer>,
23    /// GPU index buffer  
24    pub index_buffer: Option<Buffer>,
25    /// Host-side vertex buffer (for staging)
26    pub vertex_buffer_host: Option<Vec<u8>>,
27    /// Host-side index buffer (for staging)
28    pub index_buffer_host: Option<Vec<u8>>,
29    /// Current vertex buffer size in vertices
30    pub vertex_buffer_size: usize,
31    /// Current index buffer size in indices
32    pub index_buffer_size: usize,
33}
34
35impl FrameResources {
36    /// Create new empty frame resources
37    pub fn new() -> Self {
38        Self {
39            vertex_buffer: None,
40            index_buffer: None,
41            vertex_buffer_host: None,
42            index_buffer_host: None,
43            vertex_buffer_size: 0,
44            index_buffer_size: 0,
45        }
46    }
47
48    /// Ensure vertex buffer can hold the required number of vertices
49    pub fn ensure_vertex_buffer_capacity(
50        &mut self,
51        device: &Device,
52        required_vertices: usize,
53    ) -> RendererResult<()> {
54        if self.vertex_buffer.is_none() || self.vertex_buffer_size < required_vertices {
55            // Add some extra capacity to avoid frequent reallocations
56            let new_size = (required_vertices + 5000).max(self.vertex_buffer_size * 2);
57
58            // Create new GPU buffer with proper alignment
59            let buffer_size = align_size(new_size * std::mem::size_of::<DrawVert>(), 4);
60            let buffer = device.create_buffer(&BufferDescriptor {
61                label: Some("Dear ImGui Vertex Buffer"),
62                size: buffer_size as u64,
63                usage: BufferUsages::VERTEX | BufferUsages::COPY_DST,
64                mapped_at_creation: false,
65            });
66
67            // Create new host buffer
68            let host_buffer = vec![0u8; new_size * std::mem::size_of::<DrawVert>()];
69
70            self.vertex_buffer = Some(buffer);
71            self.vertex_buffer_host = Some(host_buffer);
72            self.vertex_buffer_size = new_size;
73        }
74
75        Ok(())
76    }
77
78    /// Ensure index buffer can hold the required number of indices
79    pub fn ensure_index_buffer_capacity(
80        &mut self,
81        device: &Device,
82        required_indices: usize,
83    ) -> RendererResult<()> {
84        if self.index_buffer.is_none() || self.index_buffer_size < required_indices {
85            // Add some extra capacity to avoid frequent reallocations
86            let new_size = (required_indices + 10000).max(self.index_buffer_size * 2);
87
88            // Create new GPU buffer with proper alignment
89            let buffer_size = align_size(new_size * std::mem::size_of::<DrawIdx>(), 4);
90            let buffer = device.create_buffer(&BufferDescriptor {
91                label: Some("Dear ImGui Index Buffer"),
92                size: buffer_size as u64,
93                usage: BufferUsages::INDEX | BufferUsages::COPY_DST,
94                mapped_at_creation: false,
95            });
96
97            // Create new host buffer
98            let host_buffer = vec![0u8; new_size * std::mem::size_of::<DrawIdx>()];
99
100            self.index_buffer = Some(buffer);
101            self.index_buffer_host = Some(host_buffer);
102            self.index_buffer_size = new_size;
103        }
104
105        Ok(())
106    }
107
108    /// Upload vertex data to the GPU buffer
109    pub fn upload_vertex_data(
110        &mut self,
111        queue: &Queue,
112        vertices: &[DrawVert],
113    ) -> RendererResult<()> {
114        let vertex_buffer = self.vertex_buffer.as_ref().ok_or_else(|| {
115            RendererError::InvalidRenderState("Vertex buffer not initialized".to_string())
116        })?;
117
118        let required_bytes = std::mem::size_of_val(vertices);
119        let aligned_size = align_size(required_bytes, 4);
120
121        let host_buffer = self.vertex_buffer_host.as_mut().ok_or_else(|| {
122            RendererError::InvalidRenderState("Vertex host buffer not initialized".to_string())
123        })?;
124        if aligned_size > host_buffer.len() {
125            return Err(RendererError::InvalidRenderState(
126                "Vertex host buffer capacity is too small".to_string(),
127            ));
128        }
129
130        // Avoid reinterpreting `DrawVert` as bytes: that can read uninitialized padding bytes.
131        // Pack vertices explicitly in the same layout used by Dear ImGui (pos, uv, col).
132        host_buffer[..aligned_size].fill(0);
133        const VERT_STRIDE: usize = std::mem::size_of::<DrawVert>();
134        for (i, v) in vertices.iter().enumerate() {
135            let base = i * VERT_STRIDE;
136            host_buffer[base..base + 4].copy_from_slice(&v.pos[0].to_ne_bytes());
137            host_buffer[base + 4..base + 8].copy_from_slice(&v.pos[1].to_ne_bytes());
138            host_buffer[base + 8..base + 12].copy_from_slice(&v.uv[0].to_ne_bytes());
139            host_buffer[base + 12..base + 16].copy_from_slice(&v.uv[1].to_ne_bytes());
140            host_buffer[base + 16..base + 20].copy_from_slice(&v.col.to_ne_bytes());
141        }
142
143        // Upload to GPU with proper alignment
144        queue.write_buffer(vertex_buffer, 0, &host_buffer[..aligned_size]);
145        Ok(())
146    }
147
148    /// Upload index data to the GPU buffer
149    pub fn upload_index_data(&mut self, queue: &Queue, indices: &[DrawIdx]) -> RendererResult<()> {
150        let index_buffer = self.index_buffer.as_ref().ok_or_else(|| {
151            RendererError::InvalidRenderState("Index buffer not initialized".to_string())
152        })?;
153
154        let required_bytes = std::mem::size_of_val(indices);
155        let aligned_size = align_size(required_bytes, 4);
156
157        let host_buffer = self.index_buffer_host.as_mut().ok_or_else(|| {
158            RendererError::InvalidRenderState("Index host buffer not initialized".to_string())
159        })?;
160        if aligned_size > host_buffer.len() {
161            return Err(RendererError::InvalidRenderState(
162                "Index host buffer capacity is too small".to_string(),
163            ));
164        }
165
166        host_buffer[..aligned_size].fill(0);
167        for (i, &idx) in indices.iter().enumerate() {
168            let bytes = idx.to_ne_bytes();
169            let base = i * std::mem::size_of::<DrawIdx>();
170            host_buffer[base..base + 2].copy_from_slice(&bytes);
171        }
172
173        // Upload to GPU with proper alignment
174        queue.write_buffer(index_buffer, 0, &host_buffer[..aligned_size]);
175        Ok(())
176    }
177
178    /// Get the vertex buffer for rendering
179    pub fn vertex_buffer(&self) -> Option<&Buffer> {
180        self.vertex_buffer.as_ref()
181    }
182
183    /// Get the index buffer for rendering
184    pub fn index_buffer(&self) -> Option<&Buffer> {
185        self.index_buffer.as_ref()
186    }
187
188    /// Check if buffers are ready for rendering
189    pub fn is_ready(&self) -> bool {
190        self.vertex_buffer.is_some() && self.index_buffer.is_some()
191    }
192
193    /// Get buffer statistics for debugging
194    pub fn stats(&self) -> FrameResourcesStats {
195        FrameResourcesStats {
196            vertex_buffer_size: self.vertex_buffer_size,
197            index_buffer_size: self.index_buffer_size,
198            vertex_buffer_bytes: self.vertex_buffer_size * std::mem::size_of::<DrawVert>(),
199            index_buffer_bytes: self.index_buffer_size * std::mem::size_of::<DrawIdx>(),
200        }
201    }
202}
203
204impl Default for FrameResources {
205    fn default() -> Self {
206        Self::new()
207    }
208}
209
210/// Statistics for frame resources
211#[derive(Debug, Clone)]
212pub struct FrameResourcesStats {
213    pub vertex_buffer_size: usize,
214    pub index_buffer_size: usize,
215    pub vertex_buffer_bytes: usize,
216    pub index_buffer_bytes: usize,
217}