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 librashader_reflect::reflect::presets::{CompilePresetTarget, ShaderPassArtifact};
use librashader_runtime::binding::{BindingUtil, TextureInput};
use librashader_runtime::filter_pass::FilterPassMeta;
use librashader_runtime::quad::{QuadType, IDENTITY_MVP};
use librashader_runtime::scaling::{scale_framebuffers, scale_framebuffers_with_context_callback};
use librashader_runtime::uniforms::UniformStorage;
use rayon::prelude::*;
use windows::Win32::Graphics::Direct3D11::{
@ -445,33 +447,15 @@ impl FilterChainD3D11 {
};
let mut source = original.clone();
let mut iterator = passes.iter_mut().enumerate().peekable();
// rescale render buffers to ensure all bindings are valid.
let mut source_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 = 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;
}
scale_framebuffers::<(), _, _, _>(
source.size(),
viewport.output.size,
&mut self.output_framebuffers,
&mut self.feedback_framebuffers,
&passes,
)?;
let passes_len = passes.len();
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 librashader_runtime::binding::{BindSemantics, TextureInput};
use librashader_runtime::filter_pass::FilterPassMeta;
use librashader_runtime::quad::QuadType;
use windows::Win32::Graphics::Direct3D11::{
ID3D11Buffer, ID3D11InputLayout, ID3D11PixelShader, ID3D11SamplerState,
@ -80,19 +81,18 @@ impl BindSemantics for FilterPass {
}
}
// slang_process.cpp 229
impl FilterPass {
pub 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
}
impl FilterPassMeta for FilterPass {
fn source_format(&self) -> ImageFormat {
self.source.format
}
fn config(&self) -> &ShaderPassConfig {
&self.config
}
}
// slang_process.cpp 229
impl FilterPass {
// framecount should be pre-modded
fn build_semantics<'a>(
&mut self,

View file

@ -1,10 +1,10 @@
use crate::error;
use crate::error::assume_d3d11_init;
use crate::error::{assume_d3d11_init, FilterChainError};
use crate::texture::D3D11InputView;
use crate::util::d3d11_get_closest_format;
use librashader_common::{ImageFormat, Size};
use librashader_presets::Scale2D;
use librashader_runtime::scaling::{MipmapSize, ViewportSize};
use librashader_runtime::scaling::{MipmapSize, ScaleableFramebuffer, ViewportSize};
use windows::core::Interface;
use windows::Win32::Graphics::Direct3D::D3D_SRV_DIMENSION_TEXTURE2D;
use windows::Win32::Graphics::Direct3D11::{
@ -251,3 +251,20 @@ pub const fn default_viewport(size: Size<u32>) -> D3D11_VIEWPORT {
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",
// 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";
// #[test]
// 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::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::*;
const MIPMAP_RESERVED_WORKHEAP_DESCRIPTORS: usize = 1024;
@ -593,46 +594,29 @@ impl FilterChainD3D12 {
);
// rescale render buffers to ensure all bindings are valid.
let mut source_size = source.size();
let mut iterator = passes.iter_mut().enumerate().peekable();
while let Some((index, pass)) = iterator.next() {
let should_mipmap = iterator
.peek()
.map_or(false, |(_, p)| p.config.mipmap_input);
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;
// 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(
scale_framebuffers_with_context_callback::<(), _, _, _, _>(
source.size(),
viewport.output.size,
&mut self.output_framebuffers,
&mut self.feedback_framebuffers,
&passes,
(),
|index: usize, pass: &FilterPass, output: &OwnedImage, feedback: &OwnedImage| {
// refresh inputs
self.common.feedback_textures[index] = Some(feedback.create_shader_resource_view(
&mut self.staging_heap,
pass.config.filter,
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 (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::ShaderReflection;
use librashader_runtime::binding::{BindSemantics, TextureInput};
use librashader_runtime::filter_pass::FilterPassMeta;
use librashader_runtime::quad::QuadType;
use librashader_runtime::uniforms::{NoUniformBinder, UniformStorage};
use rustc_hash::FxHashMap;
@ -74,18 +75,17 @@ impl BindSemantics<NoUniformBinder, Option<()>, RawD3D12Buffer, RawD3D12Buffer>
}
}
impl FilterPass {
pub 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
}
impl FilterPassMeta for FilterPass {
fn source_format(&self) -> ImageFormat {
self.source.format
}
fn config(&self) -> &ShaderPassConfig {
&self.config
}
}
impl FilterPass {
// framecount should be pre-modded
fn build_semantics<'a>(
&mut self,

View file

@ -1,11 +1,11 @@
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::util::d3d12_get_closest_format;
use crate::{error, util};
use librashader_common::{FilterMode, ImageFormat, Size, WrapMode};
use librashader_presets::Scale2D;
use librashader_runtime::scaling::{MipmapSize, ViewportSize};
use librashader_runtime::scaling::{MipmapSize, ScaleableFramebuffer, ViewportSize};
use std::ops::Deref;
use windows::Win32::Foundation::RECT;
use windows::Win32::Graphics::Direct3D12::{
@ -305,3 +305,20 @@ impl OwnedImage {
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::ReflectShader;
use librashader_runtime::binding::BindingUtil;
use librashader_runtime::filter_pass::FilterPassMeta;
use librashader_runtime::scaling::scale_framebuffers;
use rustc_hash::FxHashMap;
use spirv_cross::spirv::Decoration;
use std::collections::VecDeque;
@ -378,32 +380,14 @@ impl<T: GLInterface> FilterChainImpl<T> {
let mut source = original;
let mut source_size = source.image.size;
// rescale render buffers to ensure all bindings are valid.
let mut iterator = passes.iter_mut().enumerate().peekable();
while let Some((index, pass)) = iterator.next() {
let should_mipmap = iterator
.peek()
.map_or(false, |(_, p)| p.config.mipmap_input);
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
}
scale_framebuffers::<T::FramebufferInterface, _, _, _>(
source.image.size,
viewport.output.size,
&mut self.output_framebuffers,
&mut self.feedback_framebuffers,
&passes,
)?;
let passes_len = passes.len();
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_reflect::reflect::semantics::{MemberOffset, TextureBinding, UniformBinding};
use librashader_runtime::binding::{BindSemantics, ContextOffset, TextureInput};
use librashader_runtime::filter_pass::FilterPassMeta;
use rustc_hash::FxHashMap;
use crate::binding::{GlUniformBinder, GlUniformStorage, UniformLocation, VariableLocation};
@ -147,18 +148,17 @@ impl<T: GLInterface> FilterPass<T> {
}
}
impl<T: GLInterface> FilterPass<T> {
pub 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
}
impl<T: GLInterface> FilterPassMeta for FilterPass<T> {
fn source_format(&self) -> ImageFormat {
self.source.format
}
fn config(&self) -> &ShaderPassConfig {
&self.config
}
}
impl<T: GLInterface> FilterPass<T> {
// framecount should be pre-modded
fn build_semantics(
&mut self,

View file

@ -1,10 +1,11 @@
use crate::error::Result;
use crate::error::{FilterChainError, Result};
use crate::framebuffer::GLImage;
use crate::gl::FramebufferInterface;
use crate::texture::InputTexture;
use gl::types::{GLenum, GLuint};
use librashader_common::{FilterMode, ImageFormat, Size, Viewport, WrapMode};
use librashader_presets::Scale2D;
use librashader_runtime::scaling::ScaleableFramebuffer;
/// A handle to an OpenGL FBO and its backing texture with format and size information.
///
@ -50,7 +51,7 @@ impl Framebuffer {
&mut self,
scaling: Scale2D,
format: ImageFormat,
viewport: &Viewport<&Framebuffer>,
viewport: &Size<u32>,
source_size: &Size<u32>,
mipmap: bool,
) -> 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,
scaling: Scale2D,
format: ImageFormat,
viewport: &Viewport<&Framebuffer>,
viewport_size: &Size<u32>,
source_size: &Size<u32>,
mipmap: bool,
) -> Result<Size<u32>> {
@ -45,7 +45,7 @@ impl FramebufferInterface for Gl3Framebuffer {
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) {
fb.size = size;

View file

@ -35,7 +35,7 @@ impl FramebufferInterface for Gl46Framebuffer {
fb: &mut Framebuffer,
scaling: Scale2D,
format: ImageFormat,
viewport: &Viewport<&Framebuffer>,
viewport_size: &Size<u32>,
source_size: &Size<u32>,
mipmap: bool,
) -> Result<Size<u32>> {
@ -43,7 +43,7 @@ impl FramebufferInterface for Gl46Framebuffer {
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) {
fb.size = size;

View file

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

View file

@ -31,7 +31,10 @@ use std::collections::VecDeque;
use std::path::Path;
use std::sync::Arc;
use librashader_runtime::filter_pass::FilterPassMeta;
use librashader_runtime::scaling::scale_framebuffers_with_context_callback;
use rayon::prelude::*;
/// A Vulkan device and metadata that is required by the shader runtime.
pub struct VulkanObjects {
pub(crate) device: Arc<ash::Device>,
@ -613,59 +616,28 @@ impl FilterChainVulkan {
);
// rescale render buffers to ensure all bindings are valid.
let mut source_size = source.image.size;
let mut iterator = passes.iter_mut().enumerate().peekable();
while let Some((index, pass)) = iterator.next() {
let should_mipmap = iterator
.peek()
.map_or(false, |(_, p)| p.config.mipmap_input);
// needs a barrier to SHADER_READ_ONLY_OPTIMAL otherwise any non-filled in output framebuffers
// (like the final one which is just held there and not ever written to, but needs to be
// there for indexing) will always stay in UNDEFINED.
//
// since scaling is hopefully a rare occurrence (since it's tested for if the output size
// requires changing) it should be ok.
let next_size = self.output_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,
}),
)?;
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),
);
}
scale_framebuffers_with_context_callback::<(), _, _, _, _>(
source.image.size,
viewport.output.size,
&mut self.output_framebuffers,
&mut self.feedback_framebuffers,
&passes,
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,
}),
|index: usize, pass: &FilterPass, output: &OwnedImage, feedback: &OwnedImage| {
// refresh inputs
self.common.feedback_inputs[index] =
Some(feedback.as_input(pass.config.filter, pass.config.wrap_mode));
self.common.output_inputs[index] =
Some(output.as_input(pass.config.filter, pass.config.wrap_mode));
Ok(())
},
)?;
let passes_len = passes.len();
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_runtime::binding::{BindSemantics, TextureInput};
use librashader_runtime::filter_pass::FilterPassMeta;
use librashader_runtime::quad::QuadType;
use librashader_runtime::uniforms::{NoUniformBinder, UniformStorage, UniformStorageAccess};
use rustc_hash::FxHashMap;
@ -73,18 +74,17 @@ impl BindSemantics<NoUniformBinder, Option<()>, RawVulkanBuffer> for FilterPass
}
}
impl FilterPass {
pub 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
}
impl FilterPassMeta for FilterPass {
fn source_format(&self) -> ImageFormat {
self.source.format
}
fn config(&self) -> &ShaderPassConfig {
&self.config
}
}
impl FilterPass {
pub(crate) fn draw(
&mut self,
cmd: vk::CommandBuffer,

View file

@ -45,7 +45,7 @@ mod tests {
dbg!("finished");
let filter = FilterChainVulkan::load_from_path(
&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/basic.slangp",
Some(&FilterChainOptionsVulkan {

View file

@ -5,9 +5,10 @@ use crate::{error, util};
use ash::vk;
use std::sync::Arc;
use crate::error::FilterChainError;
use librashader_common::{FilterMode, ImageFormat, Size, WrapMode};
use librashader_presets::Scale2D;
use librashader_runtime::scaling::{MipmapSize, ViewportSize};
use librashader_runtime::scaling::{MipmapSize, ScaleableFramebuffer, ViewportSize};
pub struct OwnedImage {
pub device: Arc<ash::Device>,
@ -19,6 +20,7 @@ pub struct OwnedImage {
pub levels: u32,
}
#[derive(Copy, Clone)]
pub struct OwnedImageLayout {
pub(crate) dst_layout: vk::ImageLayout,
pub(crate) dst_access: vk::AccessFlags,
@ -533,3 +535,27 @@ impl AsRef<InputImage> for InputImage {
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
pub mod quad;
pub mod filter_pass;

View file

@ -1,5 +1,6 @@
use crate::filter_pass::FilterPassMeta;
use crate::scaling;
use librashader_common::Size;
use librashader_common::{ImageFormat, Size};
use librashader_presets::{Scale2D, ScaleFactor, ScaleType, Scaling};
use num_traits::AsPrimitive;
use std::ops::Mul;
@ -93,3 +94,92 @@ where
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(())
}