dear_imgui_wgpu/data.rs
1//! Core data structures for the WGPU renderer
2//!
3//! This module contains the main backend data structure and initialization info,
4//! following the pattern from imgui_impl_wgpu.cpp
5
6use crate::{FrameResources, RenderResources};
7use wgpu::*;
8
9/// Selected render state data shared with callbacks
10///
11/// This corresponds to ImGui_ImplWGPU_RenderState in the C++ implementation.
12/// This is temporarily stored during the render_draw_data() call to allow
13/// draw callbacks to access the current render state.
14#[derive(Debug)]
15pub struct WgpuRenderState {
16 /// WGPU device for creating resources (raw pointer for lifetime flexibility)
17 pub device: *const Device,
18 /// Current render pass encoder for drawing (raw pointer for lifetime flexibility)
19 pub render_pass_encoder: *mut std::ffi::c_void,
20}
21
22impl WgpuRenderState {
23 /// Create a new render state from references
24 ///
25 /// # Safety
26 ///
27 /// The caller must ensure that the device and render pass remain valid
28 /// for the lifetime of this render state.
29 pub unsafe fn new(device: &Device, render_pass: &mut RenderPass) -> Self {
30 Self {
31 device: device as *const Device,
32 render_pass_encoder: render_pass as *mut _ as *mut std::ffi::c_void,
33 }
34 }
35
36 /// Get the device reference
37 ///
38 /// # Safety
39 ///
40 /// The caller must ensure that the device pointer is still valid.
41 pub unsafe fn device(&self) -> &Device {
42 unsafe { &*self.device }
43 }
44
45 /// Get the render pass encoder reference
46 ///
47 /// # Safety
48 ///
49 /// The caller must ensure that:
50 /// 1. The render pass pointer is still valid
51 /// 2. No other mutable references to the render pass exist
52 /// 3. The lifetime is appropriate
53 ///
54 /// This method is designed for use in C++ callbacks where we need to provide
55 /// mutable access to the render pass from an immutable context.
56 ///
57 /// # Clippy Allow
58 ///
59 /// We allow `clippy::mut_from_ref` here because this is a legitimate use case
60 /// for FFI interop where we need to provide mutable access through an immutable
61 /// interface to match C++ callback expectations.
62 #[allow(clippy::mut_from_ref)]
63 pub unsafe fn render_pass_encoder(&self) -> &mut RenderPass<'_> {
64 unsafe { &mut *(self.render_pass_encoder as *mut RenderPass) }
65 }
66}
67
68/// Initialization data for ImGui WGPU renderer
69///
70/// This corresponds to ImGui_ImplWGPU_InitInfo in the C++ implementation
71#[derive(Debug, Clone)]
72pub struct WgpuInitInfo {
73 /// WGPU device
74 pub device: Device,
75 /// WGPU queue
76 pub queue: Queue,
77 /// Number of frames in flight (default: 3)
78 pub num_frames_in_flight: u32,
79 /// Render target format
80 pub render_target_format: TextureFormat,
81 /// Depth stencil format (None if no depth buffer)
82 pub depth_stencil_format: Option<TextureFormat>,
83 /// Pipeline multisample state
84 pub pipeline_multisample_state: MultisampleState,
85}
86
87impl WgpuInitInfo {
88 /// Create new initialization info with required parameters
89 pub fn new(device: Device, queue: Queue, render_target_format: TextureFormat) -> Self {
90 Self {
91 device,
92 queue,
93 num_frames_in_flight: 3,
94 render_target_format,
95 depth_stencil_format: None,
96 pipeline_multisample_state: MultisampleState {
97 count: 1,
98 mask: !0,
99 alpha_to_coverage_enabled: false,
100 },
101 }
102 }
103
104 /// Set the number of frames in flight
105 pub fn with_frames_in_flight(mut self, count: u32) -> Self {
106 self.num_frames_in_flight = count;
107 self
108 }
109
110 /// Set the depth stencil format
111 pub fn with_depth_stencil_format(mut self, format: TextureFormat) -> Self {
112 self.depth_stencil_format = Some(format);
113 self
114 }
115
116 /// Set the multisample state
117 pub fn with_multisample_state(mut self, state: MultisampleState) -> Self {
118 self.pipeline_multisample_state = state;
119 self
120 }
121}
122
123/// Main backend data structure
124///
125/// This corresponds to ImGui_ImplWGPU_Data in the C++ implementation
126pub struct WgpuBackendData {
127 /// Initialization info
128 pub init_info: WgpuInitInfo,
129 /// WGPU device
130 pub device: Device,
131 /// Default queue
132 pub queue: Queue,
133 /// Render target format
134 pub render_target_format: TextureFormat,
135 /// Depth stencil format
136 pub depth_stencil_format: Option<TextureFormat>,
137 /// Render pipeline
138 pub pipeline_state: Option<RenderPipeline>,
139 /// Render resources (samplers, uniforms, bind groups)
140 pub render_resources: RenderResources,
141 /// Frame resources (per-frame buffers)
142 pub frame_resources: Vec<FrameResources>,
143 /// Number of frames in flight
144 pub num_frames_in_flight: u32,
145 /// Current frame index
146 pub frame_index: u32,
147}
148
149impl WgpuBackendData {
150 /// Create new backend data from initialization info
151 pub fn new(init_info: WgpuInitInfo) -> Self {
152 let queue = init_info.queue.clone();
153 let num_frames = init_info.num_frames_in_flight;
154
155 // Create frame resources for each frame in flight
156 let frame_resources = (0..num_frames).map(|_| FrameResources::new()).collect();
157
158 Self {
159 device: init_info.device.clone(),
160 queue,
161 render_target_format: init_info.render_target_format,
162 depth_stencil_format: init_info.depth_stencil_format,
163 pipeline_state: None,
164 render_resources: RenderResources::new(),
165 frame_resources,
166 num_frames_in_flight: num_frames,
167 frame_index: u32::MAX, // Will be set to 0 on first frame
168 init_info,
169 }
170 }
171
172 /// Get the current frame resources
173 pub fn current_frame_resources(&mut self) -> &mut FrameResources {
174 let index = (self.frame_index % self.num_frames_in_flight) as usize;
175 &mut self.frame_resources[index]
176 }
177
178 /// Get frame resources by index
179 pub fn frame_resources_at(&mut self, index: usize) -> Option<&mut FrameResources> {
180 self.frame_resources.get_mut(index)
181 }
182
183 /// Advance to the next frame
184 pub fn next_frame(&mut self) {
185 if self.frame_index == u32::MAX {
186 self.frame_index = 0;
187 } else {
188 self.frame_index = self.frame_index.wrapping_add(1);
189 }
190 }
191
192 /// Check if the backend is initialized
193 pub fn is_initialized(&self) -> bool {
194 self.pipeline_state.is_some()
195 }
196}