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
29pub type RendererResult<T> = Result<T, RendererError>;
33
34const MAX_TEXTURE_COUNT: u32 = 1024; #[derive(Debug, Clone, Copy)]
38pub struct Options {
39 pub in_flight_frames: usize,
41 pub enable_depth_test: bool,
43 pub enable_depth_write: bool,
48 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#[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
73pub 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 #[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 #[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 #[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 let descriptor_set_layout = create_vulkan_descriptor_set_layout(&device)?;
223
224 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 let descriptor_pool = create_vulkan_descriptor_pool(&device, MAX_TEXTURE_COUNT)?;
238
239 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 #[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 #[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 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 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 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 pub fn remove_user_texture(&mut self, id: TextureId) {
461 self.textures.remove(&id);
462 }
463
464 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 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
653struct 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 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 let (vertices, vertices_mem) = create_and_fill_buffer(
724 device,
725 allocator,
726 &vertices,
727 vk::BufferUsageFlags::VERTEX_BUFFER,
728 )?;
729
730 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#[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}