egui_ash_renderer/renderer/
mod.rs

1mod allocator;
2pub mod vulkan;
3
4use std::collections::HashMap;
5
6use crate::RendererError;
7use ash::{Device, vk};
8use egui::{
9    ClippedPrimitive, ImageData, TextureId,
10    epaint::{ImageDelta, Primitive},
11};
12use mesh::*;
13use vulkan::*;
14
15use self::allocator::Allocator;
16
17#[cfg(not(any(feature = "gpu-allocator", feature = "vk-mem")))]
18use ash::Instance;
19
20#[cfg(feature = "gpu-allocator")]
21use {
22    gpu_allocator::vulkan::Allocator as GpuAllocator,
23    std::sync::{Arc, Mutex},
24};
25
26#[cfg(feature = "vk-mem")]
27use {std::sync::Arc, vk_mem::Allocator as VkMemAllocator};
28
29/// Convenient return type for function that can return a [`RendererError`].
30///
31/// [`RendererError`]: enum.RendererError.html
32pub type RendererResult<T> = Result<T, RendererError>;
33
34const MAX_TEXTURE_COUNT: u32 = 1024; // TODO: constant max size or user defined ?
35
36/// Optional parameters of the renderer.
37#[derive(Debug, Clone, Copy)]
38pub struct Options {
39    /// The number of in flight frames of the application.
40    pub in_flight_frames: usize,
41    /// If true enables depth test when rendering.
42    pub enable_depth_test: bool,
43    /// If true enables depth writes when rendering.
44    ///
45    /// Note that depth writes are always disabled when enable_depth_test is false.
46    /// See <https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/VkPipelineDepthStencilStateCreateInfo.html>
47    pub enable_depth_write: bool,
48    /// Is the target framebuffer sRGB.
49    ///
50    /// If not, the fragment shader converts colors to sRGB, otherwise it outputs color in linear space.
51    pub srgb_framebuffer: bool,
52}
53
54impl Default for Options {
55    fn default() -> Self {
56        Self {
57            in_flight_frames: 1,
58            enable_depth_test: false,
59            enable_depth_write: false,
60            srgb_framebuffer: false,
61        }
62    }
63}
64
65/// `dynamic-rendering` feature related params
66#[cfg(feature = "dynamic-rendering")]
67#[derive(Debug, Clone, Copy)]
68pub struct DynamicRendering {
69    pub color_attachment_format: vk::Format,
70    pub depth_attachment_format: Option<vk::Format>,
71}
72
73/// Vulkan renderer for egui.
74///
75/// It records rendering command to the provided command buffer at each call to [`cmd_draw`].
76///
77/// The renderer holds a set of vertex/index buffers per in flight frames. Vertex and index buffers
78/// are resized at each call to [`cmd_draw`] if draw data does not fit.
79///
80/// [`cmd_draw`]: #method.cmd_draw
81pub struct Renderer {
82    device: Device,
83    allocator: Allocator,
84    pipeline: vk::Pipeline,
85    pipeline_layout: vk::PipelineLayout,
86    descriptor_set_layout: vk::DescriptorSetLayout,
87    descriptor_pool: vk::DescriptorPool,
88    managed_textures: HashMap<TextureId, Texture>,
89    textures: HashMap<TextureId, vk::DescriptorSet>,
90    next_user_texture_id: u64,
91    options: Options,
92    frames: Option<Frames>,
93}
94
95impl Renderer {
96    /// Create a renderer using the default allocator.
97    ///
98    /// At initialization all Vulkan resources are initialized. Vertex and index buffers are not created yet.
99    ///
100    /// # Arguments
101    ///
102    /// * `instance` - A reference to a Vulkan instance.
103    /// * `physical_device` - A Vulkan physical device.
104    /// * `device` - A Vulkan device.
105    /// * `render_pass` - *without dynamic-rendering feature* - The render pass used to render the gui.
106    /// * `dynamic_rendering` - *with dynamic-rendering feature* - Dynamic rendeing parameters
107    /// * `options` - Rendering options.
108    ///
109    /// # Errors
110    ///
111    /// * [`RendererError`] - If the number of in flight frame in incorrect.
112    /// * [`RendererError`] - If any Vulkan or io error is encountered during initialization.
113    #[cfg(not(any(feature = "gpu-allocator", feature = "vk-mem")))]
114    pub fn with_default_allocator(
115        instance: &Instance,
116        physical_device: vk::PhysicalDevice,
117        device: Device,
118        #[cfg(not(feature = "dynamic-rendering"))] render_pass: vk::RenderPass,
119        #[cfg(feature = "dynamic-rendering")] dynamic_rendering: DynamicRendering,
120        options: Options,
121    ) -> RendererResult<Self> {
122        let memory_properties =
123            unsafe { instance.get_physical_device_memory_properties(physical_device) };
124
125        Self::from_allocator(
126            device,
127            Allocator::new(memory_properties),
128            #[cfg(not(feature = "dynamic-rendering"))]
129            render_pass,
130            #[cfg(feature = "dynamic-rendering")]
131            dynamic_rendering,
132            options,
133        )
134    }
135
136    /// Create a renderer using gpu-allocator.
137    ///
138    /// At initialization all Vulkan resources are initialized. Vertex and index buffers are not created yet.
139    ///
140    /// # Arguments
141    ///
142    /// * `gpu_allocator` - The allocator that will be used to allocator buffer and image memory.
143    /// * `device` - A Vulkan device.
144    /// * `render_pass` - *without dynamic-rendering feature* - The render pass used to render the gui.
145    /// * `dynamic_rendering` - *with dynamic-rendering feature* - Dynamic rendeing parameters
146    /// * `options` - Rendering options.
147    ///
148    /// # Errors
149    ///
150    /// * [`RendererError`] - If the number of in flight frame in incorrect.
151    /// * [`RendererError`] - If any Vulkan or io error is encountered during initialization.
152    #[cfg(feature = "gpu-allocator")]
153    pub fn with_gpu_allocator(
154        gpu_allocator: Arc<Mutex<GpuAllocator>>,
155        device: Device,
156        #[cfg(not(feature = "dynamic-rendering"))] render_pass: vk::RenderPass,
157        #[cfg(feature = "dynamic-rendering")] dynamic_rendering: DynamicRendering,
158        options: Options,
159    ) -> RendererResult<Self> {
160        Self::from_allocator(
161            device,
162            Allocator::new(gpu_allocator),
163            #[cfg(not(feature = "dynamic-rendering"))]
164            render_pass,
165            #[cfg(feature = "dynamic-rendering")]
166            dynamic_rendering,
167            options,
168        )
169    }
170
171    /// Create a renderer using vk-mem.
172    ///
173    /// At initialization all Vulkan resources are initialized. Vertex and index buffers are not created yet.
174    ///
175    /// # Arguments
176    ///
177    /// * `vk_mem_allocator` - The allocator that will be used to allocator buffer and image memory.
178    /// * `device` - A Vulkan device.
179    /// * `render_pass` - *without dynamic-rendering feature* - The render pass used to render the gui.
180    /// * `dynamic_rendering` - *with dynamic-rendering feature* - Dynamic rendeing parameters
181    /// * `options` - Rendering options.
182    ///
183    /// # Errors
184    ///
185    /// * [`RendererError`] - If the number of in flight frame in incorrect.
186    /// * [`RendererError`] - If any Vulkan or io error is encountered during initialization.
187    #[cfg(feature = "vk-mem")]
188    pub fn with_vk_mem_allocator(
189        vk_mem_allocator: Arc<VkMemAllocator>,
190        device: Device,
191        #[cfg(not(feature = "dynamic-rendering"))] render_pass: vk::RenderPass,
192        #[cfg(feature = "dynamic-rendering")] dynamic_rendering: DynamicRendering,
193        options: Options,
194    ) -> RendererResult<Self> {
195        Self::from_allocator(
196            device,
197            Allocator::new(vk_mem_allocator),
198            #[cfg(not(feature = "dynamic-rendering"))]
199            render_pass,
200            #[cfg(feature = "dynamic-rendering")]
201            dynamic_rendering,
202            options,
203        )
204    }
205
206    fn from_allocator(
207        device: Device,
208        allocator: Allocator,
209        #[cfg(not(feature = "dynamic-rendering"))] render_pass: vk::RenderPass,
210        #[cfg(feature = "dynamic-rendering")] dynamic_rendering: DynamicRendering,
211        options: Options,
212    ) -> RendererResult<Self> {
213        log::debug!("Creating egui renderer with options {options:?}");
214
215        if options.in_flight_frames == 0 {
216            return Err(RendererError::Init(String::from(
217                "'in_flight_frames' parameter should be at least one",
218            )));
219        }
220
221        // Descriptor set layout
222        let descriptor_set_layout = create_vulkan_descriptor_set_layout(&device)?;
223
224        // Pipeline and layout
225        let pipeline_layout = create_vulkan_pipeline_layout(&device, descriptor_set_layout)?;
226        let pipeline = create_vulkan_pipeline(
227            &device,
228            pipeline_layout,
229            #[cfg(not(feature = "dynamic-rendering"))]
230            render_pass,
231            #[cfg(feature = "dynamic-rendering")]
232            dynamic_rendering,
233            options,
234        )?;
235
236        // Descriptor pool
237        let descriptor_pool = create_vulkan_descriptor_pool(&device, MAX_TEXTURE_COUNT)?;
238
239        // Textures
240        let managed_textures = HashMap::new();
241        let textures = HashMap::new();
242
243        Ok(Self {
244            device,
245            allocator,
246            pipeline,
247            pipeline_layout,
248            descriptor_set_layout,
249            descriptor_pool,
250            managed_textures,
251            next_user_texture_id: 0,
252            textures,
253            options,
254            frames: None,
255        })
256    }
257
258    /// Change the render pass to render to.
259    ///
260    /// Useful if you need to render to a new render pass.
261    /// It will rebuild the graphics pipeline from scratch so it is an expensive operation.
262    ///
263    /// # Arguments
264    ///
265    /// * `render_pass` - The render pass used to render the gui.
266    ///
267    /// # Errors
268    ///
269    /// * [`RendererError`] - If any Vulkan error is encountered during pipeline creation.
270    #[cfg(not(feature = "dynamic-rendering"))]
271    pub fn set_render_pass(&mut self, render_pass: vk::RenderPass) -> RendererResult<()> {
272        unsafe { self.device.destroy_pipeline(self.pipeline, None) };
273        self.pipeline = create_vulkan_pipeline(
274            &self.device,
275            self.pipeline_layout,
276            render_pass,
277            self.options,
278        )?;
279        Ok(())
280    }
281
282    /// Change the dynamic rendering parameters.
283    ///
284    /// Useful if you need to render to a target of with another color/depth format.
285    /// It will rebuild the graphics pipeline from scratch so it is an expensive operation.
286    ///
287    /// # Arguments
288    ///
289    /// * `dynamic_rendering` - The new dynamic rendering parameters.
290    ///
291    /// # Errors
292    ///
293    /// * [`RendererError`] - If any Vulkan error is encountered during pipeline creation.
294    #[cfg(feature = "dynamic-rendering")]
295    pub fn set_dynamic_rendering(
296        &mut self,
297        dynamic_rendering: DynamicRendering,
298    ) -> RendererResult<()> {
299        unsafe { self.device.destroy_pipeline(self.pipeline, None) };
300        self.pipeline = create_vulkan_pipeline(
301            &self.device,
302            self.pipeline_layout,
303            dynamic_rendering,
304            self.options,
305        )?;
306        Ok(())
307    }
308
309    /// Free egui managed textures.
310    ///
311    /// You should pass the list of textures detla contained in the [`egui::TexturesDelta::set`].
312    /// This method should be called _before_ the frame starts rendering.
313    ///
314    /// # Arguments
315    ///
316    /// * `ids` - The list of ids of textures to free.
317    /// * `queue` - The queue used to copy image data on the GPU.
318    /// * `command_pool` - A Vulkan command pool used to allocate command buffers to upload textures to the gpu.
319    /// * `textures_delta` - The modifications to apply to the textures.
320    /// # Errors
321    ///
322    /// * [`RendererError`] - If any Vulkan error is encountered during pipeline creation.
323    pub fn set_textures(
324        &mut self,
325        queue: vk::Queue,
326        command_pool: vk::CommandPool,
327        textures_delta: &[(TextureId, ImageDelta)],
328    ) -> RendererResult<()> {
329        log::trace!("Setting {} textures", textures_delta.len());
330        for (id, delta) in textures_delta {
331            let (width, height, data) = match &delta.image {
332                ImageData::Color(image) => {
333                    let w = image.width() as u32;
334                    let h = image.height() as u32;
335                    let data = image
336                        .pixels
337                        .iter()
338                        .flat_map(|c| c.to_array())
339                        .collect::<Vec<_>>();
340
341                    (w, h, data)
342                }
343            };
344
345            if let Some([offset_x, offset_y]) = delta.pos {
346                log::trace!("Updating texture {id:?}");
347
348                let texture = self
349                    .managed_textures
350                    .get_mut(id)
351                    .ok_or(RendererError::BadTexture(*id))?;
352
353                texture.update(
354                    &self.device,
355                    queue,
356                    command_pool,
357                    &mut self.allocator,
358                    vk::Rect2D {
359                        offset: vk::Offset2D {
360                            x: offset_x as _,
361                            y: offset_y as _,
362                        },
363                        extent: vk::Extent2D { width, height },
364                    },
365                    data.as_slice(),
366                )?;
367            } else {
368                log::trace!("Adding texture {id:?}");
369
370                let texture = Texture::from_rgba8(
371                    &self.device,
372                    queue,
373                    command_pool,
374                    &mut self.allocator,
375                    width,
376                    height,
377                    data.as_slice(),
378                )?;
379
380                let set = create_vulkan_descriptor_set(
381                    &self.device,
382                    self.descriptor_set_layout,
383                    self.descriptor_pool,
384                    texture.image_view,
385                    texture.sampler,
386                )?;
387
388                if let Some(previous) = self.managed_textures.insert(*id, texture) {
389                    previous.destroy(&self.device, &mut self.allocator)?;
390                }
391                if let Some(previous) = self.textures.insert(*id, set) {
392                    unsafe {
393                        self.device
394                            .free_descriptor_sets(self.descriptor_pool, &[previous])?
395                    };
396                }
397            }
398        }
399
400        Ok(())
401    }
402
403    /// Free egui managed textures.
404    ///
405    /// You should pass the list of ids contained in the [`egui::TexturesDelta::free`].
406    /// This method should be called _after_ the frame is done rendering.
407    ///
408    /// # Arguments
409    ///
410    /// * `ids` - The list of ids of textures to free.
411    ///
412    /// # Errors
413    ///
414    /// * [`RendererError`] - If any Vulkan error is encountered when free the texture.
415    pub fn free_textures(&mut self, ids: &[TextureId]) -> RendererResult<()> {
416        log::trace!("Freeing {} textures", ids.len());
417        for id in ids {
418            if let Some(texture) = self.managed_textures.remove(id) {
419                texture.destroy(&self.device, &mut self.allocator)?;
420            }
421            if let Some(set) = self.textures.remove(id) {
422                unsafe {
423                    self.device
424                        .free_descriptor_sets(self.descriptor_pool, &[set])?
425                };
426            }
427        }
428
429        Ok(())
430    }
431
432    /// Add a user managed texture used by egui.
433    ///
434    /// The descriptors set passed in this method are managed by the used and *will not* be freed by the renderer.
435    /// This method will return a [`egui::TextureId`] which can then be used in a [`egui::Image`].
436    ///
437    /// # Arguments
438    ///
439    /// * `set` - The descpritor set referencing the texture to display.
440    ///
441    /// # Caveat
442    ///
443    /// Provided `vk::DescriptorSet`s must be created with a descriptor set layout that is compatible with the one used by the renderer.
444    /// See [Pipeline Layout Compatibility](https://www.khronos.org/registry/vulkan/specs/1.2-extensions/html/vkspec.html#descriptorsets-compatibility).
445    pub fn add_user_texture(&mut self, set: vk::DescriptorSet) -> TextureId {
446        let id = TextureId::User(self.next_user_texture_id);
447        self.next_user_texture_id += 1;
448        self.textures.insert(id, set);
449
450        id
451    }
452
453    /// Remove a user managed texture.
454    ///
455    /// This *does not* free the resources, it just _forgets_ about the texture.
456    ///
457    /// # Arguments
458    ///
459    /// * `id` - The id of the texture to remove.
460    pub fn remove_user_texture(&mut self, id: TextureId) {
461        self.textures.remove(&id);
462    }
463
464    /// Record commands to render the [`egui::Ui`].
465    ///
466    /// # Arguments
467    ///
468    /// * `command_buffer` - The Vulkan command buffer that command will be recorded to.
469    /// * `extent` - The extent of the surface to render to.
470    /// * `pixel_per_point` - The number of physical pixels per point. See [`egui::FullOutput::pixels_per_point`].
471    /// * `primitives` - The primitives to render. See [`egui::Context::tessellate`].
472    ///
473    /// # Errors
474    ///
475    /// * [`RendererError`] - If any Vulkan error is encountered when recording.
476    pub fn cmd_draw(
477        &mut self,
478        command_buffer: vk::CommandBuffer,
479        extent: vk::Extent2D,
480        pixels_per_point: f32,
481        primitives: &[ClippedPrimitive],
482    ) -> RendererResult<()> {
483        if primitives.is_empty() {
484            return Ok(());
485        }
486
487        if self.frames.is_none() {
488            self.frames.replace(Frames::new(
489                &self.device,
490                &mut self.allocator,
491                primitives,
492                self.options.in_flight_frames,
493            )?);
494        }
495
496        let mesh = self.frames.as_mut().unwrap().next();
497        mesh.update(&self.device, &mut self.allocator, primitives)?;
498
499        unsafe {
500            self.device.cmd_bind_pipeline(
501                command_buffer,
502                vk::PipelineBindPoint::GRAPHICS,
503                self.pipeline,
504            )
505        };
506
507        let screen_width = extent.width as f32;
508        let screen_height = extent.height as f32;
509
510        unsafe {
511            self.device.cmd_set_viewport(
512                command_buffer,
513                0,
514                &[vk::Viewport {
515                    width: screen_width,
516                    height: screen_height,
517                    max_depth: 1.0,
518                    ..Default::default()
519                }],
520            )
521        };
522
523        // Ortho projection
524        let projection = orthographic_vk(
525            0.0,
526            screen_width / pixels_per_point,
527            0.0,
528            -(screen_height / pixels_per_point),
529            -1.0,
530            1.0,
531        );
532        unsafe {
533            let push = any_as_u8_slice(&projection);
534            self.device.cmd_push_constants(
535                command_buffer,
536                self.pipeline_layout,
537                vk::ShaderStageFlags::VERTEX,
538                0,
539                push,
540            )
541        };
542
543        unsafe {
544            self.device.cmd_bind_index_buffer(
545                command_buffer,
546                mesh.indices,
547                0,
548                vk::IndexType::UINT32,
549            )
550        };
551
552        unsafe {
553            self.device
554                .cmd_bind_vertex_buffers(command_buffer, 0, &[mesh.vertices], &[0])
555        };
556
557        let mut index_offset = 0u32;
558        let mut vertex_offset = 0i32;
559        let mut current_texture_id: Option<TextureId> = None;
560
561        for p in primitives {
562            let clip_rect = p.clip_rect;
563            match &p.primitive {
564                Primitive::Mesh(m) => {
565                    let clip_x = clip_rect.min.x * pixels_per_point;
566                    let clip_y = clip_rect.min.y * pixels_per_point;
567                    let clip_w = clip_rect.max.x * pixels_per_point - clip_x;
568                    let clip_h = clip_rect.max.y * pixels_per_point - clip_y;
569
570                    let scissors = [vk::Rect2D {
571                        offset: vk::Offset2D {
572                            x: (clip_x as i32).max(0),
573                            y: (clip_y as i32).max(0),
574                        },
575                        extent: vk::Extent2D {
576                            width: clip_w.min(screen_width) as _,
577                            height: clip_h.min(screen_height) as _,
578                        },
579                    }];
580
581                    unsafe {
582                        self.device.cmd_set_scissor(command_buffer, 0, &scissors);
583                    }
584
585                    if Some(m.texture_id) != current_texture_id {
586                        let descriptor_set = *self
587                            .textures
588                            .get(&m.texture_id)
589                            .ok_or(RendererError::BadTexture(m.texture_id))?;
590
591                        unsafe {
592                            self.device.cmd_bind_descriptor_sets(
593                                command_buffer,
594                                vk::PipelineBindPoint::GRAPHICS,
595                                self.pipeline_layout,
596                                0,
597                                &[descriptor_set],
598                                &[],
599                            )
600                        };
601                        current_texture_id = Some(m.texture_id);
602                    }
603
604                    let index_count = m.indices.len() as u32;
605                    unsafe {
606                        self.device.cmd_draw_indexed(
607                            command_buffer,
608                            index_count,
609                            1,
610                            index_offset,
611                            vertex_offset,
612                            0,
613                        )
614                    };
615
616                    index_offset += index_count;
617                    vertex_offset += m.vertices.len() as i32;
618                }
619                Primitive::Callback(_) => {
620                    log::warn!("Callback primitives not yet supported")
621                }
622            }
623        }
624
625        Ok(())
626    }
627}
628
629impl Drop for Renderer {
630    fn drop(&mut self) {
631        log::debug!("Destroying egui renderer");
632        let device = &self.device;
633
634        unsafe {
635            if let Some(frames) = self.frames.take() {
636                frames
637                    .destroy(device, &mut self.allocator)
638                    .expect("Failed to destroy frame data");
639            }
640            device.destroy_pipeline(self.pipeline, None);
641            device.destroy_pipeline_layout(self.pipeline_layout, None);
642            device.destroy_descriptor_pool(self.descriptor_pool, None);
643
644            for (_, t) in self.managed_textures.drain() {
645                t.destroy(device, &mut self.allocator)
646                    .expect("Failed to destroy texture");
647            }
648            device.destroy_descriptor_set_layout(self.descriptor_set_layout, None);
649        }
650    }
651}
652
653// Structure holding data for all frames in flight.
654struct Frames {
655    index: usize,
656    count: usize,
657    meshes: Vec<Mesh>,
658}
659
660impl Frames {
661    fn new(
662        device: &Device,
663        allocator: &mut Allocator,
664        primitives: &[ClippedPrimitive],
665        count: usize,
666    ) -> RendererResult<Self> {
667        let meshes = (0..count)
668            .map(|_| Mesh::new(device, allocator, primitives))
669            .collect::<Result<Vec<_>, _>>()?;
670        Ok(Self {
671            index: 0,
672            count,
673            meshes,
674        })
675    }
676
677    fn next(&mut self) -> &mut Mesh {
678        let result = &mut self.meshes[self.index];
679        self.index = (self.index + 1) % self.count;
680        result
681    }
682
683    fn destroy(self, device: &Device, allocator: &mut Allocator) -> RendererResult<()> {
684        for mesh in self.meshes.into_iter() {
685            mesh.destroy(device, allocator)?;
686        }
687        Ok(())
688    }
689}
690
691mod mesh {
692
693    use super::allocator::{Allocate, Allocator, Memory};
694    use super::vulkan::*;
695    use crate::RendererResult;
696    use ash::{Device, vk};
697    use egui::ClippedPrimitive;
698    use egui::epaint::{Primitive, Vertex};
699    use std::mem::size_of;
700
701    /// Vertex and index buffer resources for one frame in flight.
702    pub struct Mesh {
703        pub vertices: vk::Buffer,
704        vertices_mem: Memory,
705        vertex_count: usize,
706        pub indices: vk::Buffer,
707        indices_mem: Memory,
708        index_count: usize,
709    }
710
711    impl Mesh {
712        pub fn new(
713            device: &Device,
714            allocator: &mut Allocator,
715            primitives: &[ClippedPrimitive],
716        ) -> RendererResult<Self> {
717            let vertices = create_vertices(primitives);
718            let vertex_count = vertices.len();
719            let indices = create_indices(primitives);
720            let index_count = indices.len();
721
722            // Create a vertex buffer
723            let (vertices, vertices_mem) = create_and_fill_buffer(
724                device,
725                allocator,
726                &vertices,
727                vk::BufferUsageFlags::VERTEX_BUFFER,
728            )?;
729
730            // Create an index buffer
731            let (indices, indices_mem) = create_and_fill_buffer(
732                device,
733                allocator,
734                &indices,
735                vk::BufferUsageFlags::INDEX_BUFFER,
736            )?;
737
738            Ok(Mesh {
739                vertices,
740                vertices_mem,
741                vertex_count,
742                indices,
743                indices_mem,
744                index_count,
745            })
746        }
747
748        pub fn update(
749            &mut self,
750            device: &Device,
751            allocator: &mut Allocator,
752            primitives: &[ClippedPrimitive],
753        ) -> RendererResult<()> {
754            let vertices = create_vertices(primitives);
755            if vertices.len() > self.vertex_count {
756                log::trace!("Resizing vertex buffers");
757
758                let vertex_count = vertices.len();
759                let size = vertex_count * size_of::<Vertex>();
760                let (vertices, vertices_mem) =
761                    allocator.create_buffer(device, size, vk::BufferUsageFlags::VERTEX_BUFFER)?;
762
763                self.vertex_count = vertex_count;
764
765                let old_vertices = self.vertices;
766                self.vertices = vertices;
767
768                let old_vertices_mem = std::mem::replace(&mut self.vertices_mem, vertices_mem);
769
770                allocator.destroy_buffer(device, old_vertices, old_vertices_mem)?;
771            }
772            allocator.update_buffer(device, &mut self.vertices_mem, &vertices)?;
773
774            let indices = create_indices(primitives);
775            if indices.len() > self.index_count {
776                log::trace!("Resizing index buffers");
777
778                let index_count = indices.len();
779                let size = index_count * size_of::<u32>();
780                let (indices, indices_mem) =
781                    allocator.create_buffer(device, size, vk::BufferUsageFlags::INDEX_BUFFER)?;
782
783                self.index_count = index_count;
784
785                let old_indices = self.indices;
786                self.indices = indices;
787
788                let old_indices_mem = std::mem::replace(&mut self.indices_mem, indices_mem);
789
790                allocator.destroy_buffer(device, old_indices, old_indices_mem)?;
791            }
792            allocator.update_buffer(device, &mut self.indices_mem, &indices)?;
793
794            Ok(())
795        }
796
797        pub fn destroy(self, device: &Device, allocator: &mut Allocator) -> RendererResult<()> {
798            allocator.destroy_buffer(device, self.vertices, self.vertices_mem)?;
799            allocator.destroy_buffer(device, self.indices, self.indices_mem)?;
800            Ok(())
801        }
802    }
803
804    fn create_vertices(primitives: &[ClippedPrimitive]) -> Vec<Vertex> {
805        let vertex_count = primitives
806            .iter()
807            .map(|p| match &p.primitive {
808                Primitive::Mesh(m) => m.vertices.len(),
809                _ => 0,
810            })
811            .sum();
812
813        let mut vertices = Vec::with_capacity(vertex_count);
814        for p in primitives {
815            if let Primitive::Mesh(m) = &p.primitive {
816                vertices.extend_from_slice(&m.vertices);
817            }
818        }
819        vertices
820    }
821
822    fn create_indices(primitives: &[ClippedPrimitive]) -> Vec<u32> {
823        let index_count = primitives
824            .iter()
825            .map(|p| match &p.primitive {
826                Primitive::Mesh(m) => m.indices.len(),
827                _ => 0,
828            })
829            .sum();
830
831        let mut indices = Vec::with_capacity(index_count);
832        for p in primitives {
833            if let Primitive::Mesh(m) = &p.primitive {
834                indices.extend_from_slice(&m.indices);
835            }
836        }
837
838        indices
839    }
840}
841
842/// Orthographic projection matrix for use with Vulkan.
843///
844/// This matrix is meant to be used when the source coordinate space is right-handed and y-up
845/// (the standard computer graphics coordinate space)and the destination space is right-handed
846/// and y-down, with Z (depth) clip extending from 0.0 (close) to 1.0 (far).
847///
848/// from: https://github.com/fu5ha/ultraviolet (to limit dependencies)
849#[inline]
850pub fn orthographic_vk(
851    left: f32,
852    right: f32,
853    bottom: f32,
854    top: f32,
855    near: f32,
856    far: f32,
857) -> [f32; 16] {
858    let rml = right - left;
859    let rpl = right + left;
860    let tmb = top - bottom;
861    let tpb = top + bottom;
862    let fmn = far - near;
863
864    #[rustfmt::skip]
865    let res = [
866        2.0 / rml, 0.0, 0.0, 0.0,
867        0.0, -2.0 / tmb, 0.0, 0.0,
868        0.0, 0.0, -1.0 / fmn, 0.0,
869        -(rpl / rml), -(tpb / tmb), -(near / fmn), 1.0
870    ];
871
872    res
873}