diff --git a/src/context_interface.rs b/src/context_interface.rs index 8336859..501bbff 100644 --- a/src/context_interface.rs +++ b/src/context_interface.rs @@ -55,6 +55,20 @@ impl TargetMode { } } + pub fn chain<'a, R>(&'a self, other: &'a TargetMode) -> TargetMode<(&'a T, &'a R)> { + match (self, other) { + (TargetMode::Mono(mono_self), TargetMode::Mono(mono_other)) => { + TargetMode::Mono((mono_self, mono_other)) + } + ( + TargetMode::Stereo(left_self, right_self), + TargetMode::Stereo(left_other, right_other), + ) => TargetMode::Stereo((left_self, left_other), (right_self, right_other)), + + _ => panic!("Incompatible TargetModes"), + } + } + pub fn execute(&self, mut f: F) -> anyhow::Result> where F: FnMut(&T) -> anyhow::Result, @@ -64,8 +78,52 @@ impl TargetMode { TargetMode::Stereo(l, r) => TargetMode::Stereo(f(l)?, f(r)?), }) } + + pub fn execute_into(self, mut f: F) -> anyhow::Result> + where + F: FnMut(T) -> anyhow::Result, + { + Ok(match self { + TargetMode::Mono(s) => TargetMode::Mono(f(s)?), + TargetMode::Stereo(l, r) => TargetMode::Stereo(f(l)?, f(r)?), + }) + } } +pub trait Unfold { + type Output; + + fn unfold(self) -> Self::Output; +} + +macro_rules! impl_unfold { + ([<$($var:ident: $type:ident,)+>, $rhs_var:ident: $rhs_type:ident]) => { + paste::paste! { + impl<'a, $($type,)+ $rhs_type: 'a> Unfold for TargetMode<(&'a ($($type,)+), &'a $rhs_type)> { + type Output = TargetMode<($(&'a $type,)+ &'a $rhs_type)>; + + fn unfold(self) -> Self::Output { + match self { + TargetMode::Mono( (($($var,)+), $rhs_var) ) => TargetMode::Mono(($($var,)+ &$rhs_var)), + TargetMode::Stereo( ( ( $( [], )+ ) , [] ), ( ( $( [], )+ ) , [] ) ) + => TargetMode::Stereo( ( $( [], )+ &[] ), ( $( [], )+ &[] ) ), + } + } + } + } + + }; +} + +impl_unfold!([, s: S]); +impl_unfold!([, s: S]); +impl_unfold!([, s: S]); +impl_unfold!([, s: S]); +impl_unfold!([, s: S]); +impl_unfold!([, s: S]); +impl_unfold!([, s: S]); +impl_unfold!([, s: S]); + impl Clone for TargetMode { fn clone(&self) -> TargetMode { match self { diff --git a/src/guihandler/guihandler.rs b/src/guihandler/guihandler.rs index 9f0e9e7..2fb05e5 100644 --- a/src/guihandler/guihandler.rs +++ b/src/guihandler/guihandler.rs @@ -3,14 +3,14 @@ use anyhow::Result; use assetpath::AssetPath; use serde::{Deserialize, Serialize}; use utilities::prelude::*; -use vulkan_rs::prelude::*; +use vulkan_rs::{prelude::*, render_target::sub_pass::InputAttachmentInfo}; use super::{ elements::Elements, gui::{iconizable::IconBuilderType, texturedvertex::TexturedVertex}, }; -use cgmath::ortho; +use cgmath::{ortho, vec2, vec4}; use std::sync::Weak; use std::sync::{ @@ -78,7 +78,7 @@ impl<'a> GuiHandlerCreateInfo<'a> { struct GuiSeparator { _descriptor_layout: Arc, - _pipeline: Arc, + pipeline: Arc, } struct DisplayableTexture { @@ -96,11 +96,98 @@ struct TextableColor { } struct CommandBufferState { - command_buffer: Arc, - valid: AtomicBool, text_buffers: RwLock>>>, } +struct TextToScreen { + pipeline: TargetMode, + descriptor: TargetMode>, + buffer: Arc>, +} + +impl TextToScreen { + fn new( + device: &Arc, + pipeline: TargetMode, + render_target: &TargetMode>, + ) -> Result { + let descriptor = pipeline + .chain(render_target) + .execute(|(separator, render_target)| { + let descriptor = DescriptorPool::builder() + .set_layout(separator._descriptor_layout.clone()) + .build(device.clone())? + .prepare_set() + .allocate()?; + + Self::update_descriptor(&descriptor, &*render_target.read().unwrap())?; + + Ok(descriptor) + })?; + + let edges = [ + TexturedVertex { + position: vec4(-1.0, -1.0, 0.0, 1.0), + texture_coordinates: vec2(0.0, 0.0), + }, + TexturedVertex { + position: vec4(-1.0, 1.0, 0.0, 1.0), + texture_coordinates: vec2(0.0, 1.0), + }, + TexturedVertex { + position: vec4(1.0, 1.0, 0.0, 1.0), + texture_coordinates: vec2(1.0, 1.0), + }, + TexturedVertex { + position: vec4(1.0, 1.0, 0.0, 1.0), + texture_coordinates: vec2(1.0, 1.0), + }, + TexturedVertex { + position: vec4(1.0, -1.0, 0.0, 1.0), + texture_coordinates: vec2(1.0, 0.0), + }, + TexturedVertex { + position: vec4(-1.0, -1.0, 0.0, 1.0), + texture_coordinates: vec2(0.0, 0.0), + }, + ]; + + let buffer = Buffer::builder() + .set_usage(VK_BUFFER_USAGE_VERTEX_BUFFER_BIT) + .set_memory_usage(MemoryUsage::CpuOnly) + .set_data(&edges) + .build(device.clone())?; + + Ok(Self { + pipeline, + descriptor, + buffer, + }) + } + + fn update_descriptor( + descriptor: &Arc, + render_target: &RenderTarget, + ) -> Result<()> { + // take resolve target of second sub pass as image sampler + descriptor.update(&[DescriptorWrite::input_attachments( + 0, + &[&render_target.sub_pass(1).attachments()[1].image(0)], + ) + .change_image_layout(VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)]) + } + + fn update_on_resize(&self, render_target: &TargetMode>) -> Result<()> { + self.descriptor + .chain(render_target) + .execute(|(descriptor, render_target)| { + Self::update_descriptor(descriptor, &*render_target.read().unwrap()) + })?; + + Ok(()) + } +} + pub struct GuiHandler { context: Arc, @@ -122,6 +209,8 @@ pub struct GuiHandler { rectangle_objects: TargetMode, single_color_objects: TargetMode, + text_to_screen: TextToScreen, + _bitmap_font: Arc, _bitmap_desc_pool: Arc, bitmap_desc_set: Arc, @@ -174,19 +263,9 @@ impl GuiHandler { let device = context.device(); let queue = context.queue(); - let command_buffers = match context.images() { - TargetMode::Mono(_) => { - let command_buffers = Self::create_command_buffers(device, queue, context)?; - - TargetMode::Mono(command_buffers) - } - TargetMode::Stereo(_, _) => { - let left_command_buffers = Self::create_command_buffers(device, queue, context)?; - let right_command_buffers = Self::create_command_buffers(device, queue, context)?; - - TargetMode::Stereo(left_command_buffers, right_command_buffers) - } - }; + let command_buffers = context + .images() + .execute(|_| Self::create_command_buffers(context.image_count()))?; let text_sample_count = device.max_supported_sample_count(VK_SAMPLE_COUNT_4_BIT); @@ -198,6 +277,12 @@ impl GuiHandler { let rect_objs = Self::init_rectangle_objects(device, &render_targets)?; let single_color_objects = Self::init_single_color_objects(device, &render_targets)?; + let text_to_screen = TextToScreen::new( + device, + Self::init_text_screen_objects(device, &render_targets)?, + &render_targets, + )?; + let (bitmap_texture, bitmap_desc_pool, bitmap_desc_set) = Self::init_bitmap_font( device, queue, @@ -271,6 +356,8 @@ impl GuiHandler { rectangle_objects: rect_objs, single_color_objects, + text_to_screen, + _bitmap_font: bitmap_texture, _bitmap_desc_pool: bitmap_desc_pool, bitmap_desc_set, @@ -787,6 +874,8 @@ impl GuiHandler { single_color_objects: &GuiSeparator, rectangle_objects: &GuiSeparator, text_objects: &GuiSeparator, + text_to_screen: &GuiSeparator, + text_to_screen_desc: &Arc, index: usize, ) -> Result<()> { let viewport = [VkViewport { @@ -812,7 +901,7 @@ impl GuiHandler { render_target.begin(buffer_recorder, VK_SUBPASS_CONTENTS_INLINE, index); if !elements.is_colorables_empty() { - buffer_recorder.bind_pipeline(&single_color_objects._pipeline)?; + buffer_recorder.bind_pipeline(&single_color_objects.pipeline)?; buffer_recorder.set_scissor(&scissor); buffer_recorder.set_viewport(&viewport); @@ -828,7 +917,7 @@ impl GuiHandler { } if !elements.is_displayables_empty() || !elements.is_iconizables_empty() { - buffer_recorder.bind_pipeline(&rectangle_objects._pipeline)?; + buffer_recorder.bind_pipeline(&rectangle_objects.pipeline)?; buffer_recorder.set_scissor(&scissor); buffer_recorder.set_viewport(&viewport); @@ -852,10 +941,11 @@ impl GuiHandler { } } - render_target.next_subpass(buffer_recorder, VK_SUBPASS_CONTENTS_INLINE); - + // render text, render to offscreen (multisampled) and merge resolved target with actual image if !elements.is_textables_empty() { - buffer_recorder.bind_pipeline(&text_objects._pipeline)?; + render_target.next_subpass(buffer_recorder, VK_SUBPASS_CONTENTS_INLINE); + + buffer_recorder.bind_pipeline(&text_objects.pipeline)?; buffer_recorder.set_scissor(&scissor); buffer_recorder.set_viewport(&viewport); @@ -878,6 +968,19 @@ impl GuiHandler { buffer_recorder.draw_complete_single_instance(textable.vertex_count()); } } + + // after text is written und resolved, we read image as input and draw it over the current image + render_target.next_subpass(buffer_recorder, VK_SUBPASS_CONTENTS_INLINE); + + buffer_recorder.bind_pipeline(&text_to_screen.pipeline)?; + + buffer_recorder.set_scissor(&scissor); + buffer_recorder.set_viewport(&viewport); + + buffer_recorder.bind_descriptor_sets_minimal(&[text_to_screen_desc]); + buffer_recorder.bind_vertex_buffer(&self.text_to_screen.buffer); + buffer_recorder + .draw_complete_single_instance(self.text_to_screen.buffer.size() as u32); } render_target.end(buffer_recorder); @@ -1211,11 +1314,20 @@ impl GuiHandler { clear_on_load: true, store_on_save: true, attach_sampler: false, - use_as_input: false, + use_as_input: true, clear_value: ClearValue::Color([0.0, 0.0, 0.0, 0.0]), }) .use_queue(queue.clone()) .build(device)?, + ) + .add_sub_pass( + SubPass::builder(target_images[0].width(), target_images[0].height()) + .set_input_attachment_info(InputAttachmentInfo { + sub_pass_index: 1, + input_indices: vec![1], + }) + .set_prepared_targets(target_images, 0, [0.0, 0.0, 0.0, 0.0], false) + .build(device)?, ); if let Some(render_target) = old_render_target { @@ -1225,18 +1337,11 @@ impl GuiHandler { builder.build(device) } - fn create_command_buffers( - device: &Arc, - queue: &Arc>, - context: &Arc, - ) -> Result> { - let mut command_buffers = Vec::with_capacity(context.image_count()); + fn create_command_buffers(image_count: usize) -> Result> { + let mut command_buffers = Vec::with_capacity(image_count); - for _ in 0..context.image_count() { + for _ in 0..image_count { command_buffers.push(CommandBufferState { - command_buffer: CommandBuffer::new_secondary() - .build(device.clone(), queue.clone())?, - valid: AtomicBool::new(false), text_buffers: RwLock::new(Vec::new()), }); } @@ -1315,7 +1420,7 @@ impl GuiHandler { Ok(GuiSeparator { _descriptor_layout: descriptor_layout.clone(), - _pipeline: GuiHandler::init_gui_pipeline( + pipeline: GuiHandler::init_gui_pipeline( device, input_bindings.clone(), input_attributes.clone(), @@ -1367,7 +1472,7 @@ impl GuiHandler { Ok(GuiSeparator { _descriptor_layout: descriptor_layout.clone(), - _pipeline: GuiHandler::init_gui_pipeline( + pipeline: GuiHandler::init_gui_pipeline( device, input_bindings.clone(), input_attributes.clone(), @@ -1382,6 +1487,56 @@ impl GuiHandler { }) } + fn init_text_screen_objects( + device: &Arc, + render_targets: &TargetMode>, + ) -> Result> { + let descriptor_layout = DescriptorSetLayout::builder() + .add_layout_binding( + 0, + VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, + VK_SHADER_STAGE_FRAGMENT_BIT, + 0, + ) + .build(device.clone())?; + + let pipeline_layout = PipelineLayout::builder() + .add_descriptor_set_layout(&descriptor_layout) + .build(device.clone())?; + + // pipeline creation + let vertex_shader = ShaderModule::from_slice( + device.clone(), + include_bytes!("guishader/rect.vert.spv"), + ShaderType::Vertex, + )?; + let fragment_shader = ShaderModule::from_slice( + device.clone(), + include_bytes!("guishader/rect.frag.spv"), + ShaderType::Fragment, + )?; + + let (input_bindings, input_attributes) = TexturedVertex::vertex_input_state(); + + render_targets.execute(|render_target| { + Ok(GuiSeparator { + _descriptor_layout: descriptor_layout.clone(), + + pipeline: GuiHandler::init_gui_pipeline( + device, + input_bindings.clone(), + input_attributes.clone(), + render_target.read().unwrap().render_pass(), + &pipeline_layout, + vertex_shader.clone(), + fragment_shader.clone(), + VK_SAMPLE_COUNT_1_BIT, + 2, + )?, + }) + }) + } + fn init_single_color_objects( device: &Arc, render_targets: &TargetMode>, @@ -1417,7 +1572,7 @@ impl GuiHandler { Ok(GuiSeparator { _descriptor_layout: color_layout.clone(), - _pipeline: GuiHandler::init_gui_pipeline( + pipeline: GuiHandler::init_gui_pipeline( device, input_bindings.clone(), input_attributes.clone(), @@ -1452,32 +1607,6 @@ impl GuiHandler { .default_rasterization(VK_CULL_MODE_NONE, VK_FRONT_FACE_CLOCKWISE) .build(device.clone(), pipeline_layout, render_pass, sub_pass) } - - #[inline] - fn select_rendering( - &self, - buffer_recorder: &mut CommandBufferRecorder<'_>, - command_buffer_states: &[CommandBufferState], - render_target: &RenderTarget, - single_color_objects: &GuiSeparator, - rectangle_objects: &GuiSeparator, - text_objects: &GuiSeparator, - index: usize, - ) -> Result<()> { - let command_buffer_state = &command_buffer_states[index]; - - self.render( - buffer_recorder, - command_buffer_state, - render_target, - single_color_objects, - rectangle_objects, - text_objects, - index, - )?; - - Ok(()) - } } impl GuiHandler { @@ -1487,23 +1616,6 @@ impl GuiHandler { indices: &TargetMode, ) -> Result<()> { if self.needs_update.load(SeqCst) { - match &self.command_buffers { - TargetMode::Mono(command_buffers) => { - for state in command_buffers { - state.valid.store(false, SeqCst); - } - } - TargetMode::Stereo(left_cbs, right_cbs) => { - for state in left_cbs { - state.valid.store(false, SeqCst); - } - - for state in right_cbs { - state.valid.store(false, SeqCst); - } - } - } - let mut text_changes = self.text_change_queue.write().unwrap(); if !text_changes.is_empty() { @@ -1517,68 +1629,53 @@ impl GuiHandler { self.needs_update.store(false, SeqCst); } - match ( - &self.command_buffers, - &self.render_targets, - &self.text_objects, - &self.rectangle_objects, - &self.single_color_objects, - indices, - ) { - ( - TargetMode::Mono(command_buffers), - TargetMode::Mono(render_target), - TargetMode::Mono(text_objects), - TargetMode::Mono(rectangle_objects), - TargetMode::Mono(single_color_object), - TargetMode::Mono(index), - ) => { - self.select_rendering( - buffer_recorder, + self.command_buffers + .chain(&self.render_targets) + .chain(&self.text_objects) + .unfold() + .chain(&self.rectangle_objects) + .unfold() + .chain(&self.single_color_objects) + .unfold() + .chain(indices) + .unfold() + .chain(&self.text_to_screen.pipeline) + .unfold() + .chain(&self.text_to_screen.descriptor) + .unfold() + .execute( + |( command_buffers, - &*render_target.read().unwrap(), - single_color_object, - rectangle_objects, + render_target, text_objects, - *index, - )?; - } - ( - TargetMode::Stereo(left_cbs, right_cbs), - TargetMode::Stereo(left_rt, right_rt), - TargetMode::Stereo(left_text_objects, right_text_objects), - TargetMode::Stereo(left_rectangle_objects, right_rectangle_objects), - TargetMode::Stereo(left_single_color_object, right_single_color_object), - TargetMode::Stereo(left_index, right_index), - ) => { - self.select_rendering( - buffer_recorder, - left_cbs, - &*left_rt.read().unwrap(), - left_single_color_object, - left_rectangle_objects, - left_text_objects, - *left_index, - )?; - self.select_rendering( - buffer_recorder, - right_cbs, - &*right_rt.read().unwrap(), - right_single_color_object, - right_rectangle_objects, - right_text_objects, - *right_index, - )?; - } - _ => panic!("Invalid TargetMode combination"), - }; + rectangle_objects, + single_color_object, + &&&index, + text_pipeline, + text_descriptor, + )| { + self.render( + buffer_recorder, + &command_buffers[index], + &*render_target.read().unwrap(), + single_color_object, + rectangle_objects, + text_objects, + text_pipeline, + text_descriptor, + index, + ) + }, + )?; Ok(()) } pub fn resize(&self, width: u32, height: u32) -> Result<()> { - match (self.context.images(), &self.render_targets) { - (TargetMode::Mono(images), TargetMode::Mono(render_target)) => { + self.context + .images() + .chain(&self.render_targets) + .execute(|(images, render_target)| { let mut rt_lock = render_target.write().unwrap(); *rt_lock = Self::create_render_target( @@ -1588,34 +1685,11 @@ impl GuiHandler { &self.queue, self.text_sample_count, )?; - } - ( - TargetMode::Stereo(left_images, right_images), - TargetMode::Stereo(left_render_target, right_render_target), - ) => { - let mut left_rt_lock = left_render_target.write().unwrap(); - *left_rt_lock = Self::create_render_target( - &self.device, - &left_images, - Some(&*left_rt_lock), - &self.queue, - self.text_sample_count, - )?; + Ok(()) + })?; - let mut right_rt_lock = right_render_target.write().unwrap(); - - *right_rt_lock = Self::create_render_target( - &self.device, - &right_images, - Some(&*right_rt_lock), - &self.queue, - self.text_sample_count, - )?; - } - - _ => unreachable!("unsupported case!"), - } + self.text_to_screen.update_on_resize(&self.render_targets)?; self.needs_update.store(true, SeqCst); *self.ortho.write().unwrap() = ortho(0.0, width as f32, 0.0, height as f32, -1.0, 1.0); diff --git a/src/prelude.rs b/src/prelude.rs index ce7266e..6334377 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -1,5 +1,5 @@ pub use super::builder::{builder::GuiBuilder, snippet::GuiSnippet}; -pub use super::context_interface::{ContextInterface, TargetMode}; +pub use super::context_interface::{ContextInterface, TargetMode, Unfold}; pub use super::controller_button::ControllerButton; pub use super::elements::prelude::*; pub use super::guidirection::GuiDirection;