Skip to main content

imgui_wgpu/
lib.rs

1//! # imgui-wgpu
2//!
3//! A [wgpu](https://crates.io/crates/wgpu) render backend for
4//! [imgui-rs](https://crates.io/crates/imgui).
5//!
6//! ## Quick Start
7//!
8//! ```rust,no_run
9//! # use imgui::Context;
10//! # use imgui_wgpu::{Renderer, RendererConfig};
11//! # fn example(device: &wgpu::Device, queue: &wgpu::Queue, surface_format: wgpu::TextureFormat) {
12//! // During setup:
13//! let mut imgui = Context::create();
14//! let renderer_config = RendererConfig {
15//!     texture_format: surface_format,
16//!     ..RendererConfig::new()
17//! };
18//! let mut renderer = Renderer::new(&mut imgui, &device, &queue, renderer_config);
19//!
20//! // Each frame:
21//! // let draw_data = imgui_context.render();
22//! // let mut rpass = encoder.begin_render_pass(&render_pass_desc);
23//! // renderer.render(draw_data, &queue, &device, &mut rpass).unwrap();
24//! # }
25//! ```
26//!
27//! ## Color Space
28//!
29//! The default [`RendererConfig::new`] outputs linear color, appropriate for sRGB
30//! framebuffers (e.g. `Bgra8UnormSrgb`). If you are rendering to a linear framebuffer
31//! (e.g. `Bgra8Unorm`), use [`RendererConfig::new_srgb`] instead.
32//!
33//! ## Custom Textures
34//!
35//! Create a [`Texture`], then insert it into [`Renderer::textures`] to get a
36//! [`imgui::TextureId`] for use with imgui image widgets:
37//!
38//! ```rust,no_run
39//! # use imgui_wgpu::{Renderer, Texture, TextureConfig};
40//! # fn example(device: &wgpu::Device, queue: &wgpu::Queue, renderer: &mut Renderer) {
41//! let config = TextureConfig {
42//!     size: wgpu::Extent3d { width: 64, height: 64, depth_or_array_layers: 1 },
43//!     ..Default::default()
44//! };
45//! # let pixel_data = vec![0u8; 64 * 64 * 4];
46//! let texture = Texture::new(&device, &renderer, config);
47//! texture.write(&queue, &pixel_data, 64, 64);
48//! let tex_id = renderer.textures.insert(texture);
49//! # }
50//! ```
51//!
52//! ## Split Rendering
53//!
54//! For more control over buffer lifetime, use [`Renderer::prepare`] and
55//! [`Renderer::split_render`] separately instead of [`Renderer::render`].
56//! This lets you reuse [`RenderData`] across frames and control when GPU
57//! buffers are created.
58
59use imgui::{
60    Context, DrawCmd::Elements, DrawData, DrawIdx, DrawList, DrawVert, TextureId, Textures,
61};
62use smallvec::SmallVec;
63use std::error::Error;
64use std::fmt;
65use std::mem::size_of;
66use std::sync::Arc;
67use wgpu::util::{BufferInitDescriptor, DeviceExt};
68use wgpu::*;
69
70static VS_ENTRY_POINT: &str = "vs_main";
71static FS_ENTRY_POINT_LINEAR: &str = "fs_main_linear";
72static FS_ENTRY_POINT_SRGB: &str = "fs_main_srgb";
73
74/// Result type for renderer operations.
75pub type RendererResult<T> = Result<T, RendererError>;
76
77#[repr(transparent)]
78#[derive(Debug, Copy, Clone)]
79struct DrawVertPod(DrawVert);
80
81unsafe impl bytemuck::Zeroable for DrawVertPod {}
82
83unsafe impl bytemuck::Pod for DrawVertPod {}
84
85/// Errors that can occur during rendering.
86#[derive(Clone, Debug)]
87pub enum RendererError {
88    /// A draw command referenced a [`TextureId`] that is not present in
89    /// [`Renderer::textures`].
90    BadTexture(TextureId),
91}
92
93impl fmt::Display for RendererError {
94    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
95        match *self {
96            RendererError::BadTexture(id) => {
97                write!(f, "imgui render error: bad texture id '{}'", id.id())
98            }
99        }
100    }
101}
102
103impl Error for RendererError {}
104
105#[allow(dead_code)]
106enum ShaderStage {
107    Vertex,
108    Fragment,
109    Compute,
110}
111
112/// Config for creating a texture from raw parts
113///
114#[derive(Clone)]
115pub struct RawTextureConfig<'a> {
116    /// An optional label for the bind group used for debugging.
117    pub label: Option<&'a str>,
118    /// The sampler descriptor of the texture.
119    pub sampler_desc: SamplerDescriptor<'a>,
120}
121
122/// Config for creating a texture.
123///
124/// Uses the builder pattern.
125#[derive(Clone)]
126pub struct TextureConfig<'a> {
127    /// The size of the texture.
128    pub size: Extent3d,
129    /// An optional label for the texture used for debugging.
130    pub label: Option<&'a str>,
131    /// The format of the texture, if not set uses the format from the renderer.
132    pub format: Option<TextureFormat>,
133    /// The usage of the texture.
134    pub usage: TextureUsages,
135    /// The mip level of the texture.
136    pub mip_level_count: u32,
137    /// The sample count of the texture.
138    pub sample_count: u32,
139    /// The dimension of the texture.
140    pub dimension: TextureDimension,
141    /// The sampler descriptor of the texture.
142    pub sampler_desc: SamplerDescriptor<'a>,
143}
144
145impl Default for TextureConfig<'_> {
146    /// Create a new texture config.
147    fn default() -> Self {
148        let sampler_desc = SamplerDescriptor {
149            label: Some("imgui-wgpu sampler"),
150            address_mode_u: AddressMode::ClampToEdge,
151            address_mode_v: AddressMode::ClampToEdge,
152            address_mode_w: AddressMode::ClampToEdge,
153            mag_filter: FilterMode::Linear,
154            min_filter: FilterMode::Linear,
155            mipmap_filter: MipmapFilterMode::Linear,
156            lod_min_clamp: 0.0,
157            lod_max_clamp: 100.0,
158            compare: None,
159            anisotropy_clamp: 1,
160            border_color: None,
161        };
162
163        Self {
164            size: Extent3d {
165                width: 0,
166                height: 0,
167                depth_or_array_layers: 1,
168            },
169            label: None,
170            format: None,
171            usage: TextureUsages::TEXTURE_BINDING | TextureUsages::COPY_DST,
172            mip_level_count: 1,
173            sample_count: 1,
174            dimension: TextureDimension::D2,
175            sampler_desc,
176        }
177    }
178}
179
180/// A container for a bindable texture.
181pub struct Texture {
182    texture: Arc<wgpu::Texture>,
183    view: Arc<wgpu::TextureView>,
184    bind_group: Arc<BindGroup>,
185    size: Extent3d,
186}
187
188impl Texture {
189    /// Create a `Texture` from its raw parts.
190    /// - `bind_group`: The bind group used by the texture. If it is `None`, the bind group will be created like in `Self::new`.
191    /// - `config`: The config used for creating the bind group. If `bind_group` is `Some(_)`, it will be ignored
192    pub fn from_raw_parts(
193        device: &Device,
194        renderer: &Renderer,
195        texture: Arc<wgpu::Texture>,
196        view: Arc<wgpu::TextureView>,
197        bind_group: Option<Arc<BindGroup>>,
198        config: Option<&RawTextureConfig>,
199        size: Extent3d,
200    ) -> Self {
201        let bind_group = bind_group.unwrap_or_else(|| {
202            let config = config.unwrap();
203
204            // Create the texture sampler.
205            let sampler = device.create_sampler(&config.sampler_desc);
206
207            // Create the texture bind group from the layout.
208            Arc::new(device.create_bind_group(&BindGroupDescriptor {
209                label: config.label,
210                layout: &renderer.texture_layout,
211                entries: &[
212                    BindGroupEntry {
213                        binding: 0,
214                        resource: BindingResource::TextureView(&view),
215                    },
216                    BindGroupEntry {
217                        binding: 1,
218                        resource: BindingResource::Sampler(&sampler),
219                    },
220                ],
221            }))
222        });
223
224        Self {
225            texture,
226            view,
227            bind_group,
228            size,
229        }
230    }
231
232    /// Create a new GPU texture width the specified `config`.
233    pub fn new(device: &Device, renderer: &Renderer, config: TextureConfig) -> Self {
234        // Create the wgpu texture.
235        let texture = Arc::new(device.create_texture(&TextureDescriptor {
236            label: config.label,
237            size: config.size,
238            mip_level_count: config.mip_level_count,
239            sample_count: config.sample_count,
240            dimension: config.dimension,
241            format: config.format.unwrap_or(renderer.config.texture_format),
242            usage: config.usage,
243            view_formats: &[config.format.unwrap_or(renderer.config.texture_format)],
244        }));
245
246        // Extract the texture view.
247        let view = Arc::new(texture.create_view(&TextureViewDescriptor::default()));
248
249        // Create the texture sampler.
250        let sampler = device.create_sampler(&config.sampler_desc);
251
252        // Create the texture bind group from the layout.
253        let bind_group = Arc::new(device.create_bind_group(&BindGroupDescriptor {
254            label: config.label,
255            layout: &renderer.texture_layout,
256            entries: &[
257                BindGroupEntry {
258                    binding: 0,
259                    resource: BindingResource::TextureView(&view),
260                },
261                BindGroupEntry {
262                    binding: 1,
263                    resource: BindingResource::Sampler(&sampler),
264                },
265            ],
266        }));
267
268        Self {
269            texture,
270            view,
271            bind_group,
272            size: config.size,
273        }
274    }
275
276    /// Write `data` to the texture.
277    ///
278    /// - `data`: Bitmap data matching the texture's format.
279    /// - `width`: The width of the source bitmap (`data`) in pixels.
280    /// - `height`: The height of the source bitmap (`data`) in pixels.
281    pub fn write(&self, queue: &Queue, data: &[u8], width: u32, height: u32) {
282        let bytes_per_block = self
283            .texture
284            .format()
285            .block_copy_size(None)
286            .expect("Texture format must have a valid block copy size");
287        queue.write_texture(
288            // destination (sub)texture
289            TexelCopyTextureInfo {
290                texture: &self.texture,
291                mip_level: 0,
292                origin: Origin3d { x: 0, y: 0, z: 0 },
293                aspect: TextureAspect::All,
294            },
295            // source bitmap data
296            data,
297            // layout of the source bitmap
298            TexelCopyBufferLayout {
299                offset: 0,
300                bytes_per_row: Some(width * bytes_per_block),
301                rows_per_image: Some(height),
302            },
303            // size of the source bitmap
304            Extent3d {
305                width,
306                height,
307                depth_or_array_layers: 1,
308            },
309        );
310    }
311
312    /// The width of the texture in pixels.
313    pub fn width(&self) -> u32 {
314        self.size.width
315    }
316
317    /// The height of the texture in pixels.
318    pub fn height(&self) -> u32 {
319        self.size.height
320    }
321
322    /// The depth of the texture.
323    pub fn depth(&self) -> u32 {
324        self.size.depth_or_array_layers
325    }
326
327    /// The size of the texture in pixels.
328    pub fn size(&self) -> Extent3d {
329        self.size
330    }
331
332    /// The underlying `wgpu::Texture`.
333    pub fn texture(&self) -> &wgpu::Texture {
334        &self.texture
335    }
336
337    /// The `wgpu::TextureView` of the underlying texture.
338    pub fn view(&self) -> &wgpu::TextureView {
339        &self.view
340    }
341}
342
343/// Configuration for the [`Renderer`].
344///
345/// Use [`RendererConfig::new`] for sRGB framebuffers, [`RendererConfig::new_srgb`]
346/// for linear framebuffers, or [`RendererConfig::with_shaders`] for custom shaders.
347pub struct RendererConfig<'s> {
348    /// The output texture format. Must match your render target (e.g. the surface format).
349    pub texture_format: TextureFormat,
350    /// Optional depth format. Set this if your render pass uses a depth attachment.
351    pub depth_format: Option<TextureFormat>,
352    /// MSAA sample count. Defaults to `1` (no MSAA). Must match your render pass.
353    pub sample_count: u32,
354    /// Custom shader module. If `None`, the built-in imgui shaders are used.
355    pub shader: Option<ShaderModuleDescriptor<'s>>,
356    /// Vertex shader entry point name. Only needed with custom shaders.
357    pub vertex_shader_entry_point: Option<&'s str>,
358    /// Fragment shader entry point name. Only needed with custom shaders.
359    pub fragment_shader_entry_point: Option<&'s str>,
360}
361
362impl<'s> RendererConfig<'s> {
363    /// Create a new renderer config with custom shaders.
364    pub fn with_shaders(shader: ShaderModuleDescriptor<'s>) -> Self {
365        RendererConfig {
366            texture_format: TextureFormat::Rgba8Unorm,
367            depth_format: None,
368            sample_count: 1,
369            shader: Some(shader),
370            vertex_shader_entry_point: Some(VS_ENTRY_POINT),
371            fragment_shader_entry_point: Some(FS_ENTRY_POINT_LINEAR),
372        }
373    }
374}
375
376impl Default for RendererConfig<'_> {
377    /// Create a new renderer config with precompiled default shaders outputting linear color.
378    ///
379    /// If you write to a Bgra8UnormSrgb framebuffer, this is what you want.
380    fn default() -> Self {
381        Self::new()
382    }
383}
384
385impl RendererConfig<'_> {
386    /// Create a new renderer config with precompiled default shaders outputting linear color.
387    ///
388    /// If you write to a Bgra8UnormSrgb framebuffer, this is what you want.
389    pub fn new() -> Self {
390        RendererConfig {
391            fragment_shader_entry_point: Some(FS_ENTRY_POINT_LINEAR),
392            ..Self::with_shaders(include_wgsl!("imgui.wgsl"))
393        }
394    }
395
396    /// Create a new renderer config with precompiled default shaders outputting srgb color.
397    ///
398    /// If you write to a Bgra8Unorm framebuffer, this is what you want.
399    pub fn new_srgb() -> Self {
400        RendererConfig {
401            fragment_shader_entry_point: Some(FS_ENTRY_POINT_SRGB),
402            ..Self::with_shaders(include_wgsl!("imgui.wgsl"))
403        }
404    }
405}
406
407/// Intermediate render state produced by [`Renderer::prepare`] and consumed by
408/// [`Renderer::split_render`].
409///
410/// Holds GPU buffers and draw-list metadata for a single frame. Can be reused
411/// across frames by passing the previous frame's `RenderData` back into
412/// [`Renderer::prepare`], which avoids reallocating buffers when capacity is sufficient.
413pub struct RenderData {
414    fb_size: [f32; 2],
415    last_size: [f32; 2],
416    last_pos: [f32; 2],
417    vertex_buffer: Option<Buffer>,
418    vertex_buffer_size: usize,
419    index_buffer: Option<Buffer>,
420    index_buffer_size: usize,
421    draw_list_offsets: SmallVec<[(i32, u32); 4]>,
422    render: bool,
423}
424
425/// The main imgui-wgpu renderer.
426///
427/// Manages the wgpu render pipeline, GPU buffers, and texture storage needed to
428/// render imgui draw data. Create one with [`Renderer::new`], then call
429/// [`Renderer::render`] (or [`Renderer::prepare`] + [`Renderer::split_render`])
430/// each frame inside a wgpu render pass.
431pub struct Renderer {
432    pipeline: RenderPipeline,
433    uniform_buffer: Buffer,
434    uniform_bind_group: BindGroup,
435    /// Texture storage for the font atlas and user-registered images.
436    ///
437    /// Insert custom [`Texture`]s here to get a [`TextureId`] for use with imgui
438    /// image widgets. The font atlas texture is inserted automatically.
439    pub textures: Textures<Texture>,
440    texture_layout: BindGroupLayout,
441    render_data: Option<RenderData>,
442    config: RendererConfig<'static>,
443}
444
445impl Renderer {
446    /// Create an entirely new imgui wgpu renderer.
447    pub fn new(
448        imgui: &mut Context,
449        device: &Device,
450        queue: &Queue,
451        config: RendererConfig,
452    ) -> Self {
453        let RendererConfig {
454            texture_format,
455            depth_format,
456            sample_count,
457            shader,
458            vertex_shader_entry_point,
459            fragment_shader_entry_point,
460        } = config;
461
462        // Load shaders.
463        let shader_module = device.create_shader_module(shader.unwrap());
464
465        // Create the uniform matrix buffer.
466        let size = 64;
467        let uniform_buffer = device.create_buffer(&BufferDescriptor {
468            label: Some("imgui-wgpu uniform buffer"),
469            size,
470            usage: BufferUsages::UNIFORM | BufferUsages::COPY_DST,
471            mapped_at_creation: false,
472        });
473
474        // Create the uniform matrix buffer bind group layout.
475        let uniform_layout = device.create_bind_group_layout(&BindGroupLayoutDescriptor {
476            label: None,
477            entries: &[BindGroupLayoutEntry {
478                binding: 0,
479                visibility: wgpu::ShaderStages::VERTEX,
480                ty: BindingType::Buffer {
481                    ty: BufferBindingType::Uniform,
482                    has_dynamic_offset: false,
483                    min_binding_size: None,
484                },
485                count: None,
486            }],
487        });
488
489        // Create the uniform matrix buffer bind group.
490        let uniform_bind_group = device.create_bind_group(&BindGroupDescriptor {
491            label: Some("imgui-wgpu bind group"),
492            layout: &uniform_layout,
493            entries: &[BindGroupEntry {
494                binding: 0,
495                resource: uniform_buffer.as_entire_binding(),
496            }],
497        });
498
499        // Create the texture layout for further usage.
500        let texture_layout = device.create_bind_group_layout(&BindGroupLayoutDescriptor {
501            label: Some("imgui-wgpu bind group layout"),
502            entries: &[
503                BindGroupLayoutEntry {
504                    binding: 0,
505                    visibility: wgpu::ShaderStages::FRAGMENT,
506                    ty: BindingType::Texture {
507                        multisampled: false,
508                        sample_type: TextureSampleType::Float { filterable: true },
509                        view_dimension: TextureViewDimension::D2,
510                    },
511                    count: None,
512                },
513                BindGroupLayoutEntry {
514                    binding: 1,
515                    visibility: wgpu::ShaderStages::FRAGMENT,
516                    ty: BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
517                    count: None,
518                },
519            ],
520        });
521
522        // Create the render pipeline layout.
523        let pipeline_layout = device.create_pipeline_layout(&PipelineLayoutDescriptor {
524            label: Some("imgui-wgpu pipeline layout"),
525            bind_group_layouts: &[Some(&uniform_layout), Some(&texture_layout)],
526            immediate_size: 0,
527        });
528
529        // Create the render pipeline.
530        // Create the render pipeline.
531        let pipeline = device.create_render_pipeline(&RenderPipelineDescriptor {
532            label: Some("imgui-wgpu pipeline"),
533            layout: Some(&pipeline_layout),
534            vertex: VertexState {
535                module: &shader_module,
536                entry_point: vertex_shader_entry_point,
537                compilation_options: Default::default(),
538                buffers: &[VertexBufferLayout {
539                    array_stride: size_of::<DrawVert>() as BufferAddress,
540                    step_mode: VertexStepMode::Vertex,
541                    attributes: &vertex_attr_array![0 => Float32x2, 1 => Float32x2, 2 => Unorm8x4],
542                }],
543            },
544            primitive: PrimitiveState {
545                topology: PrimitiveTopology::TriangleList,
546                strip_index_format: None,
547                front_face: FrontFace::Cw,
548                cull_mode: None,
549                polygon_mode: PolygonMode::Fill,
550                unclipped_depth: false,
551                conservative: false,
552            },
553            depth_stencil: depth_format.map(|format| wgpu::DepthStencilState {
554                format,
555                depth_write_enabled: Some(false),
556                depth_compare: None,
557                stencil: wgpu::StencilState::default(),
558                bias: DepthBiasState::default(),
559            }),
560            multisample: MultisampleState {
561                count: sample_count,
562                ..Default::default()
563            },
564            fragment: Some(FragmentState {
565                module: &shader_module,
566                entry_point: fragment_shader_entry_point,
567                compilation_options: Default::default(),
568                targets: &[Some(ColorTargetState {
569                    format: texture_format,
570                    blend: Some(BlendState {
571                        color: BlendComponent {
572                            src_factor: BlendFactor::SrcAlpha,
573                            dst_factor: BlendFactor::OneMinusSrcAlpha,
574                            operation: BlendOperation::Add,
575                        },
576                        alpha: BlendComponent {
577                            src_factor: BlendFactor::OneMinusDstAlpha,
578                            dst_factor: BlendFactor::One,
579                            operation: BlendOperation::Add,
580                        },
581                    }),
582                    write_mask: ColorWrites::ALL,
583                })],
584            }),
585            multiview_mask: None,
586            cache: None,
587        });
588
589        let mut renderer = Self {
590            pipeline,
591            uniform_buffer,
592            uniform_bind_group,
593            textures: Textures::new(),
594            texture_layout,
595            render_data: None,
596            config: RendererConfig {
597                texture_format,
598                depth_format,
599                sample_count,
600                shader: None,
601                vertex_shader_entry_point: None,
602                fragment_shader_entry_point: None,
603            },
604        };
605
606        // Immediately load the font texture to the GPU.
607        renderer.reload_font_texture(imgui, device, queue);
608
609        renderer
610    }
611
612    /// Prepares buffers for the current imgui frame.  This must be
613    /// called before `Renderer::split_render`, and its output must
614    /// be passed to the render call.
615    pub fn prepare(
616        &self,
617        draw_data: &DrawData,
618        render_data: Option<RenderData>,
619        queue: &Queue,
620        device: &Device,
621    ) -> RenderData {
622        let fb_width = draw_data.display_size[0] * draw_data.framebuffer_scale[0];
623        let fb_height = draw_data.display_size[1] * draw_data.framebuffer_scale[1];
624
625        let mut render_data = render_data.unwrap_or_else(|| RenderData {
626            fb_size: [fb_width, fb_height],
627            last_size: [0.0, 0.0],
628            last_pos: [0.0, 0.0],
629            vertex_buffer: None,
630            vertex_buffer_size: 0,
631            index_buffer: None,
632            index_buffer_size: 0,
633            draw_list_offsets: SmallVec::<[_; 4]>::new(),
634            render: false,
635        });
636
637        // If the render area is <= 0, exit here and now.
638        if fb_width <= 0.0 || fb_height <= 0.0 {
639            render_data.render = false;
640            return render_data;
641        } else {
642            render_data.render = true;
643        }
644
645        // If there are no draw lists, exit here
646        if draw_data.draw_lists_count() == 0 {
647            render_data.render = false;
648            return render_data;
649        }
650
651        // Only update matrices if the size or position changes
652        if (render_data.last_size[0] - draw_data.display_size[0]).abs() > f32::EPSILON
653            || (render_data.last_size[1] - draw_data.display_size[1]).abs() > f32::EPSILON
654            || (render_data.last_pos[0] - draw_data.display_pos[0]).abs() > f32::EPSILON
655            || (render_data.last_pos[1] - draw_data.display_pos[1]).abs() > f32::EPSILON
656        {
657            render_data.fb_size = [fb_width, fb_height];
658            render_data.last_size = draw_data.display_size;
659            render_data.last_pos = draw_data.display_pos;
660
661            let width = draw_data.display_size[0];
662            let height = draw_data.display_size[1];
663
664            let offset_x = draw_data.display_pos[0] / width;
665            let offset_y = draw_data.display_pos[1] / height;
666
667            // Create and update the transform matrix for the current frame.
668            // This is required to adapt to vulkan coordinates.
669            let matrix = [
670                [2.0 / width, 0.0, 0.0, 0.0],
671                [0.0, 2.0 / -height, 0.0, 0.0],
672                [0.0, 0.0, 1.0, 0.0],
673                [-1.0 - offset_x * 2.0, 1.0 + offset_y * 2.0, 0.0, 1.0],
674            ];
675            self.update_uniform_buffer(queue, &matrix);
676        }
677
678        render_data.draw_list_offsets.clear();
679
680        let mut vertex_count = 0;
681        let mut index_count = 0;
682        for draw_list in draw_data.draw_lists() {
683            render_data
684                .draw_list_offsets
685                .push((vertex_count as i32, index_count as u32));
686            vertex_count += draw_list.vtx_buffer().len();
687            index_count += draw_list.idx_buffer().len();
688        }
689
690        let mut vertices = Vec::with_capacity(vertex_count * std::mem::size_of::<DrawVertPod>());
691        let mut indices = Vec::with_capacity(index_count * std::mem::size_of::<DrawIdx>());
692
693        for draw_list in draw_data.draw_lists() {
694            // Safety: DrawVertPod is #[repr(transparent)] over DrawVert and DrawVert _should_ be Pod.
695            let vertices_pod: &[DrawVertPod] = unsafe { draw_list.transmute_vtx_buffer() };
696            vertices.extend_from_slice(bytemuck::cast_slice(vertices_pod));
697            indices.extend_from_slice(bytemuck::cast_slice(draw_list.idx_buffer()));
698        }
699
700        // Copies in wgpu must be padded to 4 byte alignment
701        indices.resize(
702            indices.len() + COPY_BUFFER_ALIGNMENT as usize
703                - indices.len() % COPY_BUFFER_ALIGNMENT as usize,
704            0,
705        );
706
707        // If the buffer is not created or is too small for the new indices, create a new buffer
708        if render_data.index_buffer.is_none() || render_data.index_buffer_size < indices.len() {
709            let buffer = device.create_buffer_init(&BufferInitDescriptor {
710                label: Some("imgui-wgpu index buffer"),
711                contents: &indices,
712                usage: BufferUsages::INDEX | BufferUsages::COPY_DST,
713            });
714            render_data.index_buffer = Some(buffer);
715            render_data.index_buffer_size = indices.len();
716        } else if let Some(buffer) = render_data.index_buffer.as_ref() {
717            // The buffer is large enough for the new indices, so reuse it
718            queue.write_buffer(buffer, 0, &indices);
719        } else {
720            unreachable!()
721        }
722
723        // If the buffer is not created or is too small for the new vertices, create a new buffer
724        if render_data.vertex_buffer.is_none() || render_data.vertex_buffer_size < vertices.len() {
725            let buffer = device.create_buffer_init(&BufferInitDescriptor {
726                label: Some("imgui-wgpu vertex buffer"),
727                contents: &vertices,
728                usage: BufferUsages::VERTEX | BufferUsages::COPY_DST,
729            });
730            render_data.vertex_buffer = Some(buffer);
731            render_data.vertex_buffer_size = vertices.len();
732        } else if let Some(buffer) = render_data.vertex_buffer.as_ref() {
733            // The buffer is large enough for the new vertices, so reuse it
734            queue.write_buffer(buffer, 0, &vertices);
735        } else {
736            unreachable!()
737        }
738
739        render_data
740    }
741
742    /// Render the current imgui frame.  `Renderer::prepare` must be
743    /// called first, and the output render data must be kept for the
744    /// lifetime of the renderpass.
745    pub fn split_render(
746        &self,
747        draw_data: &DrawData,
748        render_data: &RenderData,
749        rpass: &mut RenderPass<'_>,
750    ) -> RendererResult<()> {
751        if !render_data.render {
752            return Ok(());
753        }
754
755        let vertex_buffer = render_data.vertex_buffer.as_ref().unwrap();
756        if vertex_buffer.size() == 0 {
757            return Ok(());
758        }
759
760        let index_buffer = render_data.index_buffer.as_ref().unwrap();
761        if index_buffer.size() == 0 {
762            return Ok(());
763        }
764
765        rpass.set_pipeline(&self.pipeline);
766        rpass.set_bind_group(0, &self.uniform_bind_group, &[]);
767        rpass.set_vertex_buffer(0, vertex_buffer.slice(..));
768        rpass.set_index_buffer(index_buffer.slice(..), IndexFormat::Uint16);
769
770        // Execute all the imgui render work.
771        for (draw_list, bases) in draw_data
772            .draw_lists()
773            .zip(render_data.draw_list_offsets.iter())
774        {
775            self.render_draw_list(
776                rpass,
777                draw_list,
778                render_data.fb_size,
779                draw_data.display_pos,
780                draw_data.framebuffer_scale,
781                *bases,
782            )?;
783        }
784
785        Ok(())
786    }
787
788    /// Render the current imgui frame.
789    pub fn render(
790        &mut self,
791        draw_data: &DrawData,
792        queue: &Queue,
793        device: &Device,
794        rpass: &mut RenderPass<'_>,
795    ) -> RendererResult<()> {
796        let render_data = self.render_data.take();
797        self.render_data = Some(self.prepare(draw_data, render_data, queue, device));
798        self.split_render(draw_data, self.render_data.as_ref().unwrap(), rpass)
799    }
800
801    /// Render a given `DrawList` from imgui onto a wgpu frame.
802    fn render_draw_list(
803        &self,
804        rpass: &mut RenderPass<'_>,
805        draw_list: &DrawList,
806        fb_size: [f32; 2],
807        clip_off: [f32; 2],
808        clip_scale: [f32; 2],
809        (vertex_base, index_base): (i32, u32),
810    ) -> RendererResult<()> {
811        for cmd in draw_list.commands() {
812            if let Elements { count, cmd_params } = cmd {
813                let clip_rect = [
814                    (cmd_params.clip_rect[0] - clip_off[0]) * clip_scale[0],
815                    (cmd_params.clip_rect[1] - clip_off[1]) * clip_scale[1],
816                    (cmd_params.clip_rect[2] - clip_off[0]) * clip_scale[0],
817                    (cmd_params.clip_rect[3] - clip_off[1]) * clip_scale[1],
818                ];
819
820                // Set the current texture bind group on the renderpass.
821                let texture_id = cmd_params.texture_id;
822                let tex = self
823                    .textures
824                    .get(texture_id)
825                    .ok_or(RendererError::BadTexture(texture_id))?;
826                rpass.set_bind_group(1, Some(tex.bind_group.as_ref()), &[]);
827
828                // Set scissors on the renderpass.
829                let start = index_base + cmd_params.idx_offset as u32;
830                let end = start + count as u32;
831                if clip_rect[0] < fb_size[0]
832                    && clip_rect[1] < fb_size[1]
833                    && clip_rect[2] >= 0.0
834                    && clip_rect[3] >= 0.0
835                {
836                    let scissors = (
837                        clip_rect[0].max(0.0).floor() as u32,
838                        clip_rect[1].max(0.0).floor() as u32,
839                        (clip_rect[2].min(fb_size[0]) - clip_rect[0].max(0.0))
840                            .abs()
841                            .ceil() as u32,
842                        (clip_rect[3].min(fb_size[1]) - clip_rect[1].max(0.0))
843                            .abs()
844                            .ceil() as u32,
845                    );
846
847                    // XXX: Work-around for wgpu issue [1] by only issuing draw
848                    // calls if the scissor rect is valid (by wgpu's flawed
849                    // logic). Regardless, a zero-width or zero-height scissor
850                    // is essentially a no-op render anyway, so just skip it.
851                    // [1]: https://github.com/gfx-rs/wgpu/issues/1750
852                    if scissors.2 > 0 && scissors.3 > 0 {
853                        rpass.set_scissor_rect(scissors.0, scissors.1, scissors.2, scissors.3);
854
855                        // Draw the current batch of vertices with the renderpass.
856                        rpass.draw_indexed(
857                            start..end,
858                            vertex_base + cmd_params.vtx_offset as i32,
859                            0..1,
860                        );
861                    }
862                }
863            }
864        }
865        Ok(())
866    }
867
868    /// Updates the current uniform buffer containing the transform matrix.
869    fn update_uniform_buffer(&self, queue: &Queue, matrix: &[[f32; 4]; 4]) {
870        let data = bytemuck::bytes_of(matrix);
871        queue.write_buffer(&self.uniform_buffer, 0, data);
872    }
873
874    /// Updates the texture on the GPU corresponding to the current imgui font atlas.
875    ///
876    /// This has to be called after loading a font.
877    pub fn reload_font_texture(&mut self, imgui: &mut Context, device: &Device, queue: &Queue) {
878        let fonts = imgui.fonts();
879        // Remove possible font atlas texture.
880        self.textures.remove(fonts.tex_id);
881
882        // Create font texture and upload it.
883        let handle = fonts.build_rgba32_texture();
884        let font_texture_cnfig = TextureConfig {
885            label: Some("imgui-wgpu font atlas"),
886            size: Extent3d {
887                width: handle.width,
888                height: handle.height,
889                ..Default::default()
890            },
891            format: Some(TextureFormat::Rgba8Unorm),
892            ..Default::default()
893        };
894
895        let font_texture = Texture::new(device, self, font_texture_cnfig);
896        font_texture.write(queue, handle.data, handle.width, handle.height);
897        fonts.tex_id = self.textures.insert(font_texture);
898        // Clear imgui texture data to save memory.
899        fonts.clear_tex_data();
900    }
901}