rt: abstract framebuffer scaling into common code
This commit is contained in:
parent
5ffcf005a0
commit
c8a1d4d196
|
@ -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);
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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>>;
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
18
librashader-runtime/src/filter_pass.rs
Normal file
18
librashader-runtime/src/filter_pass.rs
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -28,3 +28,5 @@ pub mod binding;
|
||||||
|
|
||||||
/// VBO helper utilities
|
/// VBO helper utilities
|
||||||
pub mod quad;
|
pub mod quad;
|
||||||
|
|
||||||
|
pub mod filter_pass;
|
||||||
|
|
|
@ -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(())
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue