From 3c15a3a523452c3679b0c9bbbbf5f277dd07fac0 Mon Sep 17 00:00:00 2001 From: chyyran Date: Sat, 11 Feb 2023 00:20:35 -0500 Subject: [PATCH] rt: fix history framebuffer requirements and unify framebuffer initialization logic --- librashader-runtime-d3d11/src/filter_chain.rs | 129 ++++++------------ librashader-runtime-d3d11/src/framebuffer.rs | 10 +- .../src/hello_triangle.rs | 14 +- librashader-runtime-d3d11/src/lib.rs | 6 +- librashader-runtime-d3d11/src/texture.rs | 4 +- librashader-runtime-d3d12/src/filter_chain.rs | 85 +++--------- librashader-runtime-d3d12/src/lib.rs | 4 +- .../src/filter_chain/filter_impl.rs | 120 +++++++--------- .../src/filter_chain/mod.rs | 4 +- librashader-runtime-gl/src/filter_pass.rs | 12 +- librashader-runtime-gl/src/gl/framebuffer.rs | 20 +-- .../src/gl/gl3/framebuffer.rs | 22 +-- .../src/gl/gl3/hello_triangle.rs | 4 +- .../src/gl/gl46/framebuffer.rs | 30 ++-- .../src/gl/gl46/hello_triangle.rs | 4 +- librashader-runtime-gl/src/gl/mod.rs | 24 ++-- librashader-runtime-gl/src/lib.rs | 6 +- librashader-runtime-gl/src/texture.rs | 3 +- librashader-runtime-vk/src/filter_chain.rs | 86 ++++-------- librashader-runtime-vk/src/filter_pass.rs | 8 +- librashader-runtime-vk/src/lib.rs | 3 +- librashader-runtime/src/binding.rs | 14 +- librashader-runtime/src/framebuffer.rs | 92 +++++++++++++ librashader-runtime/src/lib.rs | 3 + librashader/src/lib.rs | 2 +- 25 files changed, 341 insertions(+), 368 deletions(-) create mode 100644 librashader-runtime/src/framebuffer.rs diff --git a/librashader-runtime-d3d11/src/filter_chain.rs b/librashader-runtime-d3d11/src/filter_chain.rs index bfaa82b..f42555d 100644 --- a/librashader-runtime-d3d11/src/filter_chain.rs +++ b/librashader-runtime-d3d11/src/filter_chain.rs @@ -5,7 +5,7 @@ use librashader_presets::{ShaderPreset, TextureConfig}; use librashader_reflect::back::targets::HLSL; use librashader_reflect::back::{CompileReflectShader, CompileShader}; use librashader_reflect::front::GlslangCompilation; -use librashader_reflect::reflect::semantics::{BindingMeta, ShaderSemantics}; +use librashader_reflect::reflect::semantics::ShaderSemantics; use librashader_reflect::reflect::ReflectShader; use librashader_runtime::image::{Image, UVDirection}; use rustc_hash::FxHashMap; @@ -16,7 +16,7 @@ use std::path::Path; use crate::draw_quad::DrawQuad; use crate::error::{assume_d3d11_init, FilterChainError}; use crate::filter_pass::{ConstantBufferBinding, FilterPass}; -use crate::framebuffer::OwnedFramebuffer; +use crate::framebuffer::OwnedImage; use crate::graphics_pipeline::D3D11State; use crate::options::{FilterChainOptionsD3D11, FrameOptionsD3D11}; use crate::samplers::SamplerSet; @@ -24,6 +24,7 @@ use crate::util::d3d11_compile_bound_shader; use crate::{error, util, D3D11OutputView}; use librashader_reflect::reflect::presets::{CompilePresetTarget, ShaderPassArtifact}; use librashader_runtime::binding::{BindingUtil, TextureInput}; +use librashader_runtime::framebuffer::FramebufferInit; use librashader_runtime::quad::QuadType; use librashader_runtime::render_target::RenderTarget; use librashader_runtime::scaling::ScaleFramebuffer; @@ -49,9 +50,9 @@ type ShaderPassMeta = pub struct FilterChainD3D11 { pub(crate) common: FilterCommon, passes: Vec, - output_framebuffers: Box<[OwnedFramebuffer]>, - feedback_framebuffers: Box<[OwnedFramebuffer]>, - history_framebuffers: VecDeque, + output_framebuffers: Box<[OwnedImage]>, + feedback_framebuffers: Box<[OwnedImage]>, + history_framebuffers: VecDeque, state: D3D11State, } @@ -102,45 +103,34 @@ impl FilterChainD3D11 { let immediate_context = unsafe { device.GetImmediateContext()? }; - // initialize output framebuffers - let mut output_framebuffers = Vec::new(); - output_framebuffers.resize_with(filters.len(), || { - OwnedFramebuffer::new(device, Size::new(1, 1), ImageFormat::R8G8B8A8Unorm, false) - }); - - // resolve all results - let output_framebuffers = output_framebuffers - .into_iter() - .collect::>>()?; - - let mut output_textures = Vec::new(); - output_textures.resize_with(filters.len(), || None); - // - // // initialize feedback framebuffers - let mut feedback_framebuffers = Vec::new(); - feedback_framebuffers.resize_with(filters.len(), || { - OwnedFramebuffer::new(device, Size::new(1, 1), ImageFormat::R8G8B8A8Unorm, false) - }); - // resolve all results - let feedback_framebuffers = feedback_framebuffers - .into_iter() - .collect::>>()?; - - let mut feedback_textures = Vec::new(); - feedback_textures.resize_with(filters.len(), || None); - // load luts let luts = FilterChainD3D11::load_luts(device, &immediate_context, &preset.textures)?; - let (history_framebuffers, history_textures) = - FilterChainD3D11::init_history(device, &filters)?; + let framebuffer_gen = + || OwnedImage::new(device, Size::new(1, 1), ImageFormat::R8G8B8A8Unorm, false); + let input_gen = || None; + let framebuffer_init = FramebufferInit::new( + filters.iter().map(|f| &f.reflection.meta), + &framebuffer_gen, + &input_gen, + ); + + // initialize output framebuffers + let (output_framebuffers, output_textures) = framebuffer_init.init_output_framebuffers()?; + + // initialize feedback framebuffers + let (feedback_framebuffers, feedback_textures) = + framebuffer_init.init_output_framebuffers()?; + + // initialize history + let (history_framebuffers, history_textures) = framebuffer_init.init_history()?; let draw_quad = DrawQuad::new(device)?; let state = D3D11State::new(device)?; Ok(FilterChainD3D11 { passes: filters, - output_framebuffers: output_framebuffers.into_boxed_slice(), - feedback_framebuffers: feedback_framebuffers.into_boxed_slice(), + output_framebuffers, + feedback_framebuffers, history_framebuffers, common: FilterCommon { d3d11: Direct3D11 { @@ -158,8 +148,8 @@ impl FilterChainD3D11 { disable_mipmaps: options.map_or(false, |o| o.force_no_mipmaps), luts, samplers, - output_textures: output_textures.into_boxed_slice(), - feedback_textures: feedback_textures.into_boxed_slice(), + output_textures, + feedback_textures, history_textures, draw_quad, }, @@ -283,37 +273,6 @@ impl FilterChainD3D11 { Ok(filters) } - fn init_history( - device: &ID3D11Device, - filters: &Vec, - ) -> error::Result<(VecDeque, Box<[Option]>)> { - let required_images = - BindingMeta::calculate_required_history(filters.iter().map(|f| &f.reflection.meta)); - - // 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 framebuffers = VecDeque::with_capacity(required_images); - framebuffers.resize_with(required_images, || { - OwnedFramebuffer::new(device, Size::new(1, 1), ImageFormat::R8G8B8A8Unorm, false) - }); - - let framebuffers = framebuffers - .into_iter() - .collect::>>()?; - - let mut history_textures = Vec::new(); - history_textures.resize_with(required_images, || None); - - Ok((framebuffers, history_textures.into_boxed_slice())) - } - fn push_history( &mut self, ctx: &ID3D11DeviceContext, @@ -394,20 +353,6 @@ impl FilterChainD3D11 { let filter = passes[0].config.filter; let wrap_mode = passes[0].config.wrap_mode; - for ((texture, fbo), pass) in self - .common - .feedback_textures - .iter_mut() - .zip(self.feedback_framebuffers.iter()) - .zip(passes.iter()) - { - *texture = Some(InputTexture::from_framebuffer( - fbo, - pass.config.wrap_mode, - pass.config.filter, - )?); - } - for (texture, fbo) in self .common .history_textures @@ -426,7 +371,7 @@ impl FilterChainD3D11 { let mut source = original.clone(); // rescale render buffers to ensure all bindings are valid. - OwnedFramebuffer::scale_framebuffers( + OwnedImage::scale_framebuffers( source.size(), viewport.output.size, &mut self.output_framebuffers, @@ -435,6 +380,22 @@ impl FilterChainD3D11 { None, )?; + // Refresh inputs for feedback textures. + // Don't need to do this for outputs because they are yet to be bound. + for ((texture, fbo), pass) in self + .common + .feedback_textures + .iter_mut() + .zip(self.feedback_framebuffers.iter()) + .zip(passes.iter()) + { + *texture = Some(InputTexture::from_framebuffer( + fbo, + pass.config.wrap_mode, + pass.config.filter, + )?); + } + let passes_len = passes.len(); let (pass, last) = passes.split_at_mut(passes_len - 1); diff --git a/librashader-runtime-d3d11/src/framebuffer.rs b/librashader-runtime-d3d11/src/framebuffer.rs index c68dd30..ddf0a13 100644 --- a/librashader-runtime-d3d11/src/framebuffer.rs +++ b/librashader-runtime-d3d11/src/framebuffer.rs @@ -21,7 +21,7 @@ use windows::Win32::Graphics::Dxgi::Common::{DXGI_FORMAT, DXGI_SAMPLE_DESC}; static CLEAR: &[f32; 4] = &[0.0f32, 0.0, 0.0, 0.0]; #[derive(Debug, Clone)] -pub(crate) struct OwnedFramebuffer { +pub(crate) struct OwnedImage { render: ID3D11Texture2D, pub(crate) size: Size, format: DXGI_FORMAT, @@ -29,13 +29,13 @@ pub(crate) struct OwnedFramebuffer { max_mipmap: u32, } -impl OwnedFramebuffer { +impl OwnedImage { pub fn new( device: &ID3D11Device, size: Size, format: ImageFormat, mipmap: bool, - ) -> error::Result { + ) -> error::Result { unsafe { let format = d3d11_get_closest_format( device, @@ -49,7 +49,7 @@ impl OwnedFramebuffer { device.CreateTexture2D(&desc, None, Some(&mut render))?; assume_d3d11_init!(render, "CreateTexture2D"); - Ok(OwnedFramebuffer { + Ok(OwnedImage { render, size, format, @@ -245,7 +245,7 @@ fn default_desc(size: Size, format: DXGI_FORMAT, mip_levels: u32) -> D3D11_ } } -impl ScaleFramebuffer for OwnedFramebuffer { +impl ScaleFramebuffer for OwnedImage { type Error = FilterChainError; type Context = (); diff --git a/librashader-runtime-d3d11/src/hello_triangle.rs b/librashader-runtime-d3d11/src/hello_triangle.rs index 018ff01..f1cd62f 100644 --- a/librashader-runtime-d3d11/src/hello_triangle.rs +++ b/librashader-runtime-d3d11/src/hello_triangle.rs @@ -568,7 +568,7 @@ pub mod d3d11_hello_triangle { // eprintln!("w: {} h: {}", backbuffer_desc.Width, backbuffer_desc.Height); self.filter .frame( - Some(&resources.deferred_context), + None, D3D11InputView { handle: srv, size: Size { @@ -593,12 +593,12 @@ pub mod d3d11_hello_triangle { ) .unwrap(); - let mut command_list = None; - resources - .deferred_context - .FinishCommandList(false, Some(&mut command_list))?; - let command_list = command_list.unwrap(); - self.context.ExecuteCommandList(&command_list, false); + // let mut command_list = None; + // resources + // .deferred_context + // .FinishCommandList(false, Some(&mut command_list))?; + // let command_list = command_list.unwrap(); + // self.context.ExecuteCommandList(&command_list, false); // self.context.CopyResource(&resources.backbuffer, &backup); } diff --git a/librashader-runtime-d3d11/src/lib.rs b/librashader-runtime-d3d11/src/lib.rs index 39e673f..7a9e8f1 100644 --- a/librashader-runtime-d3d11/src/lib.rs +++ b/librashader-runtime-d3d11/src/lib.rs @@ -38,8 +38,10 @@ mod tests { // const FILTER_PATH: &str = // "../test/slang-shaders/handheld/console-border/gbc-lcd-grid-v2.slangp"; // "../test/null.slangp", - const FILTER_PATH: &str = - "../test/slang-shaders/bezel/Mega_Bezel/Presets/MBZ__0__SMOOTH-ADV-GLASS.slangp"; + // const FILTER_PATH: &str = "../test/slang-shaders/bezel/Mega_Bezel/Presets/MBZ__0__SMOOTH-ADV-GLASS.slangp"; + + // const FILTER_PATH: &str = "../test/slang-shaders/test/history.slangp"; + const FILTER_PATH: &str = "../test/slang-shaders/test/feedback.slangp"; // const FILTER_PATH: &str = "../test/slang-shaders/crt/crt-royale.slangp"; const IMAGE_PATH: &str = "../triangle.png"; diff --git a/librashader-runtime-d3d11/src/texture.rs b/librashader-runtime-d3d11/src/texture.rs index 2e6f0d1..aa63732 100644 --- a/librashader-runtime-d3d11/src/texture.rs +++ b/librashader-runtime-d3d11/src/texture.rs @@ -13,7 +13,7 @@ use windows::Win32::Graphics::Direct3D11::{ use windows::Win32::Graphics::Dxgi::Common::DXGI_SAMPLE_DESC; use crate::error::{assume_d3d11_init, Result}; -use crate::framebuffer::OwnedFramebuffer; +use crate::framebuffer::OwnedImage; /// An image view for use as a shader resource. /// @@ -46,7 +46,7 @@ pub struct InputTexture { impl InputTexture { pub(crate) fn from_framebuffer( - fbo: &OwnedFramebuffer, + fbo: &OwnedImage, wrap_mode: WrapMode, filter: FilterMode, ) -> Result { diff --git a/librashader-runtime-d3d12/src/filter_chain.rs b/librashader-runtime-d3d12/src/filter_chain.rs index fa4b885..8f8a7c2 100644 --- a/librashader-runtime-d3d12/src/filter_chain.rs +++ b/librashader-runtime-d3d12/src/filter_chain.rs @@ -20,7 +20,7 @@ use librashader_reflect::back::targets::{DXIL, HLSL}; use librashader_reflect::back::{CompileReflectShader, CompileShader}; use librashader_reflect::front::GlslangCompilation; use librashader_reflect::reflect::presets::{CompilePresetTarget, ShaderPassArtifact}; -use librashader_reflect::reflect::semantics::{BindingMeta, ShaderSemantics, MAX_BINDINGS_COUNT}; +use librashader_reflect::reflect::semantics::{ShaderSemantics, MAX_BINDINGS_COUNT}; use librashader_reflect::reflect::ReflectShader; use librashader_runtime::binding::{BindingUtil, TextureInput}; use librashader_runtime::image::{Image, UVDirection}; @@ -46,6 +46,7 @@ use windows::Win32::Graphics::Dxgi::Common::DXGI_FORMAT_UNKNOWN; use windows::Win32::System::Threading::{CreateEventA, ResetEvent, WaitForSingleObject}; use windows::Win32::System::WindowsProgramming::INFINITE; +use librashader_runtime::framebuffer::FramebufferInit; use librashader_runtime::render_target::RenderTarget; use librashader_runtime::scaling::ScaleFramebuffer; use rayon::prelude::*; @@ -190,44 +191,31 @@ impl FilterChainD3D12 { options.map_or(false, |o| o.force_hlsl_pipeline), )?; + let framebuffer_gen = + || OwnedImage::new(device, Size::new(1, 1), ImageFormat::R8G8B8A8Unorm, false); + let input_gen = || None; + let framebuffer_init = FramebufferInit::new( + filters.iter().map(|f| &f.reflection.meta), + &framebuffer_gen, + &input_gen, + ); + // initialize output framebuffers - let mut output_framebuffers = Vec::new(); - output_framebuffers.resize_with(filters.len(), || { - OwnedImage::new(device, Size::new(1, 1), ImageFormat::R8G8B8A8Unorm, false) - }); + let (output_framebuffers, output_textures) = framebuffer_init.init_output_framebuffers()?; - // resolve all results - let output_framebuffers = output_framebuffers - .into_iter() - .collect::>>()?; - let mut output_textures = Vec::new(); - output_textures.resize_with(filters.len(), || None); + // initialize feedback framebuffers + let (feedback_framebuffers, feedback_textures) = + framebuffer_init.init_output_framebuffers()?; - // let mut output_textures = Vec::new(); - // output_textures.resize_with(filters.len(), || None); - // - // // initialize feedback framebuffers - let mut feedback_framebuffers = Vec::new(); - feedback_framebuffers.resize_with(filters.len(), || { - OwnedImage::new(device, Size::new(1, 1), ImageFormat::R8G8B8A8Unorm, false) - }); - - // resolve all results - let feedback_framebuffers = feedback_framebuffers - .into_iter() - .collect::>>()?; - let mut feedback_textures = Vec::new(); - feedback_textures.resize_with(filters.len(), || None); - - let (history_framebuffers, history_textures) = - FilterChainD3D12::init_history(device, &filters)?; + // initialize history + let (history_framebuffers, history_textures) = framebuffer_init.init_history()?; Ok(FilterChainD3D12 { common: FilterCommon { d3d12: device.clone(), samplers, - output_textures: output_textures.into_boxed_slice(), - feedback_textures: feedback_textures.into_boxed_slice(), + output_textures, + feedback_textures, luts, mipmap_gen, root_signature, @@ -245,8 +233,8 @@ impl FilterChainD3D12 { staging_heap, rtv_heap, passes: filters, - output_framebuffers: output_framebuffers.into_boxed_slice(), - feedback_framebuffers: feedback_framebuffers.into_boxed_slice(), + output_framebuffers, + feedback_framebuffers, history_framebuffers, work_heap: texture_heap, sampler_heap, @@ -256,37 +244,6 @@ impl FilterChainD3D12 { }) } - fn init_history( - device: &ID3D12Device, - filters: &Vec, - ) -> error::Result<(VecDeque, Box<[Option]>)> { - let required_images = - BindingMeta::calculate_required_history(filters.iter().map(|f| &f.reflection.meta)); - - // 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 framebuffers = VecDeque::with_capacity(required_images); - framebuffers.resize_with(required_images, || { - OwnedImage::new(device, Size::new(1, 1), ImageFormat::R8G8B8A8Unorm, false) - }); - - let framebuffers = framebuffers - .into_iter() - .collect::>>()?; - - let mut history_textures = Vec::new(); - history_textures.resize_with(required_images, || None); - - Ok((framebuffers, history_textures.into_boxed_slice())) - } - fn load_luts( device: &ID3D12Device, heap: &mut D3D12DescriptorHeap, diff --git a/librashader-runtime-d3d12/src/lib.rs b/librashader-runtime-d3d12/src/lib.rs index d57b434..80b1d50 100644 --- a/librashader-runtime-d3d12/src/lib.rs +++ b/librashader-runtime-d3d12/src/lib.rs @@ -35,7 +35,9 @@ mod tests { let sample = hello_triangle::d3d12_hello_triangle::Sample::new( // "../test/slang-shaders/crt/crt-lottes.slangp", // "../test/basic.slangp", - "../test/slang-shaders/bezel/Mega_Bezel/Presets/MBZ__0__SMOOTH-ADV-GLASS.slangp", + // "../test/slang-shaders/bezel/Mega_Bezel/Presets/MBZ__0__SMOOTH-ADV-GLASS.slangp", + "../test/slang-shaders/test/feedback.slangp", + // "../test/slang-shaders/test/history.slangp", // "../test/slang-shaders/crt/crt-royale.slangp", // "../test/slang-shaders/vhs/VHSPro.slangp", &SampleCommandLine { diff --git a/librashader-runtime-gl/src/filter_chain/filter_impl.rs b/librashader-runtime-gl/src/filter_chain/filter_impl.rs index 1047af6..bb3c9c8 100644 --- a/librashader-runtime-gl/src/filter_chain/filter_impl.rs +++ b/librashader-runtime-gl/src/filter_chain/filter_impl.rs @@ -1,25 +1,26 @@ use crate::binding::{GlUniformStorage, UniformLocation, VariableLocation}; use crate::error::FilterChainError; use crate::filter_pass::{FilterPass, UniformOffset}; -use crate::gl::{DrawQuad, Framebuffer, FramebufferInterface, GLInterface, LoadLut, UboRing}; +use crate::gl::{DrawQuad, FramebufferInterface, GLFramebuffer, GLInterface, LoadLut, UboRing}; use crate::options::{FilterChainOptionsGL, FrameOptionsGL}; use crate::samplers::SamplerSet; use crate::texture::InputTexture; use crate::util::{gl_get_version, gl_u16_to_version}; use crate::{error, util, GLImage}; use gl::types::{GLint, GLuint}; -use librashader_common::{FilterMode, Viewport, WrapMode}; +use librashader_common::Viewport; use librashader_presets::ShaderPreset; use librashader_reflect::back::cross::GlslVersion; use librashader_reflect::back::targets::GLSL; use librashader_reflect::back::{CompileReflectShader, CompileShader}; use librashader_reflect::front::GlslangCompilation; -use librashader_reflect::reflect::semantics::{BindingMeta, ShaderSemantics, UniformMeta}; +use librashader_reflect::reflect::semantics::{ShaderSemantics, UniformMeta}; use librashader_reflect::reflect::presets::{CompilePresetTarget, ShaderPassArtifact}; use librashader_reflect::reflect::ReflectShader; use librashader_runtime::binding::BindingUtil; +use librashader_runtime::framebuffer::FramebufferInit; use librashader_runtime::render_target::RenderTarget; use librashader_runtime::scaling::ScaleFramebuffer; use rustc_hash::FxHashMap; @@ -38,9 +39,9 @@ pub(crate) struct FilterChainImpl { pub(crate) common: FilterCommon, passes: Box<[FilterPass]>, draw_quad: T::DrawQuad, - output_framebuffers: Box<[Framebuffer]>, - feedback_framebuffers: Box<[Framebuffer]>, - history_framebuffers: VecDeque, + output_framebuffers: Box<[GLFramebuffer]>, + feedback_framebuffers: Box<[GLFramebuffer]>, + history_framebuffers: VecDeque, } pub(crate) struct FilterCommon { @@ -120,31 +121,40 @@ impl FilterChainImpl { let samplers = SamplerSet::new(); - // initialize output framebuffers - let mut output_framebuffers = Vec::new(); - output_framebuffers.resize_with(filters.len(), || T::FramebufferInterface::new(1)); - let mut output_textures = Vec::new(); - output_textures.resize_with(filters.len(), InputTexture::default); - - // initialize feedback framebuffers - let mut feedback_framebuffers = Vec::new(); - feedback_framebuffers.resize_with(filters.len(), || T::FramebufferInterface::new(1)); - let mut feedback_textures = Vec::new(); - feedback_textures.resize_with(filters.len(), InputTexture::default); - // load luts let luts = T::LoadLut::load_luts(&preset.textures)?; - let (history_framebuffers, history_textures) = - FilterChainImpl::init_history(&filters, default_filter, default_wrap); + let framebuffer_gen = || Ok::<_, FilterChainError>(T::FramebufferInterface::new(1)); + let input_gen = || InputTexture { + image: Default::default(), + filter: default_filter, + mip_filter: default_filter, + wrap_mode: default_wrap, + }; + + let framebuffer_init = FramebufferInit::new( + filters.iter().map(|f| &f.reflection.meta), + &framebuffer_gen, + &input_gen, + ); + + // initialize output framebuffers + let (output_framebuffers, output_textures) = framebuffer_init.init_output_framebuffers()?; + + // initialize feedback framebuffers + let (feedback_framebuffers, feedback_textures) = + framebuffer_init.init_output_framebuffers()?; + + // initialize history + let (history_framebuffers, history_textures) = framebuffer_init.init_history()?; // create vertex objects let draw_quad = T::DrawQuad::new(); Ok(FilterChainImpl { passes: filters, - output_framebuffers: output_framebuffers.into_boxed_slice(), - feedback_framebuffers: feedback_framebuffers.into_boxed_slice(), + output_framebuffers, + feedback_framebuffers, history_framebuffers, draw_quad, common: FilterCommon { @@ -159,8 +169,8 @@ impl FilterChainImpl { disable_mipmaps: options.map_or(false, |o| o.force_no_mipmaps), luts, samplers, - output_textures: output_textures.into_boxed_slice(), - feedback_textures: feedback_textures.into_boxed_slice(), + output_textures, + feedback_textures, history_textures, }, }) @@ -274,37 +284,6 @@ impl FilterChainImpl { Ok(filters.into_boxed_slice()) } - fn init_history( - filters: &[FilterPass], - filter: FilterMode, - wrap_mode: WrapMode, - ) -> (VecDeque, Box<[InputTexture]>) { - let required_images = - BindingMeta::calculate_required_history(filters.iter().map(|f| &f.reflection.meta)); - - // not using frame history; - if required_images <= 1 { - // println!("[history] not using frame history"); - return (VecDeque::new(), Box::new([])); - } - - // history0 is aliased with the original - - // eprintln!("[history] using frame history with {required_images} images"); - let mut framebuffers = VecDeque::with_capacity(required_images); - framebuffers.resize_with(required_images, || T::FramebufferInterface::new(1)); - - let mut history_textures = Vec::new(); - history_textures.resize_with(required_images, || InputTexture { - image: Default::default(), - filter, - mip_filter: filter, - wrap_mode, - }); - - (framebuffers, history_textures.into_boxed_slice()) - } - fn push_history(&mut self, input: &GLImage) -> error::Result<()> { if let Some(mut back) = self.history_framebuffers.pop_back() { if back.size != input.size || (input.format != 0 && input.format != back.format) { @@ -313,7 +292,6 @@ impl FilterChainImpl { } back.copy_from::(input)?; - self.history_framebuffers.push_front(back) } @@ -326,7 +304,7 @@ impl FilterChainImpl { pub fn frame( &mut self, frame_count: usize, - viewport: &Viewport<&Framebuffer>, + viewport: &Viewport<&GLFramebuffer>, input: &GLImage, options: Option<&FrameOptionsGL>, ) -> error::Result<()> { @@ -364,18 +342,6 @@ impl FilterChainImpl { texture.image = fbo.as_texture(filter, wrap_mode).image; } - for ((texture, fbo), pass) in self - .common - .feedback_textures - .iter_mut() - .zip(self.feedback_framebuffers.iter()) - .zip(passes.iter()) - { - texture.image = fbo - .as_texture(pass.config.filter, pass.config.wrap_mode) - .image; - } - // shader_gl3: 2067 let original = InputTexture { image: *input, @@ -387,7 +353,7 @@ impl FilterChainImpl { let mut source = original; // rescale render buffers to ensure all bindings are valid. - >::scale_framebuffers( + >::scale_framebuffers( source.image.size, viewport.output.size, &mut self.output_framebuffers, @@ -396,6 +362,20 @@ impl FilterChainImpl { None, )?; + // Refresh inputs for feedback textures. + // Don't need to do this for outputs because they are yet to be bound. + for ((texture, fbo), pass) in self + .common + .feedback_textures + .iter_mut() + .zip(self.feedback_framebuffers.iter()) + .zip(passes.iter()) + { + texture.image = fbo + .as_texture(pass.config.filter, pass.config.wrap_mode) + .image; + } + let passes_len = passes.len(); let (pass, last) = passes.split_at_mut(passes_len - 1); diff --git a/librashader-runtime-gl/src/filter_chain/mod.rs b/librashader-runtime-gl/src/filter_chain/mod.rs index c87c69b..3e9e8e0 100644 --- a/librashader-runtime-gl/src/filter_chain/mod.rs +++ b/librashader-runtime-gl/src/filter_chain/mod.rs @@ -5,7 +5,7 @@ use crate::error::{FilterChainError, Result}; use crate::filter_chain::filter_impl::FilterChainImpl; use crate::filter_chain::inner::FilterChainDispatch; use crate::options::{FilterChainOptionsGL, FrameOptionsGL}; -use crate::{Framebuffer, GLImage}; +use crate::{GLFramebuffer, GLImage}; use librashader_presets::ShaderPreset; mod filter_impl; @@ -61,7 +61,7 @@ impl FilterChainGL { pub fn frame( &mut self, input: &GLImage, - viewport: &Viewport<&Framebuffer>, + viewport: &Viewport<&GLFramebuffer>, frame_count: usize, options: Option<&FrameOptionsGL>, ) -> Result<()> { diff --git a/librashader-runtime-gl/src/filter_pass.rs b/librashader-runtime-gl/src/filter_pass.rs index 658f3f5..4a51bda 100644 --- a/librashader-runtime-gl/src/filter_pass.rs +++ b/librashader-runtime-gl/src/filter_pass.rs @@ -14,7 +14,7 @@ use crate::binding::{GlUniformBinder, GlUniformStorage, UniformLocation, Variabl use crate::filter_chain::FilterCommon; use crate::gl::{BindTexture, GLInterface, UboRing}; use crate::samplers::SamplerSet; -use crate::Framebuffer; +use crate::GLFramebuffer; use crate::texture::InputTexture; @@ -29,7 +29,7 @@ impl UniformOffset { } } -pub struct FilterPass { +pub(crate) struct FilterPass { pub reflection: ShaderReflection, pub program: GLuint, pub ubo_location: UniformLocation, @@ -81,10 +81,10 @@ impl FilterPass { parent: &FilterCommon, frame_count: u32, frame_direction: i32, - viewport: &Viewport<&Framebuffer>, + viewport: &Viewport<&GLFramebuffer>, original: &InputTexture, source: &InputTexture, - output: RenderTarget, + output: RenderTarget, ) { let framebuffer = output.output; @@ -93,7 +93,7 @@ impl FilterPass { } unsafe { - gl::BindFramebuffer(gl::FRAMEBUFFER, framebuffer.handle); + gl::BindFramebuffer(gl::FRAMEBUFFER, framebuffer.fbo); gl::UseProgram(self.program); } @@ -165,7 +165,7 @@ impl FilterPass { frame_count: u32, frame_direction: i32, fb_size: Size, - viewport: &Viewport<&Framebuffer>, + viewport: &Viewport<&GLFramebuffer>, original: &InputTexture, source: &InputTexture, ) { diff --git a/librashader-runtime-gl/src/gl/framebuffer.rs b/librashader-runtime-gl/src/gl/framebuffer.rs index 0ae48ec..7877938 100644 --- a/librashader-runtime-gl/src/gl/framebuffer.rs +++ b/librashader-runtime-gl/src/gl/framebuffer.rs @@ -11,9 +11,9 @@ use librashader_runtime::scaling::ScaleFramebuffer; /// /// Generally for use as render targets. #[derive(Debug)] -pub struct Framebuffer { +pub struct GLFramebuffer { pub(crate) image: GLuint, - pub(crate) handle: GLuint, + pub(crate) fbo: GLuint, pub(crate) size: Size, pub(crate) format: GLenum, pub(crate) max_levels: u32, @@ -21,7 +21,7 @@ pub struct Framebuffer { pub(crate) is_raw: bool, } -impl Framebuffer { +impl GLFramebuffer { /// Create a framebuffer from an already initialized texture and framebuffer. /// /// The framebuffer will not be deleted when this struct is dropped. @@ -31,14 +31,14 @@ impl Framebuffer { format: GLenum, size: Size, miplevels: u32, - ) -> Framebuffer { - Framebuffer { + ) -> GLFramebuffer { + GLFramebuffer { image: texture, size, format, max_levels: miplevels, mip_levels: miplevels, - handle: fbo, + fbo: fbo, is_raw: true, } } @@ -76,15 +76,15 @@ impl Framebuffer { } } -impl Drop for Framebuffer { +impl Drop for GLFramebuffer { fn drop(&mut self) { if self.is_raw { return; } unsafe { - if self.handle != 0 { - gl::DeleteFramebuffers(1, &self.handle); + if self.fbo != 0 { + gl::DeleteFramebuffers(1, &self.fbo); } if self.image != 0 { gl::DeleteTextures(1, &self.image); @@ -93,7 +93,7 @@ impl Drop for Framebuffer { } } // -impl ScaleFramebuffer for Framebuffer { +impl ScaleFramebuffer for GLFramebuffer { type Error = FilterChainError; type Context = (); diff --git a/librashader-runtime-gl/src/gl/gl3/framebuffer.rs b/librashader-runtime-gl/src/gl/gl3/framebuffer.rs index c704aff..12316a2 100644 --- a/librashader-runtime-gl/src/gl/gl3/framebuffer.rs +++ b/librashader-runtime-gl/src/gl/gl3/framebuffer.rs @@ -1,6 +1,6 @@ use crate::error::{FilterChainError, Result}; use crate::framebuffer::GLImage; -use crate::gl::framebuffer::Framebuffer; +use crate::gl::framebuffer::GLFramebuffer; use crate::gl::FramebufferInterface; use gl::types::{GLenum, GLint, GLsizei}; use librashader_common::{ImageFormat, Size}; @@ -11,7 +11,7 @@ use librashader_runtime::scaling::{MipmapSize, ViewportSize}; pub struct Gl3Framebuffer; impl FramebufferInterface for Gl3Framebuffer { - fn new(max_levels: u32) -> Framebuffer { + fn new(max_levels: u32) -> GLFramebuffer { let mut framebuffer = 0; unsafe { gl::GenFramebuffers(1, &mut framebuffer); @@ -19,7 +19,7 @@ impl FramebufferInterface for Gl3Framebuffer { gl::BindFramebuffer(gl::FRAMEBUFFER, 0); } - Framebuffer { + GLFramebuffer { image: 0, size: Size { width: 1, @@ -28,13 +28,13 @@ impl FramebufferInterface for Gl3Framebuffer { format: 0, max_levels, mip_levels: 0, - handle: framebuffer, + fbo: framebuffer, is_raw: false, } } fn scale( - fb: &mut Framebuffer, + fb: &mut GLFramebuffer, scaling: Scale2D, format: ImageFormat, viewport_size: &Size, @@ -66,10 +66,10 @@ impl FramebufferInterface for Gl3Framebuffer { } Ok(size) } - fn clear(fb: &Framebuffer) { + fn clear(fb: &GLFramebuffer) { unsafe { if REBIND { - gl::BindFramebuffer(gl::FRAMEBUFFER, fb.handle); + gl::BindFramebuffer(gl::FRAMEBUFFER, fb.fbo); } gl::ColorMask(gl::TRUE, gl::TRUE, gl::TRUE, gl::TRUE); gl::ClearColor(0.0, 0.0, 0.0, 0.0); @@ -79,14 +79,14 @@ impl FramebufferInterface for Gl3Framebuffer { } } } - fn copy_from(fb: &mut Framebuffer, image: &GLImage) -> Result<()> { + fn copy_from(fb: &mut GLFramebuffer, image: &GLImage) -> Result<()> { // todo: may want to use a shader and draw a quad to be faster. if image.size != fb.size || image.format != fb.format { Self::init(fb, image.size, image.format)?; } unsafe { - gl::BindFramebuffer(gl::FRAMEBUFFER, fb.handle); + gl::BindFramebuffer(gl::FRAMEBUFFER, fb.fbo); gl::FramebufferTexture2D( gl::READ_FRAMEBUFFER, @@ -149,7 +149,7 @@ impl FramebufferInterface for Gl3Framebuffer { Ok(()) } - fn init(fb: &mut Framebuffer, mut size: Size, format: impl Into) -> Result<()> { + fn init(fb: &mut GLFramebuffer, mut size: Size, format: impl Into) -> Result<()> { if fb.is_raw { return Ok(()); } @@ -157,7 +157,7 @@ impl FramebufferInterface for Gl3Framebuffer { fb.size = size; unsafe { - gl::BindFramebuffer(gl::FRAMEBUFFER, fb.handle); + gl::BindFramebuffer(gl::FRAMEBUFFER, fb.fbo); // reset the framebuffer image if fb.image != 0 { diff --git a/librashader-runtime-gl/src/gl/gl3/hello_triangle.rs b/librashader-runtime-gl/src/gl/gl3/hello_triangle.rs index db05f85..1eaf275 100644 --- a/librashader-runtime-gl/src/gl/gl3/hello_triangle.rs +++ b/librashader-runtime-gl/src/gl/gl3/hello_triangle.rs @@ -10,7 +10,7 @@ use librashader_common::{Size, Viewport}; use crate::filter_chain::FilterChainGL; use crate::framebuffer::GLImage; -use crate::Framebuffer; +use crate::GLFramebuffer; const WIDTH: u32 = 800; const HEIGHT: u32 = 600; @@ -464,7 +464,7 @@ void main() let (fb_width, fb_height) = window.get_framebuffer_size(); let (vp_width, vp_height) = window.get_size(); - let output = Framebuffer::new_from_raw( + let output = GLFramebuffer::new_from_raw( output_texture, output_framebuffer_handle, gl::RGBA8, diff --git a/librashader-runtime-gl/src/gl/gl46/framebuffer.rs b/librashader-runtime-gl/src/gl/gl46/framebuffer.rs index d674d82..de1f2ea 100644 --- a/librashader-runtime-gl/src/gl/gl46/framebuffer.rs +++ b/librashader-runtime-gl/src/gl/gl46/framebuffer.rs @@ -1,6 +1,6 @@ use crate::error::{FilterChainError, Result}; use crate::framebuffer::GLImage; -use crate::gl::framebuffer::Framebuffer; +use crate::gl::framebuffer::GLFramebuffer; use crate::gl::FramebufferInterface; use gl::types::{GLenum, GLint, GLsizei}; use librashader_common::{ImageFormat, Size}; @@ -11,13 +11,13 @@ use librashader_runtime::scaling::{MipmapSize, ViewportSize}; pub struct Gl46Framebuffer; impl FramebufferInterface for Gl46Framebuffer { - fn new(max_levels: u32) -> Framebuffer { + fn new(max_levels: u32) -> GLFramebuffer { let mut framebuffer = 0; unsafe { gl::CreateFramebuffers(1, &mut framebuffer); } - Framebuffer { + GLFramebuffer { image: 0, size: Size { width: 1, @@ -26,13 +26,13 @@ impl FramebufferInterface for Gl46Framebuffer { format: 0, max_levels, mip_levels: 0, - handle: framebuffer, + fbo: framebuffer, is_raw: false, } } fn scale( - fb: &mut Framebuffer, + fb: &mut GLFramebuffer, scaling: Scale2D, format: ImageFormat, viewport_size: &Size, @@ -66,17 +66,17 @@ impl FramebufferInterface for Gl46Framebuffer { } Ok(size) } - fn clear(fb: &Framebuffer) { + fn clear(fb: &GLFramebuffer) { unsafe { gl::ClearNamedFramebufferfv( - fb.handle, + fb.fbo, gl::COLOR, 0, [0.0f32, 0.0, 0.0, 0.0].as_ptr().cast(), ); } } - fn copy_from(fb: &mut Framebuffer, image: &GLImage) -> Result<()> { + fn copy_from(fb: &mut GLFramebuffer, image: &GLImage) -> Result<()> { // todo: confirm this behaviour for unbound image. if image.handle == 0 { return Ok(()); @@ -90,11 +90,11 @@ impl FramebufferInterface for Gl46Framebuffer { unsafe { // gl::NamedFramebufferDrawBuffer(fb.handle, gl::COLOR_ATTACHMENT1); gl::NamedFramebufferReadBuffer(image.handle, gl::COLOR_ATTACHMENT0); - gl::NamedFramebufferDrawBuffer(fb.handle, gl::COLOR_ATTACHMENT0); + gl::NamedFramebufferDrawBuffer(fb.fbo, gl::COLOR_ATTACHMENT0); gl::BlitNamedFramebuffer( image.handle, - fb.handle, + fb.fbo, 0, 0, image.size.width as GLint, @@ -110,7 +110,7 @@ impl FramebufferInterface for Gl46Framebuffer { Ok(()) } - fn init(fb: &mut Framebuffer, mut size: Size, format: impl Into) -> Result<()> { + fn init(fb: &mut GLFramebuffer, mut size: Size, format: impl Into) -> Result<()> { if fb.is_raw { return Ok(()); } @@ -120,7 +120,7 @@ impl FramebufferInterface for Gl46Framebuffer { unsafe { // reset the framebuffer image if fb.image != 0 { - gl::NamedFramebufferTexture(fb.handle, gl::COLOR_ATTACHMENT0, 0, 0); + gl::NamedFramebufferTexture(fb.fbo, gl::COLOR_ATTACHMENT0, 0, 0); gl::DeleteTextures(1, &fb.image); } @@ -149,13 +149,13 @@ impl FramebufferInterface for Gl46Framebuffer { size.height as GLsizei, ); - gl::NamedFramebufferTexture(fb.handle, gl::COLOR_ATTACHMENT0, fb.image, 0); + gl::NamedFramebufferTexture(fb.fbo, gl::COLOR_ATTACHMENT0, fb.image, 0); let status = gl::CheckFramebufferStatus(gl::FRAMEBUFFER); if status != gl::FRAMEBUFFER_COMPLETE { match status { gl::FRAMEBUFFER_UNSUPPORTED => { - gl::NamedFramebufferTexture(fb.handle, gl::COLOR_ATTACHMENT0, 0, 0); + gl::NamedFramebufferTexture(fb.fbo, gl::COLOR_ATTACHMENT0, 0, 0); gl::DeleteTextures(1, &fb.image); gl::CreateTextures(gl::TEXTURE_2D, 1, &mut fb.image); @@ -174,7 +174,7 @@ impl FramebufferInterface for Gl46Framebuffer { size.width as GLsizei, size.height as GLsizei, ); - gl::NamedFramebufferTexture(fb.handle, gl::COLOR_ATTACHMENT0, fb.image, 0); + gl::NamedFramebufferTexture(fb.fbo, gl::COLOR_ATTACHMENT0, fb.image, 0); // fb.init = // gl::CheckFramebufferStatus(gl::FRAMEBUFFER) == gl::FRAMEBUFFER_COMPLETE; } diff --git a/librashader-runtime-gl/src/gl/gl46/hello_triangle.rs b/librashader-runtime-gl/src/gl/gl46/hello_triangle.rs index 8576922..3974b6a 100644 --- a/librashader-runtime-gl/src/gl/gl46/hello_triangle.rs +++ b/librashader-runtime-gl/src/gl/gl46/hello_triangle.rs @@ -10,7 +10,7 @@ use librashader_common::{Size, Viewport}; use crate::filter_chain::FilterChainGL; use crate::framebuffer::GLImage; -use crate::Framebuffer; +use crate::GLFramebuffer; const WIDTH: u32 = 800; const HEIGHT: u32 = 600; @@ -455,7 +455,7 @@ void main() let (fb_width, fb_height) = window.get_framebuffer_size(); let (vp_width, vp_height) = window.get_size(); - let output = Framebuffer::new_from_raw( + let output = GLFramebuffer::new_from_raw( output_texture, output_framebuffer_handle, gl::RGBA8, diff --git a/librashader-runtime-gl/src/gl/mod.rs b/librashader-runtime-gl/src/gl/mod.rs index 8764fa0..b8c7f0d 100644 --- a/librashader-runtime-gl/src/gl/mod.rs +++ b/librashader-runtime-gl/src/gl/mod.rs @@ -7,7 +7,7 @@ use crate::error::Result; use crate::framebuffer::GLImage; use crate::samplers::SamplerSet; use crate::texture::InputTexture; -pub use framebuffer::Framebuffer; +pub use framebuffer::GLFramebuffer; use gl::types::{GLenum, GLuint}; use librashader_common::{ImageFormat, Size}; use librashader_presets::{Scale2D, TextureConfig}; @@ -15,17 +15,17 @@ use librashader_reflect::reflect::semantics::{TextureBinding, UboReflection}; use librashader_runtime::uniforms::UniformStorageAccess; use rustc_hash::FxHashMap; -pub trait LoadLut { +pub(crate) trait LoadLut { fn load_luts(textures: &[TextureConfig]) -> Result>; } -pub trait DrawQuad { +pub(crate) trait DrawQuad { fn new() -> Self; fn bind_vertices(&self); fn unbind_vertices(&self); } -pub trait UboRing { +pub(crate) trait UboRing { fn new(buffer_size: u32) -> Self; fn bind_for_frame( &mut self, @@ -35,27 +35,27 @@ pub trait UboRing { ); } -pub trait FramebufferInterface { - fn new(max_levels: u32) -> Framebuffer; +pub(crate) trait FramebufferInterface { + fn new(max_levels: u32) -> GLFramebuffer; fn scale( - fb: &mut Framebuffer, + fb: &mut GLFramebuffer, scaling: Scale2D, format: ImageFormat, viewport_size: &Size, source_size: &Size, mipmap: bool, ) -> Result>; - fn clear(fb: &Framebuffer); - fn copy_from(fb: &mut Framebuffer, image: &GLImage) -> Result<()>; - fn init(fb: &mut Framebuffer, size: Size, format: impl Into) -> Result<()>; + fn clear(fb: &GLFramebuffer); + fn copy_from(fb: &mut GLFramebuffer, image: &GLImage) -> Result<()>; + fn init(fb: &mut GLFramebuffer, size: Size, format: impl Into) -> Result<()>; } -pub trait BindTexture { +pub(crate) trait BindTexture { fn bind_texture(samplers: &SamplerSet, binding: &TextureBinding, texture: &InputTexture); fn gen_mipmaps(texture: &InputTexture); } -pub trait GLInterface { +pub(crate) trait GLInterface { type FramebufferInterface: FramebufferInterface; type UboRing: UboRing<16>; type DrawQuad: DrawQuad; diff --git a/librashader-runtime-gl/src/lib.rs b/librashader-runtime-gl/src/lib.rs index 099229b..78b9a52 100644 --- a/librashader-runtime-gl/src/lib.rs +++ b/librashader-runtime-gl/src/lib.rs @@ -21,7 +21,7 @@ mod texture; pub mod error; pub mod options; -pub use crate::gl::Framebuffer; +pub use crate::gl::GLFramebuffer; pub use filter_chain::FilterChainGL; pub use framebuffer::GLImage; @@ -52,7 +52,9 @@ mod tests { let (glfw, window, events, shader, vao) = gl::gl46::hello_triangle::setup(); let mut filter = FilterChainGL::load_from_path( // "../test/slang-shaders/vhs/VHSPro.slangp", - "../test/slang-shaders/crt/crt-royale.slangp", + // "../test/slang-shaders/test/history.slangp", + // "../test/slang-shaders/bezel/Mega_Bezel/Presets/MBZ__0__SMOOTH-ADV.slangp", + "../test/slang-shaders/test/feedback.slangp", Some(&FilterChainOptionsGL { glsl_version: 0, use_dsa: true, diff --git a/librashader-runtime-gl/src/texture.rs b/librashader-runtime-gl/src/texture.rs index 959cade..cc9fe5f 100644 --- a/librashader-runtime-gl/src/texture.rs +++ b/librashader-runtime-gl/src/texture.rs @@ -3,13 +3,14 @@ use crate::framebuffer::GLImage; use librashader_common::{FilterMode, WrapMode}; #[derive(Default, Debug, Copy, Clone)] -pub struct InputTexture { +pub(crate) struct InputTexture { pub image: GLImage, pub filter: FilterMode, pub mip_filter: FilterMode, pub wrap_mode: WrapMode, } +/// An OpenGL texture bound as a shader resource. impl InputTexture { pub fn is_bound(&self) -> bool { self.image.handle != 0 diff --git a/librashader-runtime-vk/src/filter_chain.rs b/librashader-runtime-vk/src/filter_chain.rs index 135252d..b2b70ac 100644 --- a/librashader-runtime-vk/src/filter_chain.rs +++ b/librashader-runtime-vk/src/filter_chain.rs @@ -19,7 +19,7 @@ use librashader_reflect::back::targets::SPIRV; use librashader_reflect::back::{CompileReflectShader, CompileShader}; use librashader_reflect::front::GlslangCompilation; use librashader_reflect::reflect::presets::{CompilePresetTarget, ShaderPassArtifact}; -use librashader_reflect::reflect::semantics::{BindingMeta, ShaderSemantics}; +use librashader_reflect::reflect::semantics::ShaderSemantics; use librashader_reflect::reflect::ReflectShader; use librashader_runtime::binding::BindingUtil; use librashader_runtime::image::{Image, UVDirection}; @@ -31,6 +31,7 @@ use std::collections::VecDeque; use std::path::Path; use std::sync::Arc; +use librashader_runtime::framebuffer::FramebufferInit; use librashader_runtime::render_target::RenderTarget; use librashader_runtime::scaling::ScaleFramebuffer; use rayon::prelude::*; @@ -145,8 +146,8 @@ pub(crate) struct FilterCommon { pub(crate) luts: FxHashMap, pub samplers: SamplerSet, pub(crate) draw_quad: DrawQuad, - pub output_inputs: Box<[Option]>, - pub feedback_inputs: Box<[Option]>, + pub output_textures: Box<[Option]>, + pub feedback_textures: Box<[Option]>, pub history_textures: Box<[Option]>, pub config: FilterMutable, pub device: Arc, @@ -256,28 +257,24 @@ 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 framebuffer_gen = + || OwnedImage::new(&device, Size::new(1, 1), ImageFormat::R8G8B8A8Unorm, 1); + let input_gen = || None; + let framebuffer_init = FramebufferInit::new( + filters.iter().map(|f| &f.reflection.meta), + &framebuffer_gen, + &input_gen, + ); - let mut output_framebuffers = Vec::new(); - output_framebuffers.resize_with(filters.len(), || { - OwnedImage::new(&device, Size::new(1, 1), ImageFormat::R8G8B8A8Unorm, 1) - }); + // initialize output framebuffers + let (output_framebuffers, output_textures) = framebuffer_init.init_output_framebuffers()?; - let mut feedback_framebuffers = Vec::new(); - feedback_framebuffers.resize_with(filters.len(), || { - OwnedImage::new(&device, Size::new(1, 1), ImageFormat::R8G8B8A8Unorm, 1) - }); + // initialize feedback framebuffers + let (feedback_framebuffers, feedback_textures) = + framebuffer_init.init_output_framebuffers()?; - let output_framebuffers: error::Result> = - output_framebuffers.into_iter().collect(); - let mut output_textures = Vec::new(); - output_textures.resize_with(filters.len(), || None); - - let feedback_framebuffers: error::Result> = - feedback_framebuffers.into_iter().collect(); - let mut feedback_textures = Vec::new(); - feedback_textures.resize_with(filters.len(), || None); + // initialize history + let (history_framebuffers, history_textures) = framebuffer_init.init_history()?; let mut intermediates = Vec::new(); intermediates.resize_with(frames_in_flight as usize, || { @@ -298,14 +295,14 @@ impl FilterChainVulkan { }, draw_quad: DrawQuad::new(&device.device, &device.alloc)?, device: device.device.clone(), - output_inputs: output_textures.into_boxed_slice(), - feedback_inputs: feedback_textures.into_boxed_slice(), + output_textures, + feedback_textures, history_textures, }, passes: filters, vulkan: device, - output_framebuffers: output_framebuffers?.into_boxed_slice(), - feedback_framebuffers: feedback_framebuffers?.into_boxed_slice(), + output_framebuffers, + feedback_framebuffers, history_framebuffers, residuals: intermediates.into_boxed_slice(), disable_mipmaps: options.map_or(false, |o| o.force_no_mipmaps), @@ -374,6 +371,7 @@ impl FilterChainVulkan { graphics_pipeline, // ubo_ring, frames_in_flight, + internal_frame_count: 0, }) }) .collect(); @@ -443,36 +441,6 @@ impl FilterChainVulkan { Ok(luts) } - fn init_history( - vulkan: &VulkanObjects, - filters: &[FilterPass], - ) -> error::Result<(VecDeque, Box<[Option]>)> { - let required_images = - BindingMeta::calculate_required_history(filters.iter().map(|f| &f.reflection.meta)); - - // 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, @@ -640,9 +608,9 @@ impl FilterChainVulkan { output: &OwnedImage, feedback: &OwnedImage| { // refresh inputs - self.common.feedback_inputs[index] = + self.common.feedback_textures[index] = Some(feedback.as_input(pass.config.filter, pass.config.wrap_mode)); - self.common.output_inputs[index] = + self.common.output_textures[index] = Some(output.as_input(pass.config.filter, pass.config.wrap_mode)); Ok(()) }), @@ -682,7 +650,7 @@ impl FilterChainVulkan { out.output.end_pass(cmd); } - source = self.common.output_inputs[index].clone().unwrap(); + source = self.common.output_textures[index].clone().unwrap(); intermediates.dispose_outputs(output_image); intermediates.dispose_framebuffers(residual_fb); } diff --git a/librashader-runtime-vk/src/filter_pass.rs b/librashader-runtime-vk/src/filter_pass.rs index 5b1a76f..a3ffcf4 100644 --- a/librashader-runtime-vk/src/filter_pass.rs +++ b/librashader-runtime-vk/src/filter_pass.rs @@ -32,6 +32,7 @@ pub struct FilterPass { pub graphics_pipeline: VulkanGraphicsPipeline, // pub ubo_ring: VkUboRing, pub frames_in_flight: u32, + pub internal_frame_count: usize, } impl TextureInput for InputImage { @@ -100,7 +101,7 @@ impl FilterPass { vbo_type: QuadType, ) -> error::Result> { let mut descriptor = self.graphics_pipeline.layout.descriptor_sets - [(frame_count % self.frames_in_flight) as usize]; + [self.internal_frame_count % self.frames_in_flight as usize]; self.build_semantics( pass_index, @@ -193,6 +194,7 @@ impl FilterPass { parent.draw_quad.draw_quad(cmd, vbo_type); self.graphics_pipeline.end_rendering(&parent.device, cmd); } + self.internal_frame_count += 1; Ok(residual) } @@ -223,10 +225,10 @@ impl FilterPass { source, &self.uniform_bindings, &self.reflection.meta.texture_meta, - parent.output_inputs[0..pass_index] + parent.output_textures[0..pass_index] .iter() .map(|o| o.as_ref()), - parent.feedback_inputs.iter().map(|o| o.as_ref()), + parent.feedback_textures.iter().map(|o| o.as_ref()), parent.history_textures.iter().map(|o| o.as_ref()), parent.luts.iter().map(|(u, i)| (*u, i.as_ref())), &self.source.parameters, diff --git a/librashader-runtime-vk/src/lib.rs b/librashader-runtime-vk/src/lib.rs index 3ca5968..3d63e04 100644 --- a/librashader-runtime-vk/src/lib.rs +++ b/librashader-runtime-vk/src/lib.rs @@ -44,7 +44,8 @@ mod tests { let filter = FilterChainVulkan::load_from_path( &base, // "../test/slang-shaders/crt/crt-royale.slangp", - "../test/slang-shaders/bezel/Mega_Bezel/Presets/MBZ__2__ADV-NO-REFLECT.slangp", + // "../test/slang-shaders/bezel/Mega_Bezel/Presets/MBZ__0__SMOOTH-ADV-GLASS.slangp", + "../test/slang-shaders/test/feedback.slangp", // "../test/basic.slangp", Some(&FilterChainOptionsVulkan { frames_in_flight: 3, diff --git a/librashader-runtime/src/binding.rs b/librashader-runtime/src/binding.rs index 83a1bce..678248b 100644 --- a/librashader-runtime/src/binding.rs +++ b/librashader-runtime/src/binding.rs @@ -310,19 +310,21 @@ impl BindingUtil for BindingMeta { for pass in pass_meta { // If a shader uses history size, but not history, we still need to keep the texture. - let texture_count = pass + let texture_max_index = pass .texture_meta .iter() .filter(|(semantics, _)| semantics.semantics == TextureSemantics::OriginalHistory) - .count(); - let texture_size_count = pass + .map(|(semantic, _)| semantic.index) + .fold(0, std::cmp::max); + let texture_size_max_index = pass .texture_size_meta .iter() .filter(|(semantics, _)| semantics.semantics == TextureSemantics::OriginalHistory) - .count(); + .map(|(semantic, _)| semantic.index) + .fold(0, std::cmp::max); - required_images = std::cmp::max(required_images, texture_count); - required_images = std::cmp::max(required_images, texture_size_count); + required_images = std::cmp::max(required_images, texture_max_index); + required_images = std::cmp::max(required_images, texture_size_max_index); } required_images diff --git a/librashader-runtime/src/framebuffer.rs b/librashader-runtime/src/framebuffer.rs new file mode 100644 index 0000000..674e8ed --- /dev/null +++ b/librashader-runtime/src/framebuffer.rs @@ -0,0 +1,92 @@ +use crate::binding::BindingUtil; +use librashader_reflect::reflect::semantics::BindingMeta; +use std::collections::VecDeque; + +/// Helper to initialize framebuffers in a graphics API agnostic way. +pub struct FramebufferInit<'a, F, I, E> { + owned_generator: &'a dyn Fn() -> Result, + input_generator: &'a dyn Fn() -> I, + required_history: usize, + filters_count: usize, +} + +impl<'a, F, I, E> FramebufferInit<'a, F, I, E> { + /// Create a new framebuffer initializer with the given + /// closures to create owned framebuffers and image views. + pub fn new( + filters: impl Iterator + ExactSizeIterator, + owned_generator: &'a dyn Fn() -> Result, + input_generator: &'a dyn Fn() -> I, + ) -> Self { + let filters_count = filters.len(); + let required_history = BindingMeta::calculate_required_history(filters); + Self { + owned_generator, + input_generator, + filters_count, + required_history, + } + } + + /// Initialize history framebuffers and views. + pub fn init_history(&self) -> Result<(VecDeque, Box<[I]>), E> { + init_history( + self.required_history, + self.owned_generator, + self.input_generator, + ) + } + + /// Initialize output framebuffers and views. + pub fn init_output_framebuffers(&self) -> Result<(Box<[F]>, Box<[I]>), E> { + init_output_framebuffers( + self.filters_count, + self.owned_generator, + self.input_generator, + ) + } +} + +fn init_history<'a, F, I, E>( + required_images: usize, + owned_generator: impl Fn() -> Result, + input_generator: impl Fn() -> I, +) -> Result<(VecDeque, Box<[I]>), E> { + if required_images <= 1 { + return Ok((VecDeque::new(), Box::new([]))); + } + + let mut framebuffers = VecDeque::with_capacity(required_images); + framebuffers.resize_with(required_images, owned_generator); + + let framebuffers = framebuffers + .into_iter() + .collect::, E>>()?; + + let mut history_textures = Vec::new(); + history_textures.resize_with(required_images, input_generator); + + Ok((framebuffers, history_textures.into_boxed_slice())) +} + +fn init_output_framebuffers( + len: usize, + owned_generator: impl Fn() -> Result, + input_generator: impl Fn() -> I, +) -> Result<(Box<[F]>, Box<[I]>), E> { + let mut output_framebuffers = Vec::new(); + output_framebuffers.resize_with(len, owned_generator); + + // resolve all results + let output_framebuffers = output_framebuffers + .into_iter() + .collect::, E>>()?; + + let mut output_textures = Vec::new(); + output_textures.resize_with(len, input_generator); + + Ok(( + output_framebuffers.into_boxed_slice(), + output_textures.into_boxed_slice(), + )) +} diff --git a/librashader-runtime/src/lib.rs b/librashader-runtime/src/lib.rs index cb2c426..04509c8 100644 --- a/librashader-runtime/src/lib.rs +++ b/librashader-runtime/src/lib.rs @@ -34,3 +34,6 @@ pub mod filter_pass; /// Common types for render targets. pub mod render_target; + +/// Helpers for handling framebuffers. +pub mod framebuffer; diff --git a/librashader/src/lib.rs b/librashader/src/lib.rs index 63d45a1..555381d 100644 --- a/librashader/src/lib.rs +++ b/librashader/src/lib.rs @@ -214,7 +214,7 @@ pub mod runtime { pub use librashader_runtime_gl::{ error, options::{FilterChainOptionsGL as FilterChainOptions, FrameOptionsGL as FrameOptions}, - FilterChainGL as FilterChain, Framebuffer, GLImage, + FilterChainGL as FilterChain, GLFramebuffer, GLImage, }; #[doc(hidden)]