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::pipelines::VERTEX_BUFFERS_DESCRIPTION_POS;
|
||||
use crate::shaders::Shaders;
|
||||
use crate::{
|
||||
create_buffer_with_data, BitmapSamplers, FilterVertex, Pipelines, PosColorVertex, PosVertex,
|
||||
create_buffer_with_data, BitmapSamplers, Pipelines, PosColorVertex, PosVertex,
|
||||
TextureTransforms, Transforms, DEFAULT_COLOR_ADJUSTMENTS,
|
||||
};
|
||||
use fnv::FnvHashMap;
|
||||
|
@ -20,9 +21,10 @@ pub struct Descriptors {
|
|||
pub quad: Quad,
|
||||
copy_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>>>,
|
||||
pub default_color_bind_group: wgpu::BindGroup,
|
||||
pub filters: Filters,
|
||||
}
|
||||
|
||||
impl Debug for Descriptors {
|
||||
|
@ -52,6 +54,7 @@ impl Descriptors {
|
|||
resource: default_color_transform.as_entire_binding(),
|
||||
}],
|
||||
});
|
||||
let filters = Filters::new(&device);
|
||||
|
||||
Self {
|
||||
adapter,
|
||||
|
@ -66,6 +69,7 @@ impl Descriptors {
|
|||
shaders,
|
||||
pipelines: Default::default(),
|
||||
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 gradient: wgpu::BindGroupLayout,
|
||||
pub blend: wgpu::BindGroupLayout,
|
||||
pub color_matrix_filter: wgpu::BindGroupLayout,
|
||||
pub blur_filter: wgpu::BindGroupLayout,
|
||||
}
|
||||
|
||||
impl BindLayouts {
|
||||
|
@ -180,75 +178,6 @@ impl BindLayouts {
|
|||
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 {
|
||||
globals,
|
||||
transforms,
|
||||
|
@ -256,8 +185,6 @@ impl BindLayouts {
|
|||
bitmap,
|
||||
gradient,
|
||||
blend,
|
||||
color_matrix_filter,
|
||||
blur_filter,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,6 +42,7 @@ mod buffer_pool;
|
|||
#[cfg(feature = "clap")]
|
||||
pub mod clap;
|
||||
pub mod descriptors;
|
||||
mod filters;
|
||||
mod layouts;
|
||||
mod mesh;
|
||||
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)]
|
||||
#[derive(Copy, Clone, Debug, Pod, Zeroable)]
|
||||
struct GradientUniforms {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use crate::blend::{ComplexBlend, TrivialBlend};
|
||||
use crate::layouts::BindLayouts;
|
||||
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 std::mem;
|
||||
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] =
|
||||
[wgpu::VertexBufferLayout {
|
||||
array_stride: std::mem::size_of::<PosColorVertex>() as u64,
|
||||
|
@ -56,8 +46,6 @@ pub struct Pipelines {
|
|||
pub bitmap: EnumMap<TrivialBlend, ShapePipeline>,
|
||||
pub gradients: ShapePipeline,
|
||||
pub complex_blends: EnumMap<ComplexBlend, ShapePipeline>,
|
||||
pub color_matrix_filter: wgpu::RenderPipeline,
|
||||
pub blur_filter: wgpu::RenderPipeline,
|
||||
}
|
||||
|
||||
impl ShapePipeline {
|
||||
|
@ -267,81 +255,6 @@ impl Pipelines {
|
|||
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 {
|
||||
color: color_pipelines,
|
||||
bitmap: EnumMap::from_array(bitmap_pipelines),
|
||||
|
@ -349,8 +262,6 @@ impl Pipelines {
|
|||
bitmap_opaque_dummy_depth,
|
||||
gradients: gradient_pipeline,
|
||||
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::surface::commands::{chunk_blends, Chunk, CommandRenderer};
|
||||
use crate::utils::{remove_srgb, supported_sample_count};
|
||||
use crate::{
|
||||
ColorAdjustments, Descriptors, FilterVertex, MaskState, Pipelines, Transforms, UniformBuffer,
|
||||
};
|
||||
use crate::{ColorAdjustments, Descriptors, MaskState, Pipelines, Transforms, UniformBuffer};
|
||||
use ruffle_render::commands::CommandList;
|
||||
use ruffle_render::filters::Filter;
|
||||
use ruffle_render::quality::StageQuality;
|
||||
use std::sync::Arc;
|
||||
use swf::{BlurFilter, ColorMatrixFilter};
|
||||
use target::CommandTarget;
|
||||
use tracing::instrument;
|
||||
use wgpu::util::DeviceExt;
|
||||
|
||||
use crate::utils::run_copy_pipeline;
|
||||
|
||||
|
@ -351,7 +347,7 @@ impl Surface {
|
|||
filter: Filter,
|
||||
) -> CommandTarget {
|
||||
let target = match filter {
|
||||
Filter::ColorMatrixFilter(filter) => self.apply_color_matrix(
|
||||
Filter::ColorMatrixFilter(filter) => descriptors.filters.color_matrix.apply(
|
||||
descriptors,
|
||||
texture_pool,
|
||||
draw_encoder,
|
||||
|
@ -360,7 +356,7 @@ impl Surface {
|
|||
source_size,
|
||||
&filter,
|
||||
),
|
||||
Filter::BlurFilter(filter) => self.apply_blur(
|
||||
Filter::BlurFilter(filter) => descriptors.filters.blur.apply(
|
||||
descriptors,
|
||||
texture_pool,
|
||||
draw_encoder,
|
||||
|
@ -372,7 +368,7 @@ impl Surface {
|
|||
_ => {
|
||||
tracing::warn!("Unsupported filter {filter:?}");
|
||||
// Apply a default color matrix - it's essentially a blit
|
||||
self.apply_color_matrix(
|
||||
descriptors.filters.color_matrix.apply(
|
||||
descriptors,
|
||||
texture_pool,
|
||||
draw_encoder,
|
||||
|
@ -390,246 +386,4 @@ impl Surface {
|
|||
target.ensure_cleared(draw_encoder);
|
||||
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);
|
||||
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