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
28pub type RendererResult<T> = Result<T, RendererError>;
32
33#[derive(Debug, Clone, Copy)]
35pub struct Options {
36 pub in_flight_frames: usize,
38 pub enable_depth_test: bool,
40 pub enable_depth_write: bool,
45 pub subpass: u32,
47 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#[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
71pub 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 #[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 #[cfg(feature = "gpu-allocator")]
173 pub fn with_gpu_allocator(
174 gpu_allocator: Arc<Mutex<GpuAllocator>>, 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 #[cfg(feature = "vk-mem")]
222 pub fn with_vk_mem_allocator(
223 vk_mem_allocator: Arc<Mutex<VkMemAllocator>>, 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 let descriptor_set_layout = create_vulkan_descriptor_set_layout(&device)?;
268
269 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 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 let descriptor_pool = create_vulkan_descriptor_pool(&device, 1)?;
302
303 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 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 #[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 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 pub fn update_fonts_texture(
405 &mut self,
406 queue: vk::Queue,
407 command_pool: vk::CommandPool,
408 imgui: &mut Context,
409 ) -> RendererResult<()> {
410 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 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 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 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 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
640struct 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 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 let (vertices, vertices_mem) = create_and_fill_buffer(
710 device,
711 allocator,
712 &vertices,
713 vk::BufferUsageFlags::VERTEX_BUFFER,
714 )?;
715
716 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}