wgpu: Refactor out filters into their own files
This commit is contained in:
parent
ad8457b54d
commit
4f6bac7840
|
@ -1,8 +1,9 @@
|
||||||
|
use crate::filters::{FilterVertex, Filters};
|
||||||
use crate::layouts::BindLayouts;
|
use crate::layouts::BindLayouts;
|
||||||
use crate::pipelines::VERTEX_BUFFERS_DESCRIPTION_POS;
|
use crate::pipelines::VERTEX_BUFFERS_DESCRIPTION_POS;
|
||||||
use crate::shaders::Shaders;
|
use crate::shaders::Shaders;
|
||||||
use crate::{
|
use crate::{
|
||||||
create_buffer_with_data, BitmapSamplers, FilterVertex, Pipelines, PosColorVertex, PosVertex,
|
create_buffer_with_data, BitmapSamplers, Pipelines, PosColorVertex, PosVertex,
|
||||||
TextureTransforms, Transforms, DEFAULT_COLOR_ADJUSTMENTS,
|
TextureTransforms, Transforms, DEFAULT_COLOR_ADJUSTMENTS,
|
||||||
};
|
};
|
||||||
use fnv::FnvHashMap;
|
use fnv::FnvHashMap;
|
||||||
|
@ -20,9 +21,10 @@ pub struct Descriptors {
|
||||||
pub quad: Quad,
|
pub quad: Quad,
|
||||||
copy_pipeline: Mutex<FnvHashMap<(u32, wgpu::TextureFormat), Arc<wgpu::RenderPipeline>>>,
|
copy_pipeline: Mutex<FnvHashMap<(u32, wgpu::TextureFormat), Arc<wgpu::RenderPipeline>>>,
|
||||||
copy_srgb_pipeline: Mutex<FnvHashMap<(u32, wgpu::TextureFormat), Arc<wgpu::RenderPipeline>>>,
|
copy_srgb_pipeline: Mutex<FnvHashMap<(u32, wgpu::TextureFormat), Arc<wgpu::RenderPipeline>>>,
|
||||||
shaders: Shaders,
|
pub shaders: Shaders,
|
||||||
pipelines: Mutex<FnvHashMap<(u32, wgpu::TextureFormat), Arc<Pipelines>>>,
|
pipelines: Mutex<FnvHashMap<(u32, wgpu::TextureFormat), Arc<Pipelines>>>,
|
||||||
pub default_color_bind_group: wgpu::BindGroup,
|
pub default_color_bind_group: wgpu::BindGroup,
|
||||||
|
pub filters: Filters,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Debug for Descriptors {
|
impl Debug for Descriptors {
|
||||||
|
@ -52,6 +54,7 @@ impl Descriptors {
|
||||||
resource: default_color_transform.as_entire_binding(),
|
resource: default_color_transform.as_entire_binding(),
|
||||||
}],
|
}],
|
||||||
});
|
});
|
||||||
|
let filters = Filters::new(&device);
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
adapter,
|
adapter,
|
||||||
|
@ -66,6 +69,7 @@ impl Descriptors {
|
||||||
shaders,
|
shaders,
|
||||||
pipelines: Default::default(),
|
pipelines: Default::default(),
|
||||||
default_color_bind_group,
|
default_color_bind_group,
|
||||||
|
filters,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,75 @@
|
||||||
|
mod blur;
|
||||||
|
mod color_matrix;
|
||||||
|
|
||||||
|
use crate::filters::blur::BlurFilter;
|
||||||
|
use crate::filters::color_matrix::ColorMatrixFilter;
|
||||||
|
use bytemuck::{Pod, Zeroable};
|
||||||
|
use wgpu::util::DeviceExt;
|
||||||
|
use wgpu::vertex_attr_array;
|
||||||
|
|
||||||
|
pub struct Filters {
|
||||||
|
pub blur: BlurFilter,
|
||||||
|
pub color_matrix: ColorMatrixFilter,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Filters {
|
||||||
|
pub fn new(device: &wgpu::Device) -> Self {
|
||||||
|
Self {
|
||||||
|
blur: BlurFilter::new(device),
|
||||||
|
color_matrix: ColorMatrixFilter::new(device),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Copy, Clone, Debug, Pod, Zeroable)]
|
||||||
|
pub struct FilterVertex {
|
||||||
|
pub position: [f32; 2],
|
||||||
|
pub uv: [f32; 2],
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const VERTEX_BUFFERS_DESCRIPTION_FILTERS: [wgpu::VertexBufferLayout; 1] =
|
||||||
|
[wgpu::VertexBufferLayout {
|
||||||
|
array_stride: std::mem::size_of::<FilterVertex>() as u64,
|
||||||
|
step_mode: wgpu::VertexStepMode::Vertex,
|
||||||
|
attributes: &vertex_attr_array![
|
||||||
|
0 => Float32x2,
|
||||||
|
1 => Float32x2,
|
||||||
|
],
|
||||||
|
}];
|
||||||
|
|
||||||
|
pub fn create_filter_vertices(
|
||||||
|
device: &wgpu::Device,
|
||||||
|
source_texture: &wgpu::Texture,
|
||||||
|
source_point: (u32, u32),
|
||||||
|
source_size: (u32, u32),
|
||||||
|
) -> wgpu::Buffer {
|
||||||
|
let source_width = source_texture.width() as f32;
|
||||||
|
let source_height = source_texture.height() as f32;
|
||||||
|
let left = source_point.0;
|
||||||
|
let top = source_point.1;
|
||||||
|
let right = left + source_size.0;
|
||||||
|
let bottom = top + source_size.1;
|
||||||
|
device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||||
|
label: create_debug_label!("Filter vertices").as_deref(),
|
||||||
|
contents: bytemuck::cast_slice(&[
|
||||||
|
FilterVertex {
|
||||||
|
position: [0.0, 0.0],
|
||||||
|
uv: [left as f32 / source_width, top as f32 / source_height],
|
||||||
|
},
|
||||||
|
FilterVertex {
|
||||||
|
position: [1.0, 0.0],
|
||||||
|
uv: [right as f32 / source_width, top as f32 / source_height],
|
||||||
|
},
|
||||||
|
FilterVertex {
|
||||||
|
position: [1.0, 1.0],
|
||||||
|
uv: [right as f32 / source_width, bottom as f32 / source_height],
|
||||||
|
},
|
||||||
|
FilterVertex {
|
||||||
|
position: [0.0, 1.0],
|
||||||
|
uv: [left as f32 / source_width, bottom as f32 / source_height],
|
||||||
|
},
|
||||||
|
]),
|
||||||
|
usage: wgpu::BufferUsages::VERTEX,
|
||||||
|
})
|
||||||
|
}
|
|
@ -0,0 +1,234 @@
|
||||||
|
use crate::backend::RenderTargetMode;
|
||||||
|
use crate::buffer_pool::TexturePool;
|
||||||
|
use crate::descriptors::Descriptors;
|
||||||
|
use crate::filters::{create_filter_vertices, VERTEX_BUFFERS_DESCRIPTION_FILTERS};
|
||||||
|
use crate::surface::target::CommandTarget;
|
||||||
|
use crate::utils::SampleCountMap;
|
||||||
|
use std::sync::OnceLock;
|
||||||
|
use swf::BlurFilter as BlurFilterArgs;
|
||||||
|
use wgpu::util::DeviceExt;
|
||||||
|
|
||||||
|
pub struct BlurFilter {
|
||||||
|
bind_group_layout: wgpu::BindGroupLayout,
|
||||||
|
pipeline_layout: wgpu::PipelineLayout,
|
||||||
|
pipelines: SampleCountMap<OnceLock<wgpu::RenderPipeline>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BlurFilter {
|
||||||
|
pub fn new(device: &wgpu::Device) -> Self {
|
||||||
|
let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
||||||
|
entries: &[
|
||||||
|
wgpu::BindGroupLayoutEntry {
|
||||||
|
binding: 0,
|
||||||
|
visibility: wgpu::ShaderStages::FRAGMENT,
|
||||||
|
ty: wgpu::BindingType::Texture {
|
||||||
|
multisampled: false,
|
||||||
|
sample_type: wgpu::TextureSampleType::Float { filterable: true },
|
||||||
|
view_dimension: wgpu::TextureViewDimension::D2,
|
||||||
|
},
|
||||||
|
count: None,
|
||||||
|
},
|
||||||
|
wgpu::BindGroupLayoutEntry {
|
||||||
|
binding: 1,
|
||||||
|
visibility: wgpu::ShaderStages::FRAGMENT,
|
||||||
|
ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
|
||||||
|
count: None,
|
||||||
|
},
|
||||||
|
wgpu::BindGroupLayoutEntry {
|
||||||
|
binding: 2,
|
||||||
|
visibility: wgpu::ShaderStages::FRAGMENT,
|
||||||
|
ty: wgpu::BindingType::Buffer {
|
||||||
|
ty: wgpu::BufferBindingType::Uniform,
|
||||||
|
has_dynamic_offset: false,
|
||||||
|
min_binding_size: wgpu::BufferSize::new(
|
||||||
|
std::mem::size_of::<[f32; 4]>() as u64
|
||||||
|
),
|
||||||
|
},
|
||||||
|
count: None,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
label: create_debug_label!("Blur filter binds").as_deref(),
|
||||||
|
});
|
||||||
|
|
||||||
|
let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
||||||
|
label: None,
|
||||||
|
bind_group_layouts: &[&bind_group_layout],
|
||||||
|
push_constant_ranges: &[],
|
||||||
|
});
|
||||||
|
|
||||||
|
Self {
|
||||||
|
pipelines: Default::default(),
|
||||||
|
pipeline_layout,
|
||||||
|
bind_group_layout,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pipeline(&self, descriptors: &Descriptors, msaa_sample_count: u32) -> &wgpu::RenderPipeline {
|
||||||
|
self.pipelines.get_or_init(msaa_sample_count, || {
|
||||||
|
let label = create_debug_label!("Blur Filter ({} msaa)", msaa_sample_count);
|
||||||
|
descriptors
|
||||||
|
.device
|
||||||
|
.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
|
||||||
|
label: label.as_deref(),
|
||||||
|
layout: Some(&self.pipeline_layout),
|
||||||
|
vertex: wgpu::VertexState {
|
||||||
|
module: &descriptors.shaders.blur_filter,
|
||||||
|
entry_point: "main_vertex",
|
||||||
|
buffers: &VERTEX_BUFFERS_DESCRIPTION_FILTERS,
|
||||||
|
},
|
||||||
|
primitive: wgpu::PrimitiveState {
|
||||||
|
topology: wgpu::PrimitiveTopology::TriangleList,
|
||||||
|
strip_index_format: None,
|
||||||
|
front_face: wgpu::FrontFace::Ccw,
|
||||||
|
cull_mode: None,
|
||||||
|
polygon_mode: wgpu::PolygonMode::default(),
|
||||||
|
unclipped_depth: false,
|
||||||
|
conservative: false,
|
||||||
|
},
|
||||||
|
depth_stencil: None,
|
||||||
|
multisample: wgpu::MultisampleState {
|
||||||
|
count: msaa_sample_count,
|
||||||
|
mask: !0,
|
||||||
|
alpha_to_coverage_enabled: false,
|
||||||
|
},
|
||||||
|
fragment: Some(wgpu::FragmentState {
|
||||||
|
module: &descriptors.shaders.blur_filter,
|
||||||
|
entry_point: "main_fragment",
|
||||||
|
targets: &[Some(wgpu::TextureFormat::Rgba8Unorm.into())],
|
||||||
|
}),
|
||||||
|
multiview: None,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
|
pub fn apply(
|
||||||
|
&self,
|
||||||
|
descriptors: &Descriptors,
|
||||||
|
texture_pool: &mut TexturePool,
|
||||||
|
draw_encoder: &mut wgpu::CommandEncoder,
|
||||||
|
source_texture: &wgpu::Texture,
|
||||||
|
source_point: (u32, u32),
|
||||||
|
source_size: (u32, u32),
|
||||||
|
filter: &BlurFilterArgs,
|
||||||
|
) -> CommandTarget {
|
||||||
|
let sample_count = source_texture.sample_count();
|
||||||
|
let format = source_texture.format();
|
||||||
|
let pipeline = self.pipeline(descriptors, sample_count);
|
||||||
|
|
||||||
|
// FIXME - this should be larger than the source texture. Figure out exactly how much larger
|
||||||
|
let targets = [
|
||||||
|
CommandTarget::new(
|
||||||
|
descriptors,
|
||||||
|
texture_pool,
|
||||||
|
wgpu::Extent3d {
|
||||||
|
width: source_size.0,
|
||||||
|
height: source_size.1,
|
||||||
|
depth_or_array_layers: 1,
|
||||||
|
},
|
||||||
|
format,
|
||||||
|
sample_count,
|
||||||
|
RenderTargetMode::FreshWithColor(wgpu::Color::TRANSPARENT),
|
||||||
|
draw_encoder,
|
||||||
|
),
|
||||||
|
CommandTarget::new(
|
||||||
|
descriptors,
|
||||||
|
texture_pool,
|
||||||
|
wgpu::Extent3d {
|
||||||
|
width: source_size.0,
|
||||||
|
height: source_size.1,
|
||||||
|
depth_or_array_layers: 1,
|
||||||
|
},
|
||||||
|
format,
|
||||||
|
sample_count,
|
||||||
|
RenderTargetMode::FreshWithColor(wgpu::Color::TRANSPARENT),
|
||||||
|
draw_encoder,
|
||||||
|
),
|
||||||
|
];
|
||||||
|
|
||||||
|
// TODO: Vertices should be per pass, and each pass needs diff sizes
|
||||||
|
let vertices = create_filter_vertices(
|
||||||
|
&descriptors.device,
|
||||||
|
source_texture,
|
||||||
|
source_point,
|
||||||
|
source_size,
|
||||||
|
);
|
||||||
|
|
||||||
|
let source_view = source_texture.create_view(&Default::default());
|
||||||
|
for i in 0..2 {
|
||||||
|
let blur_x = (filter.blur_x.to_f32() - 1.0).max(0.0);
|
||||||
|
let blur_y = (filter.blur_y.to_f32() - 1.0).max(0.0);
|
||||||
|
let current = &targets[i % 2];
|
||||||
|
let (previous_view, previous_vertices, previous_width, previous_height) = if i == 0 {
|
||||||
|
(
|
||||||
|
&source_view,
|
||||||
|
vertices.slice(..),
|
||||||
|
source_texture.width() as f32,
|
||||||
|
source_texture.height() as f32,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
let previous = &targets[(i - 1) % 2];
|
||||||
|
(
|
||||||
|
previous.color_view(),
|
||||||
|
descriptors.quad.filter_vertices.slice(..),
|
||||||
|
previous.width() as f32,
|
||||||
|
previous.height() as f32,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
let buffer = descriptors
|
||||||
|
.device
|
||||||
|
.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||||
|
label: create_debug_label!("Filter arguments").as_deref(),
|
||||||
|
contents: bytemuck::cast_slice(&[
|
||||||
|
blur_x * ((i as u32) % 2) as f32,
|
||||||
|
blur_y * (((i as u32) % 2) + 1) as f32,
|
||||||
|
previous_width,
|
||||||
|
previous_height,
|
||||||
|
]),
|
||||||
|
usage: wgpu::BufferUsages::UNIFORM,
|
||||||
|
});
|
||||||
|
let filter_group = descriptors
|
||||||
|
.device
|
||||||
|
.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||||
|
label: create_debug_label!("Filter group").as_deref(),
|
||||||
|
layout: &self.bind_group_layout,
|
||||||
|
entries: &[
|
||||||
|
wgpu::BindGroupEntry {
|
||||||
|
binding: 0,
|
||||||
|
resource: wgpu::BindingResource::TextureView(previous_view),
|
||||||
|
},
|
||||||
|
wgpu::BindGroupEntry {
|
||||||
|
binding: 1,
|
||||||
|
resource: wgpu::BindingResource::Sampler(
|
||||||
|
descriptors.bitmap_samplers.get_sampler(false, true),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
wgpu::BindGroupEntry {
|
||||||
|
binding: 2,
|
||||||
|
resource: buffer.as_entire_binding(),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
let mut render_pass = draw_encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
||||||
|
label: create_debug_label!("Blur filter").as_deref(),
|
||||||
|
color_attachments: &[current.color_attachments()],
|
||||||
|
depth_stencil_attachment: None,
|
||||||
|
});
|
||||||
|
render_pass.set_pipeline(pipeline);
|
||||||
|
|
||||||
|
render_pass.set_bind_group(0, &filter_group, &[]);
|
||||||
|
|
||||||
|
render_pass.set_vertex_buffer(0, previous_vertices);
|
||||||
|
render_pass.set_index_buffer(
|
||||||
|
descriptors.quad.indices.slice(..),
|
||||||
|
wgpu::IndexFormat::Uint32,
|
||||||
|
);
|
||||||
|
render_pass.draw_indexed(0..6, 0, 0..1);
|
||||||
|
}
|
||||||
|
|
||||||
|
targets
|
||||||
|
.into_iter()
|
||||||
|
.last()
|
||||||
|
.expect("Targets should not be empty")
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,186 @@
|
||||||
|
use crate::backend::RenderTargetMode;
|
||||||
|
use crate::buffer_pool::TexturePool;
|
||||||
|
use crate::descriptors::Descriptors;
|
||||||
|
use crate::filters::{create_filter_vertices, VERTEX_BUFFERS_DESCRIPTION_FILTERS};
|
||||||
|
use crate::surface::target::CommandTarget;
|
||||||
|
use crate::utils::SampleCountMap;
|
||||||
|
use std::sync::OnceLock;
|
||||||
|
use swf::ColorMatrixFilter as ColorMatrixFilterArgs;
|
||||||
|
use wgpu::util::DeviceExt;
|
||||||
|
|
||||||
|
pub struct ColorMatrixFilter {
|
||||||
|
bind_group_layout: wgpu::BindGroupLayout,
|
||||||
|
pipeline_layout: wgpu::PipelineLayout,
|
||||||
|
pipelines: SampleCountMap<OnceLock<wgpu::RenderPipeline>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ColorMatrixFilter {
|
||||||
|
pub fn new(device: &wgpu::Device) -> Self {
|
||||||
|
let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
||||||
|
entries: &[
|
||||||
|
wgpu::BindGroupLayoutEntry {
|
||||||
|
binding: 0,
|
||||||
|
visibility: wgpu::ShaderStages::FRAGMENT,
|
||||||
|
ty: wgpu::BindingType::Texture {
|
||||||
|
multisampled: false,
|
||||||
|
sample_type: wgpu::TextureSampleType::Float { filterable: true },
|
||||||
|
view_dimension: wgpu::TextureViewDimension::D2,
|
||||||
|
},
|
||||||
|
count: None,
|
||||||
|
},
|
||||||
|
wgpu::BindGroupLayoutEntry {
|
||||||
|
binding: 1,
|
||||||
|
visibility: wgpu::ShaderStages::FRAGMENT,
|
||||||
|
ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
|
||||||
|
count: None,
|
||||||
|
},
|
||||||
|
wgpu::BindGroupLayoutEntry {
|
||||||
|
binding: 2,
|
||||||
|
visibility: wgpu::ShaderStages::FRAGMENT,
|
||||||
|
ty: wgpu::BindingType::Buffer {
|
||||||
|
ty: wgpu::BufferBindingType::Uniform,
|
||||||
|
has_dynamic_offset: false,
|
||||||
|
min_binding_size: wgpu::BufferSize::new(
|
||||||
|
std::mem::size_of::<[f32; 20]>() as u64
|
||||||
|
),
|
||||||
|
},
|
||||||
|
count: None,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
label: create_debug_label!("Color matrix filter binds").as_deref(),
|
||||||
|
});
|
||||||
|
|
||||||
|
let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
||||||
|
label: None,
|
||||||
|
bind_group_layouts: &[&bind_group_layout],
|
||||||
|
push_constant_ranges: &[],
|
||||||
|
});
|
||||||
|
|
||||||
|
Self {
|
||||||
|
pipelines: Default::default(),
|
||||||
|
pipeline_layout,
|
||||||
|
bind_group_layout,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pipeline(&self, descriptors: &Descriptors, msaa_sample_count: u32) -> &wgpu::RenderPipeline {
|
||||||
|
self.pipelines.get_or_init(msaa_sample_count, || {
|
||||||
|
let label = create_debug_label!("Color Matrix Filter ({} msaa)", msaa_sample_count);
|
||||||
|
descriptors
|
||||||
|
.device
|
||||||
|
.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
|
||||||
|
label: label.as_deref(),
|
||||||
|
layout: Some(&self.pipeline_layout),
|
||||||
|
vertex: wgpu::VertexState {
|
||||||
|
module: &descriptors.shaders.color_matrix_filter,
|
||||||
|
entry_point: "main_vertex",
|
||||||
|
buffers: &VERTEX_BUFFERS_DESCRIPTION_FILTERS,
|
||||||
|
},
|
||||||
|
primitive: wgpu::PrimitiveState {
|
||||||
|
topology: wgpu::PrimitiveTopology::TriangleList,
|
||||||
|
strip_index_format: None,
|
||||||
|
front_face: wgpu::FrontFace::Ccw,
|
||||||
|
cull_mode: None,
|
||||||
|
polygon_mode: wgpu::PolygonMode::default(),
|
||||||
|
unclipped_depth: false,
|
||||||
|
conservative: false,
|
||||||
|
},
|
||||||
|
depth_stencil: None,
|
||||||
|
multisample: wgpu::MultisampleState {
|
||||||
|
count: msaa_sample_count,
|
||||||
|
mask: !0,
|
||||||
|
alpha_to_coverage_enabled: false,
|
||||||
|
},
|
||||||
|
fragment: Some(wgpu::FragmentState {
|
||||||
|
module: &descriptors.shaders.color_matrix_filter,
|
||||||
|
entry_point: "main_fragment",
|
||||||
|
targets: &[Some(wgpu::TextureFormat::Rgba8Unorm.into())],
|
||||||
|
}),
|
||||||
|
multiview: None,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
|
pub fn apply(
|
||||||
|
&self,
|
||||||
|
descriptors: &Descriptors,
|
||||||
|
texture_pool: &mut TexturePool,
|
||||||
|
draw_encoder: &mut wgpu::CommandEncoder,
|
||||||
|
source_texture: &wgpu::Texture,
|
||||||
|
source_point: (u32, u32),
|
||||||
|
source_size: (u32, u32),
|
||||||
|
filter: &ColorMatrixFilterArgs,
|
||||||
|
) -> CommandTarget {
|
||||||
|
let sample_count = source_texture.sample_count();
|
||||||
|
let format = source_texture.format();
|
||||||
|
let pipeline = self.pipeline(descriptors, sample_count);
|
||||||
|
|
||||||
|
let target = CommandTarget::new(
|
||||||
|
descriptors,
|
||||||
|
texture_pool,
|
||||||
|
wgpu::Extent3d {
|
||||||
|
width: source_size.0,
|
||||||
|
height: source_size.1,
|
||||||
|
depth_or_array_layers: 1,
|
||||||
|
},
|
||||||
|
format,
|
||||||
|
sample_count,
|
||||||
|
RenderTargetMode::FreshWithColor(wgpu::Color::TRANSPARENT),
|
||||||
|
draw_encoder,
|
||||||
|
);
|
||||||
|
let source_view = source_texture.create_view(&Default::default());
|
||||||
|
let buffer = descriptors
|
||||||
|
.device
|
||||||
|
.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||||
|
label: create_debug_label!("Filter arguments").as_deref(),
|
||||||
|
contents: bytemuck::cast_slice(&filter.matrix),
|
||||||
|
usage: wgpu::BufferUsages::UNIFORM,
|
||||||
|
});
|
||||||
|
let vertices = create_filter_vertices(
|
||||||
|
&descriptors.device,
|
||||||
|
source_texture,
|
||||||
|
source_point,
|
||||||
|
source_size,
|
||||||
|
);
|
||||||
|
let filter_group = descriptors
|
||||||
|
.device
|
||||||
|
.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||||
|
label: create_debug_label!("Filter group").as_deref(),
|
||||||
|
layout: &self.bind_group_layout,
|
||||||
|
entries: &[
|
||||||
|
wgpu::BindGroupEntry {
|
||||||
|
binding: 0,
|
||||||
|
resource: wgpu::BindingResource::TextureView(&source_view),
|
||||||
|
},
|
||||||
|
wgpu::BindGroupEntry {
|
||||||
|
binding: 1,
|
||||||
|
resource: wgpu::BindingResource::Sampler(
|
||||||
|
descriptors.bitmap_samplers.get_sampler(false, false),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
wgpu::BindGroupEntry {
|
||||||
|
binding: 2,
|
||||||
|
resource: buffer.as_entire_binding(),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
let mut render_pass = draw_encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
||||||
|
label: create_debug_label!("Color matrix filter").as_deref(),
|
||||||
|
color_attachments: &[target.color_attachments()],
|
||||||
|
depth_stencil_attachment: None,
|
||||||
|
});
|
||||||
|
render_pass.set_pipeline(pipeline);
|
||||||
|
|
||||||
|
render_pass.set_bind_group(0, &filter_group, &[]);
|
||||||
|
|
||||||
|
render_pass.set_vertex_buffer(0, vertices.slice(..));
|
||||||
|
render_pass.set_index_buffer(
|
||||||
|
descriptors.quad.indices.slice(..),
|
||||||
|
wgpu::IndexFormat::Uint32,
|
||||||
|
);
|
||||||
|
render_pass.draw_indexed(0..6, 0, 0..1);
|
||||||
|
drop(render_pass);
|
||||||
|
target
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,8 +9,6 @@ pub struct BindLayouts {
|
||||||
pub bitmap: wgpu::BindGroupLayout,
|
pub bitmap: wgpu::BindGroupLayout,
|
||||||
pub gradient: wgpu::BindGroupLayout,
|
pub gradient: wgpu::BindGroupLayout,
|
||||||
pub blend: wgpu::BindGroupLayout,
|
pub blend: wgpu::BindGroupLayout,
|
||||||
pub color_matrix_filter: wgpu::BindGroupLayout,
|
|
||||||
pub blur_filter: wgpu::BindGroupLayout,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BindLayouts {
|
impl BindLayouts {
|
||||||
|
@ -180,75 +178,6 @@ impl BindLayouts {
|
||||||
label: gradient_bind_layout_label.as_deref(),
|
label: gradient_bind_layout_label.as_deref(),
|
||||||
});
|
});
|
||||||
|
|
||||||
let color_matrix_filter =
|
|
||||||
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
|
||||||
entries: &[
|
|
||||||
wgpu::BindGroupLayoutEntry {
|
|
||||||
binding: 0,
|
|
||||||
visibility: wgpu::ShaderStages::FRAGMENT,
|
|
||||||
ty: wgpu::BindingType::Texture {
|
|
||||||
multisampled: false,
|
|
||||||
sample_type: wgpu::TextureSampleType::Float { filterable: true },
|
|
||||||
view_dimension: wgpu::TextureViewDimension::D2,
|
|
||||||
},
|
|
||||||
count: None,
|
|
||||||
},
|
|
||||||
wgpu::BindGroupLayoutEntry {
|
|
||||||
binding: 1,
|
|
||||||
visibility: wgpu::ShaderStages::FRAGMENT,
|
|
||||||
ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
|
|
||||||
count: None,
|
|
||||||
},
|
|
||||||
wgpu::BindGroupLayoutEntry {
|
|
||||||
binding: 2,
|
|
||||||
visibility: wgpu::ShaderStages::FRAGMENT,
|
|
||||||
ty: wgpu::BindingType::Buffer {
|
|
||||||
ty: wgpu::BufferBindingType::Uniform,
|
|
||||||
has_dynamic_offset: false,
|
|
||||||
min_binding_size: wgpu::BufferSize::new(
|
|
||||||
std::mem::size_of::<[f32; 20]>() as u64,
|
|
||||||
),
|
|
||||||
},
|
|
||||||
count: None,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
label: create_debug_label!("Color matrix filter binds").as_deref(),
|
|
||||||
});
|
|
||||||
|
|
||||||
let blur_filter = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
|
||||||
entries: &[
|
|
||||||
wgpu::BindGroupLayoutEntry {
|
|
||||||
binding: 0,
|
|
||||||
visibility: wgpu::ShaderStages::FRAGMENT,
|
|
||||||
ty: wgpu::BindingType::Texture {
|
|
||||||
multisampled: false,
|
|
||||||
sample_type: wgpu::TextureSampleType::Float { filterable: true },
|
|
||||||
view_dimension: wgpu::TextureViewDimension::D2,
|
|
||||||
},
|
|
||||||
count: None,
|
|
||||||
},
|
|
||||||
wgpu::BindGroupLayoutEntry {
|
|
||||||
binding: 1,
|
|
||||||
visibility: wgpu::ShaderStages::FRAGMENT,
|
|
||||||
ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
|
|
||||||
count: None,
|
|
||||||
},
|
|
||||||
wgpu::BindGroupLayoutEntry {
|
|
||||||
binding: 2,
|
|
||||||
visibility: wgpu::ShaderStages::FRAGMENT,
|
|
||||||
ty: wgpu::BindingType::Buffer {
|
|
||||||
ty: wgpu::BufferBindingType::Uniform,
|
|
||||||
has_dynamic_offset: false,
|
|
||||||
min_binding_size: wgpu::BufferSize::new(
|
|
||||||
std::mem::size_of::<[f32; 4]>() as u64
|
|
||||||
),
|
|
||||||
},
|
|
||||||
count: None,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
label: create_debug_label!("Blur filter binds").as_deref(),
|
|
||||||
});
|
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
globals,
|
globals,
|
||||||
transforms,
|
transforms,
|
||||||
|
@ -256,8 +185,6 @@ impl BindLayouts {
|
||||||
bitmap,
|
bitmap,
|
||||||
gradient,
|
gradient,
|
||||||
blend,
|
blend,
|
||||||
color_matrix_filter,
|
|
||||||
blur_filter,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,6 +42,7 @@ mod buffer_pool;
|
||||||
#[cfg(feature = "clap")]
|
#[cfg(feature = "clap")]
|
||||||
pub mod clap;
|
pub mod clap;
|
||||||
pub mod descriptors;
|
pub mod descriptors;
|
||||||
|
mod filters;
|
||||||
mod layouts;
|
mod layouts;
|
||||||
mod mesh;
|
mod mesh;
|
||||||
mod shaders;
|
mod shaders;
|
||||||
|
@ -136,13 +137,6 @@ impl From<TessVertex> for PosColorVertex {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
#[derive(Copy, Clone, Debug, Pod, Zeroable)]
|
|
||||||
struct FilterVertex {
|
|
||||||
position: [f32; 2],
|
|
||||||
uv: [f32; 2],
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Copy, Clone, Debug, Pod, Zeroable)]
|
#[derive(Copy, Clone, Debug, Pod, Zeroable)]
|
||||||
struct GradientUniforms {
|
struct GradientUniforms {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::blend::{ComplexBlend, TrivialBlend};
|
use crate::blend::{ComplexBlend, TrivialBlend};
|
||||||
use crate::layouts::BindLayouts;
|
use crate::layouts::BindLayouts;
|
||||||
use crate::shaders::Shaders;
|
use crate::shaders::Shaders;
|
||||||
use crate::{FilterVertex, MaskState, PosColorVertex, PosVertex, PushConstants, Transforms};
|
use crate::{MaskState, PosColorVertex, PosVertex, PushConstants, Transforms};
|
||||||
use enum_map::{enum_map, Enum, EnumMap};
|
use enum_map::{enum_map, Enum, EnumMap};
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use wgpu::{vertex_attr_array, BlendState};
|
use wgpu::{vertex_attr_array, BlendState};
|
||||||
|
@ -15,16 +15,6 @@ pub const VERTEX_BUFFERS_DESCRIPTION_POS: [wgpu::VertexBufferLayout; 1] =
|
||||||
],
|
],
|
||||||
}];
|
}];
|
||||||
|
|
||||||
pub const VERTEX_BUFFERS_DESCRIPTION_FILTERS: [wgpu::VertexBufferLayout; 1] =
|
|
||||||
[wgpu::VertexBufferLayout {
|
|
||||||
array_stride: std::mem::size_of::<FilterVertex>() as u64,
|
|
||||||
step_mode: wgpu::VertexStepMode::Vertex,
|
|
||||||
attributes: &vertex_attr_array![
|
|
||||||
0 => Float32x2,
|
|
||||||
1 => Float32x2,
|
|
||||||
],
|
|
||||||
}];
|
|
||||||
|
|
||||||
pub const VERTEX_BUFFERS_DESCRIPTION_COLOR: [wgpu::VertexBufferLayout; 1] =
|
pub const VERTEX_BUFFERS_DESCRIPTION_COLOR: [wgpu::VertexBufferLayout; 1] =
|
||||||
[wgpu::VertexBufferLayout {
|
[wgpu::VertexBufferLayout {
|
||||||
array_stride: std::mem::size_of::<PosColorVertex>() as u64,
|
array_stride: std::mem::size_of::<PosColorVertex>() as u64,
|
||||||
|
@ -56,8 +46,6 @@ pub struct Pipelines {
|
||||||
pub bitmap: EnumMap<TrivialBlend, ShapePipeline>,
|
pub bitmap: EnumMap<TrivialBlend, ShapePipeline>,
|
||||||
pub gradients: ShapePipeline,
|
pub gradients: ShapePipeline,
|
||||||
pub complex_blends: EnumMap<ComplexBlend, ShapePipeline>,
|
pub complex_blends: EnumMap<ComplexBlend, ShapePipeline>,
|
||||||
pub color_matrix_filter: wgpu::RenderPipeline,
|
|
||||||
pub blur_filter: wgpu::RenderPipeline,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ShapePipeline {
|
impl ShapePipeline {
|
||||||
|
@ -267,81 +255,6 @@ impl Pipelines {
|
||||||
msaa_sample_count,
|
msaa_sample_count,
|
||||||
));
|
));
|
||||||
|
|
||||||
let color_matrix_filter_layout =
|
|
||||||
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
|
||||||
label: None,
|
|
||||||
bind_group_layouts: &[&bind_layouts.color_matrix_filter],
|
|
||||||
push_constant_ranges: &[],
|
|
||||||
});
|
|
||||||
|
|
||||||
let color_matrix_filter = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
|
|
||||||
label: create_debug_label!("Color Matrix Filter").as_deref(),
|
|
||||||
layout: Some(&color_matrix_filter_layout),
|
|
||||||
vertex: wgpu::VertexState {
|
|
||||||
module: &shaders.color_matrix_filter,
|
|
||||||
entry_point: "main_vertex",
|
|
||||||
buffers: &VERTEX_BUFFERS_DESCRIPTION_FILTERS,
|
|
||||||
},
|
|
||||||
primitive: wgpu::PrimitiveState {
|
|
||||||
topology: wgpu::PrimitiveTopology::TriangleList,
|
|
||||||
strip_index_format: None,
|
|
||||||
front_face: wgpu::FrontFace::Ccw,
|
|
||||||
cull_mode: None,
|
|
||||||
polygon_mode: wgpu::PolygonMode::default(),
|
|
||||||
unclipped_depth: false,
|
|
||||||
conservative: false,
|
|
||||||
},
|
|
||||||
depth_stencil: None,
|
|
||||||
multisample: wgpu::MultisampleState {
|
|
||||||
count: msaa_sample_count,
|
|
||||||
mask: !0,
|
|
||||||
alpha_to_coverage_enabled: false,
|
|
||||||
},
|
|
||||||
fragment: Some(wgpu::FragmentState {
|
|
||||||
module: &shaders.color_matrix_filter,
|
|
||||||
entry_point: "main_fragment",
|
|
||||||
targets: &[Some(format.into())],
|
|
||||||
}),
|
|
||||||
multiview: None,
|
|
||||||
});
|
|
||||||
|
|
||||||
let blur_filter_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
|
||||||
label: None,
|
|
||||||
bind_group_layouts: &[&bind_layouts.blur_filter],
|
|
||||||
push_constant_ranges: &[],
|
|
||||||
});
|
|
||||||
|
|
||||||
let blur_filter = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
|
|
||||||
label: create_debug_label!("Blur Filter").as_deref(),
|
|
||||||
layout: Some(&blur_filter_layout),
|
|
||||||
vertex: wgpu::VertexState {
|
|
||||||
module: &shaders.blur_filter,
|
|
||||||
entry_point: "main_vertex",
|
|
||||||
buffers: &VERTEX_BUFFERS_DESCRIPTION_FILTERS,
|
|
||||||
},
|
|
||||||
primitive: wgpu::PrimitiveState {
|
|
||||||
topology: wgpu::PrimitiveTopology::TriangleList,
|
|
||||||
strip_index_format: None,
|
|
||||||
front_face: wgpu::FrontFace::Ccw,
|
|
||||||
cull_mode: None,
|
|
||||||
polygon_mode: wgpu::PolygonMode::default(),
|
|
||||||
unclipped_depth: false,
|
|
||||||
conservative: false,
|
|
||||||
},
|
|
||||||
depth_stencil: None,
|
|
||||||
multisample: wgpu::MultisampleState {
|
|
||||||
count: msaa_sample_count,
|
|
||||||
mask: !0,
|
|
||||||
alpha_to_coverage_enabled: false,
|
|
||||||
},
|
|
||||||
fragment: Some(wgpu::FragmentState {
|
|
||||||
module: &shaders.blur_filter,
|
|
||||||
entry_point: "main_fragment",
|
|
||||||
targets: &[Some(format.into())],
|
|
||||||
}),
|
|
||||||
multiview: None,
|
|
||||||
});
|
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
color: color_pipelines,
|
color: color_pipelines,
|
||||||
bitmap: EnumMap::from_array(bitmap_pipelines),
|
bitmap: EnumMap::from_array(bitmap_pipelines),
|
||||||
|
@ -349,8 +262,6 @@ impl Pipelines {
|
||||||
bitmap_opaque_dummy_depth,
|
bitmap_opaque_dummy_depth,
|
||||||
gradients: gradient_pipeline,
|
gradients: gradient_pipeline,
|
||||||
complex_blends: complex_blend_pipelines,
|
complex_blends: complex_blend_pipelines,
|
||||||
color_matrix_filter,
|
|
||||||
blur_filter,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,17 +7,13 @@ use crate::buffer_pool::TexturePool;
|
||||||
use crate::mesh::Mesh;
|
use crate::mesh::Mesh;
|
||||||
use crate::surface::commands::{chunk_blends, Chunk, CommandRenderer};
|
use crate::surface::commands::{chunk_blends, Chunk, CommandRenderer};
|
||||||
use crate::utils::{remove_srgb, supported_sample_count};
|
use crate::utils::{remove_srgb, supported_sample_count};
|
||||||
use crate::{
|
use crate::{ColorAdjustments, Descriptors, MaskState, Pipelines, Transforms, UniformBuffer};
|
||||||
ColorAdjustments, Descriptors, FilterVertex, MaskState, Pipelines, Transforms, UniformBuffer,
|
|
||||||
};
|
|
||||||
use ruffle_render::commands::CommandList;
|
use ruffle_render::commands::CommandList;
|
||||||
use ruffle_render::filters::Filter;
|
use ruffle_render::filters::Filter;
|
||||||
use ruffle_render::quality::StageQuality;
|
use ruffle_render::quality::StageQuality;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use swf::{BlurFilter, ColorMatrixFilter};
|
|
||||||
use target::CommandTarget;
|
use target::CommandTarget;
|
||||||
use tracing::instrument;
|
use tracing::instrument;
|
||||||
use wgpu::util::DeviceExt;
|
|
||||||
|
|
||||||
use crate::utils::run_copy_pipeline;
|
use crate::utils::run_copy_pipeline;
|
||||||
|
|
||||||
|
@ -351,7 +347,7 @@ impl Surface {
|
||||||
filter: Filter,
|
filter: Filter,
|
||||||
) -> CommandTarget {
|
) -> CommandTarget {
|
||||||
let target = match filter {
|
let target = match filter {
|
||||||
Filter::ColorMatrixFilter(filter) => self.apply_color_matrix(
|
Filter::ColorMatrixFilter(filter) => descriptors.filters.color_matrix.apply(
|
||||||
descriptors,
|
descriptors,
|
||||||
texture_pool,
|
texture_pool,
|
||||||
draw_encoder,
|
draw_encoder,
|
||||||
|
@ -360,7 +356,7 @@ impl Surface {
|
||||||
source_size,
|
source_size,
|
||||||
&filter,
|
&filter,
|
||||||
),
|
),
|
||||||
Filter::BlurFilter(filter) => self.apply_blur(
|
Filter::BlurFilter(filter) => descriptors.filters.blur.apply(
|
||||||
descriptors,
|
descriptors,
|
||||||
texture_pool,
|
texture_pool,
|
||||||
draw_encoder,
|
draw_encoder,
|
||||||
|
@ -372,7 +368,7 @@ impl Surface {
|
||||||
_ => {
|
_ => {
|
||||||
tracing::warn!("Unsupported filter {filter:?}");
|
tracing::warn!("Unsupported filter {filter:?}");
|
||||||
// Apply a default color matrix - it's essentially a blit
|
// Apply a default color matrix - it's essentially a blit
|
||||||
self.apply_color_matrix(
|
descriptors.filters.color_matrix.apply(
|
||||||
descriptors,
|
descriptors,
|
||||||
texture_pool,
|
texture_pool,
|
||||||
draw_encoder,
|
draw_encoder,
|
||||||
|
@ -390,246 +386,4 @@ impl Surface {
|
||||||
target.ensure_cleared(draw_encoder);
|
target.ensure_cleared(draw_encoder);
|
||||||
target
|
target
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
|
||||||
pub fn apply_color_matrix(
|
|
||||||
&self,
|
|
||||||
descriptors: &Descriptors,
|
|
||||||
texture_pool: &mut TexturePool,
|
|
||||||
draw_encoder: &mut wgpu::CommandEncoder,
|
|
||||||
source_texture: &wgpu::Texture,
|
|
||||||
source_point: (u32, u32),
|
|
||||||
source_size: (u32, u32),
|
|
||||||
filter: &ColorMatrixFilter,
|
|
||||||
) -> CommandTarget {
|
|
||||||
let target = CommandTarget::new(
|
|
||||||
descriptors,
|
|
||||||
texture_pool,
|
|
||||||
wgpu::Extent3d {
|
|
||||||
width: source_size.0,
|
|
||||||
height: source_size.1,
|
|
||||||
depth_or_array_layers: 1,
|
|
||||||
},
|
|
||||||
self.format,
|
|
||||||
self.sample_count,
|
|
||||||
RenderTargetMode::FreshWithColor(wgpu::Color::TRANSPARENT),
|
|
||||||
draw_encoder,
|
|
||||||
);
|
|
||||||
let source_view = source_texture.create_view(&Default::default());
|
|
||||||
let buffer = descriptors
|
|
||||||
.device
|
|
||||||
.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
|
||||||
label: create_debug_label!("Filter arguments").as_deref(),
|
|
||||||
contents: bytemuck::cast_slice(&filter.matrix),
|
|
||||||
usage: wgpu::BufferUsages::UNIFORM,
|
|
||||||
});
|
|
||||||
let vertices = create_filter_vertices(
|
|
||||||
&descriptors.device,
|
|
||||||
source_texture,
|
|
||||||
source_point,
|
|
||||||
source_size,
|
|
||||||
);
|
|
||||||
let filter_group = descriptors
|
|
||||||
.device
|
|
||||||
.create_bind_group(&wgpu::BindGroupDescriptor {
|
|
||||||
label: create_debug_label!("Filter group").as_deref(),
|
|
||||||
layout: &descriptors.bind_layouts.color_matrix_filter,
|
|
||||||
entries: &[
|
|
||||||
wgpu::BindGroupEntry {
|
|
||||||
binding: 0,
|
|
||||||
resource: wgpu::BindingResource::TextureView(&source_view),
|
|
||||||
},
|
|
||||||
wgpu::BindGroupEntry {
|
|
||||||
binding: 1,
|
|
||||||
resource: wgpu::BindingResource::Sampler(
|
|
||||||
descriptors.bitmap_samplers.get_sampler(false, false),
|
|
||||||
),
|
|
||||||
},
|
|
||||||
wgpu::BindGroupEntry {
|
|
||||||
binding: 2,
|
|
||||||
resource: buffer.as_entire_binding(),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
let mut render_pass = draw_encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
|
||||||
label: create_debug_label!("Color matrix filter").as_deref(),
|
|
||||||
color_attachments: &[target.color_attachments()],
|
|
||||||
depth_stencil_attachment: None,
|
|
||||||
});
|
|
||||||
render_pass.set_pipeline(&self.pipelines.color_matrix_filter);
|
|
||||||
|
|
||||||
render_pass.set_bind_group(0, &filter_group, &[]);
|
|
||||||
|
|
||||||
render_pass.set_vertex_buffer(0, vertices.slice(..));
|
|
||||||
render_pass.set_index_buffer(
|
|
||||||
descriptors.quad.indices.slice(..),
|
|
||||||
wgpu::IndexFormat::Uint32,
|
|
||||||
);
|
|
||||||
render_pass.draw_indexed(0..6, 0, 0..1);
|
|
||||||
drop(render_pass);
|
|
||||||
target
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
|
||||||
pub fn apply_blur(
|
|
||||||
&self,
|
|
||||||
descriptors: &Descriptors,
|
|
||||||
texture_pool: &mut TexturePool,
|
|
||||||
draw_encoder: &mut wgpu::CommandEncoder,
|
|
||||||
source_texture: &wgpu::Texture,
|
|
||||||
source_point: (u32, u32),
|
|
||||||
source_size: (u32, u32),
|
|
||||||
filter: &BlurFilter,
|
|
||||||
) -> CommandTarget {
|
|
||||||
// FIXME - this should be larger than the source texture. Figure out exactly how much larger
|
|
||||||
let targets = [
|
|
||||||
CommandTarget::new(
|
|
||||||
descriptors,
|
|
||||||
texture_pool,
|
|
||||||
wgpu::Extent3d {
|
|
||||||
width: source_size.0,
|
|
||||||
height: source_size.1,
|
|
||||||
depth_or_array_layers: 1,
|
|
||||||
},
|
|
||||||
self.format,
|
|
||||||
self.sample_count,
|
|
||||||
RenderTargetMode::FreshWithColor(wgpu::Color::TRANSPARENT),
|
|
||||||
draw_encoder,
|
|
||||||
),
|
|
||||||
CommandTarget::new(
|
|
||||||
descriptors,
|
|
||||||
texture_pool,
|
|
||||||
wgpu::Extent3d {
|
|
||||||
width: source_size.0,
|
|
||||||
height: source_size.1,
|
|
||||||
depth_or_array_layers: 1,
|
|
||||||
},
|
|
||||||
self.format,
|
|
||||||
self.sample_count,
|
|
||||||
RenderTargetMode::FreshWithColor(wgpu::Color::TRANSPARENT),
|
|
||||||
draw_encoder,
|
|
||||||
),
|
|
||||||
];
|
|
||||||
|
|
||||||
// TODO: Vertices should be per pass, and each pass needs diff sizes
|
|
||||||
let vertices = create_filter_vertices(
|
|
||||||
&descriptors.device,
|
|
||||||
source_texture,
|
|
||||||
source_point,
|
|
||||||
source_size,
|
|
||||||
);
|
|
||||||
|
|
||||||
let source_view = source_texture.create_view(&Default::default());
|
|
||||||
for i in 0..2 {
|
|
||||||
let blur_x = (filter.blur_x.to_f32() - 1.0).max(0.0);
|
|
||||||
let blur_y = (filter.blur_y.to_f32() - 1.0).max(0.0);
|
|
||||||
let current = &targets[i % 2];
|
|
||||||
let (previous_view, previous_vertices, previous_width, previous_height) = if i == 0 {
|
|
||||||
(
|
|
||||||
&source_view,
|
|
||||||
vertices.slice(..),
|
|
||||||
source_texture.width() as f32,
|
|
||||||
source_texture.height() as f32,
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
let previous = &targets[(i - 1) % 2];
|
|
||||||
(
|
|
||||||
previous.color_view(),
|
|
||||||
descriptors.quad.filter_vertices.slice(..),
|
|
||||||
previous.width() as f32,
|
|
||||||
previous.height() as f32,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
let buffer = descriptors
|
|
||||||
.device
|
|
||||||
.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
|
||||||
label: create_debug_label!("Filter arguments").as_deref(),
|
|
||||||
contents: bytemuck::cast_slice(&[
|
|
||||||
blur_x * ((i as u32) % 2) as f32,
|
|
||||||
blur_y * (((i as u32) % 2) + 1) as f32,
|
|
||||||
previous_width,
|
|
||||||
previous_height,
|
|
||||||
]),
|
|
||||||
usage: wgpu::BufferUsages::UNIFORM,
|
|
||||||
});
|
|
||||||
let filter_group = descriptors
|
|
||||||
.device
|
|
||||||
.create_bind_group(&wgpu::BindGroupDescriptor {
|
|
||||||
label: create_debug_label!("Filter group").as_deref(),
|
|
||||||
layout: &descriptors.bind_layouts.blur_filter,
|
|
||||||
entries: &[
|
|
||||||
wgpu::BindGroupEntry {
|
|
||||||
binding: 0,
|
|
||||||
resource: wgpu::BindingResource::TextureView(previous_view),
|
|
||||||
},
|
|
||||||
wgpu::BindGroupEntry {
|
|
||||||
binding: 1,
|
|
||||||
resource: wgpu::BindingResource::Sampler(
|
|
||||||
descriptors.bitmap_samplers.get_sampler(false, true),
|
|
||||||
),
|
|
||||||
},
|
|
||||||
wgpu::BindGroupEntry {
|
|
||||||
binding: 2,
|
|
||||||
resource: buffer.as_entire_binding(),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
let mut render_pass = draw_encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
|
||||||
label: create_debug_label!("Blur filter").as_deref(),
|
|
||||||
color_attachments: &[current.color_attachments()],
|
|
||||||
depth_stencil_attachment: None,
|
|
||||||
});
|
|
||||||
render_pass.set_pipeline(&self.pipelines.blur_filter);
|
|
||||||
|
|
||||||
render_pass.set_bind_group(0, &filter_group, &[]);
|
|
||||||
|
|
||||||
render_pass.set_vertex_buffer(0, previous_vertices);
|
|
||||||
render_pass.set_index_buffer(
|
|
||||||
descriptors.quad.indices.slice(..),
|
|
||||||
wgpu::IndexFormat::Uint32,
|
|
||||||
);
|
|
||||||
render_pass.draw_indexed(0..6, 0, 0..1);
|
|
||||||
}
|
|
||||||
|
|
||||||
targets
|
|
||||||
.into_iter()
|
|
||||||
.last()
|
|
||||||
.expect("Targets should not be empty")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn create_filter_vertices(
|
|
||||||
device: &wgpu::Device,
|
|
||||||
source_texture: &wgpu::Texture,
|
|
||||||
source_point: (u32, u32),
|
|
||||||
source_size: (u32, u32),
|
|
||||||
) -> wgpu::Buffer {
|
|
||||||
let source_width = source_texture.width() as f32;
|
|
||||||
let source_height = source_texture.height() as f32;
|
|
||||||
let left = source_point.0;
|
|
||||||
let top = source_point.1;
|
|
||||||
let right = left + source_size.0;
|
|
||||||
let bottom = top + source_size.1;
|
|
||||||
device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
|
||||||
label: create_debug_label!("Filter vertices").as_deref(),
|
|
||||||
contents: bytemuck::cast_slice(&[
|
|
||||||
FilterVertex {
|
|
||||||
position: [0.0, 0.0],
|
|
||||||
uv: [left as f32 / source_width, top as f32 / source_height],
|
|
||||||
},
|
|
||||||
FilterVertex {
|
|
||||||
position: [1.0, 0.0],
|
|
||||||
uv: [right as f32 / source_width, top as f32 / source_height],
|
|
||||||
},
|
|
||||||
FilterVertex {
|
|
||||||
position: [1.0, 1.0],
|
|
||||||
uv: [right as f32 / source_width, bottom as f32 / source_height],
|
|
||||||
},
|
|
||||||
FilterVertex {
|
|
||||||
position: [0.0, 1.0],
|
|
||||||
uv: [left as f32 / source_width, bottom as f32 / source_height],
|
|
||||||
},
|
|
||||||
]),
|
|
||||||
usage: wgpu::BufferUsages::VERTEX,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -288,3 +288,52 @@ pub fn run_copy_pipeline(
|
||||||
render_pass.draw_indexed(0..6, 0, 0..1);
|
render_pass.draw_indexed(0..6, 0, 0..1);
|
||||||
drop(render_pass);
|
drop(render_pass);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct SampleCountMap<T> {
|
||||||
|
one: T,
|
||||||
|
two: T,
|
||||||
|
four: T,
|
||||||
|
eight: T,
|
||||||
|
sixteen: T,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Default> Default for SampleCountMap<T> {
|
||||||
|
fn default() -> Self {
|
||||||
|
SampleCountMap {
|
||||||
|
one: Default::default(),
|
||||||
|
two: Default::default(),
|
||||||
|
four: Default::default(),
|
||||||
|
eight: Default::default(),
|
||||||
|
sixteen: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> SampleCountMap<T> {
|
||||||
|
pub fn get(&self, sample_count: u32) -> &T {
|
||||||
|
match sample_count {
|
||||||
|
1 => &self.one,
|
||||||
|
2 => &self.two,
|
||||||
|
4 => &self.four,
|
||||||
|
8 => &self.eight,
|
||||||
|
16 => &self.sixteen,
|
||||||
|
_ => unreachable!("Sample counts must be powers of two between 1..=16"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> SampleCountMap<std::sync::OnceLock<T>> {
|
||||||
|
pub fn get_or_init<F>(&self, sample_count: u32, init: F) -> &T
|
||||||
|
where
|
||||||
|
F: FnOnce() -> T,
|
||||||
|
{
|
||||||
|
match sample_count {
|
||||||
|
1 => self.one.get_or_init(init),
|
||||||
|
2 => self.two.get_or_init(init),
|
||||||
|
4 => self.four.get_or_init(init),
|
||||||
|
8 => self.eight.get_or_init(init),
|
||||||
|
16 => self.sixteen.get_or_init(init),
|
||||||
|
_ => unreachable!("Sample counts must be powers of two between 1..=16"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue