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 instance (required for multi-viewport to create per-window surfaces)
74    pub instance: Option<Instance>,
75    /// WGPU adapter (optional, but recommended for multi-viewport to query surface capabilities)
76    pub adapter: Option<Adapter>,
77    /// WGPU device
78    pub device: Device,
79    /// WGPU queue
80    pub queue: Queue,
81    /// Number of frames in flight (default: 3)
82    pub num_frames_in_flight: u32,
83    /// Render target format
84    pub render_target_format: TextureFormat,
85    /// Depth stencil format (None if no depth buffer)
86    pub depth_stencil_format: Option<TextureFormat>,
87    /// Pipeline multisample state
88    pub pipeline_multisample_state: MultisampleState,
89}
90
91impl WgpuInitInfo {
92    /// Create new initialization info with required parameters
93    pub fn new(device: Device, queue: Queue, render_target_format: TextureFormat) -> Self {
94        Self {
95            instance: None,
96            adapter: None,
97            device,
98            queue,
99            num_frames_in_flight: 3,
100            render_target_format,
101            depth_stencil_format: None,
102            pipeline_multisample_state: MultisampleState {
103                count: 1,
104                mask: !0,
105                alpha_to_coverage_enabled: false,
106            },
107        }
108    }
109
110    /// Set the number of frames in flight
111    pub fn with_frames_in_flight(mut self, count: u32) -> Self {
112        self.num_frames_in_flight = count;
113        self
114    }
115
116    /// Set the depth stencil format
117    pub fn with_depth_stencil_format(mut self, format: TextureFormat) -> Self {
118        self.depth_stencil_format = Some(format);
119        self
120    }
121
122    /// Set the multisample state
123    pub fn with_multisample_state(mut self, state: MultisampleState) -> Self {
124        self.pipeline_multisample_state = state;
125        self
126    }
127
128    /// Provide an instance for creating per-window surfaces (multi-viewport)
129    pub fn with_instance(mut self, instance: Instance) -> Self {
130        self.instance = Some(instance);
131        self
132    }
133
134    /// Provide an adapter for querying per-surface capabilities (recommended for multi-viewport)
135    pub fn with_adapter(mut self, adapter: Adapter) -> Self {
136        self.adapter = Some(adapter);
137        self
138    }
139}
140
141/// Main backend data structure
142///
143/// This corresponds to ImGui_ImplWGPU_Data in the C++ implementation
144pub struct WgpuBackendData {
145    /// Initialization info
146    pub init_info: WgpuInitInfo,
147    /// WGPU instance (if provided)
148    pub instance: Option<Instance>,
149    /// WGPU adapter (if provided)
150    pub adapter: Option<Adapter>,
151    /// WGPU device
152    pub device: Device,
153    /// Default queue
154    pub queue: Queue,
155    /// Render target format
156    pub render_target_format: TextureFormat,
157    /// Depth stencil format
158    pub depth_stencil_format: Option<TextureFormat>,
159    /// Render pipeline
160    pub pipeline_state: Option<RenderPipeline>,
161    /// Render resources (samplers, uniforms, bind groups)
162    pub render_resources: RenderResources,
163    /// Frame resources (per-frame buffers)
164    pub frame_resources: Vec<FrameResources>,
165    /// Number of frames in flight
166    pub num_frames_in_flight: u32,
167    /// Current frame index
168    pub frame_index: u32,
169}
170
171impl WgpuBackendData {
172    /// Create new backend data from initialization info
173    pub fn new(init_info: WgpuInitInfo) -> Self {
174        let queue = init_info.queue.clone();
175        let num_frames = init_info.num_frames_in_flight;
176
177        // Create frame resources for each frame in flight
178        let frame_resources = (0..num_frames).map(|_| FrameResources::new()).collect();
179
180        Self {
181            instance: init_info.instance.clone(),
182            adapter: init_info.adapter.clone(),
183            device: init_info.device.clone(),
184            queue,
185            render_target_format: init_info.render_target_format,
186            depth_stencil_format: init_info.depth_stencil_format,
187            pipeline_state: None,
188            render_resources: RenderResources::new(),
189            frame_resources,
190            num_frames_in_flight: num_frames,
191            frame_index: u32::MAX, // Will be set to 0 on first frame
192            init_info,
193        }
194    }
195
196    /// Get the current frame resources
197    pub fn current_frame_resources(&mut self) -> &mut FrameResources {
198        let index = (self.frame_index % self.num_frames_in_flight) as usize;
199        &mut self.frame_resources[index]
200    }
201
202    /// Get frame resources by index
203    pub fn frame_resources_at(&mut self, index: usize) -> Option<&mut FrameResources> {
204        self.frame_resources.get_mut(index)
205    }
206
207    /// Advance to the next frame
208    pub fn next_frame(&mut self) {
209        if self.frame_index == u32::MAX {
210            self.frame_index = 0;
211        } else {
212            self.frame_index = self.frame_index.wrapping_add(1);
213        }
214    }
215
216    /// Check if the backend is initialized
217    pub fn is_initialized(&self) -> bool {
218        self.pipeline_state.is_some()
219    }
220}