use crate::prelude::*; use anyhow::{Result, anyhow}; use assetpath::AssetPath; 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, RwLock, atomic::{AtomicBool, AtomicU32, Ordering::SeqCst}, }; 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: RwLock>>>, } 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.read().unwrap())?; 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.read().unwrap()) })?; Ok(()) } } pub struct GuiHandler { device: Arc, queue: Arc>, width: AtomicU32, height: AtomicU32, resource_base_path: Option, top_ui: RwLock>>, tooltip_ui: RwLock>>, 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: RwLock>>, internal_textures: RwLock>>, internal_colors: RwLock>, element_creator: Mutex, ortho: RwLock>, icon_descriptor_layout: Arc, needs_update: AtomicBool, text_change_queue: RwLock Result<()> + Send + Sync>>>, on_selected: RwLock Result<()> + Send + Sync>>>, #[cfg(feature = "audio")] click_sound: Option, #[cfg(feature = "audio")] hover_sound: Option, // ----- gui handling ----- layers: Mutex>, mouse_x: AtomicU32, mouse_y: AtomicU32, last_direction: Mutex, current_writeable: RwLock>>, current_hoverable: RwLock>>, current_clickable: RwLock>>, current_selectable: RwLock>>, text_sample_count: VkSampleCountFlags, callback_list: Mutex 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(Arc::new(GuiHandler { device: device.clone(), queue: queue.clone(), width: AtomicU32::new(context.width()), height: AtomicU32::new(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: RwLock::new(None), tooltip_ui: RwLock::new(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: RwLock::new(HashMap::new()), internal_textures: RwLock::new(HashMap::new()), internal_colors: RwLock::new(HashMap::new()), element_creator: Mutex::new(ElementCreator::new(device.clone(), queue.clone())?), icon_descriptor_layout, needs_update: AtomicBool::new(true), text_change_queue: RwLock::new(Vec::new()), on_selected: RwLock::new(None), layers: Mutex::default(), ortho: RwLock::new(ortho( 0.0, context.width() as f32, 0.0, context.height() as f32, -1.0, 1.0, )), mouse_x: AtomicU32::new(0), mouse_y: AtomicU32::new(0), last_direction: Mutex::new(GuiDirection::None), current_clickable: RwLock::new(None), current_hoverable: RwLock::new(None), current_selectable: RwLock::new(None), current_writeable: RwLock::new(None), text_sample_count, callback_list: Mutex::default(), })) } pub fn device(&self) -> &Arc { &self.device } pub fn queue(&self) -> &Arc> { &self.queue } pub fn width(&self) -> u32 { self.width.load(SeqCst) } pub fn height(&self) -> u32 { self.height.load(SeqCst) } pub(crate) fn icon_descriptor_layout(&self) -> &Arc { &self.icon_descriptor_layout } pub(crate) fn request_icon( &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 .write() .unwrap() .insert(path.clone(), Arc::downgrade(&image)); } Ok(image) } IconBuilderType::Path(mut path) => { let mut internal_icons = self.internal_icons.write().unwrap(); match 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())?; 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( &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(&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.read().unwrap().contains_key(&path) { Ok(self.internal_textures.read().unwrap()[&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 .write() .unwrap() .insert(path.clone(), texture.clone()); Ok(texture.clone()) } } pub(crate) fn color_descriptor(&self, color: Color) -> Result> { if self.internal_colors.read().unwrap().contains_key(&color) { Ok(self.internal_colors.read().unwrap()[&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 .write() .unwrap() .insert(color, textable_color); Ok(self.internal_colors.read().unwrap()[&color] ._descriptor_set .clone()) } } pub(crate) fn ortho(&self) -> cgmath::Matrix4 { *self.ortho.read().unwrap() } 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(&self, f: F) -> Result<()> where F: Fn() -> Result<()> + Send + Sync + 'static, { *self.on_selected.write().unwrap() = Some(Box::new(f)); Ok(()) } // --------------------------------------------------------------------- // ------------------------- event handling -------------------------- // --------------------------------------------------------------------- pub fn set_mouse_pos(&self, x: u32, y: u32) -> Result<()> { self.mouse_x.store(x, SeqCst); self.mouse_y.store(y, SeqCst); let mut hovered = None; { let layers = self.layers.lock().unwrap(); for (_, elements) in layers.iter() { for hoverable in elements.iter_hoverables() { if hoverable.is_hovered(x as i32, y as i32) { hovered = Some(hoverable.clone()); break; } } } } let mut current_hoverable = self.current_hoverable.write().unwrap(); match hovered { Some(hovered) => { if let Some(current) = &*current_hoverable { if Arc::ptr_eq(current, &hovered) { return Ok(()); } current.set_hovered(false)?; } hovered.set_hovered(true)?; *current_hoverable = Some(hovered); } None => { if current_hoverable.is_some() { // unwrap is safe, just tested for `is_some` if !current_hoverable .as_ref() .unwrap() .is_hovered(x as i32, y as i32) { current_hoverable.as_ref().unwrap().set_hovered(false)?; *current_hoverable = None; } } } } Ok(()) } pub fn mouse_position(&self) -> (u32, u32) { (self.mouse_x.load(SeqCst), self.mouse_y.load(SeqCst)) } fn find_clickable(&self) -> Result>> { let layers = self.layers.lock().unwrap(); for (_, elements) in layers.iter() { for clickable in elements.iter_clickables() { if clickable.is_pressed( self.mouse_x.load(SeqCst) as i32, self.mouse_y.load(SeqCst) as i32, ) { *self.current_clickable.write().unwrap() = Some(clickable.clone()); return Ok(Some(clickable.clone())); } } } Ok(None) } pub fn mouse_down(&self, mouse_button: MouseButton) -> Result { if mouse_button == MouseButton::Left { if let Some(tmp_clickable) = self.find_clickable()? { tmp_clickable.set_clicked(true)?; return Ok(true); } } Ok(false) } pub fn mouse_up(&self, mouse_button: MouseButton) -> Result { if mouse_button == MouseButton::Left { let mut clickable = self.current_clickable.write().unwrap(); if clickable.is_some() { clickable.as_ref().unwrap().set_clicked(false)?; if clickable.as_ref().unwrap().is_pressed( self.mouse_x.load(SeqCst) as i32, self.mouse_y.load(SeqCst) as i32, ) { if let Some(hoverable) = self.current_hoverable.read().unwrap().as_ref() { hoverable.set_hovered(true)?; } } *clickable = None; return Ok(true); } } Ok(false) } pub fn current_selectable(&self) -> Result>> { match self.current_selectable.read().unwrap().as_ref() { Some(selectable) => Ok(Some(selectable.clone())), None => Ok(None), } } pub fn accept_selection(&self) -> Result { if let Some(current_selectable) = self.current_selectable()? { current_selectable.click_event()?; return Ok(true); } Ok(false) } pub fn accept_custom_selection(&self, button: ControllerButton) -> Result { if let Some(current_selectable) = self.current_selectable()? { if current_selectable.custom_click_event(button)? { return Ok(true); } } Ok(false) } pub fn decline_topgui(&self) -> Result { // workaround for unwanted borrowing behaviour inside decline function let opt_topgui = { self.top_ui.read().unwrap().as_ref().cloned() }; if let Some(topgui) = opt_topgui { topgui.decline()?; return Ok(true); } Ok(false) } pub fn next_tab_topgui(&self, second_level: bool) -> Result { // workaround for unwanted borrowing behaviour inside decline function let opt_topgui = { self.top_ui.read().unwrap().as_ref().cloned() }; if let Some(topgui) = opt_topgui { topgui.next_tab(second_level)?; return Ok(true); } Ok(false) } pub fn previous_tab_topgui(&self, second_level: bool) -> Result { // workaround for unwanted borrowing behaviour inside decline function let opt_topgui = { self.top_ui.read().unwrap().as_ref().cloned() }; if let Some(topgui) = opt_topgui { topgui.previous_tab(second_level)?; return Ok(true); } Ok(false) } pub fn writeable(&self) -> Result>> { Ok(self.current_writeable.read().unwrap().as_ref().cloned()) } pub fn remove_char(&self) -> Result { match self.current_writeable.read().unwrap().as_ref() { Some(current_writable) => { current_writable.remove_last()?; Ok(true) } None => Ok(false), } } pub fn check_navigatable(&self) -> Result { Ok(self.current_selectable.read().unwrap().is_some()) } pub fn update_selection(&self, direction: GuiDirection) -> Result { let mut last_direction = self.last_direction.lock().unwrap(); if direction != *last_direction { *last_direction = direction; match self.current_selectable.write().unwrap().as_mut() { Some(current_selectable) => match direction { GuiDirection::Left => { if let Some(neighbour) = current_selectable.west_neighbour() { current_selectable.set_selected(false)?; *current_selectable = neighbour; current_selectable.set_selected(true)?; }; Ok(true) } GuiDirection::Right => { if let Some(neighbour) = current_selectable.east_neighbour() { current_selectable.set_selected(false)?; *current_selectable = neighbour; current_selectable.set_selected(true)?; }; Ok(true) } GuiDirection::Up => { if let Some(neighbour) = current_selectable.north_neighbour() { current_selectable.set_selected(false)?; *current_selectable = neighbour; current_selectable.set_selected(true)?; }; Ok(true) } GuiDirection::Down => { if let Some(neighbour) = current_selectable.south_neighbour() { current_selectable.set_selected(false)?; *current_selectable = neighbour; current_selectable.set_selected(true)?; }; Ok(true) } GuiDirection::None => Ok(false), }, None => Ok(false), } } else { Ok(false) } } pub(crate) fn enqueue_text_update(&self, function: Box Result<()> + Send + Sync>) { self.text_change_queue.write().unwrap().push(function); self.needs_update.store(true, SeqCst); } pub fn set_top_gui(&self, top_gui: Option>) { *self.top_ui.write().unwrap() = top_gui; } pub fn set_tooltip(&self, tooltip: Option>) { *self.tooltip_ui.write().unwrap() = tooltip; } pub(crate) fn add_callback Result<()> + Send + Sync + 'static>(&self, f: F) { self.callback_list.lock().unwrap().push(Box::new(f)); } pub fn process_callbacks(&self) -> Result<()> { let callbacks = mem::take(&mut *self.callback_list.lock().unwrap()); callbacks.into_iter().try_for_each(|callback| callback()) } fn render( &self, buffer_recorder: &mut CommandBufferRecorder<'_>, command_buffer_state: &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.load(SeqCst) as f32, height: self.height.load(SeqCst) as f32, minDepth: 0.0, maxDepth: 1.0, }]; let scissor = [VkRect2D { offset: VkOffset2D { x: 0, y: 0 }, extent: VkExtent2D { width: self.width.load(SeqCst), height: self.height.load(SeqCst), }, }]; let layers = self.layers.lock().unwrap(); for (_, elements) in 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); let mut text_buffers = command_buffer_state.text_buffers.write().unwrap(); 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); 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) => { let mut layers = $layers.lock().unwrap(); 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) => { let mut layers = $layers.lock().unwrap(); 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) => { let mut layers = $layers.lock().unwrap(); paste! { match layers.iter_mut().find(|(id, _)| *id == $layer_id) { Some((_, layer)) => { if layer.[]($element) { $update.store(true, SeqCst); } } None => { panic!("layer not present from which {} is to remove", stringify!($element)); } } } }; } // object handling impl GuiHandler { // framable pub(crate) fn add_framable(&self, layer: i32, framable: Arc) -> Result<()> { add_element!(self.layers, layer, framable); Ok(()) } pub(crate) fn delete_framable(&self, layer: i32, framable: &Arc) -> Result<()> { remove_element!(self.layers, layer, framable); Ok(()) } // hoverable pub(crate) fn add_hoverable(&self, layer: i32, hoverable: Arc) -> Result<()> { add_element!(self.layers, layer, hoverable); Ok(()) } pub(crate) fn delete_hoverable(&self, layer: i32, hoverable: &Arc) -> Result<()> { let mut current_hoverable = self.current_hoverable.write().unwrap(); if current_hoverable.is_some() { // unwrap is safe, just tested for `is_some` if Arc::ptr_eq(hoverable, current_hoverable.as_ref().unwrap()) { *current_hoverable = None; } } remove_element!(self.layers, layer, hoverable); Ok(()) } // selectable pub(crate) fn add_selectable(&self, layer: i32, selectable: Arc) -> Result<()> { add_element!(self.layers, layer, selectable); Ok(()) } pub(crate) fn delete_selectable(&self, layer: i32, selectable: &Arc) -> Result<()> { let mut current_selectable = self.current_selectable.write().unwrap(); if current_selectable.is_some() { // unwrap is safe, just tested for `is_some` if Arc::ptr_eq(selectable, current_selectable.as_ref().unwrap()) { *current_selectable = None; } } remove_element!(self.layers, layer, selectable); Ok(()) } // displayable pub(crate) fn add_displayable(&self, layer: i32, displayable: Arc) -> Result<()> { add_element!(self.layers, layer, displayable); self.needs_update.store(true, SeqCst); Ok(()) } pub(crate) fn delete_displayable( &self, layer: i32, displayable: &Arc, ) -> Result<()> { remove_element!(self.layers, layer, displayable, self.needs_update); Ok(()) } // clickable pub(crate) fn add_clickable(&self, layer: i32, clickable: Arc) -> Result<()> { add_element!(self.layers, layer, clickable); Ok(()) } pub(crate) fn delete_clickable(&self, layer: i32, clickable: &Arc) -> Result<()> { let mut current_clickable = self.current_clickable.write().unwrap(); if current_clickable.is_some() { // unwrap is safe, just tested for `is_some` if ptr::eq(&clickable, ¤t_clickable.as_ref().unwrap()) { *current_clickable = None; } } remove_element!(self.layers, layer, clickable); Ok(()) } // textable pub(crate) fn add_textable(&self, layer: i32, textable: Arc) -> Result<()> { add_element!(self.layers, layer, textable); self.needs_update.store(true, SeqCst); Ok(()) } pub(crate) fn delete_textable(&self, layer: i32, textable: &Arc) -> Result<()> { remove_element!(self.layers, layer, textable, self.needs_update); Ok(()) } // writable pub(crate) fn add_writeable(&self, layer: i32, writeable: Arc) -> Result<()> { add_element!(self.layers, layer, writeable); Ok(()) } pub(crate) fn set_active_writeable(&self, writeable: Arc) -> Result { let mut current_writeable = self.current_writeable.write().unwrap(); if let Some(current) = current_writeable.as_ref() { // change nothing if both are the same if Arc::ptr_eq(current, &writeable) { return Ok(false); } current.inactivation_event()?; } *current_writeable = Some(writeable); Ok(true) } pub(crate) fn delete_writeable(&self, layer: i32, writeable: &Arc) -> Result<()> { { let mut current_writeable = self.current_writeable.write().unwrap(); if let Some(w) = &*current_writeable { if Arc::ptr_eq(w, writeable) { w.inactivation_event()?; *current_writeable = None; } } } remove_element!(self.layers, layer, writeable); Ok(()) } // iconizable pub(crate) fn add_iconizable(&self, layer: i32, iconizable: Arc) -> Result<()> { add_element!(self.layers, layer, iconizable); self.needs_update.store(true, SeqCst); Ok(()) } pub(crate) fn delete_iconizable(&self, layer: i32, iconizable: &Arc) -> Result<()> { remove_element!(self.layers, layer, iconizable, self.needs_update); Ok(()) } pub(crate) fn set_selectable(&self, selectable: Option>) -> Result<()> { let mut current_selectable = self.current_selectable.write().unwrap(); if let Some(selectable) = current_selectable.as_ref() { selectable.set_selected(false)?; } if let Some(selectable) = selectable.as_ref() { selectable.set_selected(true)?; if let Some(on_selected) = self.on_selected.read().unwrap().as_ref() { on_selected()?; } } *current_selectable = selectable; Ok(()) } pub(crate) fn add_colorable(&self, layer: i32, colorable: Arc) -> Result<()> { add_element!(self.layers, layer, colorable); self.needs_update.store(true, SeqCst); Ok(()) } pub(crate) fn delete_colorable(&self, layer: i32, colorable: &Arc) -> Result<()> { remove_element!(self.layers, layer, colorable, self.needs_update); Ok(()) } } // private - create rendering stuff impl 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(RwLock::new(Self::create_render_target( device, images, None, queue, sample_count, )?)), TargetMode::Stereo(left_images, right_images) => TargetMode::Stereo( RwLock::new(Self::create_render_target( device, left_images, None, queue, sample_count, )?), RwLock::new(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: RwLock::new(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.read().unwrap().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.read().unwrap().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.read().unwrap().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.read().unwrap().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 GuiHandler { pub fn process( &self, buffer_recorder: &mut CommandBufferRecorder<'_>, indices: &TargetMode, ) -> Result<()> { if self.needs_update.load(SeqCst) { let mut text_changes = self.text_change_queue.write().unwrap(); if !text_changes.is_empty() { for text_change in text_changes.iter() { (text_change)()?; } text_changes.clear(); } self.needs_update.store(false, SeqCst); } self.command_buffers .chain(&self.render_targets) .chain(&self.text_objects) .unfold() .chain(&self.rectangle_objects) .unfold() .chain(&self.single_color_objects) .unfold() .chain(indices) .unfold() .chain(&self.text_to_screen.pipeline) .unfold() .chain(&self.text_to_screen.descriptor) .unfold() .execute( |( command_buffers, render_target, text_objects, rectangle_objects, single_color_object, index, text_pipeline, text_descriptor, )| { self.render( buffer_recorder, &command_buffers[****index], &*render_target.read().unwrap(), single_color_object, rectangle_objects, text_objects, text_pipeline, text_descriptor, ****index, ) }, )?; Ok(()) } pub fn resize( &self, width: u32, height: u32, images: &TargetMode>>, ) -> Result<()> { images .chain(&self.render_targets) .execute(|(images, render_target)| { let mut rt_lock = render_target.write().unwrap(); *rt_lock = Self::create_render_target( &self.device, &images, Some(&*rt_lock), &self.queue, self.text_sample_count, )?; Ok(()) })?; self.text_to_screen.update_on_resize(&self.render_targets)?; self.needs_update.store(true, SeqCst); *self.ortho.write().unwrap() = ortho(0.0, width as f32, 0.0, height as f32, -1.0, 1.0); self.width.store(width, SeqCst); self.height.store(height, SeqCst); let layers = self.layers.lock().unwrap(); for (_, elements) in layers.iter() { for framable in elements.iter_framables() { framable.resize()?; } } Ok(()) } }