imgui_wgpu/
lib.rs

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