mod runtime;
use crate::context::Context;
use librashader_common::{Size, Viewport};
use librashader_presets::ShaderFeatures;
use std::sync::Arc;
#[derive(Default)]
pub struct FiltersBrush {
filter_chains: Vec<crate::components::filters::runtime::FilterChain>,
filter_intermediates: Vec<Arc<wgpu::Texture>>,
framecount: usize,
}
impl FiltersBrush {
#[inline]
pub fn update_filters(&mut self, ctx: &Context, filter_paths: &[String]) {
self.filter_chains.clear();
self.filter_intermediates.clear();
if filter_paths.is_empty() {
return;
}
let usage_caps = ctx.surface_caps().usages;
if !usage_caps.contains(wgpu::TextureUsages::COPY_DST)
|| !usage_caps.contains(wgpu::TextureUsages::COPY_SRC)
{
tracing::warn!("The selected backend does not support the filter chains!");
return;
}
for path in filter_paths {
tracing::debug!("Loading filter {}", path);
match crate::components::filters::runtime::FilterChain::load_from_path(
path,
ShaderFeatures::NONE,
&ctx.device,
&ctx.queue,
None,
) {
Ok(f) => self.filter_chains.push(f),
Err(e) => tracing::error!("Failed to load filter {}: {}", path, e),
}
}
self.filter_intermediates.reserve(self.filter_chains.len());
let skip = if self.filter_chains.len() % 2 == 1 {
1
} else {
0
};
let size = wgpu::Extent3d {
depth_or_array_layers: 1,
width: ctx.size.width as u32,
height: ctx.size.height as u32,
};
for _ in self.filter_chains.iter().skip(skip) {
let intermediate_texture =
Arc::new(ctx.device.create_texture(&wgpu::TextureDescriptor {
label: Some("Filter Intermediate Texture"),
size,
mip_level_count: 1,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format: ctx.format,
usage: wgpu::TextureUsages::TEXTURE_BINDING
| wgpu::TextureUsages::RENDER_ATTACHMENT
| wgpu::TextureUsages::COPY_SRC
| wgpu::TextureUsages::COPY_DST,
view_formats: &[ctx.format],
}));
self.filter_intermediates.push(intermediate_texture);
}
}
#[inline]
pub fn render(
&mut self,
ctx: &Context,
encoder: &mut wgpu::CommandEncoder,
src_texture: &wgpu::Texture,
dst_texture: &wgpu::Texture,
) {
let filters_count = self.filter_chains.len();
if filters_count == 0 {
return;
}
let usage_caps = ctx.surface_caps().usages;
if !usage_caps.contains(wgpu::TextureUsages::COPY_SRC)
|| !usage_caps.contains(wgpu::TextureUsages::COPY_DST)
{
return;
}
let src_texture = {
let new_src_texture =
Arc::new(ctx.device.create_texture(&wgpu::TextureDescriptor {
label: Some("Filters Source Texture"),
size: src_texture.size(),
mip_level_count: src_texture.mip_level_count(),
sample_count: src_texture.sample_count(),
dimension: src_texture.dimension(),
format: src_texture.format(),
usage: wgpu::TextureUsages::TEXTURE_BINDING
| wgpu::TextureUsages::RENDER_ATTACHMENT
| wgpu::TextureUsages::COPY_SRC
| wgpu::TextureUsages::COPY_DST,
view_formats: &[src_texture.format()],
}));
encoder.copy_texture_to_texture(
src_texture.as_image_copy(),
new_src_texture.as_image_copy(),
new_src_texture.size(),
);
new_src_texture
};
let view_size = Size::new(ctx.size.width as u32, ctx.size.height as u32);
for (idx, filter) in self.filter_chains.iter_mut().enumerate() {
let filter_src_texture: Arc<wgpu::Texture>;
let filter_dst_texture: &wgpu::Texture;
if idx == 0 {
filter_src_texture = src_texture.clone();
if filters_count == 1 {
filter_dst_texture = dst_texture;
} else {
filter_dst_texture = &self.filter_intermediates[0];
}
} else if idx == filters_count - 1 {
filter_src_texture = self.filter_intermediates[idx - 1].clone();
filter_dst_texture = dst_texture;
} else {
filter_src_texture = self.filter_intermediates[idx - 1].clone();
filter_dst_texture = &self.filter_intermediates[idx];
}
let dst_texture_view =
filter_dst_texture.create_view(&wgpu::TextureViewDescriptor::default());
let dst_output_view =
crate::components::filters::runtime::WgpuOutputView::new_from_raw(
&dst_texture_view,
view_size,
ctx.format,
);
let dst_viewport =
Viewport::new_render_target_sized_origin(dst_output_view, None).unwrap();
let _ = self.framecount.wrapping_add(1);
if let Err(err) = filter.frame(
filter_src_texture,
&dst_viewport,
encoder,
self.framecount,
None,
ctx,
) {
tracing::error!("Filter rendering failed: {err}");
}
}
}
}