1use 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 pub queue_family: u32,
54 pub queue: vk::Queue,
56 pub render_pass: vk::RenderPass,
59 pub subpass: u32,
61 pub image_count: u32,
63 pub msaa_samples: Option<vk::SampleCountFlags>,
65 pub pipeline_cache: vk::PipelineCache,
67 pub surface_color_fmt: SurfaceColorFormat,
70 pub descriptor_pool_size: Option<u32>,
76}
77
78#[derive(Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
81pub enum SurfaceColorFormat {
82 #[default]
83 Linear,
84 Srgb,
85}
86
87impl RendererCreateInfo {
88 pub fn queue_family(mut self, queue_family: u32) -> Self {
90 self.queue_family = queue_family;
91 self
92 }
93
94 pub fn queue(mut self, queue: vk::Queue) -> Self {
96 self.queue = queue;
97 self
98 }
99
100 pub fn render_pass(mut self, render_pass: vk::RenderPass) -> Self {
102 self.render_pass = render_pass;
103 self
104 }
105
106 pub fn subpass(mut self, subpass: u32) -> Self {
108 self.subpass = subpass;
109 self
110 }
111
112 pub fn image_count(mut self, image_count: u32) -> Self {
114 self.image_count = image_count;
115 self
116 }
117
118 pub fn msaa_samples(mut self, msaa_samples: Option<vk::SampleCountFlags>) -> Self {
120 self.msaa_samples = msaa_samples;
121 self
122 }
123
124 pub fn pipeline_cache(mut self, pipeline_cache: vk::PipelineCache) -> Self {
126 self.pipeline_cache = pipeline_cache;
127 self
128 }
129
130 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 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 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 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 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 {
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}