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}