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 dear_imgui::render::{DrawIdx, DrawVert};
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/// Per-frame resources
16///
17/// This corresponds to the FrameResources struct in the C++ implementation.
18/// Each frame in flight has its own set of vertex and index buffers.
19pub struct FrameResources {
20    /// GPU vertex buffer
21    pub vertex_buffer: Option<Buffer>,
22    /// GPU index buffer  
23    pub index_buffer: Option<Buffer>,
24    /// Host-side vertex buffer (for staging)
25    pub vertex_buffer_host: Option<Vec<u8>>,
26    /// Host-side index buffer (for staging)
27    pub index_buffer_host: Option<Vec<u8>>,
28    /// Current vertex buffer size in vertices
29    pub vertex_buffer_size: usize,
30    /// Current index buffer size in indices
31    pub index_buffer_size: usize,
32}
33
34impl FrameResources {
35    /// Create new empty frame resources
36    pub fn new() -> Self {
37        Self {
38            vertex_buffer: None,
39            index_buffer: None,
40            vertex_buffer_host: None,
41            index_buffer_host: None,
42            vertex_buffer_size: 0,
43            index_buffer_size: 0,
44        }
45    }
46
47    /// Ensure vertex buffer can hold the required number of vertices
48    pub fn ensure_vertex_buffer_capacity(
49        &mut self,
50        device: &Device,
51        required_vertices: usize,
52    ) -> Result<(), String> {
53        if self.vertex_buffer.is_none() || self.vertex_buffer_size < required_vertices {
54            // Add some extra capacity to avoid frequent reallocations
55            let new_size = (required_vertices + 5000).max(self.vertex_buffer_size * 2);
56
57            // Create new GPU buffer with proper alignment
58            let buffer_size = align_size(new_size * std::mem::size_of::<DrawVert>(), 4);
59            let buffer = device.create_buffer(&BufferDescriptor {
60                label: Some("Dear ImGui Vertex Buffer"),
61                size: buffer_size as u64,
62                usage: BufferUsages::VERTEX | BufferUsages::COPY_DST,
63                mapped_at_creation: false,
64            });
65
66            // Create new host buffer
67            let host_buffer = vec![0u8; new_size * std::mem::size_of::<DrawVert>()];
68
69            self.vertex_buffer = Some(buffer);
70            self.vertex_buffer_host = Some(host_buffer);
71            self.vertex_buffer_size = new_size;
72        }
73
74        Ok(())
75    }
76
77    /// Ensure index buffer can hold the required number of indices
78    pub fn ensure_index_buffer_capacity(
79        &mut self,
80        device: &Device,
81        required_indices: usize,
82    ) -> Result<(), String> {
83        if self.index_buffer.is_none() || self.index_buffer_size < required_indices {
84            // Add some extra capacity to avoid frequent reallocations
85            let new_size = (required_indices + 10000).max(self.index_buffer_size * 2);
86
87            // Create new GPU buffer with proper alignment
88            let buffer_size = align_size(new_size * std::mem::size_of::<DrawIdx>(), 4);
89            let buffer = device.create_buffer(&BufferDescriptor {
90                label: Some("Dear ImGui Index Buffer"),
91                size: buffer_size as u64,
92                usage: BufferUsages::INDEX | BufferUsages::COPY_DST,
93                mapped_at_creation: false,
94            });
95
96            // Create new host buffer
97            let host_buffer = vec![0u8; new_size * std::mem::size_of::<DrawIdx>()];
98
99            self.index_buffer = Some(buffer);
100            self.index_buffer_host = Some(host_buffer);
101            self.index_buffer_size = new_size;
102        }
103
104        Ok(())
105    }
106
107    /// Upload vertex data to the GPU buffer
108    pub fn upload_vertex_data(
109        &mut self,
110        queue: &Queue,
111        vertices: &[DrawVert],
112    ) -> Result<(), String> {
113        let vertex_buffer = self
114            .vertex_buffer
115            .as_ref()
116            .ok_or("Vertex buffer not initialized")?;
117
118        // Convert vertices to bytes
119        let vertex_bytes = unsafe {
120            std::slice::from_raw_parts(
121                vertices.as_ptr() as *const u8,
122                std::mem::size_of_val(vertices),
123            )
124        };
125
126        // Copy to host buffer first
127        if let Some(ref mut host_buffer) = self.vertex_buffer_host
128            && vertex_bytes.len() <= host_buffer.len()
129        {
130            host_buffer[..vertex_bytes.len()].copy_from_slice(vertex_bytes);
131        }
132
133        // Upload to GPU with proper alignment
134        let aligned_size = align_size(vertex_bytes.len(), 4);
135        if aligned_size > vertex_bytes.len() {
136            // Need to pad the data to meet alignment requirements
137            let mut aligned_data = vertex_bytes.to_vec();
138            aligned_data.resize(aligned_size, 0);
139            queue.write_buffer(vertex_buffer, 0, &aligned_data);
140        } else {
141            queue.write_buffer(vertex_buffer, 0, vertex_bytes);
142        }
143        Ok(())
144    }
145
146    /// Upload index data to the GPU buffer
147    pub fn upload_index_data(&mut self, queue: &Queue, indices: &[DrawIdx]) -> Result<(), String> {
148        let index_buffer = self
149            .index_buffer
150            .as_ref()
151            .ok_or("Index buffer not initialized")?;
152
153        // Convert indices to bytes
154        let index_bytes = unsafe {
155            std::slice::from_raw_parts(
156                indices.as_ptr() as *const u8,
157                std::mem::size_of_val(indices),
158            )
159        };
160
161        // Copy to host buffer first
162        if let Some(ref mut host_buffer) = self.index_buffer_host
163            && index_bytes.len() <= host_buffer.len()
164        {
165            host_buffer[..index_bytes.len()].copy_from_slice(index_bytes);
166        }
167
168        // Upload to GPU with proper alignment
169        let aligned_size = align_size(index_bytes.len(), 4);
170        if aligned_size > index_bytes.len() {
171            // Need to pad the data to meet alignment requirements
172            let mut aligned_data = index_bytes.to_vec();
173            aligned_data.resize(aligned_size, 0);
174            queue.write_buffer(index_buffer, 0, &aligned_data);
175        } else {
176            queue.write_buffer(index_buffer, 0, index_bytes);
177        }
178        Ok(())
179    }
180
181    /// Get the vertex buffer for rendering
182    pub fn vertex_buffer(&self) -> Option<&Buffer> {
183        self.vertex_buffer.as_ref()
184    }
185
186    /// Get the index buffer for rendering
187    pub fn index_buffer(&self) -> Option<&Buffer> {
188        self.index_buffer.as_ref()
189    }
190
191    /// Check if buffers are ready for rendering
192    pub fn is_ready(&self) -> bool {
193        self.vertex_buffer.is_some() && self.index_buffer.is_some()
194    }
195
196    /// Get buffer statistics for debugging
197    pub fn stats(&self) -> FrameResourcesStats {
198        FrameResourcesStats {
199            vertex_buffer_size: self.vertex_buffer_size,
200            index_buffer_size: self.index_buffer_size,
201            vertex_buffer_bytes: self.vertex_buffer_size * std::mem::size_of::<DrawVert>(),
202            index_buffer_bytes: self.index_buffer_size * std::mem::size_of::<DrawIdx>(),
203        }
204    }
205}
206
207impl Default for FrameResources {
208    fn default() -> Self {
209        Self::new()
210    }
211}
212
213/// Statistics for frame resources
214#[derive(Debug, Clone)]
215pub struct FrameResourcesStats {
216    pub vertex_buffer_size: usize,
217    pub index_buffer_size: usize,
218    pub vertex_buffer_bytes: usize,
219    pub index_buffer_bytes: usize,
220}