vk: implement history

This commit is contained in:
chyyran 2023-01-12 00:00:45 -05:00
parent 857e994570
commit d435b43a52
4 changed files with 170 additions and 42 deletions

View file

@ -1,3 +1,4 @@
use std::collections::VecDeque;
use crate::{error, util}; use crate::{error, util};
use crate::filter_pass::FilterPass; use crate::filter_pass::FilterPass;
use crate::luts::LutTexture; use crate::luts::LutTexture;
@ -7,7 +8,7 @@ use crate::ubo_ring::VkUboRing;
use crate::vulkan_state::VulkanGraphicsPipeline; use crate::vulkan_state::VulkanGraphicsPipeline;
use ash::vk::{CommandPoolCreateFlags, PFN_vkGetInstanceProcAddr, Queue, StaticFn}; use ash::vk::{CommandPoolCreateFlags, PFN_vkGetInstanceProcAddr, Queue, StaticFn};
use ash::{vk, Device}; use ash::{vk, Device};
use librashader_common::{ImageFormat, Size}; use librashader_common::{FilterMode, ImageFormat, Size, WrapMode};
use librashader_preprocess::ShaderSource; use librashader_preprocess::ShaderSource;
use librashader_presets::{ShaderPassConfig, ShaderPreset, TextureConfig}; use librashader_presets::{ShaderPassConfig, ShaderPreset, TextureConfig};
use librashader_reflect::back::targets::SpirV; use librashader_reflect::back::targets::SpirV;
@ -130,7 +131,7 @@ pub struct FilterChainVulkan {
pub(crate) vulkan: Vulkan, pub(crate) vulkan: Vulkan,
pub(crate) output_framebuffers: Box<[OwnedImage]>, pub(crate) output_framebuffers: Box<[OwnedImage]>,
pub(crate) feedback_framebuffers: Box<[OwnedImage]>, pub(crate) feedback_framebuffers: Box<[OwnedImage]>,
// pub(crate) history_framebuffers: VecDeque<OwnedFramebuffer>, pub(crate) history_framebuffers: VecDeque<OwnedImage>,
} }
pub struct FilterMutable { pub struct FilterMutable {
@ -145,7 +146,7 @@ pub(crate) struct FilterCommon {
pub output_textures: Box<[Option<InputImage>]>, pub output_textures: Box<[Option<InputImage>]>,
pub feedback_textures: Box<[Option<InputImage>]>, pub feedback_textures: Box<[Option<InputImage>]>,
// pub history_textures: Box<[Option<Texture>]>, pub history_textures: Box<[Option<InputImage>]>,
pub config: FilterMutable, pub config: FilterMutable,
pub device: ash::Device, pub device: ash::Device,
} }
@ -153,7 +154,8 @@ pub(crate) struct FilterCommon {
#[must_use] #[must_use]
pub struct FilterChainFrameIntermediates { pub struct FilterChainFrameIntermediates {
device: ash::Device, device: ash::Device,
image_views: Vec<vk::ImageView> image_views: Vec<vk::ImageView>,
owned: Vec<OwnedImage>
} }
impl FilterChainFrameIntermediates { impl FilterChainFrameIntermediates {
@ -161,6 +163,7 @@ impl FilterChainFrameIntermediates {
FilterChainFrameIntermediates { FilterChainFrameIntermediates {
device: device.clone(), device: device.clone(),
image_views: Vec::new(), 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) { pub(crate) fn dispose_image_views(&mut self, image_view: vk::ImageView) {
self.image_views.push(image_view) self.image_views.push(image_view)
} }
pub(crate) fn dispose_owned(&mut self, owned: OwnedImage) {
self.owned.push(owned)
}
} }
impl Drop for FilterChainFrameIntermediates { impl Drop for FilterChainFrameIntermediates {
@ -214,6 +221,9 @@ impl FilterChainVulkan {
let luts = FilterChainVulkan::load_luts(&device, &preset.textures)?; let luts = FilterChainVulkan::load_luts(&device, &preset.textures)?;
let samplers = SamplerSet::new(&device.device)?; let samplers = SamplerSet::new(&device.device)?;
let (history_framebuffers, history_textures) =
FilterChainVulkan::init_history(&device, &filters)?;
let mut output_framebuffers = Vec::new(); let mut output_framebuffers = Vec::new();
output_framebuffers.resize_with(filters.len(), || { output_framebuffers.resize_with(filters.len(), || {
OwnedImage::new( OwnedImage::new(
@ -258,12 +268,14 @@ impl FilterChainVulkan {
draw_quad: DrawQuad::new(&device.device, &device.memory_properties)?, draw_quad: DrawQuad::new(&device.device, &device.memory_properties)?,
device: device.device.clone(), device: device.device.clone(),
output_textures: output_textures.into_boxed_slice(), 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, passes: filters,
vulkan: device, vulkan: device,
output_framebuffers: output_framebuffers?.into_boxed_slice(), output_framebuffers: output_framebuffers?.into_boxed_slice(),
feedback_framebuffers: feedback_framebuffers?.into_boxed_slice(), feedback_framebuffers: feedback_framebuffers?.into_boxed_slice(),
history_framebuffers,
}) })
} }
@ -441,6 +453,107 @@ impl FilterChainVulkan {
Ok(luts) Ok(luts)
} }
fn init_history(
vulkan: &Vulkan,
filters: &[FilterPass],
) -> error::Result<(VecDeque<OwnedImage>, Box<[Option<InputImage>]>)> {
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<Vec<OwnedImage>> = 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. /// Process a frame with the input image.
/// ///
/// * The input image must be in the `VK_SHADER_READ_ONLY_OPTIMAL`. /// * The input image must be in the `VK_SHADER_READ_ONLY_OPTIMAL`.
@ -465,7 +578,6 @@ impl FilterChainVulkan {
return Ok(intermediates); return Ok(intermediates);
} }
let original_image_view = unsafe { let original_image_view = unsafe {
let create_info = vk::ImageViewCreateInfo::builder() let create_info = vk::ImageViewCreateInfo::builder()
.image(input.image) .image(input.image)
@ -490,6 +602,16 @@ impl FilterChainVulkan {
let filter = passes[0].config.filter; let filter = passes[0].config.filter;
let wrap_mode = passes[0].config.wrap_mode; 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 { let original = InputImage {
image: input.clone(), image: input.clone(),
image_view: original_image_view, image_view: original_image_view,
@ -580,6 +702,9 @@ impl FilterChainVulkan {
); );
intermediates.dispose_outputs(out.output); intermediates.dispose_outputs(out.output);
} }
self.push_history(input, &mut intermediates, cmd)?;
Ok(intermediates) Ok(intermediates)
} }
} }

View file

@ -58,7 +58,7 @@ impl FilterPass {
} }
pub fn get_format(&self) -> ImageFormat { 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() { if let Some(format) = self.config.get_format_override() {
format format
} else if fb_format == ImageFormat::Unknown { } else if fb_format == ImageFormat::Unknown {
@ -281,35 +281,35 @@ impl FilterPass {
.bind_vec4(*offset, original.image.size, None); .bind_vec4(*offset, original.image.size, None);
} }
// for (index, output) in parent.history_textures.iter().enumerate() { for (index, output) in parent.history_textures.iter().enumerate() {
// let Some(output) = output else { let Some(output) = output else {
// eprintln!("no history"); eprintln!("no history");
// continue; continue;
// }; };
// if let Some(binding) = self if let Some(binding) = self
// .reflection .reflection
// .meta .meta
// .texture_meta .texture_meta
// .get(&TextureSemantics::OriginalHistory.semantics(index + 1)) .get(&TextureSemantics::OriginalHistory.semantics(index + 1))
// { {
// FilterPass::bind_texture( FilterPass::bind_texture(
// &self.device, &self.device,
// &parent.samplers, &parent.samplers,
// descriptor_set, *descriptor_set,
// binding, binding,
// output, output,
// ); );
// } }
//
// if let Some(offset) = self.uniform_bindings.get( if let Some(offset) = self.uniform_bindings.get(
// &TextureSemantics::OriginalHistory &TextureSemantics::OriginalHistory
// .semantics(index + 1) .semantics(index + 1)
// .into(), .into(),
// ) { ) {
// self.uniform_storage self.uniform_storage
// .bind_vec4(*offset, output.size, None); .bind_vec4(*offset, output.image.size, None);
// } }
// } }
// PassOutput // PassOutput
for (index, output) in parent.output_textures[0..pass_index].iter().enumerate() { 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() { for (index, feedback) in parent.feedback_textures.iter().enumerate() {
let Some(feedback) = feedback else { let Some(feedback) = feedback else {
eprintln!("no passfeedback {index}"); eprintln!("no passfeedback {index}");
@ -419,7 +419,5 @@ impl FilterPass {
.bind_vec4(*offset, lut.image.image.size, None); .bind_vec4(*offset, lut.image.image.size, None);
} }
} }
// (textures, samplers)
} }
} }

View file

@ -30,7 +30,8 @@ mod tests {
let base = VulkanBase::new(entry).unwrap(); let base = VulkanBase::new(entry).unwrap();
let mut filter = FilterChainVulkan::load_from_path( let mut filter = FilterChainVulkan::load_from_path(
&base, &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, None,
) )
.unwrap(); .unwrap();

View file

@ -23,9 +23,13 @@ impl OwnedImage {
device: ash::Device, device: ash::Device,
mem_props: vk::PhysicalDeviceMemoryProperties, mem_props: vk::PhysicalDeviceMemoryProperties,
size: Size<u32>, size: Size<u32>,
format: ImageFormat, mut format: ImageFormat,
max_miplevels: u32, max_miplevels: u32,
) -> error::Result<OwnedImage> { ) -> error::Result<OwnedImage> {
// default to something sane
if format == ImageFormat::Unknown {
format = ImageFormat::R8G8B8A8Unorm
}
let image_create_info = vk::ImageCreateInfo::builder() let image_create_info = vk::ImageCreateInfo::builder()
.image_type(vk::ImageType::TYPE_2D) .image_type(vk::ImageType::TYPE_2D)
.format(format.into()) .format(format.into())
@ -330,7 +334,7 @@ impl OwnedImage {
} }
/// SAFETY: self must fit the source image /// 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() let region = vk::ImageCopy::builder()
.src_subresource(vk::ImageSubresourceLayers::builder() .src_subresource(vk::ImageSubresourceLayers::builder()
.aspect_mask(vk::ImageAspectFlags::COLOR) .aspect_mask(vk::ImageAspectFlags::COLOR)