diff --git a/librashader-runtime-vk/src/filter_chain.rs b/librashader-runtime-vk/src/filter_chain.rs index a451906..0462d65 100644 --- a/librashader-runtime-vk/src/filter_chain.rs +++ b/librashader-runtime-vk/src/filter_chain.rs @@ -1,3 +1,4 @@ +use std::collections::VecDeque; use crate::{error, util}; use crate::filter_pass::FilterPass; use crate::luts::LutTexture; @@ -7,7 +8,7 @@ use crate::ubo_ring::VkUboRing; use crate::vulkan_state::VulkanGraphicsPipeline; use ash::vk::{CommandPoolCreateFlags, PFN_vkGetInstanceProcAddr, Queue, StaticFn}; use ash::{vk, Device}; -use librashader_common::{ImageFormat, Size}; +use librashader_common::{FilterMode, ImageFormat, Size, WrapMode}; use librashader_preprocess::ShaderSource; use librashader_presets::{ShaderPassConfig, ShaderPreset, TextureConfig}; use librashader_reflect::back::targets::SpirV; @@ -130,7 +131,7 @@ pub struct FilterChainVulkan { pub(crate) vulkan: Vulkan, pub(crate) output_framebuffers: Box<[OwnedImage]>, pub(crate) feedback_framebuffers: Box<[OwnedImage]>, - // pub(crate) history_framebuffers: VecDeque, + pub(crate) history_framebuffers: VecDeque, } pub struct FilterMutable { @@ -145,7 +146,7 @@ pub(crate) struct FilterCommon { pub output_textures: Box<[Option]>, pub feedback_textures: Box<[Option]>, - // pub history_textures: Box<[Option]>, + pub history_textures: Box<[Option]>, pub config: FilterMutable, pub device: ash::Device, } @@ -153,7 +154,8 @@ pub(crate) struct FilterCommon { #[must_use] pub struct FilterChainFrameIntermediates { device: ash::Device, - image_views: Vec + image_views: Vec, + owned: Vec } impl FilterChainFrameIntermediates { @@ -161,6 +163,7 @@ impl FilterChainFrameIntermediates { FilterChainFrameIntermediates { device: device.clone(), image_views: Vec::new(), + owned: Vec::new(), } } @@ -172,6 +175,10 @@ impl FilterChainFrameIntermediates { pub(crate) fn dispose_image_views(&mut self, image_view: vk::ImageView) { self.image_views.push(image_view) } + + pub(crate) fn dispose_owned(&mut self, owned: OwnedImage) { + self.owned.push(owned) + } } impl Drop for FilterChainFrameIntermediates { @@ -214,6 +221,9 @@ impl FilterChainVulkan { let luts = FilterChainVulkan::load_luts(&device, &preset.textures)?; let samplers = SamplerSet::new(&device.device)?; + let (history_framebuffers, history_textures) = + FilterChainVulkan::init_history(&device, &filters)?; + let mut output_framebuffers = Vec::new(); output_framebuffers.resize_with(filters.len(), || { OwnedImage::new( @@ -258,12 +268,14 @@ impl FilterChainVulkan { draw_quad: DrawQuad::new(&device.device, &device.memory_properties)?, device: device.device.clone(), output_textures: output_textures.into_boxed_slice(), - feedback_textures: feedback_textures.into_boxed_slice() + feedback_textures: feedback_textures.into_boxed_slice(), + history_textures, }, passes: filters, vulkan: device, output_framebuffers: output_framebuffers?.into_boxed_slice(), feedback_framebuffers: feedback_framebuffers?.into_boxed_slice(), + history_framebuffers, }) } @@ -441,6 +453,107 @@ impl FilterChainVulkan { Ok(luts) } + fn init_history( + vulkan: &Vulkan, + filters: &[FilterPass], + ) -> error::Result<(VecDeque, Box<[Option]>)> { + let mut required_images = 0; + + for pass in filters { + // If a shader uses history size, but not history, we still need to keep the texture. + let texture_count = pass + .reflection + .meta + .texture_meta + .iter() + .filter(|(semantics, _)| semantics.semantics == TextureSemantics::OriginalHistory) + .count(); + let texture_size_count = pass + .reflection + .meta + .texture_size_meta + .iter() + .filter(|(semantics, _)| semantics.semantics == TextureSemantics::OriginalHistory) + .count(); + + required_images = std::cmp::max(required_images, texture_count); + required_images = std::cmp::max(required_images, texture_size_count); + } + + // not using frame history; + if required_images <= 1 { + println!("[history] not using frame history"); + return Ok((VecDeque::new(), Box::new([]))); + } + + // history0 is aliased with the original + + eprintln!("[history] using frame history with {required_images} images"); + let mut images = Vec::with_capacity(required_images); + images.resize_with(required_images, || OwnedImage::new( + &vulkan, + Size::new(1, 1), + ImageFormat::R8G8B8A8Unorm, + 1 + )); + + let images: error::Result> = images.into_iter().collect(); + let images = VecDeque::from(images?); + + let mut image_views = Vec::new(); + image_views.resize_with(required_images, || None); + + Ok((images, image_views.into_boxed_slice())) + } + + // image must be in SHADER_READ_OPTIMAL + pub fn push_history(&mut self, input: &VulkanImage, intermediates: &mut FilterChainFrameIntermediates, + cmd: vk::CommandBuffer) -> error::Result<()> { + if let Some(mut back) = self.history_framebuffers.pop_back() { + if back.image.size != input.size || (input.format != vk::Format::UNDEFINED && input.format != back.image.format) { + eprintln!("[history] resizing"); + // old back will get dropped.. do we need to defer? + let old_back = + std::mem::replace(&mut back, OwnedImage::new(&self.vulkan, input.size, input.format.into(), 1)?); + intermediates.dispose_owned(old_back); + } + + unsafe { + util::vulkan_image_layout_transition_levels(&self.vulkan.device, cmd, + input.image, + 1, + vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL, + vk::ImageLayout::TRANSFER_SRC_OPTIMAL, + vk::AccessFlags::SHADER_READ, + vk::AccessFlags::TRANSFER_READ, + vk::PipelineStageFlags::FRAGMENT_SHADER, + vk::PipelineStageFlags::TRANSFER, + vk::QUEUE_FAMILY_IGNORED, + vk::QUEUE_FAMILY_IGNORED + ); + + back.copy_from(cmd, &input, vk::ImageLayout::TRANSFER_SRC_OPTIMAL); + + util::vulkan_image_layout_transition_levels(&self.vulkan.device, cmd, + input.image, + 1, + vk::ImageLayout::TRANSFER_SRC_OPTIMAL, + vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL, + vk::AccessFlags::TRANSFER_READ, + vk::AccessFlags::SHADER_READ, + vk::PipelineStageFlags::TRANSFER, + vk::PipelineStageFlags::FRAGMENT_SHADER, + vk::QUEUE_FAMILY_IGNORED, + vk::QUEUE_FAMILY_IGNORED + ); + + } + + self.history_framebuffers.push_front(back) + } + + Ok(()) + } /// Process a frame with the input image. /// /// * The input image must be in the `VK_SHADER_READ_ONLY_OPTIMAL`. @@ -465,7 +578,6 @@ impl FilterChainVulkan { return Ok(intermediates); } - let original_image_view = unsafe { let create_info = vk::ImageViewCreateInfo::builder() .image(input.image) @@ -490,6 +602,16 @@ impl FilterChainVulkan { let filter = passes[0].config.filter; let wrap_mode = passes[0].config.wrap_mode; + // update history + for (texture, image) in self + .common + .history_textures + .iter_mut() + .zip(self.history_framebuffers.iter()) + { + *texture = Some(image.as_input(filter, wrap_mode)?); + } + let original = InputImage { image: input.clone(), image_view: original_image_view, @@ -580,6 +702,9 @@ impl FilterChainVulkan { ); intermediates.dispose_outputs(out.output); } + + self.push_history(input, &mut intermediates, cmd)?; + Ok(intermediates) } } diff --git a/librashader-runtime-vk/src/filter_pass.rs b/librashader-runtime-vk/src/filter_pass.rs index 2f5caf6..c655f9a 100644 --- a/librashader-runtime-vk/src/filter_pass.rs +++ b/librashader-runtime-vk/src/filter_pass.rs @@ -58,7 +58,7 @@ impl FilterPass { } pub fn get_format(&self) -> ImageFormat { - let mut fb_format = self.source.format; + let fb_format = self.source.format; if let Some(format) = self.config.get_format_override() { format } else if fb_format == ImageFormat::Unknown { @@ -281,35 +281,35 @@ impl FilterPass { .bind_vec4(*offset, original.image.size, None); } - // for (index, output) in parent.history_textures.iter().enumerate() { - // let Some(output) = output else { - // eprintln!("no history"); - // continue; - // }; - // if let Some(binding) = self - // .reflection - // .meta - // .texture_meta - // .get(&TextureSemantics::OriginalHistory.semantics(index + 1)) - // { - // FilterPass::bind_texture( - // &self.device, - // &parent.samplers, - // descriptor_set, - // binding, - // output, - // ); - // } - // - // if let Some(offset) = self.uniform_bindings.get( - // &TextureSemantics::OriginalHistory - // .semantics(index + 1) - // .into(), - // ) { - // self.uniform_storage - // .bind_vec4(*offset, output.size, None); - // } - // } + for (index, output) in parent.history_textures.iter().enumerate() { + let Some(output) = output else { + eprintln!("no history"); + continue; + }; + if let Some(binding) = self + .reflection + .meta + .texture_meta + .get(&TextureSemantics::OriginalHistory.semantics(index + 1)) + { + FilterPass::bind_texture( + &self.device, + &parent.samplers, + *descriptor_set, + binding, + output, + ); + } + + if let Some(offset) = self.uniform_bindings.get( + &TextureSemantics::OriginalHistory + .semantics(index + 1) + .into(), + ) { + self.uniform_storage + .bind_vec4(*offset, output.image.size, None); + } + } // PassOutput for (index, output) in parent.output_textures[0..pass_index].iter().enumerate() { @@ -340,7 +340,7 @@ impl FilterPass { } } - // // PassFeedback + // PassFeedback for (index, feedback) in parent.feedback_textures.iter().enumerate() { let Some(feedback) = feedback else { eprintln!("no passfeedback {index}"); @@ -419,7 +419,5 @@ impl FilterPass { .bind_vec4(*offset, lut.image.image.size, None); } } - - // (textures, samplers) } } diff --git a/librashader-runtime-vk/src/lib.rs b/librashader-runtime-vk/src/lib.rs index 1e9b8d2..ee86bdc 100644 --- a/librashader-runtime-vk/src/lib.rs +++ b/librashader-runtime-vk/src/lib.rs @@ -30,7 +30,8 @@ mod tests { let base = VulkanBase::new(entry).unwrap(); let mut filter = FilterChainVulkan::load_from_path( &base, - "../test/slang-shaders/border/gameboy-player/gameboy-player-crt-royale.slangp", + // "../test/slang-shaders/border/gameboy-player/gameboy-player-crt-royale.slangp", + "../test/slang-shaders/bezel/Mega_Bezel/Presets/MBZ__0__SMOOTH-ADV.slangp", None, ) .unwrap(); diff --git a/librashader-runtime-vk/src/texture.rs b/librashader-runtime-vk/src/texture.rs index 3ea9ec4..eb56581 100644 --- a/librashader-runtime-vk/src/texture.rs +++ b/librashader-runtime-vk/src/texture.rs @@ -23,9 +23,13 @@ impl OwnedImage { device: ash::Device, mem_props: vk::PhysicalDeviceMemoryProperties, size: Size, - format: ImageFormat, + mut format: ImageFormat, max_miplevels: u32, ) -> error::Result { + // default to something sane + if format == ImageFormat::Unknown { + format = ImageFormat::R8G8B8A8Unorm + } let image_create_info = vk::ImageCreateInfo::builder() .image_type(vk::ImageType::TYPE_2D) .format(format.into()) @@ -330,7 +334,7 @@ impl OwnedImage { } /// SAFETY: self must fit the source image - pub unsafe fn copy_from(&self, cmd: vk::CommandBuffer, source: VulkanImage, source_layout: vk::ImageLayout) { + pub unsafe fn copy_from(&self, cmd: vk::CommandBuffer, source: &VulkanImage, source_layout: vk::ImageLayout) { let region = vk::ImageCopy::builder() .src_subresource(vk::ImageSubresourceLayers::builder() .aspect_mask(vk::ImageAspectFlags::COLOR)