use crate::prelude::*; use anyhow::Result; use assetpath::AssetPath; use serde::{Deserialize, Serialize}; use utilities::prelude::*; use vulkan_rs::prelude::*; use super::{ elements::Elements, gui::{iconizable::IconBuilderType, texturedvertex::TexturedVertex}, }; use cgmath::ortho; use std::slice; use std::sync::{ atomic::{AtomicBool, AtomicU32, Ordering::SeqCst}, Arc, Mutex, RwLock, }; use std::{collections::HashMap, ptr}; use std::{ops::Deref, sync::Weak}; use paste::paste; #[derive(Deserialize, Serialize, Clone, Debug)] pub enum Font<'a> { Path(AssetPath), Bytes(&'a [u8]), } #[derive(Deserialize, Serialize, Clone, Debug)] pub struct GuiHandlerCreateInfo<'a> { // default button textures pub menu_button: AssetPath, pub menu_button_selected: AssetPath, // path to the alphabet image #[serde(borrow)] pub font: Font<'a>, // sound info #[cfg(feature = "audio")] pub click_sound: AssetPath, #[cfg(feature = "audio")] pub hover_sound: AssetPath, // resource base directory pub resource_directory: AssetPath, } impl<'a> GuiHandlerCreateInfo<'a> { pub const fn default() -> Self { GuiHandlerCreateInfo { // default button textures menu_button: AssetPath::default(), menu_button_selected: AssetPath::default(), // path to the alphabet image font: Font::Path(AssetPath::default()), // sound info #[cfg(feature = "audio")] click_sound: AssetPath::default(), #[cfg(feature = "audio")] hover_sound: AssetPath::default(), // resource base directory resource_directory: AssetPath::default(), } } } struct GuiSeparator { _descriptor_layout: Arc, _pipeline: Arc, } struct DisplayableTexture { _descriptor_pool: Arc, _descriptor_set: Arc, _texture: Arc, } struct TextableColor { _descriptor_pool: Arc, _descriptor_set: Arc, _buffer: Arc>, } struct CommandBufferState { command_buffer: Arc, valid: AtomicBool, text_buffers: RwLock>>>, } pub struct GuiHandler { context: Arc, device: Arc, queue: Arc>, width: AtomicU32, height: AtomicU32, resource_base_path: AssetPath, top_ui: RwLock>>, tooltip_ui: RwLock>>, render_targets: TargetMode>, command_buffers: TargetMode>, text_objects: TargetMode, rectangle_objects: TargetMode, single_color_objects: TargetMode, _bitmap_font: Arc, _bitmap_desc_pool: Arc, bitmap_desc_set: Arc, text_color_layout: Arc, internal_icons: RwLock>>, internal_textures: RwLock>, internal_colors: RwLock>, ortho: RwLock>, icon_descriptor_layout: Arc, needs_update: AtomicBool, text_change_queue: RwLock Result<()> + Send + Sync>>>, on_selected: RwLock Result<()> + Send + Sync>>>, menu_button: AssetPath, menu_button_selected: AssetPath, #[cfg(feature = "audio")] click_sound: AssetPath, #[cfg(feature = "audio")] hover_sound: AssetPath, // ----- 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>>, } impl GuiHandler { pub fn new( gui_handler_create_info: GuiHandlerCreateInfo<'_>, context: &Arc, ) -> Result> { let device = context.device(); let queue = context.queue(); let command_buffers = match context.images() { TargetMode::Mono(_) => { let command_buffers = Self::create_command_buffers(device, queue, context)?; TargetMode::Mono(command_buffers) } TargetMode::Stereo(_, _) => { let left_command_buffers = Self::create_command_buffers(device, queue, context)?; let right_command_buffers = Self::create_command_buffers(device, queue, context)?; TargetMode::Stereo(left_command_buffers, right_command_buffers) } }; // let render_pass = // Self::create_render_pass(device, context.format(), context.image_layout())?; // let framebuffers = Self::create_framebuffers(device, &context.images(), &render_pass)?; let render_targets = Self::create_render_targets(device, &context.images())?; let (text_objs, color_layout) = Self::init_text_objects(device, &render_targets)?; let rect_objs = Self::init_rectangle_objects(device, &render_targets)?; let single_color_objects = Self::init_single_color_objects(device, &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.clone(), )?; 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 { context: context.clone(), device: device.clone(), queue: queue.clone(), width: AtomicU32::new(context.width()), height: AtomicU32::new(context.height()), menu_button: { let mut menu_button = gui_handler_create_info.menu_button; menu_button.set_prefix(&gui_handler_create_info.resource_directory.full_path()); menu_button }, menu_button_selected: { let mut menu_button_selected = gui_handler_create_info.menu_button_selected; menu_button_selected .set_prefix(&gui_handler_create_info.resource_directory.full_path()); menu_button_selected }, #[cfg(feature = "audio")] click_sound: { let mut click_sound = gui_handler_create_info.click_sound; click_sound.set_prefix(&gui_handler_create_info.resource_directory.full_path()); click_sound }, #[cfg(feature = "audio")] hover_sound: { let mut hover_sound = gui_handler_create_info.hover_sound; hover_sound.set_prefix(&gui_handler_create_info.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, _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()), 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), })) } pub fn device(&self) -> &Arc { &self.device } pub fn queue(&self) -> &Arc> { &self.queue } #[cfg(feature = "audio")] pub(crate) fn context(&self) -> &Arc { &self.context } 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().full_path()); } let image = Image::from_file(path)? .max_mip_map_levels() .attach_sampler(Sampler::pretty_sampler().build(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().full_path()); } let image = Image::from_file(path.clone())? .max_mip_map_levels() .attach_sampler(Sampler::pretty_sampler().build(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 image_descriptor(&self, mut path: AssetPath) -> Result> { if !path.has_prefix() { path.set_prefix(&self.resource_base_path.full_path()); } if self.internal_textures.read().unwrap().contains_key(&path) { Ok(self.internal_textures.read().unwrap()[&path] ._descriptor_set .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)?; let desc_pool = DescriptorPool::builder() .set_layout(self.text_desc_layout().clone()) .build(self.device.clone())?; let descriptor_set = DescriptorPool::prepare_set(&desc_pool).allocate()?; descriptor_set.update(&[DescriptorWrite::combined_samplers(0, &[&texture])])?; let displayable_texture = DisplayableTexture { _descriptor_pool: desc_pool, _descriptor_set: descriptor_set, _texture: texture, }; self.internal_textures .write() .unwrap() .insert(path.clone(), displayable_texture); Ok(self.internal_textures.read().unwrap()[&path] ._descriptor_set .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 buffer = Buffer::builder() .set_usage(VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT) .set_memory_usage(MemoryUsage::CpuOnly) .set_data(&color_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) -> &AssetPath { &self.resource_base_path } pub fn menu_button(&self) -> &AssetPath { &self.menu_button } pub fn menu_button_selected(&self) -> &AssetPath { &self.menu_button_selected } #[cfg(feature = "audio")] pub fn click_sound(&self) -> &AssetPath { &self.click_sound } #[cfg(feature = "audio")] pub fn hover_sound(&self) -> &AssetPath { &self.hover_sound } 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) } 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; } fn render( &self, command_buffer_state: &CommandBufferState, render_target: &RenderTarget, single_color_objects: &GuiSeparator, rectangle_objects: &GuiSeparator, text_objects: &GuiSeparator, ) -> Result<()> { if !command_buffer_state.valid.load(SeqCst) { let gui_command_buffer = &command_buffer_state.command_buffer; let inheritance_info = render_target.inheritance_info(); let mut command_buffer_begin_info = VkCommandBufferBeginInfo::new( VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT | VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT, ); command_buffer_begin_info.set_inheritance_info(&inheritance_info); let mut buffer_recorder = gui_command_buffer.begin(command_buffer_begin_info)?; 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() { 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); } } if !elements.is_textables_empty() { 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()); } } } } command_buffer_state.valid.store(true, SeqCst); } 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 impl GuiHandler { fn create_render_targets( device: &Arc, target_images: &TargetMode>>, ) -> Result>> { let create = |target_images: &Vec>| -> Result { RenderTarget::builder() .add_sub_pass( SubPass::builder(target_images[0].width(), target_images[0].height()) .set_prepared_targets(target_images, 0, [0.0, 0.0, 0.0, 0.0], false) .build(device)?, ) .build(device) }; Ok(match target_images { TargetMode::Mono(images) => TargetMode::Mono(RwLock::new(create(images)?)), TargetMode::Stereo(left_images, right_images) => TargetMode::Stereo( RwLock::new(create(left_images)?), RwLock::new(create(right_images)?), ), }) } fn create_command_buffers( device: &Arc, queue: &Arc>, context: &Arc, ) -> Result> { let mut command_buffers = Vec::with_capacity(context.image_count()); for _ in 0..context.image_count() { command_buffers.push(CommandBufferState { command_buffer: CommandBuffer::new_secondary() .build(device.clone(), queue.clone())?, valid: AtomicBool::new(false), 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_sampler(Sampler::pretty_sampler().build(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>, ) -> 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!("guishader/text.vert.spv"); let fragment_shader_text = include_bytes!("guishader/text.frag.spv"); let vertex_shader = ShaderModule::from_slice(device.clone(), vertex_shader_text, ShaderType::Vertex)?; let fragment_shader = ShaderModule::from_slice(device.clone(), fragment_shader_text, ShaderType::Fragment)?; let (input_bindings, input_attributes) = TexturedVertex::vertex_input_state(); Ok(( render_targets.execute(|render_target| { Ok(GuiSeparator { _descriptor_layout: descriptor_layout.clone(), _pipeline: GuiHandler::init_gui_pipeline( device, input_bindings.clone(), input_attributes.clone(), render_target.read().unwrap().render_pass(), &pipeline_layout, vertex_shader.clone(), fragment_shader.clone(), )?, }) })?, 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!("guishader/rect.vert.spv"), ShaderType::Vertex, )?; let fragment_shader = ShaderModule::from_slice( device.clone(), include_bytes!("guishader/rect.frag.spv"), ShaderType::Fragment, )?; let (input_bindings, input_attributes) = TexturedVertex::vertex_input_state(); render_targets.execute(|render_target| { Ok(GuiSeparator { _descriptor_layout: descriptor_layout.clone(), _pipeline: GuiHandler::init_gui_pipeline( device, input_bindings.clone(), input_attributes.clone(), render_target.read().unwrap().render_pass(), &pipeline_layout, vertex_shader.clone(), fragment_shader.clone(), )?, }) }) } 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!("guishader/single_color.vert.spv"), ShaderType::Vertex, )?; let fragment_shader = ShaderModule::from_slice( device.clone(), include_bytes!("guishader/single_color.frag.spv"), ShaderType::Fragment, )?; let (input_bindings, input_attributes) = Colorable::vertex_input_state(); render_targets.execute(|render_target| { Ok(GuiSeparator { _descriptor_layout: color_layout.clone(), _pipeline: GuiHandler::init_gui_pipeline( device, input_bindings.clone(), input_attributes.clone(), render_target.read().unwrap().render_pass(), &pipeline_layout, vertex_shader.clone(), fragment_shader.clone(), )?, }) }) } fn init_gui_pipeline( device: &Arc, binding_description: Vec, attribute_description: Vec, render_pass: &Arc, pipeline_layout: &Arc, vertex_shader: Arc, fragment_shader: Arc, ) -> Result> { Pipeline::new_graphics() .set_vertex_shader(vertex_shader, binding_description, attribute_description) .set_fragment_shader(fragment_shader) .input_assembly(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, false) .default_multisample(VK_SAMPLE_COUNT_1_BIT) .default_color_blend(vec![VkPipelineColorBlendAttachmentState::default()]) .default_rasterization(VK_CULL_MODE_NONE, VK_FRONT_FACE_CLOCKWISE) .build(device.clone(), pipeline_layout, render_pass, 0) } #[inline] fn select_rendering( &self, buffer_recorder: &mut CommandBufferRecorder<'_>, command_buffer_states: &[CommandBufferState], render_target: &RenderTarget, single_color_objects: &GuiSeparator, rectangle_objects: &GuiSeparator, text_objects: &GuiSeparator, index: usize, ) -> Result<()> { let command_buffer_state = &command_buffer_states[index]; self.render( command_buffer_state, render_target, single_color_objects, rectangle_objects, text_objects, )?; render_target.begin( buffer_recorder, VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS, index, ); buffer_recorder.execute_commands(&[&command_buffer_state.command_buffer]); render_target.end(buffer_recorder); Ok(()) } } impl GuiHandler { pub fn process( &self, buffer_recorder: &mut CommandBufferRecorder<'_>, indices: &TargetMode, ) -> Result<()> { if self.needs_update.load(SeqCst) { match &self.command_buffers { TargetMode::Mono(command_buffers) => { for state in command_buffers { state.valid.store(false, SeqCst); } } TargetMode::Stereo(left_cbs, right_cbs) => { for state in left_cbs { state.valid.store(false, SeqCst); } for state in right_cbs { state.valid.store(false, 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); } match ( &self.command_buffers, &self.render_targets, &self.text_objects, &self.rectangle_objects, &self.single_color_objects, indices, ) { ( TargetMode::Mono(command_buffers), TargetMode::Mono(render_target), TargetMode::Mono(text_objects), TargetMode::Mono(rectangle_objects), TargetMode::Mono(single_color_object), TargetMode::Mono(index), ) => { self.select_rendering( buffer_recorder, command_buffers, &*render_target.read().unwrap(), single_color_object, rectangle_objects, text_objects, *index, )?; } ( TargetMode::Stereo(left_cbs, right_cbs), TargetMode::Stereo(left_rt, right_rt), TargetMode::Stereo(left_text_objects, right_text_objects), TargetMode::Stereo(left_rectangle_objects, right_rectangle_objects), TargetMode::Stereo(left_single_color_object, right_single_color_object), TargetMode::Stereo(left_index, right_index), ) => { self.select_rendering( buffer_recorder, left_cbs, &*left_rt.read().unwrap(), left_single_color_object, left_rectangle_objects, left_text_objects, *left_index, )?; self.select_rendering( buffer_recorder, right_cbs, &*right_rt.read().unwrap(), right_single_color_object, right_rectangle_objects, right_text_objects, *right_index, )?; } _ => panic!("Invalid TargetMode combination"), }; Ok(()) } pub fn resize(&self, width: u32, height: u32) -> Result<()> { todo!(); // *self.framebuffers.lock().unwrap() = Self::create_framebuffers( // self.context.device(), // &self.context.images(), // &self.render_pass, // )?; 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(()) } }