use crate::prelude::*; use anyhow::{Result, anyhow}; use assetpath::AssetPath; use ecs::World; use serde::{Deserialize, Serialize}; use utilities::{impl_reprc, prelude::*}; use vulkan_rs::{prelude::*, render_target::sub_pass::InputAttachmentInfo}; use super::{ elements::Elements, gui::{ colorable::ColorableVertex, iconizable::IconBuilderType, texturedvertex::TexturedVertex, }, }; use cgmath::{ortho, vec2}; use std::sync::{Arc, Mutex}; use std::{collections::HashMap, ptr}; use std::{mem, sync::Weak}; use paste::paste; #[derive(Deserialize, Serialize, Clone, Debug)] pub enum Font<'a> { Path(AssetPath), Bytes(&'a [u8]), } impl Default for Font<'_> { fn default() -> Self { Self::Path(AssetPath::default()) } } #[derive(Deserialize, Serialize, Clone, Debug)] pub struct GuiHandlerCreateInfo<'a> { // path to the alphabet image #[serde(borrow)] pub font: Font<'a>, // sound info #[cfg(feature = "audio")] pub click_sound: Option, #[cfg(feature = "audio")] pub hover_sound: Option, // resource base directory pub resource_directory: Option, } impl<'a> GuiHandlerCreateInfo<'a> { pub const fn new() -> Self { GuiHandlerCreateInfo { // path to the alphabet image font: Font::Bytes(include_bytes!("default_font.png")), // sound info #[cfg(feature = "audio")] click_sound: None, #[cfg(feature = "audio")] hover_sound: None, // resource base directory resource_directory: None, } } } struct GuiSeparator { _descriptor_layout: Arc, pipeline: Arc, } impl_reprc!( struct ColorBuffer { #[assume_reprc] color: f32, } ); struct TextableColor { _descriptor_pool: Arc, _descriptor_set: Arc, _buffer: Arc>, } struct CommandBufferState { text_buffers: Vec>>, } struct TextToScreen { pipeline: TargetMode, descriptor: TargetMode>, buffer: Arc>, } impl TextToScreen { fn new( device: &Arc, pipeline: TargetMode, render_target: &TargetMode, ) -> Result { let descriptor = pipeline .chain(render_target) .execute(|(separator, render_target)| { let descriptor = DescriptorPool::builder() .set_layout(separator._descriptor_layout.clone()) .build(device.clone())? .prepare_set() .allocate()?; Self::update_descriptor(&descriptor, render_target)?; Ok(descriptor) })?; let edges = [ TexturedVertex { position: vec2(-1.0, -1.0), texture_coordinates: vec2(0.0, 0.0), }, TexturedVertex { position: vec2(-1.0, 1.0), texture_coordinates: vec2(0.0, 1.0), }, TexturedVertex { position: vec2(1.0, 1.0), texture_coordinates: vec2(1.0, 1.0), }, TexturedVertex { position: vec2(1.0, 1.0), texture_coordinates: vec2(1.0, 1.0), }, TexturedVertex { position: vec2(1.0, -1.0), texture_coordinates: vec2(1.0, 0.0), }, TexturedVertex { position: vec2(-1.0, -1.0), texture_coordinates: vec2(0.0, 0.0), }, ]; let buffer = Buffer::builder() .set_usage(VK_BUFFER_USAGE_VERTEX_BUFFER_BIT) .set_memory_usage(MemoryUsage::CpuOnly) .set_data(&edges) .build(device.clone())?; Ok(Self { pipeline, descriptor, buffer, }) } fn update_descriptor( descriptor: &Arc, render_target: &RenderTarget, ) -> Result<()> { // take resolve target of second sub pass as image sampler descriptor.update(&[DescriptorWrite::input_attachments( 0, &[&render_target.sub_pass(1).attachments()[1].image(0)], ) .change_image_layout(VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)]) } fn update_on_resize(&self, render_target: &TargetMode) -> Result<()> { self.descriptor .chain(render_target) .execute(|(descriptor, render_target)| { Self::update_descriptor(descriptor, render_target) })?; Ok(()) } } pub struct GuiHandler { device: Arc, queue: Arc>, width: u32, height: u32, resource_base_path: Option, top_ui: Option>, tooltip_ui: Option>, render_targets: TargetMode, command_buffers: TargetMode>, text_objects: TargetMode, rectangle_objects: TargetMode, single_color_objects: TargetMode, text_to_screen: TextToScreen, _bitmap_font: Arc, _bitmap_desc_pool: Arc, bitmap_desc_set: Arc, text_color_layout: Arc, internal_icons: HashMap>, internal_textures: HashMap>, internal_colors: HashMap, element_creator: Mutex, ortho: cgmath::Matrix4, icon_descriptor_layout: Arc, needs_update: bool, text_change_queue: Vec Result<()> + Send + Sync>>, on_selected: Option Result<()> + Send + Sync>>, #[cfg(feature = "audio")] click_sound: Option, #[cfg(feature = "audio")] hover_sound: Option, // ----- gui handling ----- layers: Vec<(i32, Elements)>, mouse_x: u32, mouse_y: u32, last_direction: GuiDirection, current_writeable: Option>, current_hoverable: Option>, current_clickable: Option>, current_selectable: Option>, text_sample_count: VkSampleCountFlags, callback_list: Vec Result<()> + Send + Sync>>, } impl GuiHandler { pub fn new( gui_handler_create_info: GuiHandlerCreateInfo<'_>, context: &impl ContextInterface, ) -> Result { let device = context.device(); let queue = context.queue(); let command_buffers = context .images() .execute(|_| Self::create_command_buffers(context.image_count()))?; let text_sample_count = device.max_supported_sample_count(VK_SAMPLE_COUNT_4_BIT); let render_targets = Self::create_render_targets(device, &context.images(), queue, text_sample_count)?; let (text_objs, color_layout) = Self::init_text_objects(device, &render_targets, text_sample_count)?; let rect_objs = Self::init_rectangle_objects(device, &render_targets)?; let single_color_objects = Self::init_single_color_objects(device, &render_targets)?; let text_to_screen = TextToScreen::new( device, Self::init_text_screen_objects(device, &render_targets)?, &render_targets, )?; let (bitmap_texture, bitmap_desc_pool, bitmap_desc_set) = Self::init_bitmap_font( device, queue, match &text_objs { TargetMode::Mono(l) => l, TargetMode::Stereo(l, _) => l, } ._descriptor_layout .clone(), gui_handler_create_info.font, )?; let icon_descriptor_layout = DescriptorSetLayout::builder() .add_layout_binding( 0, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT, 0, ) .build(device.clone())?; Ok(GuiHandler { device: device.clone(), queue: queue.clone(), width: context.width(), height: context.height(), #[cfg(feature = "audio")] click_sound: gui_handler_create_info.click_sound.map(|mut click_sound| { click_sound.set_prefix( &gui_handler_create_info .resource_directory .as_ref() .expect("missing resource directory") .full_path(), ); click_sound }), #[cfg(feature = "audio")] hover_sound: gui_handler_create_info.hover_sound.map(|mut hover_sound| { hover_sound.set_prefix( &gui_handler_create_info .resource_directory .as_ref() .expect("missing resource directory") .full_path(), ); hover_sound }), resource_base_path: gui_handler_create_info.resource_directory, top_ui: None, tooltip_ui: None, render_targets, command_buffers, text_objects: text_objs, rectangle_objects: rect_objs, single_color_objects, text_to_screen, _bitmap_font: bitmap_texture, _bitmap_desc_pool: bitmap_desc_pool, bitmap_desc_set, text_color_layout: color_layout, internal_icons: HashMap::new(), internal_textures: HashMap::new(), internal_colors: HashMap::new(), element_creator: Mutex::new(ElementCreator::new(device.clone(), queue.clone())?), icon_descriptor_layout, needs_update: true, text_change_queue: Vec::new(), on_selected: None, layers: Vec::new(), ortho: ortho( 0.0, context.width() as f32, 0.0, context.height() as f32, -1.0, 1.0, ), mouse_x: 0, mouse_y: 0, last_direction: GuiDirection::None, current_clickable: None, current_hoverable: None, current_selectable: None, current_writeable: None, text_sample_count, callback_list: Vec::new(), }) } pub fn device(&self) -> &Arc { &self.device } pub fn queue(&self) -> &Arc> { &self.queue } pub fn width(&self) -> u32 { self.width } pub fn height(&self) -> u32 { self.height } pub(crate) fn icon_descriptor_layout(&self) -> &Arc { &self.icon_descriptor_layout } pub(crate) fn request_icon( &mut self, icon_builder: impl Into, ) -> Result> { let icon_builder = icon_builder.into(); match icon_builder { IconBuilderType::Image(image) => { if let Some(path) = image.file_name() { self.internal_icons .insert(path.clone(), Arc::downgrade(&image)); } Ok(image) } IconBuilderType::Path(mut path) => match self.internal_icons.get_mut(&path) { Some(weak_image) => match weak_image.upgrade() { Some(image) => Ok(image), None => { if !path.has_prefix() { path.set_prefix( &self .resource_base_path .as_ref() .ok_or(anyhow!("resource base path not set!"))? .full_path(), ); } let image = Image::from_file(path)? .max_mip_map_levels() .attach_pretty_sampler(&self.device)? .build(&self.device, &self.queue)?; *weak_image = Arc::downgrade(&image); Ok(image) } }, None => { if !path.has_prefix() { path.set_prefix( &self .resource_base_path .as_ref() .ok_or(anyhow!("resource base path not set!"))? .full_path(), ); } let image = Image::from_file(path.clone())? .max_mip_map_levels() .attach_pretty_sampler(self.device())? .build(self.device(), self.queue())?; self.internal_icons.insert(path, Arc::downgrade(&image)); Ok(image) } }, } } fn text_desc_layout(&self) -> &Arc { &match &self.text_objects { TargetMode::Mono(l) => l, TargetMode::Stereo(l, _) => l, } ._descriptor_layout } pub(crate) fn displayable_image_from_descriptor( &mut self, width: u32, height: u32, descriptor: ElementDescriptor, ) -> Result> { self.element_creator .lock() .unwrap() .get(width, height, descriptor) } pub(crate) fn image_descriptor_set(&self) -> Result> { let desc_pool = DescriptorPool::builder() .set_layout(self.text_desc_layout().clone()) .build(self.device.clone())?; DescriptorPool::prepare_set(&desc_pool).allocate() } pub(crate) fn displayable_image_from_path( &mut self, mut path: AssetPath, ) -> Result> { if !path.has_prefix() { path.set_prefix( &self .resource_base_path .as_ref() .ok_or(anyhow!("resource base path not set!"))? .full_path(), ); } if self.internal_textures.contains_key(&path) { Ok(self.internal_textures[&path].clone()) } else { let texture = Image::from_file(path.clone())? .format(VK_FORMAT_R8G8B8A8_UNORM) .attach_sampler(Sampler::nearest_sampler().build(&self.device)?) .build(&self.device, &self.queue)?; self.internal_textures.insert(path.clone(), texture.clone()); Ok(texture.clone()) } } pub(crate) fn color_descriptor(&mut self, color: Color) -> Result> { if self.internal_colors.contains_key(&color) { Ok(self.internal_colors[&color]._descriptor_set.clone()) } else { let desc_pool = DescriptorPool::builder() .set_layout(self.text_color_layout.clone()) .build(self.device.clone())?; let desc_set = DescriptorPool::prepare_set(&desc_pool).allocate()?; let color_array: [f32; 3] = color.into(); let color_buffer_array: [ColorBuffer; 3] = color_array .into_iter() .map(|f| ColorBuffer { color: f }) .collect::>() .try_into() .unwrap_or_else(|_: Vec| { unreachable!("create array from vec from an array") }); let buffer = Buffer::builder() .set_usage(VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT) .set_memory_usage(MemoryUsage::CpuOnly) .set_data(&color_buffer_array) .build(self.device.clone())?; desc_set.update(&[DescriptorWrite::uniform_buffers(0, &[&buffer])])?; let textable_color = TextableColor { _descriptor_pool: desc_pool, _descriptor_set: desc_set, _buffer: buffer, }; self.internal_colors.insert(color, textable_color); Ok(self.internal_colors[&color]._descriptor_set.clone()) } } pub(crate) fn ortho(&self) -> cgmath::Matrix4 { self.ortho } pub(crate) fn resource_base_path(&self) -> Option<&AssetPath> { self.resource_base_path.as_ref() } #[cfg(feature = "audio")] pub fn click_sound(&self) -> Option<&AssetPath> { self.click_sound.as_ref() } #[cfg(feature = "audio")] pub fn hover_sound(&self) -> Option<&AssetPath> { self.hover_sound.as_ref() } pub fn set_on_selected_event(&mut self, f: F) where F: Fn() -> Result<()> + Send + Sync + 'static, { self.on_selected = Some(Box::new(f)); } // --------------------------------------------------------------------- // ------------------------- event handling -------------------------- // --------------------------------------------------------------------- pub fn set_mouse_pos(&mut self, x: u32, y: u32) -> Result<()> { self.mouse_x = x; self.mouse_y = y; let mut hovered = None; { for (_, elements) in self.layers.iter() { for hoverable in elements.iter_hoverables() { if hoverable.is_hovered(x as i32, y as i32) { hovered = Some(hoverable.clone()); break; } } } } let callback_self = unsafe { remove_life_time_mut(self) }; match hovered { Some(hovered) => { if let Some(current) = &self.current_hoverable { if Arc::ptr_eq(current, &hovered) { return Ok(()); } current.set_hovered(callback_self, false)?; } hovered.set_hovered(self, true)?; self.current_hoverable = Some(hovered); } None => { if self.current_hoverable.is_some() { // unwrap is safe, just tested for `is_some` if !self .current_hoverable .as_ref() .unwrap() .is_hovered(x as i32, y as i32) { self.current_hoverable .as_ref() .unwrap() .set_hovered(callback_self, false)?; self.current_hoverable = None; } } } } Ok(()) } pub fn mouse_position(&self) -> (u32, u32) { (self.mouse_x, self.mouse_y) } fn find_clickable(&mut self) -> Result>> { for (_, elements) in self.layers.iter() { for clickable in elements.iter_clickables() { if clickable.is_pressed(self.mouse_x as i32, self.mouse_y as i32) { self.current_clickable = Some(clickable.clone()); return Ok(Some(clickable.clone())); } } } Ok(None) } pub fn mouse_down(&mut self, mouse_button: MouseButton) -> Result { if mouse_button == MouseButton::Left { if let Some(tmp_clickable) = self.find_clickable()? { tmp_clickable.set_clicked(self, true)?; return Ok(true); } } Ok(false) } pub fn mouse_up(&mut self, mouse_button: MouseButton) -> Result { let callback_self = unsafe { remove_life_time_mut(self) }; if mouse_button == MouseButton::Left { if self.current_clickable.is_some() { self.current_clickable .as_ref() .unwrap() .set_clicked(callback_self, false)?; if self .current_clickable .as_ref() .unwrap() .is_pressed(self.mouse_x as i32, self.mouse_y as i32) { if let Some(hoverable) = self.current_hoverable.as_ref() { hoverable.set_hovered(callback_self, true)?; } } self.current_clickable = None; return Ok(true); } } Ok(false) } pub fn current_selectable(&self) -> Option> { self.current_selectable.clone() } pub fn accept_selection(&mut self) -> Result { let callback_self = unsafe { remove_life_time_mut(self) }; if let Some(current_selectable) = &self.current_selectable { current_selectable.click_event(callback_self)?; return Ok(true); } Ok(false) } pub fn accept_custom_selection( &self, world: &mut World, button: ControllerButton, ) -> Result { if let Some(current_selectable) = &self.current_selectable { if current_selectable.custom_click_event(world, button)? { return Ok(true); } } Ok(false) } pub fn decline_topgui(&mut self, world: &mut World) -> Result { // workaround for unwanted borrowing behaviour inside decline function let opt_top_level_gui = self.top_ui.as_ref().cloned(); if let Some(top_level_gui) = opt_top_level_gui { if let Some(top_gui) = top_level_gui.top_gui() { top_gui.decline(world)?; return Ok(true); } } Ok(false) } pub fn next_tab_topgui(&mut self, world: &mut World, second_level: bool) -> Result { // workaround for unwanted borrowing behaviour inside decline function let opt_top_level_gui = self.top_ui.as_ref().cloned(); if let Some(top_level_gui) = opt_top_level_gui { if let Some(top_gui) = top_level_gui.top_gui() { top_gui.next_tab(world, second_level)?; return Ok(true); } } Ok(false) } pub fn previous_tab_topgui(&mut self, world: &mut World, second_level: bool) -> Result { // workaround for unwanted borrowing behaviour inside decline function let opt_top_level_gui = self.top_ui.as_ref().cloned(); if let Some(top_level_gui) = opt_top_level_gui { if let Some(top_gui) = top_level_gui.top_gui() { top_gui.previous_tab(world, second_level)?; return Ok(true); } } Ok(false) } pub fn writeable(&self) -> Option> { self.current_writeable.clone() } pub fn remove_char(&mut self) -> Result { let callback_self = unsafe { remove_life_time_mut(self) }; match &self.current_writeable { Some(current_writable) => { current_writable.remove_last(callback_self)?; Ok(true) } None => Ok(false), } } pub fn check_navigatable(&self) -> bool { self.current_selectable.is_some() } pub fn update_selection(&mut self, direction: GuiDirection) -> Result { if direction != self.last_direction { self.last_direction = direction; let callback_self = unsafe { remove_life_time_mut(self) }; match &mut self.current_selectable { Some(current_selectable) => match direction { GuiDirection::Left => { if let Some(neighbour) = current_selectable.west_neighbour() { current_selectable.set_selected(callback_self, false)?; *current_selectable = neighbour; current_selectable.set_selected(callback_self, true)?; }; Ok(true) } GuiDirection::Right => { if let Some(neighbour) = current_selectable.east_neighbour() { current_selectable.set_selected(callback_self, false)?; *current_selectable = neighbour; current_selectable.set_selected(callback_self, true)?; }; Ok(true) } GuiDirection::Up => { if let Some(neighbour) = current_selectable.north_neighbour() { current_selectable.set_selected(callback_self, false)?; *current_selectable = neighbour; current_selectable.set_selected(callback_self, true)?; }; Ok(true) } GuiDirection::Down => { if let Some(neighbour) = current_selectable.south_neighbour() { current_selectable.set_selected(callback_self, false)?; *current_selectable = neighbour; current_selectable.set_selected(callback_self, true)?; }; Ok(true) } GuiDirection::None => Ok(false), }, None => Ok(false), } } else { Ok(false) } } pub(crate) fn enqueue_text_update( &mut self, function: Box Result<()> + Send + Sync>, ) { self.text_change_queue.push(function); self.needs_update = true; } pub fn set_top_gui( &mut self, world: &mut World, top_gui: Option>, ) -> Result<()> { match (&self.top_ui, &top_gui) { (Some(current), Some(incoming)) => { if !Arc::ptr_eq(current, incoming) { current.disable(world)?; } } (Some(current), None) => { current.disable(world)?; } _ => (), } self.top_ui = top_gui; Ok(()) } pub fn set_tooltip( &mut self, world: &mut World, tooltip: Option>, ) -> Result<()> { match (&self.tooltip_ui, &tooltip) { (Some(current), Some(incoming)) => { if !Arc::ptr_eq(current, incoming) { current.disable(world)?; } } (Some(current), None) => { current.disable(world)?; } _ => (), } self.tooltip_ui = tooltip; Ok(()) } pub(crate) fn add_callback Result<()> + Send + Sync + 'static>( &mut self, f: F, ) { self.callback_list.push(Box::new(f)); } pub fn process_callbacks(&mut self, world: &mut World) -> Result<()> { let callbacks = mem::take(&mut self.callback_list); callbacks .into_iter() .try_for_each(|callback| callback(world)) } fn render( &self, buffer_recorder: &mut CommandBufferRecorder<'_>, command_buffer_state: &mut CommandBufferState, render_target: &RenderTarget, single_color_objects: &GuiSeparator, rectangle_objects: &GuiSeparator, text_objects: &GuiSeparator, text_to_screen: &GuiSeparator, text_to_screen_desc: &Arc, index: usize, ) -> Result<()> { let viewport = [VkViewport { x: 0.0, y: 0.0, width: self.width as f32, height: self.height as f32, minDepth: 0.0, maxDepth: 1.0, }]; let scissor = [VkRect2D { offset: VkOffset2D { x: 0, y: 0 }, extent: VkExtent2D { width: self.width, height: self.height, }, }]; for (_, elements) in self.layers.iter() { render_target.begin(buffer_recorder, VK_SUBPASS_CONTENTS_INLINE, index); if !elements.is_colorables_empty() { buffer_recorder.bind_pipeline(&single_color_objects.pipeline)?; buffer_recorder.set_scissor(&scissor); buffer_recorder.set_viewport(&viewport); // ---------- render colorables ---------- for colorable in elements.iter_colorables() { buffer_recorder.bind_vertex_buffer(colorable.buffer()); buffer_recorder.bind_descriptor_sets_minimal(&[&colorable.descriptor_set()]); buffer_recorder.draw_complete_single_instance(6); } } if !elements.is_displayables_empty() || !elements.is_iconizables_empty() { buffer_recorder.bind_pipeline(&rectangle_objects.pipeline)?; buffer_recorder.set_scissor(&scissor); buffer_recorder.set_viewport(&viewport); // ---------- render displayables ---------- for displayable in elements.iter_displayables() { buffer_recorder.bind_vertex_buffer(displayable.buffer()); buffer_recorder.bind_descriptor_sets_minimal(&[&displayable.descriptor_set()]); buffer_recorder.draw_complete_single_instance(6); } // ---------- render iconizables ---------- for iconizable in elements.iter_iconizables() { buffer_recorder.bind_vertex_buffer(iconizable.buffer()); buffer_recorder.bind_descriptor_sets_minimal(&[iconizable.descriptor_set()]); buffer_recorder.draw_complete_single_instance(6); } } // render text, render to offscreen (multisampled) and merge resolved target with actual image if !elements.is_textables_empty() { render_target.next_subpass(buffer_recorder, VK_SUBPASS_CONTENTS_INLINE); buffer_recorder.bind_pipeline(&text_objects.pipeline)?; buffer_recorder.set_scissor(&scissor); buffer_recorder.set_viewport(&viewport); command_buffer_state.text_buffers.clear(); // ---------- render textables ---------- for textable in elements.iter_textables() { if let Some(text_buffer) = textable.buffer() { buffer_recorder.bind_vertex_buffer(&text_buffer); command_buffer_state.text_buffers.push(text_buffer); buffer_recorder.bind_descriptor_sets_minimal(&[ &self.bitmap_desc_set, &textable.descriptor_set(), ]); buffer_recorder.draw_complete_single_instance(textable.vertex_count()); } } // after text is written und resolved, we read image as input and draw it over the current image render_target.next_subpass(buffer_recorder, VK_SUBPASS_CONTENTS_INLINE); buffer_recorder.bind_pipeline(&text_to_screen.pipeline)?; buffer_recorder.set_scissor(&scissor); buffer_recorder.set_viewport(&viewport); buffer_recorder.bind_descriptor_sets_minimal(&[text_to_screen_desc]); buffer_recorder.bind_vertex_buffer(&self.text_to_screen.buffer); buffer_recorder .draw_complete_single_instance(self.text_to_screen.buffer.size() as u32); } else { // dummy advance sub passes // since ending render pass and not being in the last sub pass seems to be an error render_target.next_subpass(buffer_recorder, VK_SUBPASS_CONTENTS_INLINE); render_target.next_subpass(buffer_recorder, VK_SUBPASS_CONTENTS_INLINE); } render_target.end(buffer_recorder); } Ok(()) } } macro_rules! add_element { ($layers: expr, $layer_id: ident, $element: ident) => { paste! { match $layers.iter_mut().find(|(id, _)| *id == $layer_id) { Some((_, layer)) => { layer.[]($element); } None => { let mut elements = Elements::default(); elements.[]($element); $layers.push(($layer_id, elements)); $layers.sort_by(|(left_id, _), (right_id, _)| left_id.cmp(right_id)); } } } }; } macro_rules! remove_element { ($layers: expr, $layer_id: ident, $element: ident) => { paste! { match $layers.iter_mut().find(|(id, _)| *id == $layer_id) { Some((_, layer)) => { layer.[]($element); } None => { panic!("layer not present from which {} is to remove", stringify!($element)); } } } }; ($layers: expr, $layer_id: ident, $element: ident, $update: expr) => { paste! { match $layers.iter_mut().find(|(id, _)| *id == $layer_id) { Some((_, layer)) => { if layer.[]($element) { $update = true; } } None => { panic!("layer not present from which {} is to remove", stringify!($element)); } } } }; } // object handling impl<'a> GuiHandler { // framable pub(crate) fn add_framable(&mut self, layer: i32, framable: Arc) -> Result<()> { add_element!(self.layers, layer, framable); Ok(()) } pub(crate) fn delete_framable(&mut self, layer: i32, framable: &Arc) -> Result<()> { remove_element!(self.layers, layer, framable); Ok(()) } // hoverable pub(crate) fn add_hoverable(&mut self, layer: i32, hoverable: Arc) -> Result<()> { add_element!(self.layers, layer, hoverable); Ok(()) } pub(crate) fn delete_hoverable( &mut self, layer: i32, hoverable: &Arc, ) -> Result<()> { if self.current_hoverable.is_some() { // unwrap is safe, just tested for `is_some` if Arc::ptr_eq(hoverable, self.current_hoverable.as_ref().unwrap()) { self.current_hoverable = None; } } remove_element!(self.layers, layer, hoverable); Ok(()) } // selectable pub(crate) fn add_selectable(&mut self, layer: i32, selectable: Arc) -> Result<()> { add_element!(self.layers, layer, selectable); Ok(()) } pub(crate) fn delete_selectable( &mut self, layer: i32, selectable: &Arc, ) -> Result<()> { if self.current_selectable.is_some() { // unwrap is safe, just tested for `is_some` if Arc::ptr_eq(selectable, self.current_selectable.as_ref().unwrap()) { self.current_selectable = None; } } remove_element!(self.layers, layer, selectable); Ok(()) } // displayable pub(crate) fn add_displayable( &mut self, layer: i32, displayable: Arc, ) -> Result<()> { add_element!(self.layers, layer, displayable); self.needs_update = true; Ok(()) } pub(crate) fn delete_displayable( &mut self, layer: i32, displayable: &Arc, ) -> Result<()> { remove_element!(self.layers, layer, displayable, self.needs_update); Ok(()) } // clickable pub(crate) fn add_clickable(&mut self, layer: i32, clickable: Arc) -> Result<()> { add_element!(self.layers, layer, clickable); Ok(()) } pub(crate) fn delete_clickable( &mut self, layer: i32, clickable: &Arc, ) -> Result<()> { if self.current_clickable.is_some() { // unwrap is safe, just tested for `is_some` if ptr::eq(clickable, self.current_clickable.as_ref().unwrap()) { self.current_clickable = None; } } remove_element!(self.layers, layer, clickable); Ok(()) } // textable pub(crate) fn add_textable(&mut self, layer: i32, textable: Arc) -> Result<()> { add_element!(self.layers, layer, textable); self.needs_update = true; Ok(()) } pub(crate) fn delete_textable(&mut self, layer: i32, textable: &Arc) -> Result<()> { remove_element!(self.layers, layer, textable, self.needs_update); Ok(()) } // writable pub(crate) fn add_writeable(&mut self, layer: i32, writeable: Arc) -> Result<()> { add_element!(self.layers, layer, writeable); Ok(()) } pub(crate) fn set_active_writeable(&mut self, writeable: Arc) -> Result { let callback_self = unsafe { remove_life_time_mut(self) }; if let Some(current) = self.current_writeable.as_ref() { // change nothing if both are the same if Arc::ptr_eq(current, &writeable) { return Ok(false); } current.inactivation_event(callback_self)?; } self.current_writeable = Some(writeable); Ok(true) } pub(crate) fn delete_writeable( &mut self, layer: i32, writeable: &Arc, ) -> Result<()> { let callback_self = unsafe { remove_life_time_mut(self) }; if let Some(w) = &self.current_writeable { if Arc::ptr_eq(w, writeable) { w.inactivation_event(callback_self)?; self.current_writeable = None; } } remove_element!(self.layers, layer, writeable); Ok(()) } // iconizable pub(crate) fn add_iconizable(&mut self, layer: i32, iconizable: Arc) -> Result<()> { add_element!(self.layers, layer, iconizable); self.needs_update = true; Ok(()) } pub(crate) fn delete_iconizable( &mut self, layer: i32, iconizable: &Arc, ) -> Result<()> { remove_element!(self.layers, layer, iconizable, self.needs_update); Ok(()) } pub(crate) fn set_selectable(&mut self, selectable: Option>) -> Result<()> { let callback_self = unsafe { remove_life_time_mut(self) }; if let Some(selectable) = &self.current_selectable { selectable.set_selected(callback_self, false)?; } if let Some(selectable) = &selectable { selectable.set_selected(self, true)?; if let Some(on_selected) = &self.on_selected { on_selected()?; } } self.current_selectable = selectable; Ok(()) } pub(crate) fn add_colorable(&mut self, layer: i32, colorable: Arc) -> Result<()> { add_element!(self.layers, layer, colorable); self.needs_update = true; Ok(()) } pub(crate) fn delete_colorable( &mut self, layer: i32, colorable: &Arc, ) -> Result<()> { remove_element!(self.layers, layer, colorable, self.needs_update); Ok(()) } } // private - create rendering stuff impl<'a> GuiHandler { fn create_render_targets( device: &Arc, target_images: &TargetMode>>, queue: &Arc>, sample_count: VkSampleCountFlags, ) -> Result> { Ok(match target_images { TargetMode::Mono(images) => TargetMode::Mono(Self::create_render_target( device, images, None, queue, sample_count, )?), TargetMode::Stereo(left_images, right_images) => TargetMode::Stereo( Self::create_render_target(device, left_images, None, queue, sample_count)?, Self::create_render_target(device, right_images, None, queue, sample_count)?, ), }) } fn create_render_target( device: &Arc, target_images: &Vec>, old_render_target: Option<&RenderTarget>, queue: &Arc>, sample_count: VkSampleCountFlags, ) -> Result { let mut builder = RenderTarget::builder() .add_sub_pass( SubPass::builder(target_images[0].width(), target_images[0].height()) .set_prepared_targets(target_images, 0, None) .build(device)?, ) .add_sub_pass( SubPass::builder(target_images[0].width(), target_images[0].height()) .add_target_info(CustomTarget { usage: VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT.into(), format: VK_FORMAT_R8G8B8A8_UNORM, clear_on_load: true, store_on_save: true, attach_sampler: false, use_as_input: false, clear_value: ClearValue::Color([0.0, 0.0, 0.0, 0.0]), }) .set_sample_count(sample_count) .add_resolve_targets(CustomTarget::resolve( VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, VK_FORMAT_R8G8B8A8_UNORM, None, false, true, )) .use_queue(queue.clone()) .build(device)?, ) .add_sub_pass( SubPass::builder(target_images[0].width(), target_images[0].height()) .set_input_attachment_info(InputAttachmentInfo { sub_pass_index: 1, input_indices: vec![1], }) .set_prepared_targets(target_images, 0, None) .build(device)?, ); if let Some(render_target) = old_render_target { builder = builder.preserve_old_render_pass(render_target); } builder.build(device) } fn create_command_buffers(image_count: usize) -> Result> { let mut command_buffers = Vec::with_capacity(image_count); for _ in 0..image_count { command_buffers.push(CommandBufferState { text_buffers: Vec::new(), }); } Ok(command_buffers) } fn init_bitmap_font( device: &Arc, queue: &Arc>, descriptor_layout: Arc, font: Font<'_>, ) -> Result<(Arc, Arc, Arc)> { let texture = match font { Font::Path(path) => Image::from_file(path)?, Font::Bytes(bytes) => Image::from_slice(bytes)?, } .format(VK_FORMAT_R8G8B8A8_UNORM) .max_mip_map_levels() .attach_pretty_sampler(device)? .build(device, queue)?; let descriptor_pool = DescriptorPool::builder() .set_layout(descriptor_layout) .build(device.clone())?; let descriptor_set = DescriptorPool::prepare_set(&descriptor_pool).allocate()?; descriptor_set.update(&[DescriptorWrite::combined_samplers(0, &[&texture])])?; Ok((texture, descriptor_pool, descriptor_set)) } fn init_text_objects( device: &Arc, render_targets: &TargetMode, sample_count: VkSampleCountFlags, ) -> Result<(TargetMode, Arc)> { // --- layout creation --- let descriptor_layout = DescriptorSetLayout::builder() .add_layout_binding( 0, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT, 0, ) .build(device.clone())?; let color_layout = DescriptorSetLayout::builder() .add_layout_binding( 0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_SHADER_STAGE_FRAGMENT_BIT, 0, ) .build(device.clone())?; let pipeline_layout = PipelineLayout::builder() .add_descriptor_set_layout(&descriptor_layout) .add_descriptor_set_layout(&color_layout) .build(device.clone())?; // --- pipeline creation --- let vertex_shader_text = include_bytes!("gui_shader/text.vert.spv"); let fragment_shader_text = include_bytes!("gui_shader/text.frag.spv"); let vertex_shader = ShaderModule::from_slice(device.clone(), vertex_shader_text)?; let fragment_shader = ShaderModule::from_slice(device.clone(), fragment_shader_text)?; Ok(( render_targets.execute(|render_target| { Ok(GuiSeparator { _descriptor_layout: descriptor_layout.clone(), pipeline: GuiHandler::init_gui_pipeline::( device, render_target.render_pass(), &pipeline_layout, vertex_shader.clone(), fragment_shader.clone(), sample_count, 1, )?, }) })?, color_layout, )) } fn init_rectangle_objects( device: &Arc, render_targets: &TargetMode, ) -> Result> { let descriptor_layout = DescriptorSetLayout::builder() .add_layout_binding( 0, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT, 0, ) .build(device.clone())?; let pipeline_layout = PipelineLayout::builder() .add_descriptor_set_layout(&descriptor_layout) .build(device.clone())?; // pipeline creation let vertex_shader = ShaderModule::from_slice(device.clone(), include_bytes!("gui_shader/rect.vert.spv"))?; let fragment_shader = ShaderModule::from_slice(device.clone(), include_bytes!("gui_shader/rect.frag.spv"))?; render_targets.execute(|render_target| { Ok(GuiSeparator { _descriptor_layout: descriptor_layout.clone(), pipeline: GuiHandler::init_gui_pipeline::( device, render_target.render_pass(), &pipeline_layout, vertex_shader.clone(), fragment_shader.clone(), VK_SAMPLE_COUNT_1_BIT, 0, )?, }) }) } fn init_text_screen_objects( device: &Arc, render_targets: &TargetMode, ) -> Result> { let descriptor_layout = DescriptorSetLayout::builder() .add_layout_binding( 0, VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, VK_SHADER_STAGE_FRAGMENT_BIT, 0, ) .build(device.clone())?; let pipeline_layout = PipelineLayout::builder() .add_descriptor_set_layout(&descriptor_layout) .build(device.clone())?; // pipeline creation let vertex_shader = ShaderModule::from_slice(device.clone(), include_bytes!("gui_shader/rect.vert.spv"))?; let fragment_shader = ShaderModule::from_slice( device.clone(), include_bytes!("gui_shader/input_rect.frag.spv"), )?; render_targets.execute(|render_target| { Ok(GuiSeparator { _descriptor_layout: descriptor_layout.clone(), pipeline: GuiHandler::init_gui_pipeline::( device, render_target.render_pass(), &pipeline_layout, vertex_shader.clone(), fragment_shader.clone(), VK_SAMPLE_COUNT_1_BIT, 2, )?, }) }) } fn init_single_color_objects( device: &Arc, render_targets: &TargetMode, ) -> Result> { let color_layout = DescriptorSetLayout::builder() .add_layout_binding( 0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_SHADER_STAGE_FRAGMENT_BIT, 0, ) .build(device.clone())?; let pipeline_layout = PipelineLayout::builder() .add_descriptor_set_layout(&color_layout) .build(device.clone())?; // pipeline creation let vertex_shader = ShaderModule::from_slice( device.clone(), include_bytes!("gui_shader/single_color.vert.spv"), )?; let fragment_shader = ShaderModule::from_slice( device.clone(), include_bytes!("gui_shader/single_color.frag.spv"), )?; render_targets.execute(|render_target| { Ok(GuiSeparator { _descriptor_layout: color_layout.clone(), pipeline: GuiHandler::init_gui_pipeline::( device, render_target.render_pass(), &pipeline_layout, vertex_shader.clone(), fragment_shader.clone(), VK_SAMPLE_COUNT_1_BIT, 0, )?, }) }) } fn init_gui_pipeline( device: &Arc, render_pass: &Arc, pipeline_layout: &Arc, vertex_shader: Arc>, fragment_shader: Arc>, sample_count: VkSampleCountFlags, sub_pass: u32, ) -> Result> { Pipeline::new_graphics() .set_vertex_shader::(vertex_shader) .set_fragment_shader(fragment_shader) .input_assembly(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, false) .default_multisample(sample_count) .default_color_blend(vec![VkPipelineColorBlendAttachmentState::default()]) .default_rasterization(VK_CULL_MODE_NONE, VK_FRONT_FACE_CLOCKWISE) .build(device.clone(), pipeline_layout, render_pass, sub_pass) } } impl<'a> GuiHandler { pub fn process( &mut self, buffer_recorder: &mut CommandBufferRecorder<'_>, indices: &TargetMode, ) -> Result<()> { if self.needs_update { if !self.text_change_queue.is_empty() { for text_change in self.text_change_queue.iter() { (text_change)()?; } self.text_change_queue.clear(); } self.needs_update = false; } let callback_self = unsafe { remove_life_time_mut(self) }; self.command_buffers .chain_mut_1(&self.render_targets) .chain_mut_1(&mut self.text_objects) .unfold() .chain_mut_1(&self.rectangle_objects) .unfold() .chain_mut_1(&self.single_color_objects) .unfold() .chain_mut_1(indices) .unfold() .chain_mut_1(&self.text_to_screen.pipeline) .unfold() .chain_mut_1(&self.text_to_screen.descriptor) .unfold() .execute_into( |( command_buffers, render_target, text_objects, rectangle_objects, single_color_object, index, text_pipeline, text_descriptor, )| { callback_self.render( buffer_recorder, &mut command_buffers[***index], render_target, single_color_object, rectangle_objects, text_objects, text_pipeline, text_descriptor, ***index, ) }, )?; Ok(()) } pub fn resize( &mut self, width: u32, height: u32, images: &TargetMode>>, ) -> Result<()> { images.chain_mut_2(&mut self.render_targets).execute_into( |(images, old_render_target)| { let new_render_target = Self::create_render_target( &self.device, &images, Some(old_render_target), &self.queue, self.text_sample_count, )?; *old_render_target = new_render_target; Ok(()) }, )?; self.text_to_screen.update_on_resize(&self.render_targets)?; self.needs_update = true; self.ortho = ortho(0.0, width as f32, 0.0, height as f32, -1.0, 1.0); self.width = width; self.height = height; let callback_self = unsafe { remove_life_time_mut(self) }; for (_, elements) in self.layers.iter() { for framable in elements.iter_framables() { framable.resize(callback_self)?; } } Ok(()) } } impl<'a> From<&'a ecs::World> for &'a GuiHandler { fn from(world: &'a ecs::World) -> &'a GuiHandler { world.resources.get::() } }