dear_imgui_wgpu/renderer/mod.rs
1//! Main WGPU renderer implementation
2//!
3//! This module contains the main WgpuRenderer struct and its implementation,
4//! following the pattern from imgui_impl_wgpu.cpp
5//!
6//! Texture Updates Flow (ImGui 1.92+)
7//! - During `Context::render()`, Dear ImGui emits a list of textures to be processed in
8//! `DrawData::textures()` (see `dear_imgui_rs::render::DrawData::textures`). Each item is an
9//! `ImTextureData*` with a `Status` field:
10//! - `WantCreate`: create a GPU texture, upload all pixels, set `TexID`, then set status `OK`.
11//! - `WantUpdates`: upload `UpdateRect` (and any queued rects) then set `OK`.
12//! - `WantDestroy`: schedule/destroy GPU texture; if unused for some frames, set `Destroyed`.
13//! - This backend honors these transitions in its texture module; users can simply pass
14//! `&mut TextureData` to UI/draw calls and let the backend handle the rest.
15
16use crate::GammaMode;
17use crate::{
18 FrameResources, RenderResources, RendererError, RendererResult, ShaderManager, Uniforms,
19 WgpuBackendData, WgpuInitInfo, WgpuTextureManager,
20};
21use dear_imgui_rs::{BackendFlags, Context, render::DrawData};
22use wgpu::*;
23
24// Debug logging helper (off by default). Enable by building this crate with
25// `--features mv-log` to see multi-viewport renderer traces.
26#[allow(unused_macros)]
27macro_rules! mvlog {
28 ($($arg:tt)*) => {
29 if cfg!(feature = "mv-log") { eprintln!($($arg)*); }
30 }
31}
32/// Main WGPU renderer for Dear ImGui
33
34///
35/// This corresponds to the main renderer functionality in imgui_impl_wgpu.cpp
36pub struct WgpuRenderer {
37 /// Backend data
38 backend_data: Option<WgpuBackendData>,
39 /// Shader manager
40 shader_manager: ShaderManager,
41 /// Texture manager
42 texture_manager: WgpuTextureManager,
43 /// Default texture for fallback
44 default_texture: Option<TextureView>,
45 /// Registered font atlas texture id (if created via font-atlas fallback)
46 font_texture_id: Option<u64>,
47 /// Gamma mode: automatic (by format), force linear (1.0), or force 2.2
48 gamma_mode: GammaMode,
49}
50
51impl WgpuRenderer {
52 /// Create a new WGPU renderer with full initialization (recommended)
53 ///
54 /// This is the preferred way to create a WGPU renderer as it ensures proper
55 /// initialization order and is consistent with other backends.
56 ///
57 /// # Arguments
58 /// * `init_info` - WGPU initialization information (device, queue, format)
59 /// * `imgui_ctx` - Dear ImGui context to configure
60 ///
61 /// # Example
62 /// ```rust,no_run
63 /// use dear_imgui_wgpu::{WgpuRenderer, WgpuInitInfo};
64 ///
65 /// let init_info = WgpuInitInfo::new(device, queue, surface_format);
66 /// let mut renderer = WgpuRenderer::new(init_info, &mut imgui_context)?;
67 /// ```
68 pub fn new(init_info: WgpuInitInfo, imgui_ctx: &mut Context) -> RendererResult<Self> {
69 let mut renderer = Self::empty();
70 renderer.init_with_context(init_info, imgui_ctx)?;
71 Ok(renderer)
72 }
73
74 /// Create an empty WGPU renderer for advanced usage
75 ///
76 /// This creates an uninitialized renderer that must be initialized later
77 /// using `init_with_context()`. Most users should use `new()` instead.
78 ///
79 /// # Example
80 /// ```rust,no_run
81 /// use dear_imgui_wgpu::{WgpuRenderer, WgpuInitInfo};
82 ///
83 /// let mut renderer = WgpuRenderer::empty();
84 /// let init_info = WgpuInitInfo::new(device, queue, surface_format);
85 /// renderer.init_with_context(init_info, &mut imgui_context)?;
86 /// ```
87 pub fn empty() -> Self {
88 Self {
89 backend_data: None,
90 shader_manager: ShaderManager::new(),
91 texture_manager: WgpuTextureManager::new(),
92 default_texture: None,
93 font_texture_id: None,
94 gamma_mode: GammaMode::Auto,
95 }
96 }
97
98 /// Initialize the renderer
99 ///
100 /// This corresponds to ImGui_ImplWGPU_Init in the C++ implementation
101 pub fn init(&mut self, init_info: WgpuInitInfo) -> RendererResult<()> {
102 // Create backend data
103 let mut backend_data = WgpuBackendData::new(init_info);
104
105 // Initialize render resources
106 backend_data
107 .render_resources
108 .initialize(&backend_data.device)?;
109
110 // Initialize shaders
111 self.shader_manager.initialize(&backend_data.device)?;
112
113 // Create default texture (1x1 white pixel)
114 let default_texture =
115 self.create_default_texture(&backend_data.device, &backend_data.queue)?;
116 self.default_texture = Some(default_texture);
117
118 // Create device objects (pipeline, etc.)
119 self.create_device_objects(&mut backend_data)?;
120
121 self.backend_data = Some(backend_data);
122 Ok(())
123 }
124
125 /// Initialize the renderer with ImGui context configuration (without font atlas for WASM)
126 ///
127 /// This is a variant of init_with_context that skips font atlas preparation,
128 /// useful for WASM builds where font atlas memory sharing is problematic.
129 pub fn new_without_font_atlas(
130 init_info: WgpuInitInfo,
131 imgui_ctx: &mut Context,
132 ) -> RendererResult<Self> {
133 let mut renderer = Self::empty();
134
135 // First initialize the renderer
136 renderer.init(init_info)?;
137
138 // Then configure the ImGui context with backend capabilities
139 renderer.configure_imgui_context(imgui_ctx);
140
141 // Skip font atlas preparation for WASM
142 // The default font will be used automatically by Dear ImGui
143
144 Ok(renderer)
145 }
146
147 /// Initialize the renderer with ImGui context configuration
148 ///
149 /// This is a convenience method that combines init() and configure_imgui_context()
150 /// to ensure proper initialization order, similar to the glow backend approach.
151 pub fn init_with_context(
152 &mut self,
153 init_info: WgpuInitInfo,
154 imgui_ctx: &mut Context,
155 ) -> RendererResult<()> {
156 // First initialize the renderer
157 self.init(init_info)?;
158
159 // Then configure the ImGui context with backend capabilities
160 // This must be done BEFORE preparing the font atlas
161 self.configure_imgui_context(imgui_ctx);
162
163 // Finally prepare the font atlas
164 self.prepare_font_atlas(imgui_ctx)?;
165
166 Ok(())
167 }
168
169 /// Set gamma mode
170 pub fn set_gamma_mode(&mut self, mode: GammaMode) {
171 self.gamma_mode = mode;
172 }
173
174 /// Configure Dear ImGui context with WGPU backend capabilities
175 pub fn configure_imgui_context(&self, imgui_context: &mut Context) {
176 let io = imgui_context.io_mut();
177 let mut flags = io.backend_flags();
178
179 // Set WGPU renderer capabilities
180 // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes.
181 flags.insert(BackendFlags::RENDERER_HAS_VTX_OFFSET);
182 // We can honor ImGuiPlatformIO::Textures[] requests during render.
183 flags.insert(BackendFlags::RENDERER_HAS_TEXTURES);
184
185 #[cfg(feature = "multi-viewport")]
186 {
187 // We can render additional platform windows
188 flags.insert(BackendFlags::RENDERER_HAS_VIEWPORTS);
189 }
190
191 io.set_backend_flags(flags);
192 // Note: Dear ImGui doesn't expose set_backend_renderer_name in the Rust bindings yet
193 }
194
195 /// Prepare font atlas for rendering
196 pub fn prepare_font_atlas(&mut self, imgui_ctx: &mut Context) -> RendererResult<()> {
197 if let Some(backend_data) = &self.backend_data {
198 let device = backend_data.device.clone();
199 let queue = backend_data.queue.clone();
200 self.reload_font_texture(imgui_ctx, &device, &queue)?;
201 // Fallback: if draw_data-based texture updates are not triggered for the font atlas
202 // on this Dear ImGui version/config, upload the font atlas now and assign a TexID.
203 if self.font_texture_id.is_none() {
204 if let Some(tex_id) =
205 self.try_upload_font_atlas_legacy(imgui_ctx, &device, &queue)?
206 {
207 if cfg!(debug_assertions) {
208 tracing::debug!(
209 target: "dear-imgui-wgpu",
210 "[dear-imgui-wgpu][debug] Font atlas uploaded via fallback (legacy-only) path; user textures use modern ImTextureData. tex_id={}",
211 tex_id
212 );
213 }
214 self.font_texture_id = Some(tex_id);
215 }
216 } else if cfg!(debug_assertions) {
217 tracing::debug!(
218 target: "dear-imgui-wgpu",
219 "[dear-imgui-wgpu][debug] Font atlas tex_id already set: {:?}",
220 self.font_texture_id
221 );
222 }
223 }
224 Ok(())
225 }
226
227 // create_device_objects moved to renderer/pipeline.rs
228
229 /// Create a default 1x1 white texture
230 fn create_default_texture(
231 &self,
232 device: &Device,
233 queue: &Queue,
234 ) -> RendererResult<TextureView> {
235 let texture = device.create_texture(&TextureDescriptor {
236 label: Some("Dear ImGui Default Texture"),
237 size: Extent3d {
238 width: 1,
239 height: 1,
240 depth_or_array_layers: 1,
241 },
242 mip_level_count: 1,
243 sample_count: 1,
244 dimension: TextureDimension::D2,
245 format: TextureFormat::Rgba8Unorm,
246 usage: TextureUsages::TEXTURE_BINDING | TextureUsages::COPY_DST,
247 view_formats: &[],
248 });
249
250 // Upload white pixel
251 queue.write_texture(
252 wgpu::TexelCopyTextureInfo {
253 texture: &texture,
254 mip_level: 0,
255 origin: wgpu::Origin3d::ZERO,
256 aspect: wgpu::TextureAspect::All,
257 },
258 &[255u8, 255u8, 255u8, 255u8], // RGBA white
259 wgpu::TexelCopyBufferLayout {
260 offset: 0,
261 bytes_per_row: Some(4),
262 rows_per_image: Some(1),
263 },
264 Extent3d {
265 width: 1,
266 height: 1,
267 depth_or_array_layers: 1,
268 },
269 );
270
271 Ok(texture.create_view(&TextureViewDescriptor::default()))
272 }
273
274 /// Load font texture from Dear ImGui context
275 ///
276 /// With the new texture management system in Dear ImGui 1.92+, font textures are
277 /// automatically managed through ImDrawData->Textures[] during rendering.
278 /// However, we need to ensure the font atlas is built and ready before the first render.
279 // reload_font_texture moved to renderer/font_atlas.rs
280
281 /// Legacy/fallback path: upload font atlas texture immediately and assign TexID.
282 /// Returns Some(tex_id) on success, None if texdata is unavailable.
283 // try_upload_font_atlas_legacy moved to renderer/font_atlas.rs
284
285 /// Get the texture manager
286 pub fn texture_manager(&self) -> &WgpuTextureManager {
287 &self.texture_manager
288 }
289
290 /// Get the texture manager mutably
291 pub fn texture_manager_mut(&mut self) -> &mut WgpuTextureManager {
292 &mut self.texture_manager
293 }
294
295 /// Check if the renderer is initialized
296 pub fn is_initialized(&self) -> bool {
297 self.backend_data.is_some()
298 }
299
300 /// Update a single texture manually
301 ///
302 /// This corresponds to ImGui_ImplWGPU_UpdateTexture in the C++ implementation.
303 /// Use this when you need precise control over texture update timing.
304 ///
305 /// # Returns
306 ///
307 /// Returns a `TextureUpdateResult` that contains any status/ID updates that need
308 /// to be applied to the texture data. This follows Rust's principle of explicit
309 /// state management.
310 ///
311 /// # Example
312 ///
313 /// ```rust,no_run
314 /// # use dear_imgui_wgpu::*;
315 /// # fn example() -> Result<(), Box<dyn std::error::Error>> {
316 /// # let mut renderer = WgpuRenderer::new();
317 /// # let mut texture_data = dear_imgui_rs::TextureData::new();
318 /// let result = renderer.update_texture(&texture_data)?;
319 /// result.apply_to(&mut texture_data);
320 /// # Ok(())
321 /// # }
322 /// ```
323 pub fn update_texture(
324 &mut self,
325 texture_data: &dear_imgui_rs::TextureData,
326 ) -> RendererResult<crate::TextureUpdateResult> {
327 if let Some(backend_data) = &self.backend_data {
328 self.texture_manager
329 .update_single_texture(texture_data, &backend_data.device, &backend_data.queue)
330 .map_err(RendererError::TextureCreationFailed)
331 } else {
332 Err(RendererError::InvalidRenderState(
333 "Renderer not initialized".to_string(),
334 ))
335 }
336 }
337
338 /// Called every frame to prepare for rendering
339 ///
340 /// This corresponds to ImGui_ImplWGPU_NewFrame in the C++ implementation
341 pub fn new_frame(&mut self) -> RendererResult<()> {
342 let needs_recreation = if let Some(backend_data) = &self.backend_data {
343 backend_data.pipeline_state.is_none()
344 } else {
345 false
346 };
347
348 if needs_recreation {
349 // Extract the backend data temporarily to avoid borrow checker issues
350 let mut backend_data = self.backend_data.take().unwrap();
351 self.create_device_objects(&mut backend_data)?;
352 self.backend_data = Some(backend_data);
353 }
354 Ok(())
355 }
356
357 /// Render Dear ImGui draw data
358 ///
359 /// This corresponds to ImGui_ImplWGPU_RenderDrawData in the C++ implementation
360 pub fn render_draw_data(
361 &mut self,
362 draw_data: &DrawData,
363 render_pass: &mut RenderPass,
364 ) -> RendererResult<()> {
365 mvlog!(
366 "[wgpu-mv] render_draw_data: valid={} lists={} fb_scale=({:.2},{:.2}) disp=({:.1},{:.1})",
367 draw_data.valid(),
368 draw_data.draw_lists_count(),
369 draw_data.framebuffer_scale()[0],
370 draw_data.framebuffer_scale()[1],
371 draw_data.display_size()[0],
372 draw_data.display_size()[1]
373 );
374 // Early out if nothing to draw (avoid binding/drawing without buffers)
375 let mut total_vtx_count = 0usize;
376 let mut total_idx_count = 0usize;
377 for dl in draw_data.draw_lists() {
378 total_vtx_count += dl.vtx_buffer().len();
379 total_idx_count += dl.idx_buffer().len();
380 }
381 if total_vtx_count == 0 || total_idx_count == 0 {
382 mvlog!("[wgpu-mv] no vertices/indices; skipping render");
383 return Ok(());
384 }
385
386 let backend_data = self.backend_data.as_mut().ok_or_else(|| {
387 RendererError::InvalidRenderState("Renderer not initialized".to_string())
388 })?;
389
390 // Avoid rendering when minimized
391 let fb_width = (draw_data.display_size[0] * draw_data.framebuffer_scale[0]) as i32;
392 let fb_height = (draw_data.display_size[1] * draw_data.framebuffer_scale[1]) as i32;
393 if fb_width <= 0 || fb_height <= 0 || !draw_data.valid() {
394 return Ok(());
395 }
396
397 mvlog!("[wgpu-mv] handle_texture_updates");
398 self.texture_manager.handle_texture_updates(
399 draw_data,
400 &backend_data.device,
401 &backend_data.queue,
402 );
403
404 // Advance to next frame
405 mvlog!("[wgpu-mv] next_frame before: {}", backend_data.frame_index);
406 backend_data.next_frame();
407 mvlog!("[wgpu-mv] next_frame after: {}", backend_data.frame_index);
408
409 // Prepare frame resources
410 mvlog!("[wgpu-mv] prepare_frame_resources");
411 Self::prepare_frame_resources_static(draw_data, backend_data)?;
412
413 // Compute gamma based on renderer mode
414 let gamma = match self.gamma_mode {
415 GammaMode::Auto => Uniforms::gamma_for_format(backend_data.render_target_format),
416 GammaMode::Linear => 1.0,
417 GammaMode::Gamma22 => 2.2,
418 };
419
420 // Setup render state
421 mvlog!("[wgpu-mv] setup_render_state");
422 Self::setup_render_state_static(draw_data, render_pass, backend_data, gamma)?;
423 // Override viewport to the provided framebuffer size to avoid partial viewport issues
424 render_pass.set_viewport(0.0, 0.0, fb_width as f32, fb_height as f32, 0.0, 1.0);
425
426 // Setup render state structure (for callbacks and custom texture bindings)
427 // Note: We need to be careful with lifetimes here, so we'll set it just before rendering
428 // and clear it immediately after
429 unsafe {
430 // Use _Nil variant as our bindings export it
431 let platform_io = dear_imgui_rs::sys::igGetPlatformIO_Nil();
432
433 // Create a temporary render state structure
434 mvlog!("[wgpu-mv] create render_state");
435 let mut render_state = crate::WgpuRenderState::new(&backend_data.device, render_pass);
436
437 // Set the render state pointer
438 (*platform_io).Renderer_RenderState =
439 &mut render_state as *mut _ as *mut std::ffi::c_void;
440
441 // Render draw lists with the render state exposed
442 let result = Self::render_draw_lists_static(
443 &mut self.texture_manager,
444 &self.default_texture,
445 draw_data,
446 render_pass,
447 backend_data,
448 gamma,
449 );
450
451 // Clear the render state pointer
452 (*platform_io).Renderer_RenderState = std::ptr::null_mut();
453
454 if let Err(e) = result {
455 eprintln!("[wgpu-mv] render_draw_lists_static error: {:?}", e);
456 return Err(e);
457 }
458 }
459
460 Ok(())
461 }
462
463 /// Render Dear ImGui draw data with explicit framebuffer size override
464 ///
465 /// This clones the logic of `render_draw_data` but clamps scissor to the provided
466 /// `fb_width`/`fb_height` instead of deriving it from `DrawData`.
467 pub fn render_draw_data_with_fb_size(
468 &mut self,
469 draw_data: &DrawData,
470 render_pass: &mut RenderPass,
471 fb_width: u32,
472 fb_height: u32,
473 ) -> RendererResult<()> {
474 mvlog!(
475 "[wgpu-mv] render_draw_data(with_fb) lists={} override_fb=({}, {}) disp=({:.1},{:.1})",
476 draw_data.draw_lists_count(),
477 fb_width,
478 fb_height,
479 draw_data.display_size()[0],
480 draw_data.display_size()[1]
481 );
482 let total_vtx_count: usize = draw_data.draw_lists().map(|dl| dl.vtx_buffer().len()).sum();
483 let total_idx_count: usize = draw_data.draw_lists().map(|dl| dl.idx_buffer().len()).sum();
484 if total_vtx_count == 0 || total_idx_count == 0 {
485 return Ok(());
486 }
487 let backend_data = self.backend_data.as_mut().ok_or_else(|| {
488 RendererError::InvalidRenderState("Renderer not initialized".to_string())
489 })?;
490
491 // Skip if invalid/minimized
492 if fb_width == 0 || fb_height == 0 || !draw_data.valid() {
493 return Ok(());
494 }
495
496 self.texture_manager.handle_texture_updates(
497 draw_data,
498 &backend_data.device,
499 &backend_data.queue,
500 );
501
502 backend_data.next_frame();
503 Self::prepare_frame_resources_static(draw_data, backend_data)?;
504
505 let gamma = match self.gamma_mode {
506 GammaMode::Auto => Uniforms::gamma_for_format(backend_data.render_target_format),
507 GammaMode::Linear => 1.0,
508 GammaMode::Gamma22 => 2.2,
509 };
510
511 Self::setup_render_state_static(draw_data, render_pass, backend_data, gamma)?;
512
513 unsafe {
514 let platform_io = dear_imgui_rs::sys::igGetPlatformIO_Nil();
515 let mut render_state = crate::WgpuRenderState::new(&backend_data.device, render_pass);
516 (*platform_io).Renderer_RenderState =
517 &mut render_state as *mut _ as *mut std::ffi::c_void;
518
519 // Reuse core routine but clamp scissor by overriding framebuffer bounds.
520 let mut global_idx_offset: u32 = 0;
521 let mut global_vtx_offset: i32 = 0;
522 let clip_off = draw_data.display_pos();
523 let clip_scale = draw_data.framebuffer_scale();
524 let fbw = fb_width as f32;
525 let fbh = fb_height as f32;
526
527 for draw_list in draw_data.draw_lists() {
528 let vtx_buffer = draw_list.vtx_buffer();
529 let idx_buffer = draw_list.idx_buffer();
530 let mut cmd_i = 0;
531 for cmd in draw_list.commands() {
532 match cmd {
533 dear_imgui_rs::render::DrawCmd::Elements {
534 count,
535 cmd_params,
536 raw_cmd,
537 } => {
538 // Texture bind group resolution mirrors render_draw_lists_static
539 let texture_bind_group = {
540 // Resolve effective ImTextureID using raw_cmd (modern texture path)
541 let tex_id = unsafe {
542 dear_imgui_rs::sys::ImDrawCmd_GetTexID(
543 raw_cmd as *mut dear_imgui_rs::sys::ImDrawCmd,
544 )
545 } as u64;
546 if tex_id == 0 {
547 if let Some(default_tex) = &self.default_texture {
548 backend_data
549 .render_resources
550 .get_or_create_image_bind_group(
551 &backend_data.device,
552 0,
553 default_tex,
554 )?
555 .clone()
556 } else {
557 return Err(RendererError::InvalidRenderState(
558 "Default texture not available".to_string(),
559 ));
560 }
561 } else if let Some(wgpu_texture) =
562 self.texture_manager.get_texture(tex_id)
563 {
564 backend_data
565 .render_resources
566 .get_or_create_image_bind_group(
567 &backend_data.device,
568 tex_id,
569 &wgpu_texture.texture_view,
570 )?
571 .clone()
572 } else if let Some(default_tex) = &self.default_texture {
573 backend_data
574 .render_resources
575 .get_or_create_image_bind_group(
576 &backend_data.device,
577 0,
578 default_tex,
579 )?
580 .clone()
581 } else {
582 return Err(RendererError::InvalidRenderState(
583 "Texture not found and no default texture".to_string(),
584 ));
585 }
586 };
587 render_pass.set_bind_group(1, &texture_bind_group, &[]);
588
589 // Compute clip rect in framebuffer space
590 let mut clip_min_x =
591 (cmd_params.clip_rect[0] - clip_off[0]) * clip_scale[0];
592 let mut clip_min_y =
593 (cmd_params.clip_rect[1] - clip_off[1]) * clip_scale[1];
594 let mut clip_max_x =
595 (cmd_params.clip_rect[2] - clip_off[0]) * clip_scale[0];
596 let mut clip_max_y =
597 (cmd_params.clip_rect[3] - clip_off[1]) * clip_scale[1];
598 // Clamp to override framebuffer bounds
599 clip_min_x = clip_min_x.max(0.0);
600 clip_min_y = clip_min_y.max(0.0);
601 clip_max_x = clip_max_x.min(fbw);
602 clip_max_y = clip_max_y.min(fbh);
603 if clip_max_x <= clip_min_x || clip_max_y <= clip_min_y {
604 cmd_i += 1;
605 continue;
606 }
607 render_pass.set_scissor_rect(
608 clip_min_x as u32,
609 clip_min_y as u32,
610 (clip_max_x - clip_min_x) as u32,
611 (clip_max_y - clip_min_y) as u32,
612 );
613 let start_index = cmd_params.idx_offset as u32 + global_idx_offset;
614 let end_index = start_index + count as u32;
615 let vertex_offset = (cmd_params.vtx_offset as i32) + global_vtx_offset;
616 render_pass.draw_indexed(start_index..end_index, vertex_offset, 0..1);
617 }
618 dear_imgui_rs::render::DrawCmd::ResetRenderState => {
619 Self::setup_render_state_static(
620 draw_data,
621 render_pass,
622 backend_data,
623 gamma,
624 )?;
625 }
626 dear_imgui_rs::render::DrawCmd::RawCallback { .. } => {
627 // Unsupported raw callbacks; skip.
628 }
629 }
630 cmd_i += 1;
631 }
632
633 global_idx_offset += idx_buffer.len() as u32;
634 global_vtx_offset += vtx_buffer.len() as i32;
635 }
636
637 (*platform_io).Renderer_RenderState = std::ptr::null_mut();
638 }
639
640 Ok(())
641 }
642
643 /// Prepare frame resources (buffers)
644 // prepare_frame_resources_static moved to renderer/draw.rs
645
646 /// Setup render state
647 ///
648 /// This corresponds to ImGui_ImplWGPU_SetupRenderState in the C++ implementation
649 // setup_render_state_static moved to renderer/draw.rs
650
651 /// Render all draw lists
652 // render_draw_lists_static moved to renderer/draw.rs
653
654 /// Invalidate device objects
655 ///
656 /// This corresponds to ImGui_ImplWGPU_InvalidateDeviceObjects in the C++ implementation
657 pub fn invalidate_device_objects(&mut self) -> RendererResult<()> {
658 if let Some(ref mut backend_data) = self.backend_data {
659 backend_data.pipeline_state = None;
660 backend_data.render_resources = RenderResources::new();
661
662 // Clear frame resources
663 for frame_resources in &mut backend_data.frame_resources {
664 *frame_resources = FrameResources::new();
665 }
666 }
667
668 // Clear texture manager
669 self.texture_manager.clear();
670 self.default_texture = None;
671 self.font_texture_id = None;
672
673 Ok(())
674 }
675
676 /// Shutdown the renderer
677 ///
678 /// This corresponds to ImGui_ImplWGPU_Shutdown in the C++ implementation
679 pub fn shutdown(&mut self) {
680 self.invalidate_device_objects().ok();
681 self.backend_data = None;
682 }
683}
684
685// Submodules for renderer features
686mod draw;
687mod external_textures;
688mod font_atlas;
689#[cfg(feature = "multi-viewport")]
690pub mod multi_viewport;
691mod pipeline;
692
693impl Default for WgpuRenderer {
694 fn default() -> Self {
695 Self::empty()
696 }
697}