ui/src/gui_handler/gui_handler.rs

1684 lines
54 KiB
Rust
Raw Normal View History

2023-01-16 09:53:52 +00:00
use crate::prelude::*;
use anyhow::Result;
use assetpath::AssetPath;
2023-01-17 06:08:29 +00:00
use serde::{Deserialize, Serialize};
2023-02-01 13:42:25 +00:00
use utilities::{impl_reprc, prelude::*};
2023-01-22 15:02:46 +00:00
use vulkan_rs::{prelude::*, render_target::sub_pass::InputAttachmentInfo};
2023-01-16 09:53:52 +00:00
use super::{
elements::Elements,
2023-01-27 12:46:54 +00:00
gui::{
colorable::ColorableVertex, iconizable::IconBuilderType, texturedvertex::TexturedVertex,
},
2023-01-16 09:53:52 +00:00
};
2024-05-13 13:03:54 +00:00
use cgmath::{ortho, vec2};
2023-01-16 09:53:52 +00:00
use std::sync::{
Arc, Mutex, RwLock,
2025-02-27 09:43:07 +00:00
atomic::{AtomicBool, AtomicU32, Ordering::SeqCst},
2023-01-16 09:53:52 +00:00
};
use std::{collections::HashMap, ptr};
2024-08-29 09:09:17 +00:00
use std::{mem, sync::Weak};
2023-01-16 09:53:52 +00:00
use paste::paste;
2023-01-17 06:24:20 +00:00
#[derive(Deserialize, Serialize, Clone, Debug)]
2023-01-18 11:24:56 +00:00
pub enum Font<'a> {
Path(AssetPath),
Bytes(&'a [u8]),
}
2023-01-21 13:40:58 +00:00
impl Default for Font<'_> {
fn default() -> Self {
Self::Path(AssetPath::default())
}
}
#[derive(Deserialize, Serialize, Clone, Debug, Default)]
2023-01-18 11:24:56 +00:00
pub struct GuiHandlerCreateInfo<'a> {
2023-01-16 09:53:52 +00:00
// path to the alphabet image
2023-01-18 11:24:56 +00:00
#[serde(borrow)]
2023-01-18 11:27:08 +00:00
pub font: Font<'a>,
2023-01-16 09:53:52 +00:00
// sound info
2023-01-17 06:07:19 +00:00
#[cfg(feature = "audio")]
2025-02-28 05:26:57 +00:00
pub click_sound: Option<AssetPath>,
2023-01-17 06:07:19 +00:00
#[cfg(feature = "audio")]
2025-02-28 05:26:57 +00:00
pub hover_sound: Option<AssetPath>,
2023-01-16 09:53:52 +00:00
// resource base directory
2025-02-28 05:26:57 +00:00
pub resource_directory: Option<AssetPath>,
2023-01-16 09:53:52 +00:00
}
2023-01-18 11:24:56 +00:00
impl<'a> GuiHandlerCreateInfo<'a> {
2023-01-21 13:40:58 +00:00
pub const fn new() -> Self {
2023-01-17 06:24:20 +00:00
GuiHandlerCreateInfo {
// path to the alphabet image
2025-02-28 05:26:57 +00:00
font: Font::Bytes(include_bytes!("default_font.png")),
2023-01-17 06:24:20 +00:00
// sound info
#[cfg(feature = "audio")]
2025-02-28 05:26:57 +00:00
click_sound: None,
2023-01-17 06:24:20 +00:00
#[cfg(feature = "audio")]
2025-02-28 05:26:57 +00:00
hover_sound: None,
2023-01-17 06:24:20 +00:00
// resource base directory
2025-02-28 05:26:57 +00:00
resource_directory: None,
2023-01-17 06:24:20 +00:00
}
}
}
2023-01-16 09:53:52 +00:00
struct GuiSeparator {
_descriptor_layout: Arc<DescriptorSetLayout>,
2023-01-22 15:02:46 +00:00
pipeline: Arc<Pipeline>,
2023-01-16 09:53:52 +00:00
}
2023-02-01 13:42:25 +00:00
impl_reprc!(
struct ColorBuffer {
#[assume_reprc]
color: f32,
}
);
2023-01-16 09:53:52 +00:00
struct TextableColor {
_descriptor_pool: Arc<DescriptorPool>,
_descriptor_set: Arc<DescriptorSet>,
2023-02-01 13:42:25 +00:00
_buffer: Arc<Buffer<ColorBuffer>>,
2023-01-16 09:53:52 +00:00
}
struct CommandBufferState {
text_buffers: RwLock<Vec<Arc<Buffer<TexturedVertex>>>>,
}
2023-01-22 15:02:46 +00:00
struct TextToScreen {
pipeline: TargetMode<GuiSeparator>,
descriptor: TargetMode<Arc<DescriptorSet>>,
buffer: Arc<Buffer<TexturedVertex>>,
}
impl TextToScreen {
fn new(
device: &Arc<Device>,
pipeline: TargetMode<GuiSeparator>,
render_target: &TargetMode<RwLock<RenderTarget>>,
) -> Result<Self> {
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 {
2024-05-13 13:03:54 +00:00
position: vec2(-1.0, -1.0),
2023-01-22 15:02:46 +00:00
texture_coordinates: vec2(0.0, 0.0),
},
TexturedVertex {
2024-05-13 13:03:54 +00:00
position: vec2(-1.0, 1.0),
2023-01-22 15:02:46 +00:00
texture_coordinates: vec2(0.0, 1.0),
},
TexturedVertex {
2024-05-13 13:03:54 +00:00
position: vec2(1.0, 1.0),
2023-01-22 15:02:46 +00:00
texture_coordinates: vec2(1.0, 1.0),
},
TexturedVertex {
2024-05-13 13:03:54 +00:00
position: vec2(1.0, 1.0),
2023-01-22 15:02:46 +00:00
texture_coordinates: vec2(1.0, 1.0),
},
TexturedVertex {
2024-05-13 13:03:54 +00:00
position: vec2(1.0, -1.0),
2023-01-22 15:02:46 +00:00
texture_coordinates: vec2(1.0, 0.0),
},
TexturedVertex {
2024-05-13 13:03:54 +00:00
position: vec2(-1.0, -1.0),
2023-01-22 15:02:46 +00:00
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<DescriptorSet>,
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<RwLock<RenderTarget>>) -> Result<()> {
self.descriptor
.chain(render_target)
.execute(|(descriptor, render_target)| {
Self::update_descriptor(descriptor, &*render_target.read().unwrap())
})?;
Ok(())
}
}
2023-01-16 09:53:52 +00:00
pub struct GuiHandler {
device: Arc<Device>,
queue: Arc<Mutex<Queue>>,
width: AtomicU32,
height: AtomicU32,
resource_base_path: AssetPath,
top_ui: RwLock<Option<Arc<dyn TopGui>>>,
tooltip_ui: RwLock<Option<Arc<dyn TopGui>>>,
render_targets: TargetMode<RwLock<RenderTarget>>,
2023-01-16 09:53:52 +00:00
command_buffers: TargetMode<Vec<CommandBufferState>>,
text_objects: TargetMode<GuiSeparator>,
rectangle_objects: TargetMode<GuiSeparator>,
single_color_objects: TargetMode<GuiSeparator>,
2023-01-16 09:53:52 +00:00
2023-01-22 15:02:46 +00:00
text_to_screen: TextToScreen,
2023-01-16 09:53:52 +00:00
_bitmap_font: Arc<Image>,
_bitmap_desc_pool: Arc<DescriptorPool>,
bitmap_desc_set: Arc<DescriptorSet>,
text_color_layout: Arc<DescriptorSetLayout>,
internal_icons: RwLock<HashMap<AssetPath, Weak<Image>>>,
2024-03-25 11:33:56 +00:00
internal_textures: RwLock<HashMap<AssetPath, Arc<Image>>>,
2023-01-16 09:53:52 +00:00
internal_colors: RwLock<HashMap<Color, TextableColor>>,
2024-03-25 11:33:56 +00:00
element_creator: Mutex<ElementCreator>,
2023-01-16 09:53:52 +00:00
ortho: RwLock<cgmath::Matrix4<f32>>,
icon_descriptor_layout: Arc<DescriptorSetLayout>,
needs_update: AtomicBool,
text_change_queue: RwLock<Vec<Box<dyn Fn() -> Result<()> + Send + Sync>>>,
on_selected: RwLock<Option<Box<dyn Fn() -> Result<()> + Send + Sync>>>,
2023-01-17 06:07:19 +00:00
#[cfg(feature = "audio")]
2025-02-28 05:26:57 +00:00
click_sound: Option<AssetPath>,
2023-01-17 06:07:19 +00:00
#[cfg(feature = "audio")]
2025-02-28 05:26:57 +00:00
hover_sound: Option<AssetPath>,
2023-01-16 09:53:52 +00:00
// ----- gui handling -----
layers: Mutex<Vec<(i32, Elements)>>,
mouse_x: AtomicU32,
mouse_y: AtomicU32,
last_direction: Mutex<GuiDirection>,
current_writeable: RwLock<Option<Arc<Writeable>>>,
current_hoverable: RwLock<Option<Arc<Hoverable>>>,
current_clickable: RwLock<Option<Arc<Clickable>>>,
current_selectable: RwLock<Option<Arc<Selectable>>>,
text_sample_count: VkSampleCountFlags,
2024-08-29 09:09:17 +00:00
callback_list: Mutex<Vec<Box<dyn FnOnce() -> Result<()> + Send + Sync>>>,
2023-01-16 09:53:52 +00:00
}
impl GuiHandler {
pub fn new(
2023-01-18 11:24:56 +00:00
gui_handler_create_info: GuiHandlerCreateInfo<'_>,
2025-02-27 09:43:07 +00:00
context: &impl ContextInterface,
2023-01-16 09:53:52 +00:00
) -> Result<Arc<GuiHandler>> {
let device = context.device();
let queue = context.queue();
2023-01-22 15:02:46 +00:00
let command_buffers = context
.images()
.execute(|_| Self::create_command_buffers(context.image_count()))?;
2023-01-16 09:53:52 +00:00
let text_sample_count = device.max_supported_sample_count(VK_SAMPLE_COUNT_4_BIT);
2023-01-16 09:53:52 +00:00
let render_targets =
Self::create_render_targets(device, &context.images(), queue, text_sample_count)?;
2023-01-21 22:34:01 +00:00
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)?;
2023-01-16 09:53:52 +00:00
2023-01-22 15:02:46 +00:00
let text_to_screen = TextToScreen::new(
device,
Self::init_text_screen_objects(device, &render_targets)?,
&render_targets,
)?;
2023-01-16 09:53:52 +00:00
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(),
2025-02-28 05:38:41 +00:00
gui_handler_create_info.font,
2023-01-16 09:53:52 +00:00
)?;
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())?;
2023-01-16 11:58:59 +00:00
Ok(Arc::new(GuiHandler {
2023-01-16 09:53:52 +00:00
device: device.clone(),
queue: queue.clone(),
2023-01-16 11:58:59 +00:00
width: AtomicU32::new(context.width()),
height: AtomicU32::new(context.height()),
2023-01-16 09:53:52 +00:00
2023-01-17 06:07:19 +00:00
#[cfg(feature = "audio")]
2025-02-28 05:26:57 +00:00
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(),
);
2023-01-16 09:53:52 +00:00
click_sound
2025-02-28 05:26:57 +00:00
}),
2023-01-16 09:53:52 +00:00
2023-01-17 06:07:19 +00:00
#[cfg(feature = "audio")]
2025-02-28 05:26:57 +00:00
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(),
);
2023-01-16 09:53:52 +00:00
hover_sound
2025-02-28 05:26:57 +00:00
}),
2023-01-16 09:53:52 +00:00
2025-02-28 05:26:57 +00:00
resource_base_path: gui_handler_create_info
.resource_directory
.expect("missing resource directory"),
2023-01-16 09:53:52 +00:00
top_ui: RwLock::new(None),
tooltip_ui: RwLock::new(None),
render_targets,
2023-01-16 09:53:52 +00:00
command_buffers,
text_objects: text_objs,
rectangle_objects: rect_objs,
single_color_objects,
2023-01-22 15:02:46 +00:00
text_to_screen,
2023-01-16 09:53:52 +00:00
_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()),
2024-03-25 11:33:56 +00:00
element_creator: Mutex::new(ElementCreator::new(device.clone(), queue.clone())?),
2023-01-16 09:53:52 +00:00
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,
2023-01-16 11:58:59 +00:00
context.width() as f32,
2023-01-16 09:53:52 +00:00
0.0,
2023-01-16 11:58:59 +00:00
context.height() as f32,
2023-01-16 09:53:52 +00:00
-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,
2024-08-29 09:09:17 +00:00
callback_list: Mutex::default(),
2023-01-16 11:58:59 +00:00
}))
2023-01-16 09:53:52 +00:00
}
pub fn device(&self) -> &Arc<Device> {
&self.device
}
pub fn queue(&self) -> &Arc<Mutex<Queue>> {
&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<DescriptorSetLayout> {
&self.icon_descriptor_layout
}
pub(crate) fn request_icon(
&self,
icon_builder: impl Into<IconBuilderType>,
) -> Result<Arc<Image>> {
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<DescriptorSetLayout> {
&match &self.text_objects {
TargetMode::Mono(l) => l,
TargetMode::Stereo(l, _) => l,
}
._descriptor_layout
}
2024-03-25 11:33:56 +00:00
pub(crate) fn displayable_image_from_descriptor(
&self,
width: u32,
height: u32,
descriptor: ElementDescriptor,
) -> Result<Arc<Image>> {
self.element_creator
.lock()
.unwrap()
.get(width, height, descriptor)
}
pub(crate) fn image_descriptor_set(&self) -> Result<Arc<DescriptorSet>> {
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<Arc<Image>> {
2023-01-16 09:53:52 +00:00
if !path.has_prefix() {
path.set_prefix(&self.resource_base_path.full_path());
}
if self.internal_textures.read().unwrap().contains_key(&path) {
2024-03-25 11:33:56 +00:00
Ok(self.internal_textures.read().unwrap()[&path].clone())
2023-01-16 09:53:52 +00:00
} 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()
2024-03-25 11:33:56 +00:00
.insert(path.clone(), texture.clone());
2023-01-16 09:53:52 +00:00
2024-03-25 11:33:56 +00:00
Ok(texture.clone())
2023-01-16 09:53:52 +00:00
}
}
pub(crate) fn color_descriptor(&self, color: Color) -> Result<Arc<DescriptorSet>> {
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();
2023-02-01 13:42:25 +00:00
let color_buffer_array: [ColorBuffer; 3] = color_array
.into_iter()
.map(|f| ColorBuffer { color: f })
.collect::<Vec<ColorBuffer>>()
.try_into()
.unwrap_or_else(|_: Vec<ColorBuffer>| {
unreachable!("create array from vec from an array")
});
2023-01-16 09:53:52 +00:00
let buffer = Buffer::builder()
.set_usage(VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT)
.set_memory_usage(MemoryUsage::CpuOnly)
2023-02-01 13:42:25 +00:00
.set_data(&color_buffer_array)
2023-01-16 09:53:52 +00:00
.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<f32> {
*self.ortho.read().unwrap()
}
pub(crate) fn resource_base_path(&self) -> &AssetPath {
&self.resource_base_path
}
2023-01-17 06:07:19 +00:00
#[cfg(feature = "audio")]
2025-02-28 05:26:57 +00:00
pub fn click_sound(&self) -> Option<&AssetPath> {
self.click_sound.as_ref()
2023-01-16 09:53:52 +00:00
}
2023-01-17 06:07:19 +00:00
#[cfg(feature = "audio")]
2025-02-28 05:26:57 +00:00
pub fn hover_sound(&self) -> Option<&AssetPath> {
self.hover_sound.as_ref()
2023-01-16 09:53:52 +00:00
}
pub fn set_on_selected_event<F>(&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<Option<Arc<Clickable>>> {
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<bool> {
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<bool> {
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)
}
2024-05-18 07:39:15 +00:00
pub fn current_selectable(&self) -> Result<Option<Arc<Selectable>>> {
2023-01-16 09:53:52 +00:00
match self.current_selectable.read().unwrap().as_ref() {
Some(selectable) => Ok(Some(selectable.clone())),
None => Ok(None),
}
}
pub fn accept_selection(&self) -> Result<bool> {
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<bool> {
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<bool> {
// 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<bool> {
// 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<bool> {
// 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<Option<Arc<Writeable>>> {
Ok(self.current_writeable.read().unwrap().as_ref().cloned())
}
pub fn remove_char(&self) -> Result<bool> {
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<bool> {
Ok(self.current_selectable.read().unwrap().is_some())
}
pub fn update_selection(&self, direction: GuiDirection) -> Result<bool> {
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<dyn Fn() -> 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<Arc<dyn TopGui>>) {
*self.top_ui.write().unwrap() = top_gui;
}
pub fn set_tooltip(&self, tooltip: Option<Arc<dyn TopGui>>) {
*self.tooltip_ui.write().unwrap() = tooltip;
}
2024-08-29 09:09:17 +00:00
pub(crate) fn add_callback<F: FnOnce() -> Result<()> + Send + Sync + 'static>(&self, f: F) {
self.callback_list.lock().unwrap().push(Box::new(f));
}
pub fn process_callbacks(&self) -> Result<()> {
2024-08-29 09:13:28 +00:00
let callbacks = mem::take(&mut *self.callback_list.lock().unwrap());
callbacks.into_iter().try_for_each(|callback| callback())
2024-08-29 09:09:17 +00:00
}
2023-01-16 09:53:52 +00:00
fn render(
&self,
2023-01-21 22:34:01 +00:00
buffer_recorder: &mut CommandBufferRecorder<'_>,
2023-01-16 09:53:52 +00:00
command_buffer_state: &CommandBufferState,
render_target: &RenderTarget,
single_color_objects: &GuiSeparator,
rectangle_objects: &GuiSeparator,
text_objects: &GuiSeparator,
2023-01-22 15:02:46 +00:00
text_to_screen: &GuiSeparator,
text_to_screen_desc: &Arc<DescriptorSet>,
index: usize,
2023-01-16 09:53:52 +00:00
) -> Result<()> {
2023-01-21 22:34:01 +00:00
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),
},
}];
2023-01-16 09:53:52 +00:00
2023-01-21 22:34:01 +00:00
let layers = self.layers.lock().unwrap();
2023-01-16 09:53:52 +00:00
2023-01-21 22:34:01 +00:00
for (_, elements) in layers.iter() {
render_target.begin(buffer_recorder, VK_SUBPASS_CONTENTS_INLINE, index);
2023-01-21 22:34:01 +00:00
if !elements.is_colorables_empty() {
2023-01-22 15:02:46 +00:00
buffer_recorder.bind_pipeline(&single_color_objects.pipeline)?;
2023-01-16 09:53:52 +00:00
2023-01-21 22:34:01 +00:00
buffer_recorder.set_scissor(&scissor);
buffer_recorder.set_viewport(&viewport);
2023-01-16 09:53:52 +00:00
2023-01-21 22:34:01 +00:00
// ---------- render colorables ----------
for colorable in elements.iter_colorables() {
buffer_recorder.bind_vertex_buffer(colorable.buffer());
2023-01-16 09:53:52 +00:00
2023-01-21 22:34:01 +00:00
buffer_recorder.bind_descriptor_sets_minimal(&[&colorable.descriptor_set()]);
2023-01-16 09:53:52 +00:00
2023-01-21 22:34:01 +00:00
buffer_recorder.draw_complete_single_instance(6);
2023-01-16 09:53:52 +00:00
}
2023-01-21 22:34:01 +00:00
}
2023-01-16 09:53:52 +00:00
2023-01-21 22:34:01 +00:00
if !elements.is_displayables_empty() || !elements.is_iconizables_empty() {
2023-01-22 15:02:46 +00:00
buffer_recorder.bind_pipeline(&rectangle_objects.pipeline)?;
2023-01-16 09:53:52 +00:00
2023-01-21 22:34:01 +00:00
buffer_recorder.set_scissor(&scissor);
buffer_recorder.set_viewport(&viewport);
2023-01-16 09:53:52 +00:00
2023-01-21 22:34:01 +00:00
// ---------- render displayables ----------
for displayable in elements.iter_displayables() {
buffer_recorder.bind_vertex_buffer(displayable.buffer());
2023-01-16 09:53:52 +00:00
2023-01-21 22:34:01 +00:00
buffer_recorder.bind_descriptor_sets_minimal(&[&displayable.descriptor_set()]);
2023-01-16 09:53:52 +00:00
2023-01-21 22:34:01 +00:00
buffer_recorder.draw_complete_single_instance(6);
}
2023-01-16 09:53:52 +00:00
2023-01-21 22:34:01 +00:00
// ---------- render iconizables ----------
for iconizable in elements.iter_iconizables() {
buffer_recorder.bind_vertex_buffer(iconizable.buffer());
2023-01-16 09:53:52 +00:00
2023-01-21 22:34:01 +00:00
buffer_recorder.bind_descriptor_sets_minimal(&[iconizable.descriptor_set()]);
2023-01-16 09:53:52 +00:00
2023-01-21 22:34:01 +00:00
buffer_recorder.draw_complete_single_instance(6);
2023-01-16 09:53:52 +00:00
}
2023-01-21 22:34:01 +00:00
}
2023-01-16 09:53:52 +00:00
2023-01-22 15:02:46 +00:00
// render text, render to offscreen (multisampled) and merge resolved target with actual image
2023-01-21 22:34:01 +00:00
if !elements.is_textables_empty() {
2023-01-22 15:02:46 +00:00
render_target.next_subpass(buffer_recorder, VK_SUBPASS_CONTENTS_INLINE);
buffer_recorder.bind_pipeline(&text_objects.pipeline)?;
2023-01-16 09:53:52 +00:00
2023-01-21 22:34:01 +00:00
buffer_recorder.set_scissor(&scissor);
buffer_recorder.set_viewport(&viewport);
2023-01-16 09:53:52 +00:00
2023-01-21 22:34:01 +00:00
let mut text_buffers = command_buffer_state.text_buffers.write().unwrap();
text_buffers.clear();
2023-01-16 09:53:52 +00:00
2023-01-21 22:34:01 +00:00
// ---------- render textables ----------
for textable in elements.iter_textables() {
if let Some(text_buffer) = textable.buffer() {
buffer_recorder.bind_vertex_buffer(&text_buffer);
2023-01-16 09:53:52 +00:00
2023-01-21 22:34:01 +00:00
text_buffers.push(text_buffer);
2023-01-16 09:53:52 +00:00
2023-01-21 22:34:01 +00:00
buffer_recorder.bind_descriptor_sets_minimal(&[
&self.bitmap_desc_set,
&textable.descriptor_set(),
]);
2023-01-16 09:53:52 +00:00
2023-01-21 22:34:01 +00:00
buffer_recorder.draw_complete_single_instance(textable.vertex_count());
2023-01-16 09:53:52 +00:00
}
}
2023-01-22 15:02:46 +00:00
// 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);
2023-01-26 08:24:53 +00:00
} 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);
2023-01-16 09:53:52 +00:00
}
2023-01-21 22:34:01 +00:00
render_target.end(buffer_recorder);
2023-01-16 09:53:52 +00:00
}
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.[<add_ $element>]($element);
}
None => {
let mut elements = Elements::default();
elements.[<add_ $element>]($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.[<delete_ $element>]($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.[<delete_ $element>]($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<Framable>) -> Result<()> {
add_element!(self.layers, layer, framable);
Ok(())
}
pub(crate) fn delete_framable(&self, layer: i32, framable: &Arc<Framable>) -> Result<()> {
remove_element!(self.layers, layer, framable);
Ok(())
}
// hoverable
pub(crate) fn add_hoverable(&self, layer: i32, hoverable: Arc<Hoverable>) -> Result<()> {
add_element!(self.layers, layer, hoverable);
Ok(())
}
pub(crate) fn delete_hoverable(&self, layer: i32, hoverable: &Arc<Hoverable>) -> 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<Selectable>) -> Result<()> {
add_element!(self.layers, layer, selectable);
Ok(())
}
pub(crate) fn delete_selectable(&self, layer: i32, selectable: &Arc<Selectable>) -> 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<Displayable>) -> Result<()> {
add_element!(self.layers, layer, displayable);
self.needs_update.store(true, SeqCst);
Ok(())
}
pub(crate) fn delete_displayable(
&self,
layer: i32,
displayable: &Arc<Displayable>,
) -> Result<()> {
remove_element!(self.layers, layer, displayable, self.needs_update);
Ok(())
}
// clickable
pub(crate) fn add_clickable(&self, layer: i32, clickable: Arc<Clickable>) -> Result<()> {
add_element!(self.layers, layer, clickable);
Ok(())
}
pub(crate) fn delete_clickable(&self, layer: i32, clickable: &Arc<Clickable>) -> 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, &current_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<Textable>) -> Result<()> {
add_element!(self.layers, layer, textable);
self.needs_update.store(true, SeqCst);
Ok(())
}
pub(crate) fn delete_textable(&self, layer: i32, textable: &Arc<Textable>) -> Result<()> {
remove_element!(self.layers, layer, textable, self.needs_update);
Ok(())
}
// writable
pub(crate) fn add_writeable(&self, layer: i32, writeable: Arc<Writeable>) -> Result<()> {
add_element!(self.layers, layer, writeable);
Ok(())
}
pub(crate) fn set_active_writeable(&self, writeable: Arc<Writeable>) -> Result<bool> {
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<Writeable>) -> 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<Iconizable>) -> Result<()> {
add_element!(self.layers, layer, iconizable);
self.needs_update.store(true, SeqCst);
Ok(())
}
pub(crate) fn delete_iconizable(&self, layer: i32, iconizable: &Arc<Iconizable>) -> Result<()> {
remove_element!(self.layers, layer, iconizable, self.needs_update);
Ok(())
}
pub(crate) fn set_selectable(&self, selectable: Option<Arc<Selectable>>) -> 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<Colorable>) -> Result<()> {
add_element!(self.layers, layer, colorable);
self.needs_update.store(true, SeqCst);
Ok(())
}
pub(crate) fn delete_colorable(&self, layer: i32, colorable: &Arc<Colorable>) -> Result<()> {
remove_element!(self.layers, layer, colorable, self.needs_update);
Ok(())
}
}
2024-05-18 07:39:15 +00:00
// private - create rendering stuff
2023-01-16 09:53:52 +00:00
impl GuiHandler {
fn create_render_targets(
2023-01-16 09:53:52 +00:00
device: &Arc<Device>,
target_images: &TargetMode<Vec<Arc<Image>>>,
queue: &Arc<Mutex<Queue>>,
sample_count: VkSampleCountFlags,
) -> Result<TargetMode<RwLock<RenderTarget>>> {
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,
)?),
),
})
2023-01-16 09:53:52 +00:00
}
fn create_render_target(
device: &Arc<Device>,
target_images: &Vec<Arc<Image>>,
old_render_target: Option<&RenderTarget>,
queue: &Arc<Mutex<Queue>>,
sample_count: VkSampleCountFlags,
) -> Result<RenderTarget> {
let mut builder = 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)?,
)
.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(),
2023-01-21 22:34:01 +00:00
format: VK_FORMAT_R8G8B8A8_UNORM,
2023-01-22 17:18:24 +00:00
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)
2023-01-22 17:18:24 +00:00
.add_resolve_targets(CustomTarget::resolve(
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
VK_FORMAT_R8G8B8A8_UNORM,
None,
false,
true,
))
.use_queue(queue.clone())
.build(device)?,
2023-01-22 15:02:46 +00:00
)
.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, [0.0, 0.0, 0.0, 0.0], false)
.build(device)?,
);
if let Some(render_target) = old_render_target {
builder = builder.preserve_old_render_pass(render_target);
}
builder.build(device)
}
2023-01-22 15:02:46 +00:00
fn create_command_buffers(image_count: usize) -> Result<Vec<CommandBufferState>> {
let mut command_buffers = Vec::with_capacity(image_count);
2023-01-16 09:53:52 +00:00
2023-01-22 15:02:46 +00:00
for _ in 0..image_count {
2023-01-16 09:53:52 +00:00
command_buffers.push(CommandBufferState {
text_buffers: RwLock::new(Vec::new()),
});
}
Ok(command_buffers)
}
fn init_bitmap_font(
device: &Arc<Device>,
queue: &Arc<Mutex<Queue>>,
descriptor_layout: Arc<DescriptorSetLayout>,
2023-01-18 11:24:56 +00:00
font: Font<'_>,
2023-01-16 09:53:52 +00:00
) -> Result<(Arc<Image>, Arc<DescriptorPool>, Arc<DescriptorSet>)> {
2023-01-18 11:24:56 +00:00
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)?;
2023-01-16 09:53:52 +00:00
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<Device>,
render_targets: &TargetMode<RwLock<RenderTarget>>,
2023-01-21 22:34:01 +00:00
sample_count: VkSampleCountFlags,
) -> Result<(TargetMode<GuiSeparator>, Arc<DescriptorSetLayout>)> {
2023-01-16 09:53:52 +00:00
// --- 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 ---
2025-02-27 09:43:07 +00:00
let vertex_shader_text = include_bytes!("gui_shader/text.vert.spv");
let fragment_shader_text = include_bytes!("gui_shader/text.frag.spv");
2023-01-16 09:53:52 +00:00
2023-01-27 12:46:54 +00:00
let vertex_shader = ShaderModule::from_slice(device.clone(), vertex_shader_text)?;
let fragment_shader = ShaderModule::from_slice(device.clone(), fragment_shader_text)?;
2023-01-16 09:53:52 +00:00
Ok((
render_targets.execute(|render_target| {
Ok(GuiSeparator {
_descriptor_layout: descriptor_layout.clone(),
2023-01-27 12:46:54 +00:00
pipeline: GuiHandler::init_gui_pipeline::<TexturedVertex>(
device,
render_target.read().unwrap().render_pass(),
&pipeline_layout,
vertex_shader.clone(),
fragment_shader.clone(),
2023-01-21 22:34:01 +00:00
sample_count,
1,
)?,
})
})?,
2023-01-16 09:53:52 +00:00
color_layout,
))
}
fn init_rectangle_objects(
device: &Arc<Device>,
render_targets: &TargetMode<RwLock<RenderTarget>>,
) -> Result<TargetMode<GuiSeparator>> {
2023-01-16 09:53:52 +00:00
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
2023-01-27 12:46:54 +00:00
let vertex_shader =
2025-02-27 09:43:07 +00:00
ShaderModule::from_slice(device.clone(), include_bytes!("gui_shader/rect.vert.spv"))?;
2023-01-27 12:46:54 +00:00
let fragment_shader =
2025-02-27 09:43:07 +00:00
ShaderModule::from_slice(device.clone(), include_bytes!("gui_shader/rect.frag.spv"))?;
2023-01-16 09:53:52 +00:00
render_targets.execute(|render_target| {
Ok(GuiSeparator {
_descriptor_layout: descriptor_layout.clone(),
2023-01-27 12:46:54 +00:00
pipeline: GuiHandler::init_gui_pipeline::<TexturedVertex>(
device,
render_target.read().unwrap().render_pass(),
&pipeline_layout,
vertex_shader.clone(),
fragment_shader.clone(),
2023-01-21 22:34:01 +00:00
VK_SAMPLE_COUNT_1_BIT,
0,
)?,
})
2023-01-16 09:53:52 +00:00
})
}
2023-01-22 15:02:46 +00:00
fn init_text_screen_objects(
device: &Arc<Device>,
render_targets: &TargetMode<RwLock<RenderTarget>>,
) -> Result<TargetMode<GuiSeparator>> {
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
2023-01-27 12:46:54 +00:00
let vertex_shader =
2025-02-27 09:43:07 +00:00
ShaderModule::from_slice(device.clone(), include_bytes!("gui_shader/rect.vert.spv"))?;
2023-01-22 15:02:46 +00:00
let fragment_shader = ShaderModule::from_slice(
device.clone(),
2025-02-27 09:43:07 +00:00
include_bytes!("gui_shader/input_rect.frag.spv"),
2023-01-22 15:02:46 +00:00
)?;
render_targets.execute(|render_target| {
Ok(GuiSeparator {
_descriptor_layout: descriptor_layout.clone(),
2023-01-27 12:46:54 +00:00
pipeline: GuiHandler::init_gui_pipeline::<TexturedVertex>(
2023-01-22 15:02:46 +00:00
device,
render_target.read().unwrap().render_pass(),
&pipeline_layout,
vertex_shader.clone(),
fragment_shader.clone(),
VK_SAMPLE_COUNT_1_BIT,
2,
)?,
})
})
}
2023-01-16 09:53:52 +00:00
fn init_single_color_objects(
device: &Arc<Device>,
render_targets: &TargetMode<RwLock<RenderTarget>>,
) -> Result<TargetMode<GuiSeparator>> {
2023-01-16 09:53:52 +00:00
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(),
2025-02-27 09:43:07 +00:00
include_bytes!("gui_shader/single_color.vert.spv"),
2023-01-16 09:53:52 +00:00
)?;
let fragment_shader = ShaderModule::from_slice(
device.clone(),
2025-02-27 09:43:07 +00:00
include_bytes!("gui_shader/single_color.frag.spv"),
2023-01-16 09:53:52 +00:00
)?;
render_targets.execute(|render_target| {
Ok(GuiSeparator {
_descriptor_layout: color_layout.clone(),
2023-01-27 12:46:54 +00:00
pipeline: GuiHandler::init_gui_pipeline::<ColorableVertex>(
device,
render_target.read().unwrap().render_pass(),
&pipeline_layout,
vertex_shader.clone(),
fragment_shader.clone(),
2023-01-21 22:34:01 +00:00
VK_SAMPLE_COUNT_1_BIT,
0,
)?,
})
2023-01-16 09:53:52 +00:00
})
}
2023-01-27 12:46:54 +00:00
fn init_gui_pipeline<T: VertexInputDescription>(
2023-01-16 09:53:52 +00:00
device: &Arc<Device>,
render_pass: &Arc<RenderPass>,
pipeline_layout: &Arc<PipelineLayout>,
2024-03-24 20:40:35 +00:00
vertex_shader: Arc<ShaderModule<shader_type::Vertex>>,
fragment_shader: Arc<ShaderModule<shader_type::Fragment>>,
2023-01-21 22:34:01 +00:00
sample_count: VkSampleCountFlags,
sub_pass: u32,
2023-01-16 09:53:52 +00:00
) -> Result<Arc<Pipeline>> {
Pipeline::new_graphics()
2023-01-27 12:46:54 +00:00
.set_vertex_shader::<T>(vertex_shader)
2023-01-16 09:53:52 +00:00
.set_fragment_shader(fragment_shader)
.input_assembly(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, false)
2023-01-21 22:34:01 +00:00
.default_multisample(sample_count)
2023-01-16 09:53:52 +00:00
.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)
2023-01-16 09:53:52 +00:00
}
}
2023-01-16 11:58:59 +00:00
impl GuiHandler {
pub fn process(
2023-01-16 09:53:52 +00:00
&self,
buffer_recorder: &mut CommandBufferRecorder<'_>,
indices: &TargetMode<usize>,
) -> 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);
}
2023-01-22 15:02:46 +00:00
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,
2023-01-22 15:02:46 +00:00
render_target,
text_objects,
2023-01-22 15:02:46 +00:00
rectangle_objects,
single_color_object,
2025-02-27 09:43:07 +00:00
index,
2023-01-22 15:02:46 +00:00
text_pipeline,
text_descriptor,
)| {
self.render(
buffer_recorder,
2025-02-27 09:43:07 +00:00
&command_buffers[****index],
2023-01-22 15:02:46 +00:00
&*render_target.read().unwrap(),
single_color_object,
rectangle_objects,
text_objects,
text_pipeline,
text_descriptor,
2025-02-27 09:43:07 +00:00
****index,
2023-01-22 15:02:46 +00:00
)
},
)?;
2023-01-16 09:53:52 +00:00
Ok(())
}
2024-08-27 09:24:44 +00:00
pub fn resize(
&self,
width: u32,
height: u32,
images: &TargetMode<Vec<Arc<Image>>>,
) -> Result<()> {
images
2023-01-22 15:02:46 +00:00
.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,
)?;
2023-01-22 15:02:46 +00:00
Ok(())
})?;
2023-01-22 15:02:46 +00:00
self.text_to_screen.update_on_resize(&self.render_targets)?;
2023-01-16 09:53:52 +00:00
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(())
}
}