imgui_rs_vulkan_renderer/renderer/
mod.rs

1mod allocator;
2pub mod vulkan;
3
4use crate::RendererError;
5use ash::{vk, Device};
6use imgui::{Context, DrawCmd, DrawCmdParams, DrawData, TextureId, Textures};
7use mesh::*;
8use ultraviolet::projection::orthographic_vk;
9use vulkan::*;
10
11use self::allocator::Allocator;
12
13#[cfg(not(any(feature = "gpu-allocator", feature = "vk-mem")))]
14use ash::Instance;
15
16#[cfg(feature = "gpu-allocator")]
17use {
18    gpu_allocator::vulkan::Allocator as GpuAllocator,
19    std::sync::{Arc, Mutex},
20};
21
22#[cfg(feature = "vk-mem")]
23use {
24    std::sync::{Arc, Mutex},
25    vk_mem::Allocator as VkMemAllocator,
26};
27
28/// Convenient return type for function that can return a [`RendererError`].
29///
30/// [`RendererError`]: enum.RendererError.html
31pub type RendererResult<T> = Result<T, RendererError>;
32
33/// Optional parameters of the renderer.
34#[derive(Debug, Clone, Copy)]
35pub struct Options {
36    /// The number of in flight frames of the application.
37    pub in_flight_frames: usize,
38    /// If true enables depth test when rendering.
39    pub enable_depth_test: bool,
40    /// If true enables depth writes when rendering.
41    ///
42    /// Note that depth writes are always disabled when enable_depth_test is false.
43    /// See <https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/VkPipelineDepthStencilStateCreateInfo.html>
44    pub enable_depth_write: bool,
45    /// Subpass for the graphics pipeline.
46    pub subpass: u32,
47    /// Sample count for the graphics pipeline multisampling state
48    pub sample_count: vk::SampleCountFlags,
49}
50
51impl Default for Options {
52    fn default() -> Self {
53        Self {
54            in_flight_frames: 1,
55            enable_depth_test: false,
56            enable_depth_write: false,
57            subpass: 0,
58            sample_count: vk::SampleCountFlags::TYPE_1
59        }
60    }
61}
62
63/// `dynamic-rendering` feature related params
64#[cfg(feature = "dynamic-rendering")]
65#[derive(Debug, Clone, Copy)]
66pub struct DynamicRendering {
67    pub color_attachment_format: vk::Format,
68    pub depth_attachment_format: Option<vk::Format>,
69}
70
71/// Vulkan renderer for imgui.
72///
73/// It records rendering command to the provided command buffer at each call to [`cmd_draw`].
74///
75/// The renderer holds a set of vertex/index buffers per in flight frames. Vertex and index buffers
76/// are resized at each call to [`cmd_draw`] if draw data does not fit.
77///
78/// [`cmd_draw`]: #method.cmd_draw
79pub struct Renderer {
80    device: Device,
81    allocator: Allocator,
82    pipeline: vk::Pipeline,
83    pipeline_layout: vk::PipelineLayout,
84    descriptor_set_layout: vk::DescriptorSetLayout,
85    fonts_texture: Option<Texture>,
86    descriptor_pool: vk::DescriptorPool,
87    descriptor_set: vk::DescriptorSet,
88    textures: Textures<vk::DescriptorSet>,
89    options: Options,
90    frames: Option<Frames>,
91}
92
93impl Renderer {
94    /// Initialize and return a new instance of the renderer.
95    ///
96    /// At initialization all Vulkan resources are initialized and font texture is created and
97    /// uploaded to the gpu. Vertex and index buffers are not created yet.
98    ///
99    /// # Arguments
100    ///
101    /// * `instance` - A reference to a Vulkan instance.
102    /// * `physical_device` - A Vulkan physical device.
103    /// * `device` - A Vulkan device.
104    /// * `queue` - A Vulkan queue.
105    ///             It will be used to submit commands during initialization to upload
106    ///             data to the gpu. The type of queue must be supported by the following
107    ///             commands: [vkCmdCopyBufferToImage](https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/vkCmdCopyBufferToImage.html),
108    ///             [vkCmdPipelineBarrier](https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/vkCmdPipelineBarrier.html)
109    /// * `command_pool` - A Vulkan command pool used to allocate command buffers to upload textures to the gpu.
110    /// * `render_pass` - *without dynamic-rendering feature* - The render pass used to render the gui.
111    /// * `dynamic_rendering` - *with dynamic-rendering feature* - Dynamic rendeing parameters
112    /// * `imgui` - The imgui context.
113    /// * `options` - Optional parameters of the renderer.
114    ///
115    /// # Errors
116    ///
117    /// * [`RendererError`] - If the number of in flight frame in incorrect.
118    /// * [`RendererError`] - If any Vulkan or io error is encountered during initialization.
119    #[cfg(not(any(feature = "gpu-allocator", feature = "vk-mem")))]
120    pub fn with_default_allocator(
121        instance: &Instance,
122        physical_device: vk::PhysicalDevice,
123        device: Device,
124        queue: vk::Queue,
125        command_pool: vk::CommandPool,
126        #[cfg(not(feature = "dynamic-rendering"))] render_pass: vk::RenderPass,
127        #[cfg(feature = "dynamic-rendering")] dynamic_rendering: DynamicRendering,
128        imgui: &mut Context,
129        options: Option<Options>,
130    ) -> RendererResult<Self> {
131        let memory_properties =
132            unsafe { instance.get_physical_device_memory_properties(physical_device) };
133
134        Self::from_allocator(
135            device,
136            queue,
137            command_pool,
138            Allocator::new(memory_properties),
139            #[cfg(not(feature = "dynamic-rendering"))]
140            render_pass,
141            #[cfg(feature = "dynamic-rendering")]
142            dynamic_rendering,
143            imgui,
144            options,
145        )
146    }
147
148    /// Initialize and return a new instance of the renderer.
149    ///
150    /// At initialization all Vulkan resources are initialized and font texture is created and
151    /// uploaded to the gpu. Vertex and index buffers are not created yet.
152    ///
153    /// # Arguments
154    ///
155    /// * `gpu_allocator` - The allocator that will be used to allocator buffer and image memory.
156    /// * `device` - A Vulkan device.
157    /// * `queue` - A Vulkan queue.
158    ///             It will be used to submit commands during initialization to upload
159    ///             data to the gpu. The type of queue must be supported by the following
160    ///             commands: [vkCmdCopyBufferToImage](https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/vkCmdCopyBufferToImage.html),
161    ///             [vkCmdPipelineBarrier](https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/vkCmdPipelineBarrier.html)
162    /// * `command_pool` - A Vulkan command pool used to allocate command buffers to upload textures to the gpu.
163    /// * `render_pass` - *without dynamic-rendering feature* - The render pass used to render the gui.
164    /// * `dynamic_rendering` - *with dynamic-rendering feature* - Dynamic rendeing parameters
165    /// * `imgui` - The imgui context.
166    /// * `options` - Optional parameters of the renderer.
167    ///
168    /// # Errors
169    ///
170    /// * [`RendererError`] - If the number of in flight frame in incorrect.
171    /// * [`RendererError`] - If any Vulkan or io error is encountered during initialization.
172    #[cfg(feature = "gpu-allocator")]
173    pub fn with_gpu_allocator(
174        gpu_allocator: Arc<Mutex<GpuAllocator>>, // TODO: Another way ?
175        device: Device,
176        queue: vk::Queue,
177        command_pool: vk::CommandPool,
178        #[cfg(not(feature = "dynamic-rendering"))] render_pass: vk::RenderPass,
179        #[cfg(feature = "dynamic-rendering")] dynamic_rendering: DynamicRendering,
180        imgui: &mut Context,
181        options: Option<Options>,
182    ) -> RendererResult<Self> {
183        Self::from_allocator(
184            device,
185            queue,
186            command_pool,
187            Allocator::new(gpu_allocator),
188            #[cfg(not(feature = "dynamic-rendering"))]
189            render_pass,
190            #[cfg(feature = "dynamic-rendering")]
191            dynamic_rendering,
192            imgui,
193            options,
194        )
195    }
196
197    /// Initialize and return a new instance of the renderer.
198    ///
199    /// At initialization all Vulkan resources are initialized and font texture is created and
200    /// uploaded to the gpu. Vertex and index buffers are not created yet.
201    ///
202    /// # Arguments
203    ///
204    /// * `vk_mem_allocator` - The allocator that will be used to allocator buffer and image memory.
205    /// * `device` - A Vulkan device.
206    /// * `queue` - A Vulkan queue.
207    ///             It will be used to submit commands during initialization to upload
208    ///             data to the gpu. The type of queue must be supported by the following
209    ///             commands: [vkCmdCopyBufferToImage](https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/vkCmdCopyBufferToImage.html),
210    ///             [vkCmdPipelineBarrier](https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/vkCmdPipelineBarrier.html)
211    /// * `command_pool` - A Vulkan command pool used to allocate command buffers to upload textures to the gpu.
212    /// * `render_pass` - *without dynamic-rendering feature* - The render pass used to render the gui.
213    /// * `dynamic_rendering` - *with dynamic-rendering feature* - Dynamic rendeing parameters
214    /// * `imgui` - The imgui context.
215    /// * `options` - Optional parameters of the renderer.
216    ///
217    /// # Errors
218    ///
219    /// * [`RendererError`] - If the number of in flight frame in incorrect.
220    /// * [`RendererError`] - If any Vulkan or io error is encountered during initialization.
221    #[cfg(feature = "vk-mem")]
222    pub fn with_vk_mem_allocator(
223        vk_mem_allocator: Arc<Mutex<VkMemAllocator>>, // TODO: Another way ?
224        device: Device,
225        queue: vk::Queue,
226        command_pool: vk::CommandPool,
227        #[cfg(not(feature = "dynamic-rendering"))] render_pass: vk::RenderPass,
228        #[cfg(feature = "dynamic-rendering")] dynamic_rendering: DynamicRendering,
229        imgui: &mut Context,
230        options: Option<Options>,
231    ) -> RendererResult<Self> {
232        Self::from_allocator(
233            device,
234            queue,
235            command_pool,
236            Allocator::new(vk_mem_allocator),
237            #[cfg(not(feature = "dynamic-rendering"))]
238            render_pass,
239            #[cfg(feature = "dynamic-rendering")]
240            dynamic_rendering,
241            imgui,
242            options,
243        )
244    }
245
246    fn from_allocator(
247        device: Device,
248        queue: vk::Queue,
249        command_pool: vk::CommandPool,
250        mut allocator: Allocator,
251        #[cfg(not(feature = "dynamic-rendering"))] render_pass: vk::RenderPass,
252        #[cfg(feature = "dynamic-rendering")] dynamic_rendering: DynamicRendering,
253        imgui: &mut Context,
254        options: Option<Options>,
255    ) -> RendererResult<Self> {
256        let options = options.unwrap_or_default();
257
258        log::debug!("Creating imgui renderer with options {options:?}");
259
260        if options.in_flight_frames == 0 {
261            return Err(RendererError::Init(String::from(
262                "'in_flight_frames' parameter should be at least one",
263            )));
264        }
265
266        // Descriptor set layout
267        let descriptor_set_layout = create_vulkan_descriptor_set_layout(&device)?;
268
269        // Pipeline and layout
270        let pipeline_layout = create_vulkan_pipeline_layout(&device, descriptor_set_layout)?;
271        let pipeline = create_vulkan_pipeline(
272            &device,
273            pipeline_layout,
274            #[cfg(not(feature = "dynamic-rendering"))]
275            render_pass,
276            #[cfg(feature = "dynamic-rendering")]
277            dynamic_rendering,
278            options,
279        )?;
280
281        // Fonts texture
282        let fonts_texture = {
283            let fonts = imgui.fonts();
284            let atlas_texture = fonts.build_rgba32_texture();
285
286            Texture::from_rgba8(
287                &device,
288                queue,
289                command_pool,
290                &mut allocator,
291                atlas_texture.width,
292                atlas_texture.height,
293                atlas_texture.data,
294            )?
295        };
296
297        let fonts = imgui.fonts();
298        fonts.tex_id = TextureId::from(usize::MAX);
299
300        // Descriptor pool
301        let descriptor_pool = create_vulkan_descriptor_pool(&device, 1)?;
302
303        // Descriptor set
304        let descriptor_set = create_vulkan_descriptor_set(
305            &device,
306            descriptor_set_layout,
307            descriptor_pool,
308            fonts_texture.image_view,
309            fonts_texture.sampler,
310        )?;
311
312        // Textures
313        let textures = Textures::new();
314
315        Ok(Self {
316            device,
317            allocator,
318            pipeline,
319            pipeline_layout,
320            descriptor_set_layout,
321            fonts_texture: Some(fonts_texture),
322            descriptor_pool,
323            descriptor_set,
324            textures,
325            options,
326            frames: None,
327        })
328    }
329
330    /// Change the render pass to render to.
331    ///
332    /// Useful if you need to render to a new render pass but don't want to rebuild
333    /// the entire renderer. It will rebuild the graphics pipeline from scratch so it
334    /// is an expensive operation.
335    ///
336    /// # Arguments
337    ///
338    /// * `render_pass` - The render pass used to render the gui.
339    ///
340    /// # Errors
341    ///
342    /// * [`RendererError`] - If any Vulkan error is encountered during pipeline creation.
343    #[cfg(not(feature = "dynamic-rendering"))]
344    pub fn set_render_pass(&mut self, render_pass: vk::RenderPass) -> RendererResult<()> {
345        unsafe { self.device.destroy_pipeline(self.pipeline, None) };
346        self.pipeline = create_vulkan_pipeline(
347            &self.device,
348            self.pipeline_layout,
349            render_pass,
350            self.options,
351        )?;
352        Ok(())
353    }
354
355    /// Returns the texture mapping used by the renderer to lookup textures.
356    ///
357    /// Textures are provided by the application as `vk::DescriptorSet`s.
358    ///
359    /// # Example
360    ///
361    /// ```ignore
362    /// let descriptor_set = ...;
363    /// // Insert a vk::DescriptorSet in the renderer textures map.
364    /// // The renderer returns a generated texture id.
365    /// let texture_id = renderer.textures().insert(descriptor_set);
366    /// ...
367    /// // Create an `Image` that references the texture by its id.
368    /// Image::new(texture_id, [100, 100]).build(&ui);
369    /// ```
370    ///
371    /// # Caveat
372    ///
373    /// Provided `vk::DescriptorSet`s must be created with a descriptor set layout that is compatible with the one used by the renderer.
374    /// See [Pipeline Layout Compatibility](https://www.khronos.org/registry/vulkan/specs/1.2-extensions/html/vkspec.html#descriptorsets-compatibility).
375    pub fn textures(&mut self) -> &mut Textures<vk::DescriptorSet> {
376        &mut self.textures
377    }
378
379    fn lookup_descriptor_set(&self, texture_id: TextureId) -> RendererResult<vk::DescriptorSet> {
380        if texture_id.id() == usize::MAX {
381            Ok(self.descriptor_set)
382        } else if let Some(descriptor_set) = self.textures.get(texture_id) {
383            Ok(*descriptor_set)
384        } else {
385            Err(RendererError::BadTexture(texture_id))
386        }
387    }
388
389    /// Update the fonts texture after having added new fonts to imgui.
390    ///
391    /// # Arguments
392    ///
393    /// * `queue` - A Vulkan queue.
394    ///             It will be used to submit commands during initialization to upload
395    ///             data to the gpu. The type of queue must be supported by the following
396    ///             commands: [vkCmdCopyBufferToImage](https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/vkCmdCopyBufferToImage.html),
397    ///             [vkCmdPipelineBarrier](https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/vkCmdPipelineBarrier.html)
398    /// * `command_pool` - A Vulkan command pool used to allocate command buffers to upload textures to the gpu.
399    /// * `imgui` - The imgui context.
400    ///
401    /// # Errors
402    ///
403    /// * [`RendererError`] - If any error is encountered during texture update.
404    pub fn update_fonts_texture(
405        &mut self,
406        queue: vk::Queue,
407        command_pool: vk::CommandPool,
408        imgui: &mut Context,
409    ) -> RendererResult<()> {
410        // Generate the new fonts texture
411        let fonts_texture = {
412            let fonts = imgui.fonts();
413            let atlas_texture = fonts.build_rgba32_texture();
414
415            Texture::from_rgba8(
416                &self.device,
417                queue,
418                command_pool,
419                &mut self.allocator,
420                atlas_texture.width,
421                atlas_texture.height,
422                atlas_texture.data,
423            )?
424        };
425
426        let fonts = imgui.fonts();
427        fonts.tex_id = TextureId::from(usize::MAX);
428
429        // Free Descriptor set the create a new one
430        let old_descriptor_set = self.descriptor_set;
431        unsafe {
432            self.device
433                .free_descriptor_sets(self.descriptor_pool, &[old_descriptor_set])?
434        };
435        self.descriptor_set = create_vulkan_descriptor_set(
436            &self.device,
437            self.descriptor_set_layout,
438            self.descriptor_pool,
439            fonts_texture.image_view,
440            fonts_texture.sampler,
441        )?;
442
443        // Free old fonts texture
444        let mut old_texture = self.fonts_texture.replace(fonts_texture);
445        if let Some(texture) = old_texture.take() {
446            texture.destroy(&self.device, &mut self.allocator)?;
447        }
448
449        Ok(())
450    }
451
452    /// Record commands required to render the gui.RendererError.
453    ///
454    /// # Arguments
455    ///
456    /// * `command_buffer` - The Vulkan command buffer that command will be recorded to.
457    /// * `draw_data` - A reference to the imgui `DrawData` containing rendering data.
458    ///
459    /// # Errors
460    ///
461    /// * [`RendererError`] - If any Vulkan error is encountered during command recording.
462    pub fn cmd_draw(
463        &mut self,
464        command_buffer: vk::CommandBuffer,
465        draw_data: &DrawData,
466    ) -> RendererResult<()> {
467        if draw_data.total_vtx_count == 0 {
468            return Ok(());
469        }
470
471        if self.frames.is_none() {
472            self.frames.replace(Frames::new(
473                &self.device,
474                &mut self.allocator,
475                draw_data,
476                self.options.in_flight_frames,
477            )?);
478        }
479
480        let mesh = self.frames.as_mut().unwrap().next();
481        mesh.update(&self.device, &mut self.allocator, draw_data)?;
482
483        unsafe {
484            self.device.cmd_bind_pipeline(
485                command_buffer,
486                vk::PipelineBindPoint::GRAPHICS,
487                self.pipeline,
488            )
489        };
490
491        let framebuffer_width = draw_data.framebuffer_scale[0] * draw_data.display_size[0];
492        let framebuffer_height = draw_data.framebuffer_scale[1] * draw_data.display_size[1];
493        let viewports = [vk::Viewport {
494            width: framebuffer_width,
495            height: framebuffer_height,
496            max_depth: 1.0,
497            ..Default::default()
498        }];
499
500        unsafe { self.device.cmd_set_viewport(command_buffer, 0, &viewports) };
501
502        // Ortho projection
503        let projection = orthographic_vk(
504            0.0,
505            draw_data.display_size[0],
506            0.0,
507            -draw_data.display_size[1],
508            -1.0,
509            1.0,
510        );
511        unsafe {
512            let push = any_as_u8_slice(&projection);
513            self.device.cmd_push_constants(
514                command_buffer,
515                self.pipeline_layout,
516                vk::ShaderStageFlags::VERTEX,
517                0,
518                push,
519            )
520        };
521
522        unsafe {
523            self.device.cmd_bind_index_buffer(
524                command_buffer,
525                mesh.indices,
526                0,
527                vk::IndexType::UINT16,
528            )
529        };
530
531        unsafe {
532            self.device
533                .cmd_bind_vertex_buffers(command_buffer, 0, &[mesh.vertices], &[0])
534        };
535
536        let mut index_offset = 0;
537        let mut vertex_offset = 0;
538        let mut current_texture_id: Option<TextureId> = None;
539        let clip_offset = draw_data.display_pos;
540        let clip_scale = draw_data.framebuffer_scale;
541        for draw_list in draw_data.draw_lists() {
542            for command in draw_list.commands() {
543                match command {
544                    DrawCmd::Elements {
545                        count,
546                        cmd_params:
547                            DrawCmdParams {
548                                clip_rect,
549                                texture_id,
550                                vtx_offset,
551                                idx_offset,
552                            },
553                    } => {
554                        unsafe {
555                            let clip_x = (clip_rect[0] - clip_offset[0]) * clip_scale[0];
556                            let clip_y = (clip_rect[1] - clip_offset[1]) * clip_scale[1];
557                            let clip_w = (clip_rect[2] - clip_offset[0]) * clip_scale[0] - clip_x;
558                            let clip_h = (clip_rect[3] - clip_offset[1]) * clip_scale[1] - clip_y;
559
560                            let scissors = [vk::Rect2D {
561                                offset: vk::Offset2D {
562                                    x: (clip_x as i32).max(0),
563                                    y: (clip_y as i32).max(0),
564                                },
565                                extent: vk::Extent2D {
566                                    width: clip_w as _,
567                                    height: clip_h as _,
568                                },
569                            }];
570                            self.device.cmd_set_scissor(command_buffer, 0, &scissors);
571                        }
572
573                        if Some(texture_id) != current_texture_id {
574                            let descriptor_set = self.lookup_descriptor_set(texture_id)?;
575                            unsafe {
576                                self.device.cmd_bind_descriptor_sets(
577                                    command_buffer,
578                                    vk::PipelineBindPoint::GRAPHICS,
579                                    self.pipeline_layout,
580                                    0,
581                                    &[descriptor_set],
582                                    &[],
583                                )
584                            };
585                            current_texture_id = Some(texture_id);
586                        }
587
588                        unsafe {
589                            self.device.cmd_draw_indexed(
590                                command_buffer,
591                                count as _,
592                                1,
593                                index_offset + idx_offset as u32,
594                                vertex_offset + vtx_offset as i32,
595                                0,
596                            )
597                        };
598                    }
599                    DrawCmd::ResetRenderState => {
600                        log::trace!("Reset render state command not yet supported")
601                    }
602                    DrawCmd::RawCallback { .. } => {
603                        log::trace!("Raw callback command not yet supported")
604                    }
605                }
606            }
607
608            index_offset += draw_list.idx_buffer().len() as u32;
609            vertex_offset += draw_list.vtx_buffer().len() as i32;
610        }
611
612        Ok(())
613    }
614}
615
616impl Drop for Renderer {
617    fn drop(&mut self) {
618        log::debug!("Destroying ImGui Renderer");
619        let device = &self.device;
620
621        unsafe {
622            if let Some(frames) = self.frames.take() {
623                frames
624                    .destroy(device, &mut self.allocator)
625                    .expect("Failed to destroy frame data");
626            }
627            device.destroy_pipeline(self.pipeline, None);
628            device.destroy_pipeline_layout(self.pipeline_layout, None);
629            device.destroy_descriptor_pool(self.descriptor_pool, None);
630            self.fonts_texture
631                .take()
632                .unwrap()
633                .destroy(device, &mut self.allocator)
634                .expect("Failed to fronts data");
635            device.destroy_descriptor_set_layout(self.descriptor_set_layout, None);
636        }
637    }
638}
639
640// Structure holding data for all frames in flight.
641struct Frames {
642    index: usize,
643    count: usize,
644    meshes: Vec<Mesh>,
645}
646
647impl Frames {
648    fn new(
649        device: &Device,
650        allocator: &mut Allocator,
651        draw_data: &DrawData,
652        count: usize,
653    ) -> RendererResult<Self> {
654        let meshes = (0..count)
655            .map(|_| Mesh::new(device, allocator, draw_data))
656            .collect::<Result<Vec<_>, _>>()?;
657        Ok(Self {
658            index: 0,
659            count,
660            meshes,
661        })
662    }
663
664    fn next(&mut self) -> &mut Mesh {
665        let result = &mut self.meshes[self.index];
666        self.index = (self.index + 1) % self.count;
667        result
668    }
669
670    fn destroy(self, device: &Device, allocator: &mut Allocator) -> RendererResult<()> {
671        for mesh in self.meshes.into_iter() {
672            mesh.destroy(device, allocator)?;
673        }
674        Ok(())
675    }
676}
677
678mod mesh {
679
680    use super::allocator::{Allocate, Allocator, Memory};
681    use super::vulkan::*;
682    use crate::RendererResult;
683    use ash::{vk, Device};
684    use imgui::{DrawData, DrawVert};
685    use std::mem::size_of;
686
687    /// Vertex and index buffer resources for one frame in flight.
688    pub struct Mesh {
689        pub vertices: vk::Buffer,
690        vertices_mem: Memory,
691        vertex_count: usize,
692        pub indices: vk::Buffer,
693        indices_mem: Memory,
694        index_count: usize,
695    }
696
697    impl Mesh {
698        pub fn new(
699            device: &Device,
700            allocator: &mut Allocator,
701            draw_data: &DrawData,
702        ) -> RendererResult<Self> {
703            let vertices = create_vertices(draw_data);
704            let vertex_count = vertices.len();
705            let indices = create_indices(draw_data);
706            let index_count = indices.len();
707
708            // Create a vertex buffer
709            let (vertices, vertices_mem) = create_and_fill_buffer(
710                device,
711                allocator,
712                &vertices,
713                vk::BufferUsageFlags::VERTEX_BUFFER,
714            )?;
715
716            // Create an index buffer
717            let (indices, indices_mem) = create_and_fill_buffer(
718                device,
719                allocator,
720                &indices,
721                vk::BufferUsageFlags::INDEX_BUFFER,
722            )?;
723
724            Ok(Mesh {
725                vertices,
726                vertices_mem,
727                vertex_count,
728                indices,
729                indices_mem,
730                index_count,
731            })
732        }
733
734        pub fn update(
735            &mut self,
736            device: &Device,
737            allocator: &mut Allocator,
738            draw_data: &DrawData,
739        ) -> RendererResult<()> {
740            let vertices = create_vertices(draw_data);
741            if draw_data.total_vtx_count as usize > self.vertex_count {
742                log::trace!("Resizing vertex buffers");
743
744                let vertex_count = vertices.len();
745                let size = vertex_count * size_of::<DrawVert>();
746                let (vertices, vertices_mem) =
747                    allocator.create_buffer(device, size, vk::BufferUsageFlags::VERTEX_BUFFER)?;
748
749                self.vertex_count = vertex_count;
750
751                let old_vertices = self.vertices;
752                self.vertices = vertices;
753
754                let old_vertices_mem = std::mem::replace(&mut self.vertices_mem, vertices_mem);
755
756                allocator.destroy_buffer(device, old_vertices, old_vertices_mem)?;
757            }
758            allocator.update_buffer(device, &mut self.vertices_mem, &vertices)?;
759
760            let indices = create_indices(draw_data);
761            if draw_data.total_idx_count as usize > self.index_count {
762                log::trace!("Resizing index buffers");
763
764                let index_count = indices.len();
765                let size = index_count * size_of::<u16>();
766                let (indices, indices_mem) =
767                    allocator.create_buffer(device, size, vk::BufferUsageFlags::INDEX_BUFFER)?;
768
769                self.index_count = index_count;
770
771                let old_indices = self.indices;
772                self.indices = indices;
773
774                let old_indices_mem = std::mem::replace(&mut self.indices_mem, indices_mem);
775
776                allocator.destroy_buffer(device, old_indices, old_indices_mem)?;
777            }
778            allocator.update_buffer(device, &mut self.indices_mem, &indices)?;
779
780            Ok(())
781        }
782
783        pub fn destroy(self, device: &Device, allocator: &mut Allocator) -> RendererResult<()> {
784            allocator.destroy_buffer(device, self.vertices, self.vertices_mem)?;
785            allocator.destroy_buffer(device, self.indices, self.indices_mem)?;
786            Ok(())
787        }
788    }
789
790    fn create_vertices(draw_data: &DrawData) -> Vec<DrawVert> {
791        let vertex_count = draw_data.total_vtx_count as usize;
792        let mut vertices = Vec::with_capacity(vertex_count);
793        for draw_list in draw_data.draw_lists() {
794            vertices.extend_from_slice(draw_list.vtx_buffer());
795        }
796        vertices
797    }
798
799    fn create_indices(draw_data: &DrawData) -> Vec<u16> {
800        let index_count = draw_data.total_idx_count as usize;
801        let mut indices = Vec::with_capacity(index_count);
802        for draw_list in draw_data.draw_lists() {
803            indices.extend_from_slice(draw_list.idx_buffer());
804        }
805        indices
806    }
807}