rt: fix history framebuffer requirements and unify framebuffer initialization logic

This commit is contained in:
chyyran 2023-02-11 00:20:35 -05:00
parent 115382d8f0
commit 3c15a3a523
25 changed files with 341 additions and 368 deletions

View file

@ -5,7 +5,7 @@ use librashader_presets::{ShaderPreset, TextureConfig};
use librashader_reflect::back::targets::HLSL; use librashader_reflect::back::targets::HLSL;
use librashader_reflect::back::{CompileReflectShader, CompileShader}; use librashader_reflect::back::{CompileReflectShader, CompileShader};
use librashader_reflect::front::GlslangCompilation; 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_reflect::reflect::ReflectShader;
use librashader_runtime::image::{Image, UVDirection}; use librashader_runtime::image::{Image, UVDirection};
use rustc_hash::FxHashMap; use rustc_hash::FxHashMap;
@ -16,7 +16,7 @@ use std::path::Path;
use crate::draw_quad::DrawQuad; use crate::draw_quad::DrawQuad;
use crate::error::{assume_d3d11_init, FilterChainError}; use crate::error::{assume_d3d11_init, FilterChainError};
use crate::filter_pass::{ConstantBufferBinding, FilterPass}; use crate::filter_pass::{ConstantBufferBinding, FilterPass};
use crate::framebuffer::OwnedFramebuffer; use crate::framebuffer::OwnedImage;
use crate::graphics_pipeline::D3D11State; use crate::graphics_pipeline::D3D11State;
use crate::options::{FilterChainOptionsD3D11, FrameOptionsD3D11}; use crate::options::{FilterChainOptionsD3D11, FrameOptionsD3D11};
use crate::samplers::SamplerSet; use crate::samplers::SamplerSet;
@ -24,6 +24,7 @@ use crate::util::d3d11_compile_bound_shader;
use crate::{error, util, D3D11OutputView}; use crate::{error, util, D3D11OutputView};
use librashader_reflect::reflect::presets::{CompilePresetTarget, ShaderPassArtifact}; use librashader_reflect::reflect::presets::{CompilePresetTarget, ShaderPassArtifact};
use librashader_runtime::binding::{BindingUtil, TextureInput}; use librashader_runtime::binding::{BindingUtil, TextureInput};
use librashader_runtime::framebuffer::FramebufferInit;
use librashader_runtime::quad::QuadType; use librashader_runtime::quad::QuadType;
use librashader_runtime::render_target::RenderTarget; use librashader_runtime::render_target::RenderTarget;
use librashader_runtime::scaling::ScaleFramebuffer; use librashader_runtime::scaling::ScaleFramebuffer;
@ -49,9 +50,9 @@ type ShaderPassMeta =
pub struct FilterChainD3D11 { pub struct FilterChainD3D11 {
pub(crate) common: FilterCommon, pub(crate) common: FilterCommon,
passes: Vec<FilterPass>, passes: Vec<FilterPass>,
output_framebuffers: Box<[OwnedFramebuffer]>, output_framebuffers: Box<[OwnedImage]>,
feedback_framebuffers: Box<[OwnedFramebuffer]>, feedback_framebuffers: Box<[OwnedImage]>,
history_framebuffers: VecDeque<OwnedFramebuffer>, history_framebuffers: VecDeque<OwnedImage>,
state: D3D11State, state: D3D11State,
} }
@ -102,45 +103,34 @@ impl FilterChainD3D11 {
let immediate_context = unsafe { device.GetImmediateContext()? }; 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::<error::Result<Vec<OwnedFramebuffer>>>()?;
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::<error::Result<Vec<OwnedFramebuffer>>>()?;
let mut feedback_textures = Vec::new();
feedback_textures.resize_with(filters.len(), || None);
// load luts // load luts
let luts = FilterChainD3D11::load_luts(device, &immediate_context, &preset.textures)?; let luts = FilterChainD3D11::load_luts(device, &immediate_context, &preset.textures)?;
let (history_framebuffers, history_textures) = let framebuffer_gen =
FilterChainD3D11::init_history(device, &filters)?; || 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 draw_quad = DrawQuad::new(device)?;
let state = D3D11State::new(device)?; let state = D3D11State::new(device)?;
Ok(FilterChainD3D11 { Ok(FilterChainD3D11 {
passes: filters, passes: filters,
output_framebuffers: output_framebuffers.into_boxed_slice(), output_framebuffers,
feedback_framebuffers: feedback_framebuffers.into_boxed_slice(), feedback_framebuffers,
history_framebuffers, history_framebuffers,
common: FilterCommon { common: FilterCommon {
d3d11: Direct3D11 { d3d11: Direct3D11 {
@ -158,8 +148,8 @@ impl FilterChainD3D11 {
disable_mipmaps: options.map_or(false, |o| o.force_no_mipmaps), disable_mipmaps: options.map_or(false, |o| o.force_no_mipmaps),
luts, luts,
samplers, samplers,
output_textures: output_textures.into_boxed_slice(), output_textures,
feedback_textures: feedback_textures.into_boxed_slice(), feedback_textures,
history_textures, history_textures,
draw_quad, draw_quad,
}, },
@ -283,37 +273,6 @@ impl FilterChainD3D11 {
Ok(filters) Ok(filters)
} }
fn init_history(
device: &ID3D11Device,
filters: &Vec<FilterPass>,
) -> error::Result<(VecDeque<OwnedFramebuffer>, Box<[Option<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 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::<error::Result<VecDeque<OwnedFramebuffer>>>()?;
let mut history_textures = Vec::new();
history_textures.resize_with(required_images, || None);
Ok((framebuffers, history_textures.into_boxed_slice()))
}
fn push_history( fn push_history(
&mut self, &mut self,
ctx: &ID3D11DeviceContext, ctx: &ID3D11DeviceContext,
@ -394,20 +353,6 @@ impl FilterChainD3D11 {
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;
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 for (texture, fbo) in self
.common .common
.history_textures .history_textures
@ -426,7 +371,7 @@ impl FilterChainD3D11 {
let mut source = original.clone(); let mut source = original.clone();
// rescale render buffers to ensure all bindings are valid. // rescale render buffers to ensure all bindings are valid.
OwnedFramebuffer::scale_framebuffers( OwnedImage::scale_framebuffers(
source.size(), source.size(),
viewport.output.size, viewport.output.size,
&mut self.output_framebuffers, &mut self.output_framebuffers,
@ -435,6 +380,22 @@ impl FilterChainD3D11 {
None, 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 passes_len = passes.len();
let (pass, last) = passes.split_at_mut(passes_len - 1); let (pass, last) = passes.split_at_mut(passes_len - 1);

View file

@ -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]; static CLEAR: &[f32; 4] = &[0.0f32, 0.0, 0.0, 0.0];
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub(crate) struct OwnedFramebuffer { pub(crate) struct OwnedImage {
render: ID3D11Texture2D, render: ID3D11Texture2D,
pub(crate) size: Size<u32>, pub(crate) size: Size<u32>,
format: DXGI_FORMAT, format: DXGI_FORMAT,
@ -29,13 +29,13 @@ pub(crate) struct OwnedFramebuffer {
max_mipmap: u32, max_mipmap: u32,
} }
impl OwnedFramebuffer { impl OwnedImage {
pub fn new( pub fn new(
device: &ID3D11Device, device: &ID3D11Device,
size: Size<u32>, size: Size<u32>,
format: ImageFormat, format: ImageFormat,
mipmap: bool, mipmap: bool,
) -> error::Result<OwnedFramebuffer> { ) -> error::Result<OwnedImage> {
unsafe { unsafe {
let format = d3d11_get_closest_format( let format = d3d11_get_closest_format(
device, device,
@ -49,7 +49,7 @@ impl OwnedFramebuffer {
device.CreateTexture2D(&desc, None, Some(&mut render))?; device.CreateTexture2D(&desc, None, Some(&mut render))?;
assume_d3d11_init!(render, "CreateTexture2D"); assume_d3d11_init!(render, "CreateTexture2D");
Ok(OwnedFramebuffer { Ok(OwnedImage {
render, render,
size, size,
format, format,
@ -245,7 +245,7 @@ fn default_desc(size: Size<u32>, format: DXGI_FORMAT, mip_levels: u32) -> D3D11_
} }
} }
impl ScaleFramebuffer for OwnedFramebuffer { impl ScaleFramebuffer for OwnedImage {
type Error = FilterChainError; type Error = FilterChainError;
type Context = (); type Context = ();

View file

@ -568,7 +568,7 @@ pub mod d3d11_hello_triangle {
// eprintln!("w: {} h: {}", backbuffer_desc.Width, backbuffer_desc.Height); // eprintln!("w: {} h: {}", backbuffer_desc.Width, backbuffer_desc.Height);
self.filter self.filter
.frame( .frame(
Some(&resources.deferred_context), None,
D3D11InputView { D3D11InputView {
handle: srv, handle: srv,
size: Size { size: Size {
@ -593,12 +593,12 @@ pub mod d3d11_hello_triangle {
) )
.unwrap(); .unwrap();
let mut command_list = None; // let mut command_list = None;
resources // resources
.deferred_context // .deferred_context
.FinishCommandList(false, Some(&mut command_list))?; // .FinishCommandList(false, Some(&mut command_list))?;
let command_list = command_list.unwrap(); // let command_list = command_list.unwrap();
self.context.ExecuteCommandList(&command_list, false); // self.context.ExecuteCommandList(&command_list, false);
// self.context.CopyResource(&resources.backbuffer, &backup); // self.context.CopyResource(&resources.backbuffer, &backup);
} }

View file

@ -38,8 +38,10 @@ mod tests {
// const FILTER_PATH: &str = // const FILTER_PATH: &str =
// "../test/slang-shaders/handheld/console-border/gbc-lcd-grid-v2.slangp"; // "../test/slang-shaders/handheld/console-border/gbc-lcd-grid-v2.slangp";
// "../test/null.slangp", // "../test/null.slangp",
const FILTER_PATH: &str = // const FILTER_PATH: &str = "../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";
// 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 FILTER_PATH: &str = "../test/slang-shaders/crt/crt-royale.slangp";
const IMAGE_PATH: &str = "../triangle.png"; const IMAGE_PATH: &str = "../triangle.png";

View file

@ -13,7 +13,7 @@ use windows::Win32::Graphics::Direct3D11::{
use windows::Win32::Graphics::Dxgi::Common::DXGI_SAMPLE_DESC; use windows::Win32::Graphics::Dxgi::Common::DXGI_SAMPLE_DESC;
use crate::error::{assume_d3d11_init, Result}; use crate::error::{assume_d3d11_init, Result};
use crate::framebuffer::OwnedFramebuffer; use crate::framebuffer::OwnedImage;
/// An image view for use as a shader resource. /// An image view for use as a shader resource.
/// ///
@ -46,7 +46,7 @@ pub struct InputTexture {
impl InputTexture { impl InputTexture {
pub(crate) fn from_framebuffer( pub(crate) fn from_framebuffer(
fbo: &OwnedFramebuffer, fbo: &OwnedImage,
wrap_mode: WrapMode, wrap_mode: WrapMode,
filter: FilterMode, filter: FilterMode,
) -> Result<Self> { ) -> Result<Self> {

View file

@ -20,7 +20,7 @@ use librashader_reflect::back::targets::{DXIL, HLSL};
use librashader_reflect::back::{CompileReflectShader, CompileShader}; use librashader_reflect::back::{CompileReflectShader, CompileShader};
use librashader_reflect::front::GlslangCompilation; use librashader_reflect::front::GlslangCompilation;
use librashader_reflect::reflect::presets::{CompilePresetTarget, ShaderPassArtifact}; 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_reflect::reflect::ReflectShader;
use librashader_runtime::binding::{BindingUtil, TextureInput}; use librashader_runtime::binding::{BindingUtil, TextureInput};
use librashader_runtime::image::{Image, UVDirection}; 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::Threading::{CreateEventA, ResetEvent, WaitForSingleObject};
use windows::Win32::System::WindowsProgramming::INFINITE; use windows::Win32::System::WindowsProgramming::INFINITE;
use librashader_runtime::framebuffer::FramebufferInit;
use librashader_runtime::render_target::RenderTarget; use librashader_runtime::render_target::RenderTarget;
use librashader_runtime::scaling::ScaleFramebuffer; use librashader_runtime::scaling::ScaleFramebuffer;
use rayon::prelude::*; use rayon::prelude::*;
@ -190,44 +191,31 @@ impl FilterChainD3D12 {
options.map_or(false, |o| o.force_hlsl_pipeline), 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 // initialize output framebuffers
let mut output_framebuffers = Vec::new(); let (output_framebuffers, output_textures) = framebuffer_init.init_output_framebuffers()?;
output_framebuffers.resize_with(filters.len(), || {
OwnedImage::new(device, Size::new(1, 1), ImageFormat::R8G8B8A8Unorm, false)
});
// resolve all results // initialize feedback framebuffers
let output_framebuffers = output_framebuffers let (feedback_framebuffers, feedback_textures) =
.into_iter() framebuffer_init.init_output_framebuffers()?;
.collect::<error::Result<Vec<OwnedImage>>>()?;
let mut output_textures = Vec::new();
output_textures.resize_with(filters.len(), || None);
// let mut output_textures = Vec::new(); // initialize history
// output_textures.resize_with(filters.len(), || None); let (history_framebuffers, history_textures) = framebuffer_init.init_history()?;
//
// // 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::<error::Result<Vec<OwnedImage>>>()?;
let mut feedback_textures = Vec::new();
feedback_textures.resize_with(filters.len(), || None);
let (history_framebuffers, history_textures) =
FilterChainD3D12::init_history(device, &filters)?;
Ok(FilterChainD3D12 { Ok(FilterChainD3D12 {
common: FilterCommon { common: FilterCommon {
d3d12: device.clone(), d3d12: device.clone(),
samplers, samplers,
output_textures: output_textures.into_boxed_slice(), output_textures,
feedback_textures: feedback_textures.into_boxed_slice(), feedback_textures,
luts, luts,
mipmap_gen, mipmap_gen,
root_signature, root_signature,
@ -245,8 +233,8 @@ impl FilterChainD3D12 {
staging_heap, staging_heap,
rtv_heap, rtv_heap,
passes: filters, passes: filters,
output_framebuffers: output_framebuffers.into_boxed_slice(), output_framebuffers,
feedback_framebuffers: feedback_framebuffers.into_boxed_slice(), feedback_framebuffers,
history_framebuffers, history_framebuffers,
work_heap: texture_heap, work_heap: texture_heap,
sampler_heap, sampler_heap,
@ -256,37 +244,6 @@ impl FilterChainD3D12 {
}) })
} }
fn init_history(
device: &ID3D12Device,
filters: &Vec<FilterPass>,
) -> error::Result<(VecDeque<OwnedImage>, Box<[Option<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 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::<error::Result<VecDeque<OwnedImage>>>()?;
let mut history_textures = Vec::new();
history_textures.resize_with(required_images, || None);
Ok((framebuffers, history_textures.into_boxed_slice()))
}
fn load_luts( fn load_luts(
device: &ID3D12Device, device: &ID3D12Device,
heap: &mut D3D12DescriptorHeap<CpuStagingHeap>, heap: &mut D3D12DescriptorHeap<CpuStagingHeap>,

View file

@ -35,7 +35,9 @@ mod tests {
let sample = hello_triangle::d3d12_hello_triangle::Sample::new( let sample = hello_triangle::d3d12_hello_triangle::Sample::new(
// "../test/slang-shaders/crt/crt-lottes.slangp", // "../test/slang-shaders/crt/crt-lottes.slangp",
// "../test/basic.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/crt/crt-royale.slangp",
// "../test/slang-shaders/vhs/VHSPro.slangp", // "../test/slang-shaders/vhs/VHSPro.slangp",
&SampleCommandLine { &SampleCommandLine {

View file

@ -1,25 +1,26 @@
use crate::binding::{GlUniformStorage, UniformLocation, VariableLocation}; use crate::binding::{GlUniformStorage, UniformLocation, VariableLocation};
use crate::error::FilterChainError; use crate::error::FilterChainError;
use crate::filter_pass::{FilterPass, UniformOffset}; 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::options::{FilterChainOptionsGL, FrameOptionsGL};
use crate::samplers::SamplerSet; use crate::samplers::SamplerSet;
use crate::texture::InputTexture; use crate::texture::InputTexture;
use crate::util::{gl_get_version, gl_u16_to_version}; use crate::util::{gl_get_version, gl_u16_to_version};
use crate::{error, util, GLImage}; use crate::{error, util, GLImage};
use gl::types::{GLint, GLuint}; use gl::types::{GLint, GLuint};
use librashader_common::{FilterMode, Viewport, WrapMode}; use librashader_common::Viewport;
use librashader_presets::ShaderPreset; use librashader_presets::ShaderPreset;
use librashader_reflect::back::cross::GlslVersion; use librashader_reflect::back::cross::GlslVersion;
use librashader_reflect::back::targets::GLSL; use librashader_reflect::back::targets::GLSL;
use librashader_reflect::back::{CompileReflectShader, CompileShader}; use librashader_reflect::back::{CompileReflectShader, CompileShader};
use librashader_reflect::front::GlslangCompilation; 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::presets::{CompilePresetTarget, ShaderPassArtifact};
use librashader_reflect::reflect::ReflectShader; use librashader_reflect::reflect::ReflectShader;
use librashader_runtime::binding::BindingUtil; use librashader_runtime::binding::BindingUtil;
use librashader_runtime::framebuffer::FramebufferInit;
use librashader_runtime::render_target::RenderTarget; use librashader_runtime::render_target::RenderTarget;
use librashader_runtime::scaling::ScaleFramebuffer; use librashader_runtime::scaling::ScaleFramebuffer;
use rustc_hash::FxHashMap; use rustc_hash::FxHashMap;
@ -38,9 +39,9 @@ pub(crate) struct FilterChainImpl<T: GLInterface> {
pub(crate) common: FilterCommon, pub(crate) common: FilterCommon,
passes: Box<[FilterPass<T>]>, passes: Box<[FilterPass<T>]>,
draw_quad: T::DrawQuad, draw_quad: T::DrawQuad,
output_framebuffers: Box<[Framebuffer]>, output_framebuffers: Box<[GLFramebuffer]>,
feedback_framebuffers: Box<[Framebuffer]>, feedback_framebuffers: Box<[GLFramebuffer]>,
history_framebuffers: VecDeque<Framebuffer>, history_framebuffers: VecDeque<GLFramebuffer>,
} }
pub(crate) struct FilterCommon { pub(crate) struct FilterCommon {
@ -120,31 +121,40 @@ impl<T: GLInterface> FilterChainImpl<T> {
let samplers = SamplerSet::new(); 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 // load luts
let luts = T::LoadLut::load_luts(&preset.textures)?; let luts = T::LoadLut::load_luts(&preset.textures)?;
let (history_framebuffers, history_textures) = let framebuffer_gen = || Ok::<_, FilterChainError>(T::FramebufferInterface::new(1));
FilterChainImpl::init_history(&filters, default_filter, default_wrap); 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 // create vertex objects
let draw_quad = T::DrawQuad::new(); let draw_quad = T::DrawQuad::new();
Ok(FilterChainImpl { Ok(FilterChainImpl {
passes: filters, passes: filters,
output_framebuffers: output_framebuffers.into_boxed_slice(), output_framebuffers,
feedback_framebuffers: feedback_framebuffers.into_boxed_slice(), feedback_framebuffers,
history_framebuffers, history_framebuffers,
draw_quad, draw_quad,
common: FilterCommon { common: FilterCommon {
@ -159,8 +169,8 @@ impl<T: GLInterface> FilterChainImpl<T> {
disable_mipmaps: options.map_or(false, |o| o.force_no_mipmaps), disable_mipmaps: options.map_or(false, |o| o.force_no_mipmaps),
luts, luts,
samplers, samplers,
output_textures: output_textures.into_boxed_slice(), output_textures,
feedback_textures: feedback_textures.into_boxed_slice(), feedback_textures,
history_textures, history_textures,
}, },
}) })
@ -274,37 +284,6 @@ impl<T: GLInterface> FilterChainImpl<T> {
Ok(filters.into_boxed_slice()) Ok(filters.into_boxed_slice())
} }
fn init_history(
filters: &[FilterPass<T>],
filter: FilterMode,
wrap_mode: WrapMode,
) -> (VecDeque<Framebuffer>, 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<()> { fn push_history(&mut self, input: &GLImage) -> error::Result<()> {
if let Some(mut back) = self.history_framebuffers.pop_back() { if let Some(mut back) = self.history_framebuffers.pop_back() {
if back.size != input.size || (input.format != 0 && input.format != back.format) { if back.size != input.size || (input.format != 0 && input.format != back.format) {
@ -313,7 +292,6 @@ impl<T: GLInterface> FilterChainImpl<T> {
} }
back.copy_from::<T::FramebufferInterface>(input)?; back.copy_from::<T::FramebufferInterface>(input)?;
self.history_framebuffers.push_front(back) self.history_framebuffers.push_front(back)
} }
@ -326,7 +304,7 @@ impl<T: GLInterface> FilterChainImpl<T> {
pub fn frame( pub fn frame(
&mut self, &mut self,
frame_count: usize, frame_count: usize,
viewport: &Viewport<&Framebuffer>, viewport: &Viewport<&GLFramebuffer>,
input: &GLImage, input: &GLImage,
options: Option<&FrameOptionsGL>, options: Option<&FrameOptionsGL>,
) -> error::Result<()> { ) -> error::Result<()> {
@ -364,18 +342,6 @@ impl<T: GLInterface> FilterChainImpl<T> {
texture.image = fbo.as_texture(filter, wrap_mode).image; 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 // shader_gl3: 2067
let original = InputTexture { let original = InputTexture {
image: *input, image: *input,
@ -387,7 +353,7 @@ impl<T: GLInterface> FilterChainImpl<T> {
let mut source = original; let mut source = original;
// rescale render buffers to ensure all bindings are valid. // rescale render buffers to ensure all bindings are valid.
<Framebuffer as ScaleFramebuffer<T::FramebufferInterface>>::scale_framebuffers( <GLFramebuffer as ScaleFramebuffer<T::FramebufferInterface>>::scale_framebuffers(
source.image.size, source.image.size,
viewport.output.size, viewport.output.size,
&mut self.output_framebuffers, &mut self.output_framebuffers,
@ -396,6 +362,20 @@ impl<T: GLInterface> FilterChainImpl<T> {
None, 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 passes_len = passes.len();
let (pass, last) = passes.split_at_mut(passes_len - 1); let (pass, last) = passes.split_at_mut(passes_len - 1);

View file

@ -5,7 +5,7 @@ use crate::error::{FilterChainError, Result};
use crate::filter_chain::filter_impl::FilterChainImpl; use crate::filter_chain::filter_impl::FilterChainImpl;
use crate::filter_chain::inner::FilterChainDispatch; use crate::filter_chain::inner::FilterChainDispatch;
use crate::options::{FilterChainOptionsGL, FrameOptionsGL}; use crate::options::{FilterChainOptionsGL, FrameOptionsGL};
use crate::{Framebuffer, GLImage}; use crate::{GLFramebuffer, GLImage};
use librashader_presets::ShaderPreset; use librashader_presets::ShaderPreset;
mod filter_impl; mod filter_impl;
@ -61,7 +61,7 @@ impl FilterChainGL {
pub fn frame( pub fn frame(
&mut self, &mut self,
input: &GLImage, input: &GLImage,
viewport: &Viewport<&Framebuffer>, viewport: &Viewport<&GLFramebuffer>,
frame_count: usize, frame_count: usize,
options: Option<&FrameOptionsGL>, options: Option<&FrameOptionsGL>,
) -> Result<()> { ) -> Result<()> {

View file

@ -14,7 +14,7 @@ use crate::binding::{GlUniformBinder, GlUniformStorage, UniformLocation, Variabl
use crate::filter_chain::FilterCommon; use crate::filter_chain::FilterCommon;
use crate::gl::{BindTexture, GLInterface, UboRing}; use crate::gl::{BindTexture, GLInterface, UboRing};
use crate::samplers::SamplerSet; use crate::samplers::SamplerSet;
use crate::Framebuffer; use crate::GLFramebuffer;
use crate::texture::InputTexture; use crate::texture::InputTexture;
@ -29,7 +29,7 @@ impl UniformOffset {
} }
} }
pub struct FilterPass<T: GLInterface> { pub(crate) struct FilterPass<T: GLInterface> {
pub reflection: ShaderReflection, pub reflection: ShaderReflection,
pub program: GLuint, pub program: GLuint,
pub ubo_location: UniformLocation<GLuint>, pub ubo_location: UniformLocation<GLuint>,
@ -81,10 +81,10 @@ impl<T: GLInterface> FilterPass<T> {
parent: &FilterCommon, parent: &FilterCommon,
frame_count: u32, frame_count: u32,
frame_direction: i32, frame_direction: i32,
viewport: &Viewport<&Framebuffer>, viewport: &Viewport<&GLFramebuffer>,
original: &InputTexture, original: &InputTexture,
source: &InputTexture, source: &InputTexture,
output: RenderTarget<Framebuffer, GLint>, output: RenderTarget<GLFramebuffer, GLint>,
) { ) {
let framebuffer = output.output; let framebuffer = output.output;
@ -93,7 +93,7 @@ impl<T: GLInterface> FilterPass<T> {
} }
unsafe { unsafe {
gl::BindFramebuffer(gl::FRAMEBUFFER, framebuffer.handle); gl::BindFramebuffer(gl::FRAMEBUFFER, framebuffer.fbo);
gl::UseProgram(self.program); gl::UseProgram(self.program);
} }
@ -165,7 +165,7 @@ impl<T: GLInterface> FilterPass<T> {
frame_count: u32, frame_count: u32,
frame_direction: i32, frame_direction: i32,
fb_size: Size<u32>, fb_size: Size<u32>,
viewport: &Viewport<&Framebuffer>, viewport: &Viewport<&GLFramebuffer>,
original: &InputTexture, original: &InputTexture,
source: &InputTexture, source: &InputTexture,
) { ) {

View file

@ -11,9 +11,9 @@ use librashader_runtime::scaling::ScaleFramebuffer;
/// ///
/// Generally for use as render targets. /// Generally for use as render targets.
#[derive(Debug)] #[derive(Debug)]
pub struct Framebuffer { pub struct GLFramebuffer {
pub(crate) image: GLuint, pub(crate) image: GLuint,
pub(crate) handle: GLuint, pub(crate) fbo: GLuint,
pub(crate) size: Size<u32>, pub(crate) size: Size<u32>,
pub(crate) format: GLenum, pub(crate) format: GLenum,
pub(crate) max_levels: u32, pub(crate) max_levels: u32,
@ -21,7 +21,7 @@ pub struct Framebuffer {
pub(crate) is_raw: bool, pub(crate) is_raw: bool,
} }
impl Framebuffer { impl GLFramebuffer {
/// Create a framebuffer from an already initialized texture and framebuffer. /// Create a framebuffer from an already initialized texture and framebuffer.
/// ///
/// The framebuffer will not be deleted when this struct is dropped. /// The framebuffer will not be deleted when this struct is dropped.
@ -31,14 +31,14 @@ impl Framebuffer {
format: GLenum, format: GLenum,
size: Size<u32>, size: Size<u32>,
miplevels: u32, miplevels: u32,
) -> Framebuffer { ) -> GLFramebuffer {
Framebuffer { GLFramebuffer {
image: texture, image: texture,
size, size,
format, format,
max_levels: miplevels, max_levels: miplevels,
mip_levels: miplevels, mip_levels: miplevels,
handle: fbo, fbo: fbo,
is_raw: true, is_raw: true,
} }
} }
@ -76,15 +76,15 @@ impl Framebuffer {
} }
} }
impl Drop for Framebuffer { impl Drop for GLFramebuffer {
fn drop(&mut self) { fn drop(&mut self) {
if self.is_raw { if self.is_raw {
return; return;
} }
unsafe { unsafe {
if self.handle != 0 { if self.fbo != 0 {
gl::DeleteFramebuffers(1, &self.handle); gl::DeleteFramebuffers(1, &self.fbo);
} }
if self.image != 0 { if self.image != 0 {
gl::DeleteTextures(1, &self.image); gl::DeleteTextures(1, &self.image);
@ -93,7 +93,7 @@ impl Drop for Framebuffer {
} }
} }
// //
impl<T: FramebufferInterface> ScaleFramebuffer<T> for Framebuffer { impl<T: FramebufferInterface> ScaleFramebuffer<T> for GLFramebuffer {
type Error = FilterChainError; type Error = FilterChainError;
type Context = (); type Context = ();

View file

@ -1,6 +1,6 @@
use crate::error::{FilterChainError, Result}; use crate::error::{FilterChainError, Result};
use crate::framebuffer::GLImage; use crate::framebuffer::GLImage;
use crate::gl::framebuffer::Framebuffer; use crate::gl::framebuffer::GLFramebuffer;
use crate::gl::FramebufferInterface; use crate::gl::FramebufferInterface;
use gl::types::{GLenum, GLint, GLsizei}; use gl::types::{GLenum, GLint, GLsizei};
use librashader_common::{ImageFormat, Size}; use librashader_common::{ImageFormat, Size};
@ -11,7 +11,7 @@ use librashader_runtime::scaling::{MipmapSize, ViewportSize};
pub struct Gl3Framebuffer; pub struct Gl3Framebuffer;
impl FramebufferInterface for Gl3Framebuffer { impl FramebufferInterface for Gl3Framebuffer {
fn new(max_levels: u32) -> Framebuffer { fn new(max_levels: u32) -> GLFramebuffer {
let mut framebuffer = 0; let mut framebuffer = 0;
unsafe { unsafe {
gl::GenFramebuffers(1, &mut framebuffer); gl::GenFramebuffers(1, &mut framebuffer);
@ -19,7 +19,7 @@ impl FramebufferInterface for Gl3Framebuffer {
gl::BindFramebuffer(gl::FRAMEBUFFER, 0); gl::BindFramebuffer(gl::FRAMEBUFFER, 0);
} }
Framebuffer { GLFramebuffer {
image: 0, image: 0,
size: Size { size: Size {
width: 1, width: 1,
@ -28,13 +28,13 @@ impl FramebufferInterface for Gl3Framebuffer {
format: 0, format: 0,
max_levels, max_levels,
mip_levels: 0, mip_levels: 0,
handle: framebuffer, fbo: framebuffer,
is_raw: false, is_raw: false,
} }
} }
fn scale( fn scale(
fb: &mut Framebuffer, fb: &mut GLFramebuffer,
scaling: Scale2D, scaling: Scale2D,
format: ImageFormat, format: ImageFormat,
viewport_size: &Size<u32>, viewport_size: &Size<u32>,
@ -66,10 +66,10 @@ impl FramebufferInterface for Gl3Framebuffer {
} }
Ok(size) Ok(size)
} }
fn clear<const REBIND: bool>(fb: &Framebuffer) { fn clear<const REBIND: bool>(fb: &GLFramebuffer) {
unsafe { unsafe {
if REBIND { 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::ColorMask(gl::TRUE, gl::TRUE, gl::TRUE, gl::TRUE);
gl::ClearColor(0.0, 0.0, 0.0, 0.0); 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. // todo: may want to use a shader and draw a quad to be faster.
if image.size != fb.size || image.format != fb.format { if image.size != fb.size || image.format != fb.format {
Self::init(fb, image.size, image.format)?; Self::init(fb, image.size, image.format)?;
} }
unsafe { unsafe {
gl::BindFramebuffer(gl::FRAMEBUFFER, fb.handle); gl::BindFramebuffer(gl::FRAMEBUFFER, fb.fbo);
gl::FramebufferTexture2D( gl::FramebufferTexture2D(
gl::READ_FRAMEBUFFER, gl::READ_FRAMEBUFFER,
@ -149,7 +149,7 @@ impl FramebufferInterface for Gl3Framebuffer {
Ok(()) Ok(())
} }
fn init(fb: &mut Framebuffer, mut size: Size<u32>, format: impl Into<GLenum>) -> Result<()> { fn init(fb: &mut GLFramebuffer, mut size: Size<u32>, format: impl Into<GLenum>) -> Result<()> {
if fb.is_raw { if fb.is_raw {
return Ok(()); return Ok(());
} }
@ -157,7 +157,7 @@ impl FramebufferInterface for Gl3Framebuffer {
fb.size = size; fb.size = size;
unsafe { unsafe {
gl::BindFramebuffer(gl::FRAMEBUFFER, fb.handle); gl::BindFramebuffer(gl::FRAMEBUFFER, fb.fbo);
// reset the framebuffer image // reset the framebuffer image
if fb.image != 0 { if fb.image != 0 {

View file

@ -10,7 +10,7 @@ use librashader_common::{Size, Viewport};
use crate::filter_chain::FilterChainGL; use crate::filter_chain::FilterChainGL;
use crate::framebuffer::GLImage; use crate::framebuffer::GLImage;
use crate::Framebuffer; use crate::GLFramebuffer;
const WIDTH: u32 = 800; const WIDTH: u32 = 800;
const HEIGHT: u32 = 600; const HEIGHT: u32 = 600;
@ -464,7 +464,7 @@ void main()
let (fb_width, fb_height) = window.get_framebuffer_size(); let (fb_width, fb_height) = window.get_framebuffer_size();
let (vp_width, vp_height) = window.get_size(); let (vp_width, vp_height) = window.get_size();
let output = Framebuffer::new_from_raw( let output = GLFramebuffer::new_from_raw(
output_texture, output_texture,
output_framebuffer_handle, output_framebuffer_handle,
gl::RGBA8, gl::RGBA8,

View file

@ -1,6 +1,6 @@
use crate::error::{FilterChainError, Result}; use crate::error::{FilterChainError, Result};
use crate::framebuffer::GLImage; use crate::framebuffer::GLImage;
use crate::gl::framebuffer::Framebuffer; use crate::gl::framebuffer::GLFramebuffer;
use crate::gl::FramebufferInterface; use crate::gl::FramebufferInterface;
use gl::types::{GLenum, GLint, GLsizei}; use gl::types::{GLenum, GLint, GLsizei};
use librashader_common::{ImageFormat, Size}; use librashader_common::{ImageFormat, Size};
@ -11,13 +11,13 @@ use librashader_runtime::scaling::{MipmapSize, ViewportSize};
pub struct Gl46Framebuffer; pub struct Gl46Framebuffer;
impl FramebufferInterface for Gl46Framebuffer { impl FramebufferInterface for Gl46Framebuffer {
fn new(max_levels: u32) -> Framebuffer { fn new(max_levels: u32) -> GLFramebuffer {
let mut framebuffer = 0; let mut framebuffer = 0;
unsafe { unsafe {
gl::CreateFramebuffers(1, &mut framebuffer); gl::CreateFramebuffers(1, &mut framebuffer);
} }
Framebuffer { GLFramebuffer {
image: 0, image: 0,
size: Size { size: Size {
width: 1, width: 1,
@ -26,13 +26,13 @@ impl FramebufferInterface for Gl46Framebuffer {
format: 0, format: 0,
max_levels, max_levels,
mip_levels: 0, mip_levels: 0,
handle: framebuffer, fbo: framebuffer,
is_raw: false, is_raw: false,
} }
} }
fn scale( fn scale(
fb: &mut Framebuffer, fb: &mut GLFramebuffer,
scaling: Scale2D, scaling: Scale2D,
format: ImageFormat, format: ImageFormat,
viewport_size: &Size<u32>, viewport_size: &Size<u32>,
@ -66,17 +66,17 @@ impl FramebufferInterface for Gl46Framebuffer {
} }
Ok(size) Ok(size)
} }
fn clear<const REBIND: bool>(fb: &Framebuffer) { fn clear<const REBIND: bool>(fb: &GLFramebuffer) {
unsafe { unsafe {
gl::ClearNamedFramebufferfv( gl::ClearNamedFramebufferfv(
fb.handle, fb.fbo,
gl::COLOR, gl::COLOR,
0, 0,
[0.0f32, 0.0, 0.0, 0.0].as_ptr().cast(), [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. // todo: confirm this behaviour for unbound image.
if image.handle == 0 { if image.handle == 0 {
return Ok(()); return Ok(());
@ -90,11 +90,11 @@ impl FramebufferInterface for Gl46Framebuffer {
unsafe { unsafe {
// gl::NamedFramebufferDrawBuffer(fb.handle, gl::COLOR_ATTACHMENT1); // gl::NamedFramebufferDrawBuffer(fb.handle, gl::COLOR_ATTACHMENT1);
gl::NamedFramebufferReadBuffer(image.handle, gl::COLOR_ATTACHMENT0); gl::NamedFramebufferReadBuffer(image.handle, gl::COLOR_ATTACHMENT0);
gl::NamedFramebufferDrawBuffer(fb.handle, gl::COLOR_ATTACHMENT0); gl::NamedFramebufferDrawBuffer(fb.fbo, gl::COLOR_ATTACHMENT0);
gl::BlitNamedFramebuffer( gl::BlitNamedFramebuffer(
image.handle, image.handle,
fb.handle, fb.fbo,
0, 0,
0, 0,
image.size.width as GLint, image.size.width as GLint,
@ -110,7 +110,7 @@ impl FramebufferInterface for Gl46Framebuffer {
Ok(()) Ok(())
} }
fn init(fb: &mut Framebuffer, mut size: Size<u32>, format: impl Into<GLenum>) -> Result<()> { fn init(fb: &mut GLFramebuffer, mut size: Size<u32>, format: impl Into<GLenum>) -> Result<()> {
if fb.is_raw { if fb.is_raw {
return Ok(()); return Ok(());
} }
@ -120,7 +120,7 @@ impl FramebufferInterface for Gl46Framebuffer {
unsafe { unsafe {
// reset the framebuffer image // reset the framebuffer image
if fb.image != 0 { 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); gl::DeleteTextures(1, &fb.image);
} }
@ -149,13 +149,13 @@ impl FramebufferInterface for Gl46Framebuffer {
size.height 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);
let status = gl::CheckFramebufferStatus(gl::FRAMEBUFFER); let status = gl::CheckFramebufferStatus(gl::FRAMEBUFFER);
if status != gl::FRAMEBUFFER_COMPLETE { if status != gl::FRAMEBUFFER_COMPLETE {
match status { match status {
gl::FRAMEBUFFER_UNSUPPORTED => { 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::DeleteTextures(1, &fb.image);
gl::CreateTextures(gl::TEXTURE_2D, 1, &mut fb.image); gl::CreateTextures(gl::TEXTURE_2D, 1, &mut fb.image);
@ -174,7 +174,7 @@ impl FramebufferInterface for Gl46Framebuffer {
size.width as GLsizei, size.width as GLsizei,
size.height 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 = // fb.init =
// gl::CheckFramebufferStatus(gl::FRAMEBUFFER) == gl::FRAMEBUFFER_COMPLETE; // gl::CheckFramebufferStatus(gl::FRAMEBUFFER) == gl::FRAMEBUFFER_COMPLETE;
} }

View file

@ -10,7 +10,7 @@ use librashader_common::{Size, Viewport};
use crate::filter_chain::FilterChainGL; use crate::filter_chain::FilterChainGL;
use crate::framebuffer::GLImage; use crate::framebuffer::GLImage;
use crate::Framebuffer; use crate::GLFramebuffer;
const WIDTH: u32 = 800; const WIDTH: u32 = 800;
const HEIGHT: u32 = 600; const HEIGHT: u32 = 600;
@ -455,7 +455,7 @@ void main()
let (fb_width, fb_height) = window.get_framebuffer_size(); let (fb_width, fb_height) = window.get_framebuffer_size();
let (vp_width, vp_height) = window.get_size(); let (vp_width, vp_height) = window.get_size();
let output = Framebuffer::new_from_raw( let output = GLFramebuffer::new_from_raw(
output_texture, output_texture,
output_framebuffer_handle, output_framebuffer_handle,
gl::RGBA8, gl::RGBA8,

View file

@ -7,7 +7,7 @@ use crate::error::Result;
use crate::framebuffer::GLImage; use crate::framebuffer::GLImage;
use crate::samplers::SamplerSet; use crate::samplers::SamplerSet;
use crate::texture::InputTexture; use crate::texture::InputTexture;
pub use framebuffer::Framebuffer; pub use framebuffer::GLFramebuffer;
use gl::types::{GLenum, GLuint}; use gl::types::{GLenum, GLuint};
use librashader_common::{ImageFormat, Size}; use librashader_common::{ImageFormat, Size};
use librashader_presets::{Scale2D, TextureConfig}; use librashader_presets::{Scale2D, TextureConfig};
@ -15,17 +15,17 @@ use librashader_reflect::reflect::semantics::{TextureBinding, UboReflection};
use librashader_runtime::uniforms::UniformStorageAccess; use librashader_runtime::uniforms::UniformStorageAccess;
use rustc_hash::FxHashMap; use rustc_hash::FxHashMap;
pub trait LoadLut { pub(crate) trait LoadLut {
fn load_luts(textures: &[TextureConfig]) -> Result<FxHashMap<usize, InputTexture>>; fn load_luts(textures: &[TextureConfig]) -> Result<FxHashMap<usize, InputTexture>>;
} }
pub trait DrawQuad { pub(crate) trait DrawQuad {
fn new() -> Self; fn new() -> Self;
fn bind_vertices(&self); fn bind_vertices(&self);
fn unbind_vertices(&self); fn unbind_vertices(&self);
} }
pub trait UboRing<const SIZE: usize> { pub(crate) trait UboRing<const SIZE: usize> {
fn new(buffer_size: u32) -> Self; fn new(buffer_size: u32) -> Self;
fn bind_for_frame( fn bind_for_frame(
&mut self, &mut self,
@ -35,27 +35,27 @@ pub trait UboRing<const SIZE: usize> {
); );
} }
pub trait FramebufferInterface { pub(crate) trait FramebufferInterface {
fn new(max_levels: u32) -> Framebuffer; fn new(max_levels: u32) -> GLFramebuffer;
fn scale( fn scale(
fb: &mut Framebuffer, fb: &mut GLFramebuffer,
scaling: Scale2D, scaling: Scale2D,
format: ImageFormat, format: ImageFormat,
viewport_size: &Size<u32>, viewport_size: &Size<u32>,
source_size: &Size<u32>, source_size: &Size<u32>,
mipmap: bool, mipmap: bool,
) -> Result<Size<u32>>; ) -> Result<Size<u32>>;
fn clear<const REBIND: bool>(fb: &Framebuffer); fn clear<const REBIND: bool>(fb: &GLFramebuffer);
fn copy_from(fb: &mut Framebuffer, image: &GLImage) -> Result<()>; fn copy_from(fb: &mut GLFramebuffer, image: &GLImage) -> Result<()>;
fn init(fb: &mut Framebuffer, size: Size<u32>, format: impl Into<GLenum>) -> Result<()>; fn init(fb: &mut GLFramebuffer, size: Size<u32>, format: impl Into<GLenum>) -> Result<()>;
} }
pub trait BindTexture { pub(crate) trait BindTexture {
fn bind_texture(samplers: &SamplerSet, binding: &TextureBinding, texture: &InputTexture); fn bind_texture(samplers: &SamplerSet, binding: &TextureBinding, texture: &InputTexture);
fn gen_mipmaps(texture: &InputTexture); fn gen_mipmaps(texture: &InputTexture);
} }
pub trait GLInterface { pub(crate) trait GLInterface {
type FramebufferInterface: FramebufferInterface; type FramebufferInterface: FramebufferInterface;
type UboRing: UboRing<16>; type UboRing: UboRing<16>;
type DrawQuad: DrawQuad; type DrawQuad: DrawQuad;

View file

@ -21,7 +21,7 @@ mod texture;
pub mod error; pub mod error;
pub mod options; pub mod options;
pub use crate::gl::Framebuffer; pub use crate::gl::GLFramebuffer;
pub use filter_chain::FilterChainGL; pub use filter_chain::FilterChainGL;
pub use framebuffer::GLImage; pub use framebuffer::GLImage;
@ -52,7 +52,9 @@ mod tests {
let (glfw, window, events, shader, vao) = gl::gl46::hello_triangle::setup(); let (glfw, window, events, shader, vao) = gl::gl46::hello_triangle::setup();
let mut filter = FilterChainGL::load_from_path( let mut filter = FilterChainGL::load_from_path(
// "../test/slang-shaders/vhs/VHSPro.slangp", // "../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 { Some(&FilterChainOptionsGL {
glsl_version: 0, glsl_version: 0,
use_dsa: true, use_dsa: true,

View file

@ -3,13 +3,14 @@ use crate::framebuffer::GLImage;
use librashader_common::{FilterMode, WrapMode}; use librashader_common::{FilterMode, WrapMode};
#[derive(Default, Debug, Copy, Clone)] #[derive(Default, Debug, Copy, Clone)]
pub struct InputTexture { pub(crate) struct InputTexture {
pub image: GLImage, pub image: GLImage,
pub filter: FilterMode, pub filter: FilterMode,
pub mip_filter: FilterMode, pub mip_filter: FilterMode,
pub wrap_mode: WrapMode, pub wrap_mode: WrapMode,
} }
/// An OpenGL texture bound as a shader resource.
impl InputTexture { impl InputTexture {
pub fn is_bound(&self) -> bool { pub fn is_bound(&self) -> bool {
self.image.handle != 0 self.image.handle != 0

View file

@ -19,7 +19,7 @@ use librashader_reflect::back::targets::SPIRV;
use librashader_reflect::back::{CompileReflectShader, CompileShader}; use librashader_reflect::back::{CompileReflectShader, CompileShader};
use librashader_reflect::front::GlslangCompilation; use librashader_reflect::front::GlslangCompilation;
use librashader_reflect::reflect::presets::{CompilePresetTarget, ShaderPassArtifact}; 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_reflect::reflect::ReflectShader;
use librashader_runtime::binding::BindingUtil; use librashader_runtime::binding::BindingUtil;
use librashader_runtime::image::{Image, UVDirection}; use librashader_runtime::image::{Image, UVDirection};
@ -31,6 +31,7 @@ use std::collections::VecDeque;
use std::path::Path; use std::path::Path;
use std::sync::Arc; use std::sync::Arc;
use librashader_runtime::framebuffer::FramebufferInit;
use librashader_runtime::render_target::RenderTarget; use librashader_runtime::render_target::RenderTarget;
use librashader_runtime::scaling::ScaleFramebuffer; use librashader_runtime::scaling::ScaleFramebuffer;
use rayon::prelude::*; use rayon::prelude::*;
@ -145,8 +146,8 @@ pub(crate) struct FilterCommon {
pub(crate) luts: FxHashMap<usize, LutTexture>, pub(crate) luts: FxHashMap<usize, LutTexture>,
pub samplers: SamplerSet, pub samplers: SamplerSet,
pub(crate) draw_quad: DrawQuad, pub(crate) draw_quad: DrawQuad,
pub output_inputs: Box<[Option<InputImage>]>, pub output_textures: Box<[Option<InputImage>]>,
pub feedback_inputs: Box<[Option<InputImage>]>, pub feedback_textures: Box<[Option<InputImage>]>,
pub history_textures: Box<[Option<InputImage>]>, pub history_textures: Box<[Option<InputImage>]>,
pub config: FilterMutable, pub config: FilterMutable,
pub device: Arc<ash::Device>, pub device: Arc<ash::Device>,
@ -256,28 +257,24 @@ 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) = let framebuffer_gen =
FilterChainVulkan::init_history(&device, &filters)?; || 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(); // initialize output framebuffers
output_framebuffers.resize_with(filters.len(), || { let (output_framebuffers, output_textures) = framebuffer_init.init_output_framebuffers()?;
OwnedImage::new(&device, Size::new(1, 1), ImageFormat::R8G8B8A8Unorm, 1)
});
let mut feedback_framebuffers = Vec::new(); // initialize feedback framebuffers
feedback_framebuffers.resize_with(filters.len(), || { let (feedback_framebuffers, feedback_textures) =
OwnedImage::new(&device, Size::new(1, 1), ImageFormat::R8G8B8A8Unorm, 1) framebuffer_init.init_output_framebuffers()?;
});
let output_framebuffers: error::Result<Vec<OwnedImage>> = // initialize history
output_framebuffers.into_iter().collect(); let (history_framebuffers, history_textures) = framebuffer_init.init_history()?;
let mut output_textures = Vec::new();
output_textures.resize_with(filters.len(), || None);
let feedback_framebuffers: error::Result<Vec<OwnedImage>> =
feedback_framebuffers.into_iter().collect();
let mut feedback_textures = Vec::new();
feedback_textures.resize_with(filters.len(), || None);
let mut intermediates = Vec::new(); let mut intermediates = Vec::new();
intermediates.resize_with(frames_in_flight as usize, || { intermediates.resize_with(frames_in_flight as usize, || {
@ -298,14 +295,14 @@ impl FilterChainVulkan {
}, },
draw_quad: DrawQuad::new(&device.device, &device.alloc)?, draw_quad: DrawQuad::new(&device.device, &device.alloc)?,
device: device.device.clone(), device: device.device.clone(),
output_inputs: output_textures.into_boxed_slice(), output_textures,
feedback_inputs: feedback_textures.into_boxed_slice(), feedback_textures,
history_textures, history_textures,
}, },
passes: filters, passes: filters,
vulkan: device, vulkan: device,
output_framebuffers: output_framebuffers?.into_boxed_slice(), output_framebuffers,
feedback_framebuffers: feedback_framebuffers?.into_boxed_slice(), feedback_framebuffers,
history_framebuffers, history_framebuffers,
residuals: intermediates.into_boxed_slice(), residuals: intermediates.into_boxed_slice(),
disable_mipmaps: options.map_or(false, |o| o.force_no_mipmaps), disable_mipmaps: options.map_or(false, |o| o.force_no_mipmaps),
@ -374,6 +371,7 @@ impl FilterChainVulkan {
graphics_pipeline, graphics_pipeline,
// ubo_ring, // ubo_ring,
frames_in_flight, frames_in_flight,
internal_frame_count: 0,
}) })
}) })
.collect(); .collect();
@ -443,36 +441,6 @@ impl FilterChainVulkan {
Ok(luts) Ok(luts)
} }
fn init_history(
vulkan: &VulkanObjects,
filters: &[FilterPass],
) -> error::Result<(VecDeque<OwnedImage>, Box<[Option<InputImage>]>)> {
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<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 // image must be in SHADER_READ_OPTIMAL
pub fn push_history( pub fn push_history(
&mut self, &mut self,
@ -640,9 +608,9 @@ impl FilterChainVulkan {
output: &OwnedImage, output: &OwnedImage,
feedback: &OwnedImage| { feedback: &OwnedImage| {
// refresh inputs // refresh inputs
self.common.feedback_inputs[index] = self.common.feedback_textures[index] =
Some(feedback.as_input(pass.config.filter, pass.config.wrap_mode)); 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)); Some(output.as_input(pass.config.filter, pass.config.wrap_mode));
Ok(()) Ok(())
}), }),
@ -682,7 +650,7 @@ impl FilterChainVulkan {
out.output.end_pass(cmd); 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_outputs(output_image);
intermediates.dispose_framebuffers(residual_fb); intermediates.dispose_framebuffers(residual_fb);
} }

View file

@ -32,6 +32,7 @@ pub struct FilterPass {
pub graphics_pipeline: VulkanGraphicsPipeline, pub graphics_pipeline: VulkanGraphicsPipeline,
// pub ubo_ring: VkUboRing, // pub ubo_ring: VkUboRing,
pub frames_in_flight: u32, pub frames_in_flight: u32,
pub internal_frame_count: usize,
} }
impl TextureInput for InputImage { impl TextureInput for InputImage {
@ -100,7 +101,7 @@ impl FilterPass {
vbo_type: QuadType, vbo_type: QuadType,
) -> error::Result<Option<vk::Framebuffer>> { ) -> error::Result<Option<vk::Framebuffer>> {
let mut descriptor = self.graphics_pipeline.layout.descriptor_sets 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( self.build_semantics(
pass_index, pass_index,
@ -193,6 +194,7 @@ impl FilterPass {
parent.draw_quad.draw_quad(cmd, vbo_type); parent.draw_quad.draw_quad(cmd, vbo_type);
self.graphics_pipeline.end_rendering(&parent.device, cmd); self.graphics_pipeline.end_rendering(&parent.device, cmd);
} }
self.internal_frame_count += 1;
Ok(residual) Ok(residual)
} }
@ -223,10 +225,10 @@ impl FilterPass {
source, source,
&self.uniform_bindings, &self.uniform_bindings,
&self.reflection.meta.texture_meta, &self.reflection.meta.texture_meta,
parent.output_inputs[0..pass_index] parent.output_textures[0..pass_index]
.iter() .iter()
.map(|o| o.as_ref()), .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.history_textures.iter().map(|o| o.as_ref()),
parent.luts.iter().map(|(u, i)| (*u, i.as_ref())), parent.luts.iter().map(|(u, i)| (*u, i.as_ref())),
&self.source.parameters, &self.source.parameters,

View file

@ -44,7 +44,8 @@ mod tests {
let filter = FilterChainVulkan::load_from_path( let filter = FilterChainVulkan::load_from_path(
&base, &base,
// "../test/slang-shaders/crt/crt-royale.slangp", // "../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", // "../test/basic.slangp",
Some(&FilterChainOptionsVulkan { Some(&FilterChainOptionsVulkan {
frames_in_flight: 3, frames_in_flight: 3,

View file

@ -310,19 +310,21 @@ impl BindingUtil for BindingMeta {
for pass in pass_meta { for pass in pass_meta {
// If a shader uses history size, but not history, we still need to keep the texture. // 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 .texture_meta
.iter() .iter()
.filter(|(semantics, _)| semantics.semantics == TextureSemantics::OriginalHistory) .filter(|(semantics, _)| semantics.semantics == TextureSemantics::OriginalHistory)
.count(); .map(|(semantic, _)| semantic.index)
let texture_size_count = pass .fold(0, std::cmp::max);
let texture_size_max_index = pass
.texture_size_meta .texture_size_meta
.iter() .iter()
.filter(|(semantics, _)| semantics.semantics == TextureSemantics::OriginalHistory) .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_max_index);
required_images = std::cmp::max(required_images, texture_size_count); required_images = std::cmp::max(required_images, texture_size_max_index);
} }
required_images required_images

View file

@ -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<F, E>,
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<Item = &'a BindingMeta> + ExactSizeIterator,
owned_generator: &'a dyn Fn() -> Result<F, E>,
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<F>, 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<F, E>,
input_generator: impl Fn() -> I,
) -> Result<(VecDeque<F>, 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::<Result<VecDeque<F>, 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<F, I, E>(
len: usize,
owned_generator: impl Fn() -> Result<F, E>,
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::<Result<Vec<F>, 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(),
))
}

View file

@ -34,3 +34,6 @@ pub mod filter_pass;
/// Common types for render targets. /// Common types for render targets.
pub mod render_target; pub mod render_target;
/// Helpers for handling framebuffers.
pub mod framebuffer;

View file

@ -214,7 +214,7 @@ pub mod runtime {
pub use librashader_runtime_gl::{ pub use librashader_runtime_gl::{
error, error,
options::{FilterChainOptionsGL as FilterChainOptions, FrameOptionsGL as FrameOptions}, options::{FilterChainOptionsGL as FilterChainOptions, FrameOptionsGL as FrameOptions},
FilterChainGL as FilterChain, Framebuffer, GLImage, FilterChainGL as FilterChain, GLFramebuffer, GLImage,
}; };
#[doc(hidden)] #[doc(hidden)]