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-01-16 11:58:59 +00:00
|
|
|
use utilities::prelude::*;
|
|
|
|
use vulkan_rs::prelude::*;
|
2023-01-16 09:53:52 +00:00
|
|
|
|
|
|
|
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;
|
|
|
|
|
2023-01-17 06:08:29 +00:00
|
|
|
#[derive(Deserialize, Serialize, Clone, Default, Debug)]
|
2023-01-16 09:53:52 +00:00
|
|
|
pub struct GuiHandlerCreateInfo {
|
|
|
|
// default button textures
|
|
|
|
pub menu_button: AssetPath,
|
|
|
|
pub menu_button_selected: AssetPath,
|
|
|
|
|
|
|
|
// path to the alphabet image
|
|
|
|
pub font_path: AssetPath,
|
|
|
|
|
|
|
|
// sound info
|
2023-01-17 06:07:19 +00:00
|
|
|
#[cfg(feature = "audio")]
|
2023-01-16 09:53:52 +00:00
|
|
|
pub click_sound: AssetPath,
|
2023-01-17 06:07:19 +00:00
|
|
|
#[cfg(feature = "audio")]
|
2023-01-16 09:53:52 +00:00
|
|
|
pub hover_sound: AssetPath,
|
|
|
|
|
|
|
|
// resource base directory
|
|
|
|
pub resource_directory: AssetPath,
|
|
|
|
}
|
|
|
|
|
|
|
|
struct GuiSeparator {
|
|
|
|
_descriptor_layout: Arc<DescriptorSetLayout>,
|
|
|
|
_pipeline_layout: Arc<PipelineLayout>,
|
|
|
|
|
|
|
|
_pipeline: Arc<Pipeline>,
|
|
|
|
}
|
|
|
|
|
|
|
|
struct DisplayableTexture {
|
|
|
|
_descriptor_pool: Arc<DescriptorPool>,
|
|
|
|
_descriptor_set: Arc<DescriptorSet>,
|
|
|
|
|
|
|
|
_texture: Arc<Image>,
|
|
|
|
}
|
|
|
|
|
|
|
|
struct TextableColor {
|
|
|
|
_descriptor_pool: Arc<DescriptorPool>,
|
|
|
|
_descriptor_set: Arc<DescriptorSet>,
|
|
|
|
|
|
|
|
_buffer: Arc<Buffer<f32>>,
|
|
|
|
}
|
|
|
|
|
|
|
|
struct CommandBufferState {
|
|
|
|
command_buffer: Arc<CommandBuffer>,
|
|
|
|
valid: AtomicBool,
|
|
|
|
text_buffers: RwLock<Vec<Arc<Buffer<TexturedVertex>>>>,
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct GuiHandler {
|
2023-01-16 11:58:59 +00:00
|
|
|
context: Arc<dyn ContextInterface>,
|
2023-01-16 09:53:52 +00:00
|
|
|
|
|
|
|
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_pass: Arc<RenderPass>,
|
|
|
|
framebuffers: Mutex<TargetMode<Vec<Arc<Framebuffer>>>>,
|
|
|
|
command_buffers: TargetMode<Vec<CommandBufferState>>,
|
|
|
|
|
|
|
|
text_objects: GuiSeparator,
|
|
|
|
rectangle_objects: GuiSeparator,
|
|
|
|
single_color_objects: GuiSeparator,
|
|
|
|
|
|
|
|
_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>>>,
|
|
|
|
internal_textures: RwLock<HashMap<AssetPath, DisplayableTexture>>,
|
|
|
|
internal_colors: RwLock<HashMap<Color, TextableColor>>,
|
|
|
|
|
|
|
|
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>>>,
|
|
|
|
|
|
|
|
menu_button: AssetPath,
|
|
|
|
menu_button_selected: AssetPath,
|
2023-01-17 06:07:19 +00:00
|
|
|
|
|
|
|
#[cfg(feature = "audio")]
|
2023-01-16 09:53:52 +00:00
|
|
|
click_sound: AssetPath,
|
2023-01-17 06:07:19 +00:00
|
|
|
|
|
|
|
#[cfg(feature = "audio")]
|
2023-01-16 09:53:52 +00:00
|
|
|
hover_sound: AssetPath,
|
|
|
|
|
|
|
|
// ----- 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>>>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl GuiHandler {
|
|
|
|
pub fn new(
|
|
|
|
gui_handler_create_info: GuiHandlerCreateInfo,
|
2023-01-16 11:58:59 +00:00
|
|
|
context: &Arc<dyn ContextInterface>,
|
2023-01-16 09:53:52 +00:00
|
|
|
) -> Result<Arc<GuiHandler>> {
|
|
|
|
let device = context.device();
|
|
|
|
let queue = context.queue();
|
|
|
|
|
2023-01-16 11:58:59 +00:00
|
|
|
let command_buffers = match context.images() {
|
|
|
|
TargetMode::Mono(_) => {
|
|
|
|
let command_buffers = Self::create_command_buffers(device, queue, context)?;
|
2023-01-16 09:53:52 +00:00
|
|
|
|
2023-01-16 11:58:59 +00:00
|
|
|
TargetMode::Mono(command_buffers)
|
2023-01-16 09:53:52 +00:00
|
|
|
}
|
|
|
|
TargetMode::Stereo(_, _) => {
|
2023-01-16 11:58:59 +00:00
|
|
|
let left_command_buffers = Self::create_command_buffers(device, queue, context)?;
|
|
|
|
let right_command_buffers = Self::create_command_buffers(device, queue, context)?;
|
2023-01-16 09:53:52 +00:00
|
|
|
|
|
|
|
TargetMode::Stereo(left_command_buffers, right_command_buffers)
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
let render_pass =
|
2023-01-16 11:58:59 +00:00
|
|
|
Self::create_render_pass(device, context.format(), context.image_layout())?;
|
|
|
|
let framebuffers = Self::create_framebuffers(device, &context.images(), &render_pass)?;
|
2023-01-16 09:53:52 +00:00
|
|
|
|
|
|
|
let (text_objs, color_layout) = Self::init_text_objects(device, &render_pass)?;
|
|
|
|
let rect_objs = Self::init_rectangle_objects(device, &render_pass)?;
|
|
|
|
let single_color_objects = Self::init_single_color_objects(device, &render_pass)?;
|
|
|
|
|
|
|
|
let (bitmap_texture, bitmap_desc_pool, bitmap_desc_set) = Self::init_bitmap_font(
|
|
|
|
device,
|
|
|
|
queue,
|
|
|
|
text_objs._descriptor_layout.clone(),
|
|
|
|
gui_handler_create_info.font_path.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())?;
|
|
|
|
|
2023-01-16 11:58:59 +00:00
|
|
|
Ok(Arc::new(GuiHandler {
|
2023-01-16 09:53:52 +00:00
|
|
|
context: context.clone(),
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
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
|
|
|
|
},
|
|
|
|
|
2023-01-17 06:07:19 +00:00
|
|
|
#[cfg(feature = "audio")]
|
2023-01-16 09:53:52 +00:00
|
|
|
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
|
|
|
|
},
|
|
|
|
|
2023-01-17 06:07:19 +00:00
|
|
|
#[cfg(feature = "audio")]
|
2023-01-16 09:53:52 +00:00
|
|
|
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_pass,
|
|
|
|
framebuffers: Mutex::new(framebuffers),
|
|
|
|
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,
|
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),
|
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
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(feature = "audio")]
|
2023-01-16 12:27:54 +00:00
|
|
|
pub(crate) fn context(&self) -> &Arc<dyn ContextInterface> {
|
2023-01-16 09:53:52 +00:00
|
|
|
&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<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)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) fn image_descriptor(&self, mut path: AssetPath) -> Result<Arc<DescriptorSet>> {
|
|
|
|
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_objects._descriptor_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<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();
|
|
|
|
|
|
|
|
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<f32> {
|
|
|
|
*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
|
|
|
|
}
|
|
|
|
|
2023-01-17 06:07:19 +00:00
|
|
|
#[cfg(feature = "audio")]
|
2023-01-16 09:53:52 +00:00
|
|
|
pub fn click_sound(&self) -> &AssetPath {
|
|
|
|
&self.click_sound
|
|
|
|
}
|
|
|
|
|
2023-01-17 06:07:19 +00:00
|
|
|
#[cfg(feature = "audio")]
|
2023-01-16 09:53:52 +00:00
|
|
|
pub fn hover_sound(&self) -> &AssetPath {
|
|
|
|
&self.hover_sound
|
|
|
|
}
|
|
|
|
|
|
|
|
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)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn current_selectable(&self) -> Result<Option<Arc<Selectable>>> {
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
fn render(
|
|
|
|
&self,
|
|
|
|
command_buffer_state: &CommandBufferState,
|
|
|
|
framebuffer: &Arc<Framebuffer>,
|
|
|
|
) -> Result<()> {
|
|
|
|
if !command_buffer_state.valid.load(SeqCst) {
|
|
|
|
let gui_command_buffer = &command_buffer_state.command_buffer;
|
|
|
|
|
|
|
|
let inheritance_info = CommandBuffer::inheritance_info(
|
|
|
|
Some(&self.render_pass),
|
|
|
|
Some(0),
|
|
|
|
Some(framebuffer),
|
|
|
|
None,
|
|
|
|
);
|
|
|
|
|
|
|
|
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(&self.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(&self.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(&self.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.[<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, ¤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<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(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// private
|
|
|
|
impl GuiHandler {
|
|
|
|
/// Creates a simple render pass for gui rendering
|
|
|
|
/// Only color framebuffer is attached
|
|
|
|
fn create_render_pass(
|
|
|
|
device: &Arc<Device>,
|
|
|
|
final_format: VkFormat,
|
|
|
|
target_layout: VkImageLayout,
|
|
|
|
) -> Result<Arc<RenderPass>> {
|
|
|
|
let target_reference = VkAttachmentReference {
|
|
|
|
attachment: 0,
|
|
|
|
layout: VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
|
|
|
|
};
|
|
|
|
|
|
|
|
let subpass_descriptions = [VkSubpassDescription::new(
|
|
|
|
0,
|
|
|
|
&[],
|
|
|
|
slice::from_ref(&target_reference),
|
|
|
|
&[],
|
|
|
|
None,
|
|
|
|
&[],
|
|
|
|
)];
|
|
|
|
|
|
|
|
let attachments = [VkAttachmentDescription::new(
|
|
|
|
0,
|
|
|
|
final_format,
|
|
|
|
VK_SAMPLE_COUNT_1_BIT,
|
|
|
|
VK_ATTACHMENT_LOAD_OP_LOAD,
|
|
|
|
VK_ATTACHMENT_STORE_OP_STORE,
|
|
|
|
VK_ATTACHMENT_LOAD_OP_DONT_CARE,
|
|
|
|
VK_ATTACHMENT_STORE_OP_DONT_CARE,
|
|
|
|
target_layout,
|
|
|
|
target_layout,
|
|
|
|
)];
|
|
|
|
|
|
|
|
let src_access = Image::src_layout_to_access(target_layout);
|
|
|
|
let dst_access = Image::dst_layout_to_access(target_layout);
|
|
|
|
let render_pass_image_access = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
|
|
|
|
|
|
|
|
let dependencies = [
|
|
|
|
VkSubpassDependency::new(
|
|
|
|
VK_SUBPASS_EXTERNAL,
|
|
|
|
0,
|
|
|
|
CommandBuffer::access_to_stage(src_access),
|
|
|
|
CommandBuffer::access_to_stage(render_pass_image_access),
|
|
|
|
src_access,
|
|
|
|
render_pass_image_access,
|
|
|
|
VK_DEPENDENCY_BY_REGION_BIT,
|
|
|
|
),
|
|
|
|
VkSubpassDependency::new(
|
|
|
|
0,
|
|
|
|
VK_SUBPASS_EXTERNAL,
|
|
|
|
CommandBuffer::access_to_stage(render_pass_image_access),
|
|
|
|
CommandBuffer::access_to_stage(dst_access),
|
|
|
|
render_pass_image_access,
|
|
|
|
dst_access,
|
|
|
|
VK_DEPENDENCY_BY_REGION_BIT,
|
|
|
|
),
|
|
|
|
];
|
|
|
|
|
|
|
|
let renderpass = RenderPass::new(
|
|
|
|
device.clone(),
|
|
|
|
&subpass_descriptions,
|
|
|
|
&attachments,
|
|
|
|
&dependencies,
|
|
|
|
)?;
|
|
|
|
|
|
|
|
Ok(renderpass)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn create_framebuffers(
|
|
|
|
device: &Arc<Device>,
|
|
|
|
target_images: &TargetMode<Vec<Arc<Image>>>,
|
|
|
|
render_pass: &Arc<RenderPass>,
|
|
|
|
) -> Result<TargetMode<Vec<Arc<Framebuffer>>>> {
|
|
|
|
// closure to create array of framebuffer from array of images
|
|
|
|
let create_framebuffer = |device: &Arc<Device>,
|
|
|
|
images: &Vec<Arc<Image>>,
|
|
|
|
render_pass: &Arc<RenderPass>|
|
|
|
|
-> Result<Vec<Arc<Framebuffer>>> {
|
|
|
|
let mut framebuffers = Vec::with_capacity(images.len());
|
|
|
|
|
|
|
|
for image in images.iter() {
|
|
|
|
Image::convert_layout(image, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR)?;
|
|
|
|
|
|
|
|
framebuffers.push(
|
|
|
|
Framebuffer::builder()
|
|
|
|
.set_render_pass(render_pass)
|
|
|
|
.add_attachment(image)
|
|
|
|
.build(device.clone())?,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(framebuffers)
|
|
|
|
};
|
|
|
|
|
|
|
|
match target_images {
|
2023-01-16 11:58:59 +00:00
|
|
|
TargetMode::Mono(images) => {
|
2023-01-16 09:53:52 +00:00
|
|
|
let framebuffers = create_framebuffer(device, images, render_pass)?;
|
|
|
|
|
2023-01-16 11:58:59 +00:00
|
|
|
Ok(TargetMode::Mono(framebuffers))
|
2023-01-16 09:53:52 +00:00
|
|
|
}
|
|
|
|
TargetMode::Stereo(left_images, right_images) => {
|
|
|
|
let left_framebuffers = create_framebuffer(device, left_images, render_pass)?;
|
|
|
|
let right_framebuffers = create_framebuffer(device, right_images, render_pass)?;
|
|
|
|
|
|
|
|
Ok(TargetMode::Stereo(left_framebuffers, right_framebuffers))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn create_command_buffers(
|
|
|
|
device: &Arc<Device>,
|
|
|
|
queue: &Arc<Mutex<Queue>>,
|
2023-01-16 11:58:59 +00:00
|
|
|
context: &Arc<dyn ContextInterface>,
|
2023-01-16 09:53:52 +00:00
|
|
|
) -> Result<Vec<CommandBufferState>> {
|
2023-01-16 11:58:59 +00:00
|
|
|
let mut command_buffers = Vec::with_capacity(context.image_count());
|
2023-01-16 09:53:52 +00:00
|
|
|
|
2023-01-16 11:58:59 +00:00
|
|
|
for _ in 0..context.image_count() {
|
2023-01-16 09:53:52 +00:00
|
|
|
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<Device>,
|
|
|
|
queue: &Arc<Mutex<Queue>>,
|
|
|
|
descriptor_layout: Arc<DescriptorSetLayout>,
|
|
|
|
path: AssetPath,
|
|
|
|
) -> Result<(Arc<Image>, Arc<DescriptorPool>, Arc<DescriptorSet>)> {
|
|
|
|
let texture = Image::from_file(path)?
|
|
|
|
.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<Device>,
|
|
|
|
render_pass: &Arc<RenderPass>,
|
|
|
|
) -> Result<(GuiSeparator, Arc<DescriptorSetLayout>)> {
|
|
|
|
// --- 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();
|
|
|
|
|
|
|
|
let pipeline = GuiHandler::init_gui_pipeline(
|
|
|
|
device,
|
|
|
|
input_bindings,
|
|
|
|
input_attributes,
|
|
|
|
render_pass,
|
|
|
|
&pipeline_layout,
|
|
|
|
vertex_shader,
|
|
|
|
fragment_shader,
|
|
|
|
)?;
|
|
|
|
|
|
|
|
Ok((
|
|
|
|
GuiSeparator {
|
|
|
|
_descriptor_layout: descriptor_layout,
|
|
|
|
_pipeline_layout: pipeline_layout,
|
|
|
|
|
|
|
|
_pipeline: pipeline,
|
|
|
|
},
|
|
|
|
color_layout,
|
|
|
|
))
|
|
|
|
}
|
|
|
|
|
|
|
|
fn init_rectangle_objects(
|
|
|
|
device: &Arc<Device>,
|
|
|
|
render_pass: &Arc<RenderPass>,
|
|
|
|
) -> Result<GuiSeparator> {
|
|
|
|
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();
|
|
|
|
|
|
|
|
let pipeline = GuiHandler::init_gui_pipeline(
|
|
|
|
device,
|
|
|
|
input_bindings,
|
|
|
|
input_attributes,
|
|
|
|
render_pass,
|
|
|
|
&pipeline_layout,
|
|
|
|
vertex_shader,
|
|
|
|
fragment_shader,
|
|
|
|
)?;
|
|
|
|
|
|
|
|
Ok(GuiSeparator {
|
|
|
|
_descriptor_layout: descriptor_layout,
|
|
|
|
_pipeline_layout: pipeline_layout,
|
|
|
|
|
|
|
|
_pipeline: pipeline,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
fn init_single_color_objects(
|
|
|
|
device: &Arc<Device>,
|
|
|
|
render_pass: &Arc<RenderPass>,
|
|
|
|
) -> Result<GuiSeparator> {
|
|
|
|
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();
|
|
|
|
|
|
|
|
let pipeline = GuiHandler::init_gui_pipeline(
|
|
|
|
device,
|
|
|
|
input_bindings,
|
|
|
|
input_attributes,
|
|
|
|
render_pass,
|
|
|
|
&pipeline_layout,
|
|
|
|
vertex_shader,
|
|
|
|
fragment_shader,
|
|
|
|
)?;
|
|
|
|
|
|
|
|
Ok(GuiSeparator {
|
|
|
|
_descriptor_layout: color_layout,
|
|
|
|
_pipeline_layout: pipeline_layout,
|
|
|
|
|
|
|
|
_pipeline: pipeline,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
fn init_gui_pipeline(
|
|
|
|
device: &Arc<Device>,
|
|
|
|
binding_description: Vec<VkVertexInputBindingDescription>,
|
|
|
|
attribute_description: Vec<VkVertexInputAttributeDescription>,
|
|
|
|
render_pass: &Arc<RenderPass>,
|
|
|
|
pipeline_layout: &Arc<PipelineLayout>,
|
|
|
|
vertex_shader: Arc<ShaderModule>,
|
|
|
|
fragment_shader: Arc<ShaderModule>,
|
|
|
|
) -> Result<Arc<Pipeline>> {
|
|
|
|
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],
|
|
|
|
framebuffers: &[Arc<Framebuffer>],
|
|
|
|
index: usize,
|
|
|
|
) -> Result<()> {
|
|
|
|
let command_buffer_state = &command_buffer_states[index];
|
|
|
|
let framebuffer = &framebuffers[index];
|
|
|
|
|
|
|
|
self.render(command_buffer_state, framebuffer)?;
|
|
|
|
|
|
|
|
buffer_recorder.begin_render_pass_full(
|
|
|
|
&self.render_pass,
|
|
|
|
framebuffer,
|
|
|
|
&[],
|
|
|
|
VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS,
|
|
|
|
);
|
|
|
|
|
|
|
|
buffer_recorder.execute_commands(&[&command_buffer_state.command_buffer]);
|
|
|
|
|
|
|
|
buffer_recorder.end_render_pass();
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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) {
|
|
|
|
match &self.command_buffers {
|
2023-01-16 11:58:59 +00:00
|
|
|
TargetMode::Mono(command_buffers) => {
|
2023-01-16 09:53:52 +00:00
|
|
|
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.framebuffers.lock().unwrap().deref(),
|
|
|
|
indices,
|
|
|
|
) {
|
|
|
|
(
|
2023-01-16 11:58:59 +00:00
|
|
|
TargetMode::Mono(command_buffers),
|
|
|
|
TargetMode::Mono(framebuffers),
|
|
|
|
TargetMode::Mono(index),
|
2023-01-16 09:53:52 +00:00
|
|
|
) => {
|
|
|
|
self.select_rendering(buffer_recorder, command_buffers, framebuffers, *index)?;
|
|
|
|
}
|
|
|
|
(
|
|
|
|
TargetMode::Stereo(left_cbs, right_cbs),
|
|
|
|
TargetMode::Stereo(left_frb, right_frb),
|
|
|
|
TargetMode::Stereo(left_index, right_index),
|
|
|
|
) => {
|
|
|
|
self.select_rendering(buffer_recorder, left_cbs, left_frb, *left_index)?;
|
|
|
|
self.select_rendering(buffer_recorder, right_cbs, right_frb, *right_index)?;
|
|
|
|
}
|
|
|
|
_ => panic!("Invalid TargetMode combination"),
|
|
|
|
};
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2023-01-16 11:58:59 +00:00
|
|
|
pub fn resize(&self, width: u32, height: u32) -> Result<()> {
|
2023-01-16 09:53:52 +00:00
|
|
|
*self.framebuffers.lock().unwrap() = Self::create_framebuffers(
|
|
|
|
self.context.device(),
|
2023-01-16 11:58:59 +00:00
|
|
|
&self.context.images(),
|
2023-01-16 09:53:52 +00:00
|
|
|
&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(())
|
|
|
|
}
|
|
|
|
}
|