use ash::vk;
use ash::vk::{AttachmentLoadOp, AttachmentStoreOp, ClearColorValue, ClearValue, DescriptorSet, DescriptorSetLayout, ImageLayout, Offset2D, Rect2D, RenderingAttachmentInfo};
use egui::{Context, FullOutput, TextureId, ViewportId};
use egui_ash_renderer::{DynamicRendering, Options};
use egui_ash_renderer::vulkan::{create_vulkan_descriptor_set, create_vulkan_descriptor_set_layout};
use egui_winit::State;
use crate::app::Window;
use crate::graphics::Renderer;
use crate::graphics::renderer::{RenderComponent, RenderContext};
use crate::vulkan::{Device, DescriptorPool, Image};
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use log::{error, trace};
pub trait GuiComponent {
fn gui(&mut self, gui: &GuiSystem, context: &Context);
}
pub struct GuiSystem {
pub egui_ctx: Context,
pub egui_winit: State,
pub egui_renderer: egui_ash_renderer::Renderer,
device: Device,
renderer_descriptor_pool: DescriptorPool,
egui_output: Option<FullOutput>,
texture_layout: DescriptorSetLayout,
user_textures: HashMap<TextureId, DescriptorSet>,
}
impl Drop for GuiSystem {
fn drop(&mut self) {
for (id, _) in self.user_textures.iter() {
self.egui_renderer.remove_user_texture(*id);
trace!("Destroyed user texture {:?}", id);
}
unsafe {
self.device.handle().destroy_descriptor_set_layout(self.texture_layout, None);
trace!("Destroyed gui image descriptor set layout {:?}", self.texture_layout);
}
}
}
impl GuiSystem {
pub fn new(window: &Window, renderer: &mut Renderer) -> Self {
let egui_ctx = Context::default();
egui_extras::install_image_loaders(&egui_ctx);
let egui_winit = egui_winit::State::new(
egui_ctx.clone(),
ViewportId::ROOT,
&window.display_handle(),
None,
None,
None
);
let device = renderer.device.clone();
let renderer_descriptor_pool = DescriptorPool::new(&renderer.device, 10000);
#[cfg(any(target_os = "linux", target_os = "windows"))]
let preferred_format = vk::Format::R8G8B8A8_SRGB;
#[cfg(target_os = "macos")]
let preferred_format = vk::Format::B8G8R8A8_SRGB;
let egui_renderer = egui_ash_renderer::Renderer::with_gpu_allocator(
renderer.allocator.inner.lock().unwrap().allocator.clone(),
renderer.device.handle().clone(),
DynamicRendering {
color_attachment_format: preferred_format,
depth_attachment_format: None,
},
Options {
in_flight_frames: renderer.swapchain.get_image_count() as usize,
enable_depth_test: false,
enable_depth_write: false,
srgb_framebuffer: true
}
).unwrap();
let texture_layout = create_vulkan_descriptor_set_layout(renderer.device.handle()).unwrap();
Self {
egui_ctx,
egui_winit,
egui_renderer,
egui_output: None,
device,
renderer_descriptor_pool,
texture_layout,
user_textures: HashMap::new(),
}
}
pub fn create_texture(&mut self, image: &Image) -> TextureId {
let device = self.device.handle();
let descriptor_set = create_vulkan_descriptor_set(
device,
self.texture_layout,
self.renderer_descriptor_pool.handle(),
image.image_view(),
image.sampler(),
).unwrap();
let texture_id = self.egui_renderer.add_user_texture(descriptor_set);
self.user_textures.insert(texture_id, descriptor_set);
texture_id
}
pub fn remove_texture(&mut self, texture_id: TextureId) {
unsafe {
let set = self.user_textures.remove(&texture_id).unwrap();
self.device.handle().free_descriptor_sets(self.renderer_descriptor_pool.descriptor_pool, &[set]).unwrap();
}
self.egui_renderer.remove_user_texture(texture_id);
}
pub fn on_window_event(&mut self, window: &winit::window::Window, event: &winit::event::WindowEvent) {
let _ = self.egui_winit.on_window_event(window, event);
}
pub fn update(&mut self, window: &winit::window::Window, components: &mut [Arc<Mutex<dyn GuiComponent>>]) {
let raw_input = self.egui_winit.take_egui_input(window);
self.egui_output = Some(self.egui_ctx.run(raw_input, |ctx| {
for component in &mut *components {
component.lock().unwrap().gui(self, ctx);
}
}));
}
}
impl RenderComponent for GuiSystem {
fn render(&mut self, ctx: &mut RenderContext) {
if let Some(output) = self.egui_output.take() {
self.egui_renderer
.free_textures(output.textures_delta.free.as_slice()).unwrap();
self.egui_renderer.set_textures(
*ctx.queue, ctx.command_pool.command_pool, output.textures_delta.set.as_slice()
).unwrap();
let clipped_primitives = self.egui_ctx.tessellate(
output.shapes,
output.pixels_per_point
);
let color_attachments = vec![
RenderingAttachmentInfo::default()
.image_layout(ImageLayout::COLOR_ATTACHMENT_OPTIMAL)
.load_op(AttachmentLoadOp::LOAD)
.store_op(AttachmentStoreOp::STORE)
.clear_value(ClearValue { color: ClearColorValue { float32: [1f32, 0f32, 1f32, 1f32] } })
.image_view(ctx.swapchain_image.image_view())
];
let rendering_info = vk::RenderingInfoKHR::default()
.render_area(Rect2D { offset: Offset2D { x: 0, y: 0 }, extent: ctx.swapchain_image.extent() })
.layer_count(1)
.view_mask(0)
.color_attachments(&color_attachments);
ctx.command_buffer.begin_rendering(&rendering_info);
match self.egui_renderer.cmd_draw(
ctx.command_buffer.handle(),
ctx.swapchain_image.extent(),
output.pixels_per_point,
clipped_primitives.as_slice()
) {
Ok(_) => (),
Err(e) => {
error!("{}", e);
}
}
ctx.command_buffer.end_rendering();
}
}
}