1use 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
24pub struct WgpuRenderer {
28 backend_data: Option<WgpuBackendData>,
30 shader_manager: ShaderManager,
32 texture_manager: WgpuTextureManager,
34 default_texture: Option<TextureView>,
36 font_texture_id: Option<u64>,
38 gamma_mode: GammaMode,
40}
41
42impl WgpuRenderer {
43 pub fn new(init_info: WgpuInitInfo, imgui_ctx: &mut Context) -> RendererResult<Self> {
60 let mut renderer = Self::empty();
61 renderer.init_with_context(init_info, imgui_ctx)?;
62 Ok(renderer)
63 }
64
65 pub fn empty() -> Self {
79 Self {
80 backend_data: None,
81 shader_manager: ShaderManager::new(),
82 texture_manager: WgpuTextureManager::new(),
83 default_texture: None,
84 font_texture_id: None,
85 gamma_mode: GammaMode::Auto,
86 }
87 }
88
89 pub fn init(&mut self, init_info: WgpuInitInfo) -> RendererResult<()> {
93 let mut backend_data = WgpuBackendData::new(init_info);
95
96 backend_data
98 .render_resources
99 .initialize(&backend_data.device)?;
100
101 self.shader_manager.initialize(&backend_data.device)?;
103
104 let default_texture =
106 self.create_default_texture(&backend_data.device, &backend_data.queue)?;
107 self.default_texture = Some(default_texture);
108
109 self.create_device_objects(&mut backend_data)?;
111
112 self.backend_data = Some(backend_data);
113 Ok(())
114 }
115
116 pub fn new_without_font_atlas(
121 init_info: WgpuInitInfo,
122 imgui_ctx: &mut Context,
123 ) -> RendererResult<Self> {
124 let mut renderer = Self::empty();
125
126 renderer.init(init_info)?;
128
129 renderer.configure_imgui_context(imgui_ctx);
131
132 Ok(renderer)
136 }
137
138 pub fn init_with_context(
143 &mut self,
144 init_info: WgpuInitInfo,
145 imgui_ctx: &mut Context,
146 ) -> RendererResult<()> {
147 self.init(init_info)?;
149
150 self.configure_imgui_context(imgui_ctx);
153
154 self.prepare_font_atlas(imgui_ctx)?;
156
157 Ok(())
158 }
159
160 pub fn set_gamma_mode(&mut self, mode: GammaMode) {
162 self.gamma_mode = mode;
163 }
164
165 pub fn configure_imgui_context(&self, imgui_context: &mut Context) {
167 let io = imgui_context.io_mut();
168 let mut flags = io.backend_flags();
169
170 flags.insert(BackendFlags::RENDERER_HAS_VTX_OFFSET);
173 flags.insert(BackendFlags::RENDERER_HAS_TEXTURES);
175
176 io.set_backend_flags(flags);
177 }
179
180 pub fn prepare_font_atlas(&mut self, imgui_ctx: &mut Context) -> RendererResult<()> {
182 if let Some(backend_data) = &self.backend_data {
183 let device = backend_data.device.clone();
184 let queue = backend_data.queue.clone();
185 self.reload_font_texture(imgui_ctx, &device, &queue)?;
186 if self.font_texture_id.is_none() {
189 if let Some(tex_id) =
190 self.try_upload_font_atlas_legacy(imgui_ctx, &device, &queue)?
191 {
192 if cfg!(debug_assertions) {
193 tracing::debug!(
194 target: "dear-imgui-wgpu",
195 "[dear-imgui-wgpu][debug] Font atlas uploaded via fallback (legacy-only) path; user textures use modern ImTextureData. tex_id={}",
196 tex_id
197 );
198 }
199 self.font_texture_id = Some(tex_id);
200 }
201 } else if cfg!(debug_assertions) {
202 tracing::debug!(
203 target: "dear-imgui-wgpu",
204 "[dear-imgui-wgpu][debug] Font atlas tex_id already set: {:?}",
205 self.font_texture_id
206 );
207 }
208 }
209 Ok(())
210 }
211
212 fn create_device_objects(&mut self, backend_data: &mut WgpuBackendData) -> RendererResult<()> {
216 let device = &backend_data.device;
217
218 let (common_layout, image_layout) = crate::shaders::create_bind_group_layouts(device);
220
221 let pipeline_layout = device.create_pipeline_layout(&PipelineLayoutDescriptor {
223 label: Some("Dear ImGui Pipeline Layout"),
224 bind_group_layouts: &[&common_layout, &image_layout],
225 push_constant_ranges: &[],
226 });
227
228 let shader_module = self.shader_manager.get_shader_module()?;
230
231 let vertex_buffer_layout = crate::shaders::create_vertex_buffer_layout();
233 let vertex_buffer_layouts = [vertex_buffer_layout];
234
235 let vertex_state =
237 crate::shaders::create_vertex_state(shader_module, &vertex_buffer_layouts);
238
239 let color_target = ColorTargetState {
241 format: backend_data.render_target_format,
242 blend: Some(BlendState {
243 color: BlendComponent {
244 src_factor: BlendFactor::SrcAlpha,
245 dst_factor: BlendFactor::OneMinusSrcAlpha,
246 operation: BlendOperation::Add,
247 },
248 alpha: BlendComponent {
249 src_factor: BlendFactor::One,
250 dst_factor: BlendFactor::OneMinusSrcAlpha,
251 operation: BlendOperation::Add,
252 },
253 }),
254 write_mask: ColorWrites::ALL,
255 };
256
257 let use_gamma_correction = matches!(
259 backend_data.render_target_format,
260 TextureFormat::Rgba8UnormSrgb
261 | TextureFormat::Bgra8UnormSrgb
262 | TextureFormat::Bc1RgbaUnormSrgb
263 | TextureFormat::Bc2RgbaUnormSrgb
264 | TextureFormat::Bc3RgbaUnormSrgb
265 | TextureFormat::Bc7RgbaUnormSrgb
266 );
267
268 let color_targets = [Some(color_target)];
270 let fragment_state = crate::shaders::create_fragment_state(
271 shader_module,
272 &color_targets,
273 use_gamma_correction,
274 );
275
276 let depth_stencil = backend_data
278 .depth_stencil_format
279 .map(|format| DepthStencilState {
280 format,
281 depth_write_enabled: false, depth_compare: CompareFunction::Always, stencil: StencilState {
284 front: StencilFaceState {
285 compare: CompareFunction::Always, fail_op: StencilOperation::Keep, depth_fail_op: StencilOperation::Keep, pass_op: StencilOperation::Keep, },
290 back: StencilFaceState {
291 compare: CompareFunction::Always, fail_op: StencilOperation::Keep, depth_fail_op: StencilOperation::Keep, pass_op: StencilOperation::Keep, },
296 read_mask: 0xff, write_mask: 0xff, },
299 bias: DepthBiasState::default(),
300 });
301
302 let pipeline = device.create_render_pipeline(&RenderPipelineDescriptor {
304 label: Some("Dear ImGui Render Pipeline"),
305 layout: Some(&pipeline_layout),
306 vertex: vertex_state,
307 primitive: PrimitiveState {
308 topology: PrimitiveTopology::TriangleList,
309 strip_index_format: None,
310 front_face: FrontFace::Cw,
311 cull_mode: None,
312 polygon_mode: PolygonMode::Fill,
313 unclipped_depth: false,
314 conservative: false,
315 },
316 depth_stencil,
317 multisample: backend_data.init_info.pipeline_multisample_state,
318 fragment: Some(fragment_state),
319 multiview: None,
320 cache: None,
321 });
322
323 backend_data.pipeline_state = Some(pipeline);
324 Ok(())
325 }
326
327 fn create_default_texture(
329 &self,
330 device: &Device,
331 queue: &Queue,
332 ) -> RendererResult<TextureView> {
333 let texture = device.create_texture(&TextureDescriptor {
334 label: Some("Dear ImGui Default Texture"),
335 size: Extent3d {
336 width: 1,
337 height: 1,
338 depth_or_array_layers: 1,
339 },
340 mip_level_count: 1,
341 sample_count: 1,
342 dimension: TextureDimension::D2,
343 format: TextureFormat::Rgba8Unorm,
344 usage: TextureUsages::TEXTURE_BINDING | TextureUsages::COPY_DST,
345 view_formats: &[],
346 });
347
348 queue.write_texture(
350 wgpu::TexelCopyTextureInfo {
351 texture: &texture,
352 mip_level: 0,
353 origin: wgpu::Origin3d::ZERO,
354 aspect: wgpu::TextureAspect::All,
355 },
356 &[255u8, 255u8, 255u8, 255u8], wgpu::TexelCopyBufferLayout {
358 offset: 0,
359 bytes_per_row: Some(4),
360 rows_per_image: Some(1),
361 },
362 Extent3d {
363 width: 1,
364 height: 1,
365 depth_or_array_layers: 1,
366 },
367 );
368
369 Ok(texture.create_view(&TextureViewDescriptor::default()))
370 }
371
372 fn reload_font_texture(
378 &mut self,
379 imgui_ctx: &mut Context,
380 _device: &Device,
381 _queue: &Queue,
382 ) -> RendererResult<()> {
383 let mut fonts = imgui_ctx.font_atlas_mut();
384 if !fonts.is_built() {
387 fonts.build();
388 }
389
390 Ok(())
395 }
396
397 fn try_upload_font_atlas_legacy(
400 &mut self,
401 imgui_ctx: &mut Context,
402 device: &Device,
403 queue: &Queue,
404 ) -> RendererResult<Option<u64>> {
405 let fonts = imgui_ctx.font_atlas();
407 let raw_tex = fonts.get_tex_data();
409 if raw_tex.is_null() {
410 if cfg!(debug_assertions) {
411 tracing::debug!(
412 target: "dear-imgui-wgpu",
413 "[dear-imgui-wgpu][debug] Font atlas TexData is null; skip legacy upload"
414 );
415 }
416 return Ok(None);
417 }
418 let (width, height, bpp, pixels_slice): (u32, u32, i32, Option<&[u8]>) = unsafe {
420 let w = (*raw_tex).Width as u32;
421 let h = (*raw_tex).Height as u32;
422 let bpp = (*raw_tex).BytesPerPixel;
423 let px_ptr = (*raw_tex).Pixels as *const u8;
424 if px_ptr.is_null() || w == 0 || h == 0 {
425 (w, h, bpp, None)
426 } else {
427 let size = (w as usize) * (h as usize) * (bpp as usize).max(1);
428 (w, h, bpp, Some(std::slice::from_raw_parts(px_ptr, size)))
429 }
430 };
431
432 if let Some(src) = pixels_slice {
433 if cfg!(debug_assertions) {
434 tracing::debug!(
435 target: "dear-imgui-wgpu",
436 "[dear-imgui-wgpu][debug] Font atlas texdata: {}x{} bpp={} (fallback upload for font atlas)",
437 width, height, bpp
438 );
439 }
440 let (format, converted): (wgpu::TextureFormat, Vec<u8>) = if bpp == 4 {
442 (wgpu::TextureFormat::Rgba8Unorm, src.to_vec())
443 } else if bpp == 1 {
444 let mut out = Vec::with_capacity((width as usize) * (height as usize) * 4);
446 for &a in src.iter() {
447 out.extend_from_slice(&[255, 255, 255, a]);
448 }
449 (wgpu::TextureFormat::Rgba8Unorm, out)
450 } else {
451 if cfg!(debug_assertions) {
453 tracing::debug!(
454 target: "dear-imgui-wgpu",
455 "[dear-imgui-wgpu][debug] Unexpected font atlas bpp={} -> skip",
456 bpp
457 );
458 }
459 return Ok(None);
460 };
461
462 let texture = device.create_texture(&wgpu::TextureDescriptor {
464 label: Some("Dear ImGui Font Atlas"),
465 size: wgpu::Extent3d {
466 width,
467 height,
468 depth_or_array_layers: 1,
469 },
470 mip_level_count: 1,
471 sample_count: 1,
472 dimension: wgpu::TextureDimension::D2,
473 format,
474 usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
475 view_formats: &[],
476 });
477 let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
478 let bpp = 4u32;
480 let unpadded = width * bpp;
481 let align = wgpu::COPY_BYTES_PER_ROW_ALIGNMENT;
482 let padded = unpadded.div_ceil(align) * align;
483 if padded == unpadded {
484 queue.write_texture(
485 wgpu::TexelCopyTextureInfo {
486 texture: &texture,
487 mip_level: 0,
488 origin: wgpu::Origin3d::ZERO,
489 aspect: wgpu::TextureAspect::All,
490 },
491 &converted,
492 wgpu::TexelCopyBufferLayout {
493 offset: 0,
494 bytes_per_row: Some(unpadded),
495 rows_per_image: Some(height),
496 },
497 wgpu::Extent3d {
498 width,
499 height,
500 depth_or_array_layers: 1,
501 },
502 );
503 } else {
504 let mut padded_buf = vec![0u8; (padded * height) as usize];
505 for row in 0..height as usize {
506 let src = row * (unpadded as usize);
507 let dst = row * (padded as usize);
508 padded_buf[dst..dst + (unpadded as usize)]
509 .copy_from_slice(&converted[src..src + (unpadded as usize)]);
510 }
511 queue.write_texture(
512 wgpu::TexelCopyTextureInfo {
513 texture: &texture,
514 mip_level: 0,
515 origin: wgpu::Origin3d::ZERO,
516 aspect: wgpu::TextureAspect::All,
517 },
518 &padded_buf,
519 wgpu::TexelCopyBufferLayout {
520 offset: 0,
521 bytes_per_row: Some(padded),
522 rows_per_image: Some(height),
523 },
524 wgpu::Extent3d {
525 width,
526 height,
527 depth_or_array_layers: 1,
528 },
529 );
530 if cfg!(debug_assertions) {
531 tracing::debug!(
532 target: "dear-imgui-wgpu",
533 "[dear-imgui-wgpu][debug] Upload font atlas with padded row pitch: unpadded={} padded={}",
534 unpadded, padded
535 );
536 }
537 }
538
539 let tex_id = self
541 .texture_manager
542 .register_texture(crate::WgpuTexture::new(texture, view));
543
544 {
546 let mut fonts_mut = imgui_ctx.font_atlas_mut();
547 fonts_mut.set_texture_id(dear_imgui_rs::TextureId::from(tex_id));
548 }
549 if cfg!(debug_assertions) {
550 tracing::debug!(
551 target: "dear-imgui-wgpu",
552 "[dear-imgui-wgpu][debug] Font atlas fallback upload complete: tex_id={}",
553 tex_id
554 );
555 }
556
557 return Ok(Some(tex_id));
558 }
559 if cfg!(debug_assertions) {
560 tracing::debug!(
561 target: "dear-imgui-wgpu",
562 "[dear-imgui-wgpu][debug] Font atlas has no CPU pixel buffer; skipping fallback upload (renderer will use modern texture updates)"
563 );
564 }
565 Ok(None)
566 }
567
568 pub fn texture_manager(&self) -> &WgpuTextureManager {
570 &self.texture_manager
571 }
572
573 pub fn texture_manager_mut(&mut self) -> &mut WgpuTextureManager {
575 &mut self.texture_manager
576 }
577
578 pub fn is_initialized(&self) -> bool {
580 self.backend_data.is_some()
581 }
582
583 pub fn update_texture(
607 &mut self,
608 texture_data: &dear_imgui_rs::TextureData,
609 ) -> RendererResult<crate::TextureUpdateResult> {
610 if let Some(backend_data) = &self.backend_data {
611 self.texture_manager
612 .update_single_texture(texture_data, &backend_data.device, &backend_data.queue)
613 .map_err(RendererError::TextureCreationFailed)
614 } else {
615 Err(RendererError::InvalidRenderState(
616 "Renderer not initialized".to_string(),
617 ))
618 }
619 }
620
621 pub fn new_frame(&mut self) -> RendererResult<()> {
625 let needs_recreation = if let Some(backend_data) = &self.backend_data {
626 backend_data.pipeline_state.is_none()
627 } else {
628 false
629 };
630
631 if needs_recreation {
632 let mut backend_data = self.backend_data.take().unwrap();
634 self.create_device_objects(&mut backend_data)?;
635 self.backend_data = Some(backend_data);
636 }
637 Ok(())
638 }
639
640 pub fn render_draw_data(
644 &mut self,
645 draw_data: &DrawData,
646 render_pass: &mut RenderPass,
647 ) -> RendererResult<()> {
648 let backend_data = self.backend_data.as_mut().ok_or_else(|| {
649 RendererError::InvalidRenderState("Renderer not initialized".to_string())
650 })?;
651
652 let fb_width = (draw_data.display_size[0] * draw_data.framebuffer_scale[0]) as i32;
654 let fb_height = (draw_data.display_size[1] * draw_data.framebuffer_scale[1]) as i32;
655 if fb_width <= 0 || fb_height <= 0 || !draw_data.valid() {
656 return Ok(());
657 }
658
659 self.texture_manager.handle_texture_updates(
660 draw_data,
661 &backend_data.device,
662 &backend_data.queue,
663 );
664
665 backend_data.next_frame();
667
668 Self::prepare_frame_resources_static(draw_data, backend_data)?;
670
671 let gamma = match self.gamma_mode {
673 GammaMode::Auto => Uniforms::gamma_for_format(backend_data.render_target_format),
674 GammaMode::Linear => 1.0,
675 GammaMode::Gamma22 => 2.2,
676 };
677
678 Self::setup_render_state_static(draw_data, render_pass, backend_data, gamma)?;
680
681 unsafe {
685 let platform_io = dear_imgui_rs::sys::igGetPlatformIO_Nil();
687
688 let mut render_state = crate::WgpuRenderState::new(&backend_data.device, render_pass);
690
691 (*platform_io).Renderer_RenderState =
693 &mut render_state as *mut _ as *mut std::ffi::c_void;
694
695 let result = Self::render_draw_lists_static(
697 &mut self.texture_manager,
698 &self.default_texture,
699 draw_data,
700 render_pass,
701 backend_data,
702 gamma,
703 );
704
705 (*platform_io).Renderer_RenderState = std::ptr::null_mut();
707
708 result?;
709 }
710
711 Ok(())
712 }
713
714 fn prepare_frame_resources_static(
716 draw_data: &DrawData,
717 backend_data: &mut WgpuBackendData,
718 ) -> RendererResult<()> {
719 let mut total_vtx_count = 0;
721 let mut total_idx_count = 0;
722 for draw_list in draw_data.draw_lists() {
723 total_vtx_count += draw_list.vtx_buffer().len();
724 total_idx_count += draw_list.idx_buffer().len();
725 }
726
727 if total_vtx_count == 0 || total_idx_count == 0 {
728 return Ok(());
729 }
730
731 let mut vertices = Vec::with_capacity(total_vtx_count);
733 let mut indices = Vec::with_capacity(total_idx_count);
734
735 for draw_list in draw_data.draw_lists() {
736 vertices.extend_from_slice(draw_list.vtx_buffer());
737 indices.extend_from_slice(draw_list.idx_buffer());
738 }
739
740 let frame_index = backend_data.frame_index % backend_data.num_frames_in_flight;
742 let frame_resources = &mut backend_data.frame_resources[frame_index as usize];
743
744 frame_resources
746 .ensure_vertex_buffer_capacity(&backend_data.device, total_vtx_count)
747 .map_err(RendererError::BufferCreationFailed)?;
748 frame_resources
749 .ensure_index_buffer_capacity(&backend_data.device, total_idx_count)
750 .map_err(RendererError::BufferCreationFailed)?;
751
752 frame_resources
753 .upload_vertex_data(&backend_data.queue, &vertices)
754 .map_err(RendererError::BufferCreationFailed)?;
755 frame_resources
756 .upload_index_data(&backend_data.queue, &indices)
757 .map_err(RendererError::BufferCreationFailed)?;
758
759 Ok(())
760 }
761
762 fn setup_render_state_static(
766 draw_data: &DrawData,
767 render_pass: &mut RenderPass,
768 backend_data: &WgpuBackendData,
769 gamma: f32,
770 ) -> RendererResult<()> {
771 let pipeline = backend_data
772 .pipeline_state
773 .as_ref()
774 .ok_or_else(|| RendererError::InvalidRenderState("Pipeline not created".to_string()))?;
775
776 let fb_width = draw_data.display_size[0] * draw_data.framebuffer_scale[0];
778 let fb_height = draw_data.display_size[1] * draw_data.framebuffer_scale[1];
779 render_pass.set_viewport(0.0, 0.0, fb_width, fb_height, 0.0, 1.0);
780
781 render_pass.set_pipeline(pipeline);
783
784 let mvp =
786 Uniforms::create_orthographic_matrix(draw_data.display_pos, draw_data.display_size);
787 let mut uniforms = Uniforms::new();
788 uniforms.update(mvp, gamma);
789
790 if let Some(uniform_buffer) = backend_data.render_resources.uniform_buffer() {
792 uniform_buffer.update(&backend_data.queue, &uniforms);
793 render_pass.set_bind_group(0, uniform_buffer.bind_group(), &[]);
794 }
795
796 let frame_resources = &backend_data.frame_resources
798 [(backend_data.frame_index % backend_data.num_frames_in_flight) as usize];
799 if let (Some(vertex_buffer), Some(index_buffer)) = (
800 frame_resources.vertex_buffer(),
801 frame_resources.index_buffer(),
802 ) {
803 render_pass.set_vertex_buffer(0, vertex_buffer.slice(..));
804 render_pass.set_index_buffer(index_buffer.slice(..), IndexFormat::Uint16);
805 }
806
807 Ok(())
808 }
809
810 fn render_draw_lists_static(
812 texture_manager: &mut WgpuTextureManager,
813 default_texture: &Option<TextureView>,
814 draw_data: &DrawData,
815 render_pass: &mut RenderPass,
816 backend_data: &mut WgpuBackendData,
817 gamma: f32,
818 ) -> RendererResult<()> {
819 let mut global_vtx_offset = 0i32;
820 let mut global_idx_offset = 0u32;
821 let clip_scale = draw_data.framebuffer_scale;
822 let clip_off = draw_data.display_pos;
823 let fb_width = draw_data.display_size[0] * draw_data.framebuffer_scale[0];
824 let fb_height = draw_data.display_size[1] * draw_data.framebuffer_scale[1];
825
826 for draw_list in draw_data.draw_lists() {
827 for cmd in draw_list.commands() {
828 match cmd {
829 dear_imgui_rs::render::DrawCmd::Elements {
830 count,
831 cmd_params,
832 raw_cmd,
833 } => {
834 let texture_bind_group = {
842 let tex_id = unsafe {
844 dear_imgui_rs::sys::ImDrawCmd_GetTexID(
845 raw_cmd as *mut dear_imgui_rs::sys::ImDrawCmd,
846 )
847 } as u64;
848 if tex_id == 0 {
849 if let Some(default_tex) = default_texture {
851 backend_data
852 .render_resources
853 .get_or_create_image_bind_group(
854 &backend_data.device,
855 0,
856 default_tex,
857 )?
858 .clone()
859 } else {
860 return Err(RendererError::InvalidRenderState(
861 "Default texture not available".to_string(),
862 ));
863 }
864 } else if let Some(wgpu_texture) = texture_manager.get_texture(tex_id) {
865 backend_data
866 .render_resources
867 .get_or_create_image_bind_group(
868 &backend_data.device,
869 tex_id,
870 wgpu_texture.view(),
871 )?
872 .clone()
873 } else {
874 if let Some(default_tex) = default_texture {
876 backend_data
877 .render_resources
878 .get_or_create_image_bind_group(
879 &backend_data.device,
880 0,
881 default_tex,
882 )?
883 .clone()
884 } else {
885 return Err(RendererError::InvalidRenderState(
886 "Texture not found and no default texture".to_string(),
887 ));
888 }
889 }
890 };
891
892 render_pass.set_bind_group(1, &texture_bind_group, &[]);
894
895 let clip_min_x = (cmd_params.clip_rect[0] - clip_off[0]) * clip_scale[0];
897 let clip_min_y = (cmd_params.clip_rect[1] - clip_off[1]) * clip_scale[1];
898 let clip_max_x = (cmd_params.clip_rect[2] - clip_off[0]) * clip_scale[0];
899 let clip_max_y = (cmd_params.clip_rect[3] - clip_off[1]) * clip_scale[1];
900
901 let clip_min_x = clip_min_x.max(0.0);
903 let clip_min_y = clip_min_y.max(0.0);
904 let clip_max_x = clip_max_x.min(fb_width);
905 let clip_max_y = clip_max_y.min(fb_height);
906
907 if clip_max_x <= clip_min_x || clip_max_y <= clip_min_y {
908 continue;
909 }
910
911 render_pass.set_scissor_rect(
913 clip_min_x as u32,
914 clip_min_y as u32,
915 (clip_max_x - clip_min_x) as u32,
916 (clip_max_y - clip_min_y) as u32,
917 );
918
919 let start_index = cmd_params.idx_offset as u32 + global_idx_offset;
921 let end_index = start_index + count as u32;
922 let vertex_offset = (cmd_params.vtx_offset as i32) + global_vtx_offset;
923 render_pass.draw_indexed(start_index..end_index, vertex_offset, 0..1);
924 }
925 dear_imgui_rs::render::DrawCmd::ResetRenderState => {
926 Self::setup_render_state_static(
928 draw_data,
929 render_pass,
930 backend_data,
931 gamma,
932 )?;
933 }
934 dear_imgui_rs::render::DrawCmd::RawCallback { .. } => {
935 tracing::warn!(
938 target: "dear-imgui-wgpu",
939 "Warning: Raw callbacks are not supported in WGPU renderer"
940 );
941 }
942 }
943 }
944
945 global_idx_offset += draw_list.idx_buffer().len() as u32;
946 global_vtx_offset += draw_list.vtx_buffer().len() as i32;
947 }
948
949 Ok(())
950 }
951
952 pub fn invalidate_device_objects(&mut self) -> RendererResult<()> {
956 if let Some(ref mut backend_data) = self.backend_data {
957 backend_data.pipeline_state = None;
958 backend_data.render_resources = RenderResources::new();
959
960 for frame_resources in &mut backend_data.frame_resources {
962 *frame_resources = FrameResources::new();
963 }
964 }
965
966 self.texture_manager.clear();
968 self.default_texture = None;
969 self.font_texture_id = None;
970
971 Ok(())
972 }
973
974 pub fn shutdown(&mut self) {
978 self.invalidate_device_objects().ok();
979 self.backend_data = None;
980 }
981}
982
983impl Default for WgpuRenderer {
984 fn default() -> Self {
985 Self::empty()
986 }
987}