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 returns a mutable reference and therefore requires `&mut self`.
55 /// In callbacks, you typically obtain `&mut WgpuRenderState` by casting the raw
56 /// `Renderer_RenderState` pointer provided by Dear ImGui.
57 pub unsafe fn render_pass_encoder(&mut self) -> &mut RenderPass<'_> {
58 unsafe { &mut *(self.render_pass_encoder as *mut RenderPass) }
59 }
60}
61
62/// Initialization data for ImGui WGPU renderer
63///
64/// This corresponds to ImGui_ImplWGPU_InitInfo in the C++ implementation
65#[derive(Debug, Clone)]
66pub struct WgpuInitInfo {
67 /// WGPU instance (required for multi-viewport to create per-window surfaces)
68 pub instance: Option<Instance>,
69 /// WGPU adapter (optional, but recommended for multi-viewport to query surface capabilities)
70 pub adapter: Option<Adapter>,
71 /// WGPU device
72 pub device: Device,
73 /// WGPU queue
74 pub queue: Queue,
75 /// Number of frames in flight (default: 3)
76 pub num_frames_in_flight: u32,
77 /// Render target format
78 pub render_target_format: TextureFormat,
79 /// Depth stencil format (None if no depth buffer)
80 pub depth_stencil_format: Option<TextureFormat>,
81 /// Pipeline multisample state
82 pub pipeline_multisample_state: MultisampleState,
83}
84
85impl WgpuInitInfo {
86 /// Create new initialization info with required parameters
87 pub fn new(device: Device, queue: Queue, render_target_format: TextureFormat) -> Self {
88 Self {
89 instance: None,
90 adapter: None,
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 /// Provide an instance for creating per-window surfaces (multi-viewport)
123 pub fn with_instance(mut self, instance: Instance) -> Self {
124 self.instance = Some(instance);
125 self
126 }
127
128 /// Provide an adapter for querying per-surface capabilities (recommended for multi-viewport)
129 pub fn with_adapter(mut self, adapter: Adapter) -> Self {
130 self.adapter = Some(adapter);
131 self
132 }
133}
134
135/// Main backend data structure
136///
137/// This corresponds to ImGui_ImplWGPU_Data in the C++ implementation
138pub struct WgpuBackendData {
139 /// Initialization info
140 pub init_info: WgpuInitInfo,
141 /// WGPU instance (if provided)
142 pub instance: Option<Instance>,
143 /// WGPU adapter (if provided)
144 pub adapter: Option<Adapter>,
145 /// WGPU device
146 pub device: Device,
147 /// Default queue
148 pub queue: Queue,
149 /// Render target format
150 pub render_target_format: TextureFormat,
151 /// Depth stencil format
152 pub depth_stencil_format: Option<TextureFormat>,
153 /// Render pipeline
154 pub pipeline_state: Option<RenderPipeline>,
155 /// Render resources (samplers, uniforms, bind groups)
156 pub render_resources: RenderResources,
157 /// Frame resources (per-frame buffers)
158 pub frame_resources: Vec<FrameResources>,
159 /// Number of frames in flight
160 pub num_frames_in_flight: u32,
161 /// Current frame index
162 pub frame_index: u32,
163}
164
165impl WgpuBackendData {
166 /// Create new backend data from initialization info
167 pub fn new(init_info: WgpuInitInfo) -> Self {
168 let queue = init_info.queue.clone();
169 let num_frames = init_info.num_frames_in_flight;
170
171 // Create frame resources for each frame in flight
172 let frame_resources = (0..num_frames).map(|_| FrameResources::new()).collect();
173
174 Self {
175 instance: init_info.instance.clone(),
176 adapter: init_info.adapter.clone(),
177 device: init_info.device.clone(),
178 queue,
179 render_target_format: init_info.render_target_format,
180 depth_stencil_format: init_info.depth_stencil_format,
181 pipeline_state: None,
182 render_resources: RenderResources::new(),
183 frame_resources,
184 num_frames_in_flight: num_frames,
185 frame_index: u32::MAX, // Will be set to 0 on first frame
186 init_info,
187 }
188 }
189
190 /// Get the current frame resources
191 pub fn current_frame_resources(&mut self) -> &mut FrameResources {
192 let index = (self.frame_index % self.num_frames_in_flight) as usize;
193 &mut self.frame_resources[index]
194 }
195
196 /// Get frame resources by index
197 pub fn frame_resources_at(&mut self, index: usize) -> Option<&mut FrameResources> {
198 self.frame_resources.get_mut(index)
199 }
200
201 /// Advance to the next frame
202 pub fn next_frame(&mut self) {
203 if self.frame_index == u32::MAX {
204 self.frame_index = 0;
205 } else {
206 self.frame_index = self.frame_index.wrapping_add(1);
207 }
208 }
209
210 /// Check if the backend is initialized
211 pub fn is_initialized(&self) -> bool {
212 self.pipeline_state.is_some()
213 }
214}