rt: abstract framebuffer scaling into common code

This commit is contained in:
chyyran 2023-02-07 02:12:47 -05:00
parent 5ffcf005a0
commit c8a1d4d196
20 changed files with 308 additions and 196 deletions

View file

@ -25,7 +25,9 @@ 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::filter_pass::FilterPassMeta;
use librashader_runtime::quad::{QuadType, IDENTITY_MVP}; use librashader_runtime::quad::{QuadType, IDENTITY_MVP};
use librashader_runtime::scaling::{scale_framebuffers, scale_framebuffers_with_context_callback};
use librashader_runtime::uniforms::UniformStorage; use librashader_runtime::uniforms::UniformStorage;
use rayon::prelude::*; use rayon::prelude::*;
use windows::Win32::Graphics::Direct3D11::{ use windows::Win32::Graphics::Direct3D11::{
@ -445,33 +447,15 @@ impl FilterChainD3D11 {
}; };
let mut source = original.clone(); let mut source = original.clone();
let mut iterator = passes.iter_mut().enumerate().peekable();
// rescale render buffers to ensure all bindings are valid. // rescale render buffers to ensure all bindings are valid.
let mut source_size = source.size(); scale_framebuffers::<(), _, _, _>(
while let Some((index, pass)) = iterator.next() { source.size(),
let should_mipmap = iterator viewport.output.size,
.peek() &mut self.output_framebuffers,
.map_or(false, |(_, p)| p.config.mipmap_input); &mut self.feedback_framebuffers,
&passes,
let next_size = self.output_framebuffers[index].scale( )?;
pass.config.scaling.clone(),
pass.get_format(),
&viewport.output.size,
&source_size,
should_mipmap,
)?;
self.feedback_framebuffers[index].scale(
pass.config.scaling.clone(),
pass.get_format(),
&viewport.output.size,
&source_size,
should_mipmap,
)?;
source_size = next_size;
}
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

@ -10,6 +10,7 @@ use librashader_reflect::reflect::ShaderReflection;
use rustc_hash::FxHashMap; use rustc_hash::FxHashMap;
use librashader_runtime::binding::{BindSemantics, TextureInput}; use librashader_runtime::binding::{BindSemantics, TextureInput};
use librashader_runtime::filter_pass::FilterPassMeta;
use librashader_runtime::quad::QuadType; use librashader_runtime::quad::QuadType;
use windows::Win32::Graphics::Direct3D11::{ use windows::Win32::Graphics::Direct3D11::{
ID3D11Buffer, ID3D11InputLayout, ID3D11PixelShader, ID3D11SamplerState, ID3D11Buffer, ID3D11InputLayout, ID3D11PixelShader, ID3D11SamplerState,
@ -80,19 +81,18 @@ impl BindSemantics for FilterPass {
} }
} }
// slang_process.cpp 229 impl FilterPassMeta for FilterPass {
impl FilterPass { fn source_format(&self) -> ImageFormat {
pub fn get_format(&self) -> ImageFormat { self.source.format
let fb_format = self.source.format;
if let Some(format) = self.config.get_format_override() {
format
} else if fb_format == ImageFormat::Unknown {
ImageFormat::R8G8B8A8Unorm
} else {
fb_format
}
} }
fn config(&self) -> &ShaderPassConfig {
&self.config
}
}
// slang_process.cpp 229
impl FilterPass {
// framecount should be pre-modded // framecount should be pre-modded
fn build_semantics<'a>( fn build_semantics<'a>(
&mut self, &mut self,

View file

@ -1,10 +1,10 @@
use crate::error; use crate::error;
use crate::error::assume_d3d11_init; use crate::error::{assume_d3d11_init, FilterChainError};
use crate::texture::D3D11InputView; use crate::texture::D3D11InputView;
use crate::util::d3d11_get_closest_format; use crate::util::d3d11_get_closest_format;
use librashader_common::{ImageFormat, Size}; use librashader_common::{ImageFormat, Size};
use librashader_presets::Scale2D; use librashader_presets::Scale2D;
use librashader_runtime::scaling::{MipmapSize, ViewportSize}; use librashader_runtime::scaling::{MipmapSize, ScaleableFramebuffer, ViewportSize};
use windows::core::Interface; use windows::core::Interface;
use windows::Win32::Graphics::Direct3D::D3D_SRV_DIMENSION_TEXTURE2D; use windows::Win32::Graphics::Direct3D::D3D_SRV_DIMENSION_TEXTURE2D;
use windows::Win32::Graphics::Direct3D11::{ use windows::Win32::Graphics::Direct3D11::{
@ -251,3 +251,20 @@ pub const fn default_viewport(size: Size<u32>) -> D3D11_VIEWPORT {
MaxDepth: 1.0, MaxDepth: 1.0,
} }
} }
impl<T> ScaleableFramebuffer<T> for OwnedFramebuffer {
type Error = FilterChainError;
type Context = ();
fn scale(
&mut self,
scaling: Scale2D,
format: ImageFormat,
viewport_size: &Size<u32>,
source_size: &Size<u32>,
should_mipmap: bool,
_context: Self::Context,
) -> Result<Size<u32>, Self::Error> {
self.scale(scaling, format, viewport_size, source_size, should_mipmap)
}
}

View file

@ -40,7 +40,7 @@ mod tests {
// "../test/null.slangp", // "../test/null.slangp",
// const FILTER_PATH: &str = "../test/slang-shaders/scalefx/scalefx-9x.slangp"; // const FILTER_PATH: &str = "../test/slang-shaders/scalefx/scalefx-9x.slangp";
const FILTER_PATH: &str = "../test/slang-shaders/crt/zfast-crt.slangp"; const FILTER_PATH: &str = "../test/slang-shaders/crt/crt-royale.slangp";
const IMAGE_PATH: &str = "../test/finalfightlong.png"; const IMAGE_PATH: &str = "../test/finalfightlong.png";
// #[test] // #[test]
// fn triangle_d3d11_args() { // fn triangle_d3d11_args() {

View file

@ -46,7 +46,8 @@ 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::scaling::MipmapSize; use librashader_runtime::filter_pass::FilterPassMeta;
use librashader_runtime::scaling::{scale_framebuffers_with_context_callback, MipmapSize};
use rayon::prelude::*; use rayon::prelude::*;
const MIPMAP_RESERVED_WORKHEAP_DESCRIPTORS: usize = 1024; const MIPMAP_RESERVED_WORKHEAP_DESCRIPTORS: usize = 1024;
@ -593,46 +594,29 @@ impl FilterChainD3D12 {
); );
// rescale render buffers to ensure all bindings are valid. // rescale render buffers to ensure all bindings are valid.
let mut source_size = source.size(); scale_framebuffers_with_context_callback::<(), _, _, _, _>(
let mut iterator = passes.iter_mut().enumerate().peekable(); source.size(),
while let Some((index, pass)) = iterator.next() { viewport.output.size,
let should_mipmap = iterator &mut self.output_framebuffers,
.peek() &mut self.feedback_framebuffers,
.map_or(false, |(_, p)| p.config.mipmap_input); &passes,
(),
let next_size = self.output_framebuffers[index].scale( |index: usize, pass: &FilterPass, output: &OwnedImage, feedback: &OwnedImage| {
pass.config.scaling.clone(), // refresh inputs
pass.get_format(), self.common.feedback_textures[index] = Some(feedback.create_shader_resource_view(
&viewport.output.size,
&source_size,
should_mipmap,
)?;
self.feedback_framebuffers[index].scale(
pass.config.scaling.clone(),
pass.get_format(),
&viewport.output.size,
&source_size,
should_mipmap,
)?;
source_size = next_size;
// refresh inputs
self.common.feedback_textures[index] = Some(
self.feedback_framebuffers[index].create_shader_resource_view(
&mut self.staging_heap,
pass.config.filter,
pass.config.wrap_mode,
)?,
);
self.common.output_textures[index] =
Some(self.output_framebuffers[index].create_shader_resource_view(
&mut self.staging_heap, &mut self.staging_heap,
pass.config.filter, pass.config.filter,
pass.config.wrap_mode, pass.config.wrap_mode,
)?); )?);
} self.common.output_textures[index] = Some(output.create_shader_resource_view(
&mut self.staging_heap,
pass.config.filter,
pass.config.wrap_mode,
)?);
Ok(())
},
)?;
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

@ -12,6 +12,7 @@ use librashader_presets::ShaderPassConfig;
use librashader_reflect::reflect::semantics::{MemberOffset, TextureBinding, UniformBinding}; use librashader_reflect::reflect::semantics::{MemberOffset, TextureBinding, UniformBinding};
use librashader_reflect::reflect::ShaderReflection; use librashader_reflect::reflect::ShaderReflection;
use librashader_runtime::binding::{BindSemantics, TextureInput}; use librashader_runtime::binding::{BindSemantics, TextureInput};
use librashader_runtime::filter_pass::FilterPassMeta;
use librashader_runtime::quad::QuadType; use librashader_runtime::quad::QuadType;
use librashader_runtime::uniforms::{NoUniformBinder, UniformStorage}; use librashader_runtime::uniforms::{NoUniformBinder, UniformStorage};
use rustc_hash::FxHashMap; use rustc_hash::FxHashMap;
@ -74,18 +75,17 @@ impl BindSemantics<NoUniformBinder, Option<()>, RawD3D12Buffer, RawD3D12Buffer>
} }
} }
impl FilterPass { impl FilterPassMeta for FilterPass {
pub fn get_format(&self) -> ImageFormat { fn source_format(&self) -> ImageFormat {
let fb_format = self.source.format; self.source.format
if let Some(format) = self.config.get_format_override() {
format
} else if fb_format == ImageFormat::Unknown {
ImageFormat::R8G8B8A8Unorm
} else {
fb_format
}
} }
fn config(&self) -> &ShaderPassConfig {
&self.config
}
}
impl FilterPass {
// framecount should be pre-modded // framecount should be pre-modded
fn build_semantics<'a>( fn build_semantics<'a>(
&mut self, &mut self,

View file

@ -1,11 +1,11 @@
use crate::descriptor_heap::{CpuStagingHeap, D3D12DescriptorHeap, RenderTargetHeap}; use crate::descriptor_heap::{CpuStagingHeap, D3D12DescriptorHeap, RenderTargetHeap};
use crate::error::assume_d3d12_init; use crate::error::{assume_d3d12_init, FilterChainError};
use crate::texture::{D3D12OutputView, InputTexture}; use crate::texture::{D3D12OutputView, InputTexture};
use crate::util::d3d12_get_closest_format; use crate::util::d3d12_get_closest_format;
use crate::{error, util}; use crate::{error, util};
use librashader_common::{FilterMode, ImageFormat, Size, WrapMode}; use librashader_common::{FilterMode, ImageFormat, Size, WrapMode};
use librashader_presets::Scale2D; use librashader_presets::Scale2D;
use librashader_runtime::scaling::{MipmapSize, ViewportSize}; use librashader_runtime::scaling::{MipmapSize, ScaleableFramebuffer, ViewportSize};
use std::ops::Deref; use std::ops::Deref;
use windows::Win32::Foundation::RECT; use windows::Win32::Foundation::RECT;
use windows::Win32::Graphics::Direct3D12::{ use windows::Win32::Graphics::Direct3D12::{
@ -305,3 +305,20 @@ impl OwnedImage {
Ok(size) Ok(size)
} }
} }
impl<T> ScaleableFramebuffer<T> for OwnedImage {
type Error = FilterChainError;
type Context = ();
fn scale(
&mut self,
scaling: Scale2D,
format: ImageFormat,
viewport_size: &Size<u32>,
source_size: &Size<u32>,
should_mipmap: bool,
_context: Self::Context,
) -> Result<Size<u32>, Self::Error> {
self.scale(scaling, format, viewport_size, source_size, should_mipmap)
}
}

View file

@ -21,6 +21,8 @@ use librashader_reflect::reflect::semantics::{BindingMeta, ShaderSemantics, Unif
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::filter_pass::FilterPassMeta;
use librashader_runtime::scaling::scale_framebuffers;
use rustc_hash::FxHashMap; use rustc_hash::FxHashMap;
use spirv_cross::spirv::Decoration; use spirv_cross::spirv::Decoration;
use std::collections::VecDeque; use std::collections::VecDeque;
@ -378,32 +380,14 @@ impl<T: GLInterface> FilterChainImpl<T> {
let mut source = original; let mut source = original;
let mut source_size = source.image.size;
// rescale render buffers to ensure all bindings are valid. // rescale render buffers to ensure all bindings are valid.
let mut iterator = passes.iter_mut().enumerate().peekable(); scale_framebuffers::<T::FramebufferInterface, _, _, _>(
while let Some((index, pass)) = iterator.next() { source.image.size,
let should_mipmap = iterator viewport.output.size,
.peek() &mut self.output_framebuffers,
.map_or(false, |(_, p)| p.config.mipmap_input); &mut self.feedback_framebuffers,
&passes,
let next_size = self.output_framebuffers[index].scale::<T::FramebufferInterface>( )?;
pass.config.scaling.clone(),
pass.get_format(),
viewport,
&source_size,
should_mipmap,
)?;
self.feedback_framebuffers[index].scale::<T::FramebufferInterface>(
pass.config.scaling.clone(),
pass.get_format(),
viewport,
&source_size,
should_mipmap,
)?;
source_size = next_size
}
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

@ -8,6 +8,7 @@ use librashader_preprocess::ShaderSource;
use librashader_presets::ShaderPassConfig; use librashader_presets::ShaderPassConfig;
use librashader_reflect::reflect::semantics::{MemberOffset, TextureBinding, UniformBinding}; use librashader_reflect::reflect::semantics::{MemberOffset, TextureBinding, UniformBinding};
use librashader_runtime::binding::{BindSemantics, ContextOffset, TextureInput}; use librashader_runtime::binding::{BindSemantics, ContextOffset, TextureInput};
use librashader_runtime::filter_pass::FilterPassMeta;
use rustc_hash::FxHashMap; use rustc_hash::FxHashMap;
use crate::binding::{GlUniformBinder, GlUniformStorage, UniformLocation, VariableLocation}; use crate::binding::{GlUniformBinder, GlUniformStorage, UniformLocation, VariableLocation};
@ -147,18 +148,17 @@ impl<T: GLInterface> FilterPass<T> {
} }
} }
impl<T: GLInterface> FilterPass<T> { impl<T: GLInterface> FilterPassMeta for FilterPass<T> {
pub fn get_format(&self) -> ImageFormat { fn source_format(&self) -> ImageFormat {
let fb_format = self.source.format; self.source.format
if let Some(format) = self.config.get_format_override() {
format
} else if fb_format == ImageFormat::Unknown {
ImageFormat::R8G8B8A8Unorm
} else {
fb_format
}
} }
fn config(&self) -> &ShaderPassConfig {
&self.config
}
}
impl<T: GLInterface> FilterPass<T> {
// framecount should be pre-modded // framecount should be pre-modded
fn build_semantics( fn build_semantics(
&mut self, &mut self,

View file

@ -1,10 +1,11 @@
use crate::error::Result; use crate::error::{FilterChainError, Result};
use crate::framebuffer::GLImage; use crate::framebuffer::GLImage;
use crate::gl::FramebufferInterface; use crate::gl::FramebufferInterface;
use crate::texture::InputTexture; use crate::texture::InputTexture;
use gl::types::{GLenum, GLuint}; use gl::types::{GLenum, GLuint};
use librashader_common::{FilterMode, ImageFormat, Size, Viewport, WrapMode}; use librashader_common::{FilterMode, ImageFormat, Size, Viewport, WrapMode};
use librashader_presets::Scale2D; use librashader_presets::Scale2D;
use librashader_runtime::scaling::ScaleableFramebuffer;
/// A handle to an OpenGL FBO and its backing texture with format and size information. /// A handle to an OpenGL FBO and its backing texture with format and size information.
/// ///
@ -50,7 +51,7 @@ impl Framebuffer {
&mut self, &mut self,
scaling: Scale2D, scaling: Scale2D,
format: ImageFormat, format: ImageFormat,
viewport: &Viewport<&Framebuffer>, viewport: &Size<u32>,
source_size: &Size<u32>, source_size: &Size<u32>,
mipmap: bool, mipmap: bool,
) -> Result<Size<u32>> { ) -> Result<Size<u32>> {
@ -92,3 +93,20 @@ impl Drop for Framebuffer {
} }
} }
} }
//
impl<T: FramebufferInterface> ScaleableFramebuffer<T> for Framebuffer {
type Error = FilterChainError;
type Context = ();
fn scale(
&mut self,
scaling: Scale2D,
format: ImageFormat,
viewport_size: &Size<u32>,
source_size: &Size<u32>,
should_mipmap: bool,
_context: Self::Context,
) -> Result<Size<u32>> {
self.scale::<T>(scaling, format, viewport_size, source_size, should_mipmap)
}
}

View file

@ -37,7 +37,7 @@ impl FramebufferInterface for Gl3Framebuffer {
fb: &mut Framebuffer, fb: &mut Framebuffer,
scaling: Scale2D, scaling: Scale2D,
format: ImageFormat, format: ImageFormat,
viewport: &Viewport<&Framebuffer>, viewport_size: &Size<u32>,
source_size: &Size<u32>, source_size: &Size<u32>,
mipmap: bool, mipmap: bool,
) -> Result<Size<u32>> { ) -> Result<Size<u32>> {
@ -45,7 +45,7 @@ impl FramebufferInterface for Gl3Framebuffer {
return Ok(fb.size); return Ok(fb.size);
} }
let size = source_size.scale_viewport(scaling, viewport.output.size); let size = source_size.scale_viewport(scaling, *viewport_size);
if fb.size != size || (mipmap && fb.max_levels == 1) || (!mipmap && fb.max_levels != 1) { if fb.size != size || (mipmap && fb.max_levels == 1) || (!mipmap && fb.max_levels != 1) {
fb.size = size; fb.size = size;

View file

@ -35,7 +35,7 @@ impl FramebufferInterface for Gl46Framebuffer {
fb: &mut Framebuffer, fb: &mut Framebuffer,
scaling: Scale2D, scaling: Scale2D,
format: ImageFormat, format: ImageFormat,
viewport: &Viewport<&Framebuffer>, viewport_size: &Size<u32>,
source_size: &Size<u32>, source_size: &Size<u32>,
mipmap: bool, mipmap: bool,
) -> Result<Size<u32>> { ) -> Result<Size<u32>> {
@ -43,7 +43,7 @@ impl FramebufferInterface for Gl46Framebuffer {
return Ok(fb.size); return Ok(fb.size);
} }
let size = source_size.scale_viewport(scaling, viewport.output.size); let size = source_size.scale_viewport(scaling, *viewport_size);
if fb.size != size || (mipmap && fb.max_levels == 1) || (!mipmap && fb.max_levels != 1) { if fb.size != size || (mipmap && fb.max_levels == 1) || (!mipmap && fb.max_levels != 1) {
fb.size = size; fb.size = size;

View file

@ -41,7 +41,7 @@ pub trait FramebufferInterface {
fb: &mut Framebuffer, fb: &mut Framebuffer,
scaling: Scale2D, scaling: Scale2D,
format: ImageFormat, format: ImageFormat,
viewport: &Viewport<&Framebuffer>, viewport_size: &Size<u32>,
source_size: &Size<u32>, source_size: &Size<u32>,
mipmap: bool, mipmap: bool,
) -> Result<Size<u32>>; ) -> Result<Size<u32>>;

View file

@ -31,7 +31,10 @@ use std::collections::VecDeque;
use std::path::Path; use std::path::Path;
use std::sync::Arc; use std::sync::Arc;
use librashader_runtime::filter_pass::FilterPassMeta;
use librashader_runtime::scaling::scale_framebuffers_with_context_callback;
use rayon::prelude::*; use rayon::prelude::*;
/// A Vulkan device and metadata that is required by the shader runtime. /// A Vulkan device and metadata that is required by the shader runtime.
pub struct VulkanObjects { pub struct VulkanObjects {
pub(crate) device: Arc<ash::Device>, pub(crate) device: Arc<ash::Device>,
@ -613,59 +616,28 @@ impl FilterChainVulkan {
); );
// rescale render buffers to ensure all bindings are valid. // rescale render buffers to ensure all bindings are valid.
let mut source_size = source.image.size; scale_framebuffers_with_context_callback::<(), _, _, _, _>(
let mut iterator = passes.iter_mut().enumerate().peekable(); source.image.size,
while let Some((index, pass)) = iterator.next() { viewport.output.size,
let should_mipmap = iterator &mut self.output_framebuffers,
.peek() &mut self.feedback_framebuffers,
.map_or(false, |(_, p)| p.config.mipmap_input); &passes,
Some(OwnedImageLayout {
// needs a barrier to SHADER_READ_ONLY_OPTIMAL otherwise any non-filled in output framebuffers dst_layout: vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL,
// (like the final one which is just held there and not ever written to, but needs to be dst_access: vk::AccessFlags::SHADER_READ,
// there for indexing) will always stay in UNDEFINED. src_stage: vk::PipelineStageFlags::TOP_OF_PIPE,
// dst_stage: vk::PipelineStageFlags::FRAGMENT_SHADER,
// since scaling is hopefully a rare occurrence (since it's tested for if the output size cmd,
// requires changing) it should be ok. }),
let next_size = self.output_framebuffers[index].scale( |index: usize, pass: &FilterPass, output: &OwnedImage, feedback: &OwnedImage| {
pass.config.scaling.clone(), // refresh inputs
pass.get_format(), self.common.feedback_inputs[index] =
&viewport.output.size, Some(feedback.as_input(pass.config.filter, pass.config.wrap_mode));
&source_size, self.common.output_inputs[index] =
should_mipmap, Some(output.as_input(pass.config.filter, pass.config.wrap_mode));
Some(OwnedImageLayout { Ok(())
dst_layout: vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL, },
dst_access: vk::AccessFlags::SHADER_READ, )?;
src_stage: vk::PipelineStageFlags::TOP_OF_PIPE,
dst_stage: vk::PipelineStageFlags::FRAGMENT_SHADER,
cmd,
}),
)?;
self.feedback_framebuffers[index].scale(
pass.config.scaling.clone(),
pass.get_format(),
&viewport.output.size,
&source_size,
should_mipmap,
Some(OwnedImageLayout {
dst_layout: vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL,
dst_access: vk::AccessFlags::SHADER_READ,
src_stage: vk::PipelineStageFlags::TOP_OF_PIPE,
dst_stage: vk::PipelineStageFlags::FRAGMENT_SHADER,
cmd,
}),
)?;
source_size = next_size;
// refresh inputs
self.common.feedback_inputs[index] = Some(
self.feedback_framebuffers[index]
.as_input(pass.config.filter, pass.config.wrap_mode),
);
self.common.output_inputs[index] = Some(
self.output_framebuffers[index].as_input(pass.config.filter, pass.config.wrap_mode),
);
}
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

@ -14,6 +14,7 @@ use librashader_reflect::reflect::semantics::{
}; };
use librashader_reflect::reflect::ShaderReflection; use librashader_reflect::reflect::ShaderReflection;
use librashader_runtime::binding::{BindSemantics, TextureInput}; use librashader_runtime::binding::{BindSemantics, TextureInput};
use librashader_runtime::filter_pass::FilterPassMeta;
use librashader_runtime::quad::QuadType; use librashader_runtime::quad::QuadType;
use librashader_runtime::uniforms::{NoUniformBinder, UniformStorage, UniformStorageAccess}; use librashader_runtime::uniforms::{NoUniformBinder, UniformStorage, UniformStorageAccess};
use rustc_hash::FxHashMap; use rustc_hash::FxHashMap;
@ -73,18 +74,17 @@ impl BindSemantics<NoUniformBinder, Option<()>, RawVulkanBuffer> for FilterPass
} }
} }
impl FilterPass { impl FilterPassMeta for FilterPass {
pub fn get_format(&self) -> ImageFormat { fn source_format(&self) -> ImageFormat {
let fb_format = self.source.format; self.source.format
if let Some(format) = self.config.get_format_override() {
format
} else if fb_format == ImageFormat::Unknown {
ImageFormat::R8G8B8A8Unorm
} else {
fb_format
}
} }
fn config(&self) -> &ShaderPassConfig {
&self.config
}
}
impl FilterPass {
pub(crate) fn draw( pub(crate) fn draw(
&mut self, &mut self,
cmd: vk::CommandBuffer, cmd: vk::CommandBuffer,

View file

@ -45,7 +45,7 @@ mod tests {
dbg!("finished"); dbg!("finished");
let filter = FilterChainVulkan::load_from_path( let filter = FilterChainVulkan::load_from_path(
&base, &base,
"../test/slang-shaders/border/gameboy-player/gameboy-player-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__2__ADV-NO-REFLECT.slangp",
// "../test/basic.slangp", // "../test/basic.slangp",
Some(&FilterChainOptionsVulkan { Some(&FilterChainOptionsVulkan {

View file

@ -5,9 +5,10 @@ use crate::{error, util};
use ash::vk; use ash::vk;
use std::sync::Arc; use std::sync::Arc;
use crate::error::FilterChainError;
use librashader_common::{FilterMode, ImageFormat, Size, WrapMode}; use librashader_common::{FilterMode, ImageFormat, Size, WrapMode};
use librashader_presets::Scale2D; use librashader_presets::Scale2D;
use librashader_runtime::scaling::{MipmapSize, ViewportSize}; use librashader_runtime::scaling::{MipmapSize, ScaleableFramebuffer, ViewportSize};
pub struct OwnedImage { pub struct OwnedImage {
pub device: Arc<ash::Device>, pub device: Arc<ash::Device>,
@ -19,6 +20,7 @@ pub struct OwnedImage {
pub levels: u32, pub levels: u32,
} }
#[derive(Copy, Clone)]
pub struct OwnedImageLayout { pub struct OwnedImageLayout {
pub(crate) dst_layout: vk::ImageLayout, pub(crate) dst_layout: vk::ImageLayout,
pub(crate) dst_access: vk::AccessFlags, pub(crate) dst_access: vk::AccessFlags,
@ -533,3 +535,27 @@ impl AsRef<InputImage> for InputImage {
self self
} }
} }
impl<T> ScaleableFramebuffer<T> for OwnedImage {
type Error = FilterChainError;
type Context = Option<OwnedImageLayout>;
fn scale(
&mut self,
scaling: Scale2D,
format: ImageFormat,
viewport_size: &Size<u32>,
source_size: &Size<u32>,
should_mipmap: bool,
context: Self::Context,
) -> Result<Size<u32>, Self::Error> {
self.scale(
scaling,
format,
viewport_size,
source_size,
should_mipmap,
context,
)
}
}

View file

@ -0,0 +1,18 @@
use librashader_common::ImageFormat;
use librashader_presets::ShaderPassConfig;
pub trait FilterPassMeta {
fn source_format(&self) -> ImageFormat;
fn config(&self) -> &ShaderPassConfig;
fn get_format(&self) -> ImageFormat {
let fb_format = self.source_format();
if let Some(format) = self.config().get_format_override() {
format
} else if fb_format == ImageFormat::Unknown {
ImageFormat::R8G8B8A8Unorm
} else {
fb_format
}
}
}

View file

@ -28,3 +28,5 @@ pub mod binding;
/// VBO helper utilities /// VBO helper utilities
pub mod quad; pub mod quad;
pub mod filter_pass;

View file

@ -1,5 +1,6 @@
use crate::filter_pass::FilterPassMeta;
use crate::scaling; use crate::scaling;
use librashader_common::Size; use librashader_common::{ImageFormat, Size};
use librashader_presets::{Scale2D, ScaleFactor, ScaleType, Scaling}; use librashader_presets::{Scale2D, ScaleFactor, ScaleType, Scaling};
use num_traits::AsPrimitive; use num_traits::AsPrimitive;
use std::ops::Mul; use std::ops::Mul;
@ -93,3 +94,92 @@ where
height: height.round().as_(), height: height.round().as_(),
} }
} }
/// Trait for owned framebuffer objects that can be scaled.
pub trait ScaleableFramebuffer<T> {
type Error;
type Context: Copy;
/// Scale the framebuffer according to the provided parameters, returning the new size.
fn scale(
&mut self,
scaling: Scale2D,
format: ImageFormat,
viewport_size: &Size<u32>,
source_size: &Size<u32>,
should_mipmap: bool,
context: Self::Context,
) -> Result<Size<u32>, Self::Error>;
}
/// Scale framebuffers according to the pass configs, source and viewport size.
pub fn scale_framebuffers<T, F, E, P>(
source_size: Size<u32>,
viewport_size: Size<u32>,
output: &mut [F],
feedback: &mut [F],
passes: &[P],
) -> Result<(), E>
where
F: ScaleableFramebuffer<T, Context = (), Error = E>,
P: FilterPassMeta,
{
scale_framebuffers_with_context_callback(
source_size,
viewport_size,
output,
feedback,
passes,
(),
|_, _, _, _| Ok(()),
)
}
/// Scale framebuffers according to the pass configs, source and viewport size
/// passing a context into the scale function and a callback for each framebuffer rescale.
pub fn scale_framebuffers_with_context_callback<T, F, E, C, P>(
source_size: Size<u32>,
viewport_size: Size<u32>,
output: &mut [F],
feedback: &mut [F],
passes: &[P],
context: C,
mut callback: impl FnMut(usize, &P, &F, &F) -> Result<(), E>,
) -> Result<(), E>
where
F: ScaleableFramebuffer<T, Context = C, Error = E>,
C: Copy,
P: FilterPassMeta,
{
assert_eq!(output.len(), feedback.len());
let mut iterator = passes.iter().enumerate().peekable();
let mut target_size = source_size;
while let Some((index, pass)) = iterator.next() {
let should_mipmap = iterator
.peek()
.map_or(false, |(_, p)| p.config().mipmap_input);
let next_size = output[index].scale(
pass.config().scaling.clone(),
pass.get_format(),
&viewport_size,
&target_size,
should_mipmap,
context,
)?;
feedback[index].scale(
pass.config().scaling.clone(),
pass.get_format(),
&viewport_size,
&target_size,
should_mipmap,
context,
)?;
target_size = next_size;
callback(index, &pass, &output[index], &feedback[index])?;
}
Ok(())
}