vk: implement history
This commit is contained in:
parent
857e994570
commit
d435b43a52
|
@ -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<OwnedFramebuffer>,
|
||||
pub(crate) history_framebuffers: VecDeque<OwnedImage>,
|
||||
}
|
||||
|
||||
pub struct FilterMutable {
|
||||
|
@ -145,7 +146,7 @@ pub(crate) struct FilterCommon {
|
|||
|
||||
pub output_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 device: ash::Device,
|
||||
}
|
||||
|
@ -153,7 +154,8 @@ pub(crate) struct FilterCommon {
|
|||
#[must_use]
|
||||
pub struct FilterChainFrameIntermediates {
|
||||
device: ash::Device,
|
||||
image_views: Vec<vk::ImageView>
|
||||
image_views: Vec<vk::ImageView>,
|
||||
owned: Vec<OwnedImage>
|
||||
}
|
||||
|
||||
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<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.
|
||||
///
|
||||
/// * 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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -23,9 +23,13 @@ impl OwnedImage {
|
|||
device: ash::Device,
|
||||
mem_props: vk::PhysicalDeviceMemoryProperties,
|
||||
size: Size<u32>,
|
||||
format: ImageFormat,
|
||||
mut format: ImageFormat,
|
||||
max_miplevels: u32,
|
||||
) -> error::Result<OwnedImage> {
|
||||
// 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)
|
||||
|
|
Loading…
Reference in a new issue