Skip to main content

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}