1#[cfg(feature = "mv-log")]
2use std::sync::{Mutex, OnceLock};
3
4use super::{ActiveSampler, RendererRenderStateGuard, WgpuRenderer};
5use crate::wgpu;
6use crate::{GammaMode, RendererError, RendererResult, Uniforms};
7use dear_imgui_rs::{Context, TextureId, render::DrawData, sys};
8use wgpu::RenderPass;
9
10#[allow(unused_macros)]
11macro_rules! mvlog {
12 ($($arg:tt)*) => {
13 if cfg!(feature = "mv-log") { eprintln!($($arg)*); }
14 }
15}
16
17impl WgpuRenderer {
18 pub fn render_draw_data(
22 &mut self,
23 draw_data: &mut DrawData,
24 render_pass: &mut RenderPass,
25 ) -> RendererResult<()> {
26 let platform_io = unsafe { sys::igGetPlatformIO_Nil() };
27 self.render_draw_data_ex(draw_data, render_pass, platform_io)
28 }
29
30 pub fn render_context(
36 &mut self,
37 ctx: &mut Context,
38 render_pass: &mut RenderPass,
39 ) -> RendererResult<()> {
40 let platform_io = ctx.platform_io_mut().as_raw_mut();
41 let draw_data = ctx.render();
42 self.render_draw_data_ex(draw_data, render_pass, platform_io)
43 }
44
45 pub(super) fn render_draw_data_ex(
46 &mut self,
47 draw_data: &mut DrawData,
48 render_pass: &mut RenderPass,
49 platform_io: *mut sys::ImGuiPlatformIO,
50 ) -> RendererResult<()> {
51 let mut total_vtx_count = 0usize;
53 let mut total_idx_count = 0usize;
54 for dl in draw_data.draw_lists() {
55 total_vtx_count += dl.vtx_buffer().len();
56 total_idx_count += dl.idx_buffer().len();
57 }
58 if total_vtx_count == 0 || total_idx_count == 0 {
59 return Ok(());
60 }
61
62 let backend_data = self.backend_data.as_mut().ok_or_else(|| {
63 RendererError::InvalidRenderState("Renderer not initialized".to_string())
64 })?;
65
66 let fb_width = (draw_data.display_size[0] * draw_data.framebuffer_scale[0]) as i32;
68 let fb_height = (draw_data.display_size[1] * draw_data.framebuffer_scale[1]) as i32;
69 if fb_width <= 0 || fb_height <= 0 || !draw_data.valid() {
70 return Ok(());
71 }
72
73 self.texture_manager.handle_texture_updates(
74 draw_data,
75 &backend_data.device,
76 &backend_data.queue,
77 &mut backend_data.render_resources,
78 );
79
80 backend_data.next_frame();
82
83 Self::prepare_frame_resources_static(draw_data, backend_data)?;
85
86 let gamma = match self.gamma_mode {
88 GammaMode::Auto => Uniforms::gamma_for_format(backend_data.render_target_format),
89 GammaMode::Linear => 1.0,
90 GammaMode::Gamma22 => 2.2,
91 };
92
93 Self::setup_render_state_static(draw_data, render_pass, backend_data, gamma)?;
95 render_pass.set_viewport(0.0, 0.0, fb_width as f32, fb_height as f32, 0.0, 1.0);
97
98 unsafe {
102 let mut render_state = crate::WgpuRenderState::new(&backend_data.device, render_pass);
104 let _render_state_guard = RendererRenderStateGuard::set(
105 platform_io,
106 &mut render_state as *mut _ as *mut std::ffi::c_void,
107 )?;
108
109 let result = Self::render_draw_lists_static(
111 &mut self.texture_manager,
112 &self.default_texture,
113 draw_data,
114 render_pass,
115 backend_data,
116 gamma,
117 );
118
119 if let Err(e) = result {
120 eprintln!("[wgpu-mv] render_draw_lists_static error: {:?}", e);
121 return Err(e);
122 }
123 }
124
125 Ok(())
126 }
127
128 pub fn render_draw_data_with_fb_size(
129 &mut self,
130 draw_data: &mut DrawData,
131 render_pass: &mut RenderPass,
132 fb_width: u32,
133 fb_height: u32,
134 ) -> RendererResult<()> {
135 let platform_io = unsafe { sys::igGetPlatformIO_Nil() };
136 self.render_draw_data_with_fb_size_ex(
138 draw_data,
139 render_pass,
140 fb_width,
141 fb_height,
142 true,
143 platform_io,
144 )
145 }
146
147 pub fn render_context_with_fb_size(
152 &mut self,
153 ctx: &mut Context,
154 render_pass: &mut RenderPass,
155 fb_width: u32,
156 fb_height: u32,
157 ) -> RendererResult<()> {
158 let platform_io = ctx.platform_io_mut().as_raw_mut();
159 let draw_data = ctx.render();
160 self.render_draw_data_with_fb_size_ex(
161 draw_data,
162 render_pass,
163 fb_width,
164 fb_height,
165 true,
166 platform_io,
167 )
168 }
169
170 pub(super) fn render_draw_data_with_fb_size_ex(
174 &mut self,
175 draw_data: &mut DrawData,
176 render_pass: &mut RenderPass,
177 fb_width: u32,
178 fb_height: u32,
179 advance_frame: bool,
180 platform_io: *mut sys::ImGuiPlatformIO,
181 ) -> RendererResult<()> {
182 #[cfg(feature = "mv-log")]
185 {
186 static LAST_MISMATCH: OnceLock<Mutex<Option<(u32, u32, u32, u32, bool)>>> =
187 OnceLock::new();
188 let last = LAST_MISMATCH.get_or_init(|| Mutex::new(None));
189 let expected_w = (draw_data.display_size()[0] * draw_data.framebuffer_scale()[0])
190 .round()
191 .max(0.0) as u32;
192 let expected_h = (draw_data.display_size()[1] * draw_data.framebuffer_scale()[1])
193 .round()
194 .max(0.0) as u32;
195 if expected_w != fb_width || expected_h != fb_height {
196 let key = (expected_w, expected_h, fb_width, fb_height, advance_frame);
197 let mut guard = last.lock().unwrap();
198 if *guard != Some(key) {
199 mvlog!(
200 "[wgpu-mv] fb mismatch expected=({}, {}) override=({}, {}) disp=({:.1},{:.1}) fb_scale=({:.2},{:.2}) main={}",
201 expected_w,
202 expected_h,
203 fb_width,
204 fb_height,
205 draw_data.display_size()[0],
206 draw_data.display_size()[1],
207 draw_data.framebuffer_scale()[0],
208 draw_data.framebuffer_scale()[1],
209 advance_frame
210 );
211 *guard = Some(key);
212 }
213 }
214 }
215 let total_vtx_count: usize = draw_data.draw_lists().map(|dl| dl.vtx_buffer().len()).sum();
216 let total_idx_count: usize = draw_data.draw_lists().map(|dl| dl.idx_buffer().len()).sum();
217 if total_vtx_count == 0 || total_idx_count == 0 {
218 return Ok(());
219 }
220 let backend_data = self.backend_data.as_mut().ok_or_else(|| {
221 RendererError::InvalidRenderState("Renderer not initialized".to_string())
222 })?;
223
224 if fb_width == 0 || fb_height == 0 || !draw_data.valid() {
226 return Ok(());
227 }
228
229 self.texture_manager.handle_texture_updates(
230 draw_data,
231 &backend_data.device,
232 &backend_data.queue,
233 &mut backend_data.render_resources,
234 );
235
236 if advance_frame {
237 backend_data.next_frame();
238 }
239 Self::prepare_frame_resources_static(draw_data, backend_data)?;
240
241 let gamma = match self.gamma_mode {
242 GammaMode::Auto => Uniforms::gamma_for_format(backend_data.render_target_format),
243 GammaMode::Linear => 1.0,
244 GammaMode::Gamma22 => 2.2,
245 };
246
247 Self::setup_render_state_static(draw_data, render_pass, backend_data, gamma)?;
248
249 unsafe {
250 let mut render_state = crate::WgpuRenderState::new(&backend_data.device, render_pass);
251 let _render_state_guard = RendererRenderStateGuard::set(
252 platform_io,
253 &mut render_state as *mut _ as *mut std::ffi::c_void,
254 )?;
255
256 let device = backend_data.device.clone();
259 let (common_layout, uniform_buffer, default_common_bg, nearest_common_bg) = {
260 let ub = backend_data
261 .render_resources
262 .uniform_buffer()
263 .ok_or_else(|| {
264 RendererError::InvalidRenderState(
265 "Uniform buffer not initialized".to_string(),
266 )
267 })?;
268 let nearest_bg = backend_data
269 .render_resources
270 .nearest_common_bind_group()
271 .ok_or_else(|| {
272 RendererError::InvalidRenderState(
273 "Nearest sampler bind group not initialized".to_string(),
274 )
275 })?;
276 (
277 ub.bind_group_layout().clone(),
278 ub.buffer().clone(),
279 ub.bind_group().clone(),
280 nearest_bg.clone(),
281 )
282 };
283 let mut standard_sampler = ActiveSampler::Linear;
284 let mut current_sampler = ActiveSampler::Linear;
285
286 let mut global_idx_offset: u32 = 0;
287 let mut global_vtx_offset: i32 = 0;
288 let clip_off = draw_data.display_pos();
289 let clip_scale = draw_data.framebuffer_scale();
290 let fbw = fb_width as f32;
291 let fbh = fb_height as f32;
292
293 for draw_list in draw_data.draw_lists() {
294 let vtx_buffer = draw_list.vtx_buffer();
295 let idx_buffer = draw_list.idx_buffer();
296 for cmd in draw_list.commands() {
297 match cmd {
298 dear_imgui_rs::render::DrawCmd::Elements {
299 count,
300 cmd_params,
301 raw_cmd,
302 } => {
303 let mut cmd_copy = *raw_cmd;
306 let tex_id = TextureId::from(dear_imgui_rs::sys::ImDrawCmd_GetTexID(
307 &mut cmd_copy,
308 ));
309
310 let desired_sampler = if tex_id.is_null() {
313 standard_sampler
314 } else {
315 self.texture_manager
316 .custom_sampler_id_for_texture(tex_id)
317 .map(ActiveSampler::Custom)
318 .unwrap_or(standard_sampler)
319 };
320 if desired_sampler != current_sampler {
321 match desired_sampler {
322 ActiveSampler::Linear => {
323 render_pass.set_bind_group(0, &default_common_bg, &[]);
324 }
325 ActiveSampler::Nearest => {
326 render_pass.set_bind_group(0, &nearest_common_bg, &[]);
327 }
328 ActiveSampler::Custom(sampler_id) => {
329 if let Some(bg0) = self
330 .texture_manager
331 .get_or_create_common_bind_group_for_sampler(
332 &device,
333 &common_layout,
334 &uniform_buffer,
335 sampler_id,
336 )
337 {
338 render_pass.set_bind_group(0, &bg0, &[]);
339 } else {
340 render_pass.set_bind_group(0, &default_common_bg, &[]);
341 }
342 }
343 }
344 current_sampler = desired_sampler;
345 }
346
347 let texture_bind_group = if tex_id.is_null() {
348 if let Some(default_tex) = &self.default_texture {
349 backend_data
350 .render_resources
351 .get_or_create_image_bind_group(
352 &backend_data.device,
353 TextureId::null(),
354 default_tex,
355 )?
356 .clone()
357 } else {
358 return Err(RendererError::InvalidRenderState(
359 "Default texture not available".to_string(),
360 ));
361 }
362 } else if let Some(wgpu_texture) =
363 self.texture_manager.get_texture(tex_id)
364 {
365 backend_data
366 .render_resources
367 .get_or_create_image_bind_group(
368 &backend_data.device,
369 tex_id,
370 &wgpu_texture.texture_view,
371 )?
372 .clone()
373 } else if let Some(default_tex) = &self.default_texture {
374 backend_data
375 .render_resources
376 .get_or_create_image_bind_group(
377 &backend_data.device,
378 TextureId::null(),
379 default_tex,
380 )?
381 .clone()
382 } else {
383 return Err(RendererError::InvalidRenderState(
384 "Texture not found and no default texture".to_string(),
385 ));
386 };
387 render_pass.set_bind_group(1, &texture_bind_group, &[]);
388
389 let mut clip_min_x =
391 (cmd_params.clip_rect[0] - clip_off[0]) * clip_scale[0];
392 let mut clip_min_y =
393 (cmd_params.clip_rect[1] - clip_off[1]) * clip_scale[1];
394 let mut clip_max_x =
395 (cmd_params.clip_rect[2] - clip_off[0]) * clip_scale[0];
396 let mut clip_max_y =
397 (cmd_params.clip_rect[3] - clip_off[1]) * clip_scale[1];
398 clip_min_x = clip_min_x.max(0.0);
400 clip_min_y = clip_min_y.max(0.0);
401 clip_max_x = clip_max_x.min(fbw);
402 clip_max_y = clip_max_y.min(fbh);
403 if clip_max_x <= clip_min_x || clip_max_y <= clip_min_y {
404 continue;
405 }
406 render_pass.set_scissor_rect(
407 clip_min_x as u32,
408 clip_min_y as u32,
409 (clip_max_x - clip_min_x) as u32,
410 (clip_max_y - clip_min_y) as u32,
411 );
412 let Ok(count_u32) = u32::try_from(count) else {
413 continue;
414 };
415 let Ok(idx_offset_u32) = u32::try_from(cmd_params.idx_offset) else {
416 continue;
417 };
418 let Some(start_index) = idx_offset_u32.checked_add(global_idx_offset)
419 else {
420 continue;
421 };
422 let Some(end_index) = start_index.checked_add(count_u32) else {
423 continue;
424 };
425 let Ok(vtx_offset_i32) = i32::try_from(cmd_params.vtx_offset) else {
426 continue;
427 };
428 let Some(vertex_offset) = vtx_offset_i32.checked_add(global_vtx_offset)
429 else {
430 continue;
431 };
432 render_pass.draw_indexed(start_index..end_index, vertex_offset, 0..1);
433 }
434 dear_imgui_rs::render::DrawCmd::ResetRenderState => {
435 Self::setup_render_state_static(
436 draw_data,
437 render_pass,
438 backend_data,
439 gamma,
440 )?;
441 standard_sampler = ActiveSampler::Linear;
442 current_sampler = ActiveSampler::Linear;
443 }
444 dear_imgui_rs::render::DrawCmd::SetSamplerLinear => {
445 standard_sampler = ActiveSampler::Linear;
446 if current_sampler != ActiveSampler::Linear {
447 render_pass.set_bind_group(0, &default_common_bg, &[]);
448 current_sampler = ActiveSampler::Linear;
449 }
450 }
451 dear_imgui_rs::render::DrawCmd::SetSamplerNearest => {
452 standard_sampler = ActiveSampler::Nearest;
453 if current_sampler != ActiveSampler::Nearest {
454 render_pass.set_bind_group(0, &nearest_common_bg, &[]);
455 current_sampler = ActiveSampler::Nearest;
456 }
457 }
458 dear_imgui_rs::render::DrawCmd::RawCallback { .. } => {
459 }
461 }
462 }
463
464 let idx_len_u32 = u32::try_from(idx_buffer.len())
465 .map_err(|_| RendererError::DrawBufferTooLarge { buffer: "index" })?;
466 global_idx_offset = global_idx_offset
467 .checked_add(idx_len_u32)
468 .ok_or_else(|| RendererError::DrawBufferOffsetOverflow { buffer: "index" })?;
469
470 let vtx_len_i32 = i32::try_from(vtx_buffer.len())
471 .map_err(|_| RendererError::DrawBufferTooLarge { buffer: "vertex" })?;
472 global_vtx_offset = global_vtx_offset
473 .checked_add(vtx_len_i32)
474 .ok_or_else(|| RendererError::DrawBufferOffsetOverflow { buffer: "vertex" })?;
475 }
476 }
477
478 Ok(())
479 }
480}