imgui_ash/
lib.rs

1//! Translation of the [vulkan backend](https://github.com/ocornut/imgui/blob/master/backends/imgui_impl_vulkan.cpp)
2//! of [Dear ImGui](https://github.com/ocornut/imgui) to [imgui-rs](imgui).
3//!
4//! Notable changes include the use of VMA through [vk_mem]
5//!
6//! To get started fill out a [`RendererCreateInfo`] and render in each frame
7//!
8//!
9//! ```rust
10//! use imgui_ash::{RendererCreateInfo, SurfaceColorFormat};
11//!
12//! # let your_queue_familiy = 2;
13//! # let your_queue = ash::vk::Queue::null();
14//! # let your_render_pass = ash::vk::RenderPass::null();
15//! # let your_subpass = 0;
16//! # let frames_in_flight = 2;
17//! # let your_msaa_samples = None;
18//! # let your_pipeline_cache = ash::vk::PipelineCache::null();
19//! # let your_n_textures = 2;
20//!
21//! let create_info = RendererCreateInfo::default()
22//!     .queue_family(your_queue_familiy)
23//!     .queue(your_queue)
24//!     .render_pass(your_render_pass)
25//!     .subpass(your_subpass);
26//!     .image_count(frames_in_flight)
27//!     .msaa_samples(your_msaa_samples)
28//!     .pipeline_cache(your_pipeline_cache)
29//!     .surface_color_fmt(Some(SurfaceColorFormat::Srgb))
30//!     .descriptor_pool_size(your_n_textures);
31//! ```
32//!
33//! You can then pass this create info along with device to [`Renderer::new`].
34//!
35//! Thereafter one can just call [`Renderer::new_frame`] and [`Renderer::render`]
36//! upon each frame.
37use ash::vk::{self, Handle};
38use imgui::internal::RawWrapper;
39use vk_mem::Alloc;
40
41use self::util::RaiiWrapper;
42
43mod fonts;
44mod init;
45mod util;
46
47pub const MIN_DESCRIPTOR_POOL_SIZE: u32 = 1;
48
49#[derive(Default, Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
50pub struct RendererCreateInfo {
51    /// The queue family of the device from which [`RendererCreateInfo::queue`]
52    /// was allocated from
53    pub queue_family: u32,
54    /// A queue on the passed n device with `GRAPHICS` capabilities.
55    pub queue: vk::Queue,
56    /// A render pass the created pipeline must be compatible with, only used
57    /// upon initialisation
58    pub render_pass: vk::RenderPass,
59    /// The subpass on the aforementioned [`RendererCreateInfo::render_pass`]
60    pub subpass: u32,
61    /// The number of frames in flight
62    pub image_count: u32,
63    /// Mutli sampling configuration for texture sampling
64    pub msaa_samples: Option<vk::SampleCountFlags>,
65    /// An optional pipeline cache
66    pub pipeline_cache: vk::PipelineCache,
67    /// Whether the outputting surface is linear or srgb logarithmic, generally
68    /// linear is desired and as such is the default
69    pub surface_color_fmt: SurfaceColorFormat,
70    /// The size of the descriptor pool managed by this renderer. Must be greater 
71    /// than [`MIN_DESCRIPTOR_POOL_SIZE`], any additional increment allows use 
72    /// of another custom texture. If you wanted to render 3 custom textures 
73    /// you would set this to `MIN_DESCRIPTOR_POOL_SIZE + 3` and add your 
74    /// textures with [`Renderer::add_texture`]
75    pub descriptor_pool_size: Option<u32>,
76}
77
78/// Whether the outputting surface has a linear output format or a logarithmic
79/// srgb output format, generally linear is desired and is as such the default
80#[derive(Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
81pub enum SurfaceColorFormat {
82    #[default]
83    Linear,
84    Srgb,
85}
86
87impl RendererCreateInfo {
88    /// See [`RendererCreateInfo`]
89    pub fn queue_family(mut self, queue_family: u32) -> Self {
90        self.queue_family = queue_family;
91        self
92    }
93
94    /// See [`RendererCreateInfo`]
95    pub fn queue(mut self, queue: vk::Queue) -> Self {
96        self.queue = queue;
97        self
98    }
99
100    /// See [`RendererCreateInfo`]
101    pub fn render_pass(mut self, render_pass: vk::RenderPass) -> Self {
102        self.render_pass = render_pass;
103        self
104    }
105
106    /// See [`RendererCreateInfo`]
107    pub fn subpass(mut self, subpass: u32) -> Self {
108        self.subpass = subpass;
109        self
110    }
111
112    /// See [`RendererCreateInfo`]
113    pub fn image_count(mut self, image_count: u32) -> Self {
114        self.image_count = image_count;
115        self
116    }
117
118    /// See [`RendererCreateInfo`]
119    pub fn msaa_samples(mut self, msaa_samples: Option<vk::SampleCountFlags>) -> Self {
120        self.msaa_samples = msaa_samples;
121        self
122    }
123
124    /// See [`RendererCreateInfo`]
125    pub fn pipeline_cache(mut self, pipeline_cache: vk::PipelineCache) -> Self {
126        self.pipeline_cache = pipeline_cache;
127        self
128    }
129
130    /// See [`RendererCreateInfo`]
131    pub fn surface_color_fmt(mut self, surface_color_fmt: SurfaceColorFormat) -> Self {
132        self.surface_color_fmt = surface_color_fmt;
133        self
134    }
135
136    /// See [`RendererCreateInfo`]
137    pub fn descriptor_pool_size(mut self, descriptor_pool_size: Option<u32>) -> Self {
138        self.descriptor_pool_size = descriptor_pool_size;
139        self
140    }
141}
142
143pub struct Renderer<A>
144where
145    A: vk_mem::Alloc,
146{
147    _instance: ash::Instance,
148    device: ash::Device,
149    create_info: RendererCreateInfo,
150    device_objects: DeviceObjects,
151    fonts_texture: Option<Texture>,
152    render_index: usize,
153    render_buffers: Vec<RenderBuffer>,
154
155    allocator: A,
156}
157
158struct Texture {
159    memory: vk_mem::Allocation,
160    image: vk::Image,
161    image_view: vk::ImageView,
162    descriptor_set: vk::DescriptorSet,
163}
164
165struct GpuBuffer {
166    buffer: vk::Buffer,
167    alloc: vk_mem::Allocation,
168    size: usize,
169}
170
171#[derive(Default)]
172struct RenderBuffer {
173    vertex: Option<GpuBuffer>,
174    index: Option<GpuBuffer>,
175}
176
177struct DeviceObjects {
178    tex_sampler: vk::Sampler,
179    descriptor_layout: vk::DescriptorSetLayout,
180    descriptor_pool: vk::DescriptorPool,
181    pipeline_layout: vk::PipelineLayout,
182    pipeline: vk::Pipeline,
183    vert_shader: vk::ShaderModule,
184    frag_shader: vk::ShaderModule,
185    tex_command_pool: vk::CommandPool,
186    tex_command_buffer: vk::CommandBuffer,
187    wait_fence: vk::Fence,
188}
189
190impl<A> Renderer<A>
191where
192    A: vk_mem::Alloc,
193{
194    /// Creates a new [`Renderer`]. For details, look at [`RendererCreateInfo`]
195    ///
196    /// # Safety
197    ///
198    /// - the `device`, `instance`, [`RendererCreateInfo::queue`], [`RendererCreateInfo::render_pass`] handles must be valid
199    /// - if [`RendererCreateInfo::pipeline_cache`] is not a null-handle the sampe applies
200    /// - [`RendererCreateInfo::queue`] must belong the the `device`
201    /// - the `device`, `instance`, [`RendererCreateInfo::queue`] and `allocator` must outlive the
202    ///   [`Renderer`]
203    /// - [`RendererCreateInfo::image_count`] must be greater than or equal to 2
204    /// - [`RendererCreateInfo::descriptor_pool_size`] must be greater than or equal to [`MIN_DESCRIPTOR_POOL_SIZE`] if
205    ///   supplied
206    pub unsafe fn new(
207        instance: &ash::Instance,
208        device: &ash::Device,
209        allocator: A,
210        create_info: &RendererCreateInfo,
211        context: &mut imgui::Context,
212    ) -> Result<Self, RendererCreateError> {
213        debug_assert!(
214            !instance.handle().is_null(),
215            "must pass in a valid instance (is null)"
216        );
217        debug_assert!(
218            !device.handle().is_null(),
219            "must pass in a valid device (is null)"
220        );
221        debug_assert!(
222            !create_info.queue.is_null(),
223            "must pass in a valid queue (is null)"
224        );
225        debug_assert!(
226            !create_info.render_pass.is_null(),
227            "must pass in a valid render pass (is null)"
228        );
229        debug_assert!(
230            create_info.image_count >= 2,
231            "must at least have two images, currently are {}",
232            create_info.image_count
233        );
234        debug_assert!(
235            create_info
236                .descriptor_pool_size
237                .is_none_or(|s| s >= MIN_DESCRIPTOR_POOL_SIZE),
238            "if descriptor pool size is set explicitly it needs to be bigger than MIN_DESCRIPTOR_POOL_SIZE"
239        );
240        let device_objects = unsafe { Self::create_device_objects(instance, device, create_info)? };
241        context
242            .io_mut()
243            .backend_flags
244            .insert(imgui::BackendFlags::RENDERER_HAS_VTX_OFFSET);
245        Ok(Self {
246            _instance: instance.clone(),
247            device: device.clone(),
248            allocator,
249            create_info: *create_info,
250            device_objects: device_objects.finalise(),
251            render_index: 0,
252            fonts_texture: None,
253            render_buffers: Vec::new(),
254        })
255    }
256
257    pub fn new_frame(&mut self, context: &mut imgui::Context) -> Result<(), FontsCreateError> {
258        if self.fonts_texture.is_some() {
259            return Ok(());
260        }
261        unsafe { self.create_fonts_texture(context) }
262    }
263
264    pub fn add_texture(
265        &self,
266        sampler: vk::Sampler,
267        view: vk::ImageView,
268        layout: vk::ImageLayout,
269    ) -> Result<vk::DescriptorSet, TextureError> {
270        let layouts = [self.device_objects.descriptor_layout];
271        let descriptor_set_info = vk::DescriptorSetAllocateInfo::default()
272            .descriptor_pool(self.device_objects.descriptor_pool)
273            .set_layouts(&layouts);
274
275        let descriptor_set = RaiiWrapper::new(
276            unsafe { self.device.allocate_descriptor_sets(&descriptor_set_info) }
277                .map_err(TextureError::DescriptorSetCreateError)?[0],
278            |ds| unsafe {
279                self.device
280                    .free_descriptor_sets(self.device_objects.descriptor_pool, &[ds])
281                    .expect("cannot fail, see spec");
282            },
283        );
284
285        let descriptor_image_info = [vk::DescriptorImageInfo::default()
286            .sampler(sampler)
287            .image_view(view)
288            .image_layout(layout)];
289
290        let write_desc = vk::WriteDescriptorSet::default()
291            .dst_set(*descriptor_set)
292            .descriptor_count(1)
293            .descriptor_type(vk::DescriptorType::COMBINED_IMAGE_SAMPLER)
294            .image_info(&descriptor_image_info);
295
296        unsafe {
297            self.device.update_descriptor_sets(&[write_desc], &[]);
298        }
299        Ok(descriptor_set.finalise())
300    }
301
302    /// # Safety
303    ///
304    /// - the descriptor_set must have been added with [`Renderer::add_texture`]
305    pub unsafe fn remove_texture(&self, descriptor_set: vk::DescriptorSet) {
306        unsafe {
307            self.device
308                .free_descriptor_sets(self.device_objects.descriptor_pool, &[descriptor_set])
309                .expect("cannot fail according to spec")
310        }
311    }
312
313    pub fn render(
314        &mut self,
315        draw_data: &imgui::DrawData,
316        command_buffer: vk::CommandBuffer,
317    ) -> Result<(), RendererError> {
318        let fb_width = draw_data.display_size[0] * draw_data.framebuffer_scale[0];
319        let fb_height = draw_data.display_size[1] * draw_data.framebuffer_scale[1];
320        if fb_width <= 0.0 || fb_height <= 0.0 {
321            return Ok(());
322        }
323
324        if self.render_buffers.is_empty() {
325            self.render_buffers
326                .resize_with(self.create_info.image_count as _, Default::default);
327        }
328
329        assert_eq!(
330            self.create_info.image_count,
331            self.render_buffers.len() as _,
332            "if image count has changed, call set_image_count"
333        );
334
335        let index = (self.render_index + 1) % self.render_buffers.len();
336        self.render_index = index;
337
338        if draw_data.total_vtx_count > 0 {
339            let vertex_size =
340                draw_data.total_vtx_count as usize * core::mem::size_of::<imgui::DrawVert>();
341            let idx_size =
342                draw_data.total_idx_count as usize * core::mem::size_of::<imgui::DrawIdx>();
343            create_or_update_buffer(
344                self.allocator.allocator(),
345                &mut self.render_buffers[index].vertex,
346                vertex_size,
347                vk::BufferUsageFlags::VERTEX_BUFFER,
348            )
349            .unwrap();
350            create_or_update_buffer(
351                self.allocator.allocator(),
352                &mut self.render_buffers[index].index,
353                idx_size,
354                vk::BufferUsageFlags::INDEX_BUFFER,
355            )
356            .unwrap();
357            {
358                let mut vtx_dst = unsafe {
359                    self.allocator
360                        .allocator()
361                        .map_memory(&mut self.render_buffers[index].vertex.as_mut().unwrap().alloc)
362                }
363                .map_err(RendererError::MmapError)? as *mut _;
364                let mut idx_dst = unsafe {
365                    self.allocator
366                        .allocator()
367                        .map_memory(&mut self.render_buffers[index].index.as_mut().unwrap().alloc)
368                }
369                .map_err(RendererError::MmapError)? as *mut _;
370
371                for list in draw_data.draw_lists() {
372                    unsafe {
373                        core::ptr::copy_nonoverlapping(
374                            list.vtx_buffer().as_ptr(),
375                            vtx_dst,
376                            list.vtx_buffer().len(),
377                        );
378                        core::ptr::copy_nonoverlapping(
379                            list.idx_buffer().as_ptr(),
380                            idx_dst,
381                            list.idx_buffer().len(),
382                        );
383                        vtx_dst = vtx_dst.add(list.vtx_buffer().len());
384                        idx_dst = idx_dst.add(list.idx_buffer().len());
385                    }
386                }
387
388                unsafe {
389                    self.allocator.allocator().unmap_memory(
390                        &mut self.render_buffers[index].vertex.as_mut().unwrap().alloc,
391                    );
392                    self.allocator.allocator().unmap_memory(
393                        &mut self.render_buffers[index].index.as_mut().unwrap().alloc,
394                    );
395
396                    self.allocator
397                        .allocator()
398                        .flush_allocations(
399                            [
400                                &self.render_buffers[index].vertex.as_ref().unwrap().alloc,
401                                &self.render_buffers[index].index.as_ref().unwrap().alloc,
402                            ],
403                            None,
404                            None,
405                        )
406                        .map_err(RendererError::FlushError)?;
407                }
408            }
409        }
410
411        unsafe {
412            self.setup_render_state(
413                draw_data,
414                self.device_objects.pipeline,
415                command_buffer,
416                &self.render_buffers[index],
417                fb_width,
418                fb_height,
419            )
420        };
421
422        let clip_off = draw_data.display_pos;
423        let clip_scale = draw_data.framebuffer_scale;
424
425        let mut global_vtx_offset = 0;
426        let mut global_idx_offset = 0;
427        if draw_data.draw_lists_count() > 0 {
428            for draw_list in draw_data.draw_lists() {
429                for draw_cmd in draw_list.commands() {
430                    match draw_cmd {
431                        imgui::DrawCmd::Elements { count, cmd_params } => {
432                            let mut clip_min = [
433                                (cmd_params.clip_rect[0] - clip_off[0]) * clip_scale[0],
434                                (cmd_params.clip_rect[1] - clip_off[1]) * clip_scale[1],
435                            ];
436                            let mut clip_max = [
437                                (cmd_params.clip_rect[2] - clip_off[0]) * clip_scale[0],
438                                (cmd_params.clip_rect[3] - clip_off[1]) * clip_scale[1],
439                            ];
440
441                            if clip_min[0] < 0.0 {
442                                clip_min[0] = 0.0;
443                            }
444                            if clip_min[1] < 0.0 {
445                                clip_min[1] = 0.0;
446                            }
447                            if clip_max[0] > fb_width {
448                                clip_max[0] = fb_width;
449                            }
450                            if clip_max[1] > fb_height {
451                                clip_max[1] = fb_height;
452                            }
453                            if clip_max[0] <= clip_min[0] || clip_max[1] <= clip_min[1] {
454                                continue;
455                            }
456
457                            let scissor = vk::Rect2D::default()
458                                .offset(
459                                    vk::Offset2D::default()
460                                        .x(clip_min[0] as _)
461                                        .y(clip_min[1] as _),
462                                )
463                                .extent(
464                                    vk::Extent2D::default()
465                                        .width((clip_max[0] - clip_min[0]) as _)
466                                        .height((clip_max[1] - clip_min[1]) as _),
467                                );
468
469                            let desc_set =
470                                vk::DescriptorSet::from_raw(cmd_params.texture_id.id() as _);
471                            unsafe {
472                                self.device.cmd_set_scissor(command_buffer, 0, &[scissor]);
473                                self.device.cmd_bind_descriptor_sets(
474                                    command_buffer,
475                                    vk::PipelineBindPoint::GRAPHICS,
476                                    self.device_objects.pipeline_layout,
477                                    0,
478                                    &[desc_set],
479                                    &[],
480                                );
481                                self.device.cmd_draw_indexed(
482                                    command_buffer,
483                                    count as _,
484                                    1,
485                                    (cmd_params.idx_offset + global_idx_offset) as _,
486                                    (cmd_params.vtx_offset + global_vtx_offset) as _,
487                                    0,
488                                );
489                            }
490                        }
491                        imgui::DrawCmd::ResetRenderState => unsafe {
492                            self.setup_render_state(
493                                draw_data,
494                                self.device_objects.pipeline,
495                                command_buffer,
496                                &self.render_buffers[index],
497                                fb_width,
498                                fb_height,
499                            );
500                        },
501                        imgui::DrawCmd::RawCallback { callback, raw_cmd } => unsafe {
502                            callback(draw_list.raw(), raw_cmd);
503                        },
504                    }
505                }
506                global_vtx_offset += draw_list.vtx_buffer().len();
507                global_idx_offset += draw_list.idx_buffer().len();
508            }
509        }
510        // Note: at this point both vkCmdSetViewport() and vkCmdSetScissor() have been called.
511        // Our last values will leak into user/application rendering IF:
512        // - Your app uses a pipeline with VK_DYNAMIC_STATE_VIEWPORT or VK_DYNAMIC_STATE_SCISSOR dynamic state
513        // - And you forgot to call vkCmdSetViewport() and vkCmdSetScissor() yourself to explicitly set that state.
514        // If you use VK_DYNAMIC_STATE_VIEWPORT or VK_DYNAMIC_STATE_SCISSOR you are responsible for setting the values before rendering.
515        // In theory we should aim to backup/restore those values but I am not sure this is possible.
516        // We perform a call to vkCmdSetScissor() to set back a full viewport which is likely to fix things for 99% users but technically this is not perfect. (See github imgui#4644)
517        let scissor = vk::Rect2D::default()
518            .offset(vk::Offset2D::default().x(0).y(0))
519            .extent(
520                vk::Extent2D::default()
521                    .width(fb_width as u32)
522                    .height(fb_height as u32),
523            );
524        unsafe {
525            self.device.cmd_set_scissor(command_buffer, 0, &[scissor]);
526        }
527        Ok(())
528    }
529
530    unsafe fn setup_render_state(
531        &self,
532        draw_data: &imgui::DrawData,
533        pipeline: vk::Pipeline,
534        command_buffer: vk::CommandBuffer,
535        _render_buffer: &RenderBuffer,
536        width: f32,
537        height: f32,
538    ) {
539        let device = &self.device;
540        unsafe {
541            device.cmd_bind_pipeline(command_buffer, vk::PipelineBindPoint::GRAPHICS, pipeline);
542        }
543
544        if draw_data.total_vtx_count > 0 {
545            unsafe {
546                device.cmd_bind_vertex_buffers(
547                    command_buffer,
548                    0,
549                    &[self.render_buffers[self.render_index]
550                        .vertex
551                        .as_ref()
552                        .unwrap()
553                        .buffer],
554                    &[0],
555                );
556                device.cmd_bind_index_buffer(
557                    command_buffer,
558                    self.render_buffers[self.render_index]
559                        .index
560                        .as_ref()
561                        .unwrap()
562                        .buffer,
563                    0,
564                    if core::mem::size_of::<imgui::DrawIdx>() == 2 {
565                        vk::IndexType::UINT16
566                    } else {
567                        vk::IndexType::UINT32
568                    },
569                );
570            }
571        }
572        {
573            let viewport = vk::Viewport::default()
574                .x(0.0)
575                .y(0.0)
576                .width(width)
577                .height(height)
578                .min_depth(0.0)
579                .max_depth(1.0);
580            unsafe {
581                device.cmd_set_viewport(command_buffer, 0, &[viewport]);
582            }
583        }
584
585        // setup scale and translation
586        // Our visible imgui space lies from draw_data->DisplayPps (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayPos is (0,0) for single viewport apps.
587        {
588            let scale = [
589                2.0 / draw_data.display_size[0],
590                2.0 / draw_data.display_size[1],
591            ];
592            let scale_bytes = bytemuck::cast_slice(&scale);
593            let translate = [
594                -1.0 - draw_data.display_pos[0] * scale[0],
595                -1.0 - draw_data.display_pos[1] * scale[1],
596            ];
597            let translate_bytes = bytemuck::cast_slice(&translate);
598
599            let is_srgb = [match self.create_info.surface_color_fmt {
600                SurfaceColorFormat::Srgb => vk::TRUE,
601                _ => vk::FALSE,
602            }];
603            unsafe {
604                #[allow(clippy::erasing_op)]
605                device.cmd_push_constants(
606                    command_buffer,
607                    self.device_objects.pipeline_layout,
608                    vk::ShaderStageFlags::VERTEX,
609                    core::mem::size_of::<f32>() as u32 * 0,
610                    scale_bytes,
611                );
612                device.cmd_push_constants(
613                    command_buffer,
614                    self.device_objects.pipeline_layout,
615                    vk::ShaderStageFlags::VERTEX,
616                    core::mem::size_of::<f32>() as u32 * 2,
617                    translate_bytes,
618                );
619                device.cmd_push_constants(
620                    command_buffer,
621                    self.device_objects.pipeline_layout,
622                    vk::ShaderStageFlags::VERTEX,
623                    core::mem::size_of::<f32>() as u32 * 4,
624                    bytemuck::cast_slice(&is_srgb),
625                );
626            }
627        }
628    }
629
630    unsafe fn destroy_device_objects(device: &ash::Device, device_objects: &DeviceObjects) {
631        unsafe {
632            device.destroy_command_pool(device_objects.tex_command_pool, None);
633            device.destroy_descriptor_pool(device_objects.descriptor_pool, None);
634            device.destroy_pipeline(device_objects.pipeline, None);
635            device.destroy_shader_module(device_objects.vert_shader, None);
636            device.destroy_shader_module(device_objects.frag_shader, None);
637            device.destroy_pipeline_layout(device_objects.pipeline_layout, None);
638            device.destroy_descriptor_set_layout(device_objects.descriptor_layout, None);
639            device.destroy_sampler(device_objects.tex_sampler, None);
640            device.destroy_fence(device_objects.wait_fence, None);
641        }
642    }
643}
644
645#[derive(Debug, thiserror::Error)]
646pub enum RendererCreateError {
647    #[error("failed to create texture sampler")]
648    SamplerCreateError(#[source] vk::Result),
649
650    #[error("failed to create descriptor set layout for sampling texture")]
651    DescriptorSetCreateError(#[source] vk::Result),
652
653    #[error("failed to create descriptor pool for sampling textures")]
654    DescriptorPoolCreateError(#[source] vk::Result),
655
656    #[error("failed to create pipeline layout")]
657    PipelineLayoutCreateError(#[source] vk::Result),
658
659    #[error("failed to create shader modules")]
660    ShaderModuleCreateError(#[source] vk::Result),
661
662    #[error("failed to create graphics pipeline")]
663    PipelineCreateError(#[source] vk::Result),
664
665    #[error("failed to create command pool for texture transfer")]
666    TexCommandPoolCreateError(#[source] vk::Result),
667
668    #[error("failed to create command buffer for texture transfer")]
669    TexCommandBufferAllocError(#[source] vk::Result),
670
671    #[error("failed to allocate synchronisation objects")]
672    SyncobjCreateError(#[source] vk::Result),
673}
674
675#[derive(thiserror::Error, Debug)]
676pub enum FontsCreateError {
677    #[error("failed to reset command pool for texture transfer")]
678    CommandPoolResetError(#[source] vk::Result),
679
680    #[error("failed to begin command buffer for texture transfer")]
681    CommandBeginError(#[source] vk::Result),
682
683    #[error("failed to allocate image for font atlas texture")]
684    ImageAllocError(#[source] vk::Result),
685
686    #[error("failed to create image view for font atlas texture")]
687    ImageViewCreateError(#[source] vk::Result),
688
689    #[error("failed to create texture for font atlas")]
690    TextureCreateError(
691        #[source]
692        #[from]
693        TextureError,
694    ),
695
696    #[error("failed to create image transfer buffer for copying atlas into image")]
697    TransferbufferCreateError(#[source] vk::Result),
698
699    #[error("failed to map memory for copy operation")]
700    MmapError(#[source] vk::Result),
701
702    #[error("failed to flush allocated region after copy")]
703    FlushError(#[source] vk::Result),
704
705    #[error("failed to end command buffer recording for transfer operation")]
706    CommandEndError(#[source] vk::Result),
707
708    #[error("failed to submit copy operation to queue")]
709    SubmitError(#[source] vk::Result),
710}
711
712#[derive(thiserror::Error, Debug)]
713pub enum TextureError {
714    #[error("failed to create descriptor set for texture")]
715    DescriptorSetCreateError(#[source] vk::Result),
716}
717
718#[derive(thiserror::Error, Debug)]
719pub enum RendererError {
720    #[error("failed to map memory for vertex or index copy")]
721    MmapError(#[source] vk::Result),
722
723    #[error("flushing allocations for copied vertex data failed")]
724    FlushError(#[source] vk::Result),
725}
726
727fn create_or_update_buffer(
728    allocator: &vk_mem::Allocator,
729    buffer: &mut Option<GpuBuffer>,
730    new_size: usize,
731    usage: vk::BufferUsageFlags,
732) -> ash::prelude::VkResult<()> {
733    if let Some(buf) = buffer {
734        if buf.size >= new_size {
735            return Ok(());
736        }
737        unsafe { allocator.destroy_buffer(buf.buffer, &mut buf.alloc) };
738    }
739    *buffer = None;
740
741    let buffer_info = vk::BufferCreateInfo::default()
742        .size(new_size as _)
743        .usage(usage)
744        .sharing_mode(vk::SharingMode::EXCLUSIVE);
745
746    let alloc_info = vk_mem::AllocationCreateInfo {
747        usage: vk_mem::MemoryUsage::AutoPreferDevice,
748        flags: vk_mem::AllocationCreateFlags::HOST_ACCESS_SEQUENTIAL_WRITE,
749        ..Default::default()
750    };
751
752    let (buf, alloc) = unsafe { allocator.create_buffer(&buffer_info, &alloc_info) }?;
753    *buffer = Some(GpuBuffer {
754        buffer: buf,
755        alloc,
756        size: new_size,
757    });
758    Ok(())
759}
760
761impl<A> Drop for Renderer<A>
762where
763    A: vk_mem::Alloc,
764{
765    fn drop(&mut self) {
766        unsafe {
767            for buffer in self.render_buffers.drain(..) {
768                if let Some(GpuBuffer {
769                    buffer, mut alloc, ..
770                }) = buffer.vertex
771                {
772                    self.allocator
773                        .allocator()
774                        .destroy_buffer(buffer, &mut alloc);
775                }
776                if let Some(GpuBuffer {
777                    buffer, mut alloc, ..
778                }) = buffer.index
779                {
780                    self.allocator
781                        .allocator()
782                        .destroy_buffer(buffer, &mut alloc);
783                }
784            }
785            self.destroy_fonts_texture(None);
786            Self::destroy_device_objects(&self.device, &self.device_objects);
787        }
788    }
789}