wgpu: Implement all blend modes
This commit is contained in:
parent
f9333e2626
commit
7904c3d4f0
|
@ -0,0 +1,89 @@
|
|||
/// Shader used for drawing a pending framebuffer onto a parent framebuffer
|
||||
|
||||
struct VertexOutput {
|
||||
@builtin(position) position: vec4<f32>,
|
||||
@location(0) uv: vec2<f32>,
|
||||
};
|
||||
|
||||
struct BlendOptions {
|
||||
mode: i32,
|
||||
_padding1: f32,
|
||||
_padding2: f32,
|
||||
_padding3: f32,
|
||||
}
|
||||
|
||||
@group(2) @binding(0) var parent_texture: texture_2d<f32>;
|
||||
@group(2) @binding(1) var current_texture: texture_2d<f32>;
|
||||
@group(2) @binding(2) var texture_sampler: sampler;
|
||||
@group(2) @binding(3) var<uniform> blend: BlendOptions;
|
||||
|
||||
@vertex
|
||||
fn main_vertex(in: VertexInput) -> VertexOutput {
|
||||
let pos = globals.view_matrix * transforms.world_matrix * vec4<f32>(in.position.x, in.position.y, 1.0, 1.0);
|
||||
let uv = vec2<f32>((pos.x + 1.0) / 2.0, -((pos.y - 1.0) / 2.0));
|
||||
return VertexOutput(pos, uv);
|
||||
}
|
||||
|
||||
fn blend_func(src: vec3<f32>, dst: vec3<f32>) -> vec3<f32> {
|
||||
switch (blend.mode) {
|
||||
default: {
|
||||
return src;
|
||||
}
|
||||
case 1: { // Multiply
|
||||
return src * dst;
|
||||
}
|
||||
case 2: { // Screen
|
||||
return (dst + src) - (dst * src);
|
||||
}
|
||||
case 3: { // Lighten
|
||||
return max(dst, src);
|
||||
}
|
||||
case 4: { // Darken
|
||||
return min(dst, src);
|
||||
}
|
||||
case 5: { // Difference
|
||||
return abs(dst - src);
|
||||
}
|
||||
case 8: { // Invert
|
||||
return 1.0 - dst;
|
||||
}
|
||||
case 11: { // Overlay
|
||||
var out = src;
|
||||
if (dst.r <= 0.5) { out.r = (2.0 * src.r * dst.r); } else { out.r = (1.0 - 2.0 * (1.0 - dst.r) * (1.0 - src.r)); }
|
||||
if (dst.g <= 0.5) { out.g = (2.0 * src.g * dst.g); } else { out.g = (1.0 - 2.0 * (1.0 - dst.g) * (1.0 - src.g)); }
|
||||
if (dst.b <= 0.5) { out.b = (2.0 * src.b * dst.b); } else { out.b = (1.0 - 2.0 * (1.0 - dst.b) * (1.0 - src.b)); }
|
||||
return out;
|
||||
}
|
||||
case 12: { // Hardlight
|
||||
var out = src;
|
||||
if (src.r <= 0.5) { out.r = (2.0 * src.r * dst.r); } else { out.r = (1.0 - 2.0 * (1.0 - dst.r) * (1.0 - src.r)); }
|
||||
if (src.g <= 0.5) { out.g = (2.0 * src.g * dst.g); } else { out.g = (1.0 - 2.0 * (1.0 - dst.g) * (1.0 - src.g)); }
|
||||
if (src.b <= 0.5) { out.b = (2.0 * src.b * dst.b); } else { out.b = (1.0 - 2.0 * (1.0 - dst.b) * (1.0 - src.b)); }
|
||||
return out;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@fragment
|
||||
fn main_fragment(in: VertexOutput) -> @location(0) vec4<f32> {
|
||||
// dst is the parent pixel we're blending onto - it is either 0 or 1 alpha.
|
||||
var dst: vec4<f32> = textureSample(parent_texture, texture_sampler, in.uv);
|
||||
// src is the pixel that we want to apply - it may have any alpha.
|
||||
var src: vec4<f32> = textureSample(current_texture, texture_sampler, in.uv);
|
||||
|
||||
if (src.a > 0.0) {
|
||||
if (blend.mode == 6) { // Add
|
||||
return vec4<f32>(src.rgb + dst.rgb, 1.0);
|
||||
} else if (blend.mode == 7) { // Subtract
|
||||
return vec4<f32>(dst.rgb - src.rgb, 1.0);
|
||||
} else if (blend.mode == 9) { // Alpha
|
||||
return vec4<f32>(dst.rgb * src.a, src.a * dst.a);
|
||||
} else if (blend.mode == 10) { // Erase
|
||||
return vec4<f32>(dst.rgb * (1.0 - src.a), (1.0 - src.a) * dst.a);
|
||||
} else {
|
||||
return vec4<f32>(src.rgb * (1.0 - dst.a) + dst.rgb * (1.0 - src.a) + src.a * dst.a * blend_func(src.rgb / src.a, dst.rgb/ dst.a), src.a + dst.a * (1.0 - src.a));
|
||||
}
|
||||
} else {
|
||||
return dst;
|
||||
}
|
||||
}
|
|
@ -1,8 +1,8 @@
|
|||
use crate::mesh::{DrawType, Mesh};
|
||||
use crate::surface::{BlendBuffer, DepthBuffer, FrameBuffer, ResolveBuffer, TextureBuffers};
|
||||
use crate::{
|
||||
as_texture, ColorAdjustments, Descriptors, Globals, MaskState, Pipelines, TextureTransforms,
|
||||
Transforms, UniformBuffer,
|
||||
as_texture, ColorAdjustments, Descriptors, Globals, MaskState, Pipelines, Transforms,
|
||||
UniformBuffer,
|
||||
};
|
||||
use ruffle_render::backend::ShapeHandle;
|
||||
use ruffle_render::bitmap::BitmapHandle;
|
||||
|
@ -226,6 +226,11 @@ impl<'pass, 'frame: 'pass, 'global: 'frame> CommandRenderer<'pass, 'frame, 'glob
|
|||
mask_state = renderer.mask_state;
|
||||
}
|
||||
Chunk::Blend(commands, blend_mode) => {
|
||||
let parent = match blend_mode {
|
||||
BlendMode::Alpha | BlendMode::Erase => nearest_layer,
|
||||
_ => target,
|
||||
};
|
||||
|
||||
target.update_blend_buffer(&mut draw_encoder);
|
||||
|
||||
let frame_buffer = texture_buffers.take_frame_buffer(&descriptors);
|
||||
|
@ -258,25 +263,17 @@ impl<'pass, 'frame: 'pass, 'global: 'frame> CommandRenderer<'pass, 'frame, 'glob
|
|||
false,
|
||||
);
|
||||
|
||||
let bitmap_bind_group =
|
||||
let blend_bind_group =
|
||||
descriptors
|
||||
.device
|
||||
.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||
label: None,
|
||||
layout: &descriptors.bind_layouts.bitmap,
|
||||
layout: &descriptors.bind_layouts.blend,
|
||||
entries: &[
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 0,
|
||||
resource: wgpu::BindingResource::Buffer(
|
||||
wgpu::BufferBinding {
|
||||
buffer: &descriptors.quad.texture_transforms,
|
||||
offset: 0,
|
||||
size: wgpu::BufferSize::new(std::mem::size_of::<
|
||||
TextureTransforms,
|
||||
>(
|
||||
)
|
||||
as u64),
|
||||
},
|
||||
resource: wgpu::BindingResource::TextureView(
|
||||
parent.blend_buffer.view(),
|
||||
),
|
||||
},
|
||||
wgpu::BindGroupEntry {
|
||||
|
@ -288,11 +285,18 @@ impl<'pass, 'frame: 'pass, 'global: 'frame> CommandRenderer<'pass, 'frame, 'glob
|
|||
wgpu::BindGroupEntry {
|
||||
binding: 2,
|
||||
resource: wgpu::BindingResource::Sampler(
|
||||
&descriptors.bitmap_samplers.get_sampler(false, false),
|
||||
descriptors.bitmap_samplers.get_sampler(false, false),
|
||||
),
|
||||
},
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 3,
|
||||
resource: descriptors
|
||||
.blend_buffer(blend_mode)
|
||||
.as_entire_binding(),
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
let mut render_pass =
|
||||
draw_encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
||||
label: None,
|
||||
|
@ -314,10 +318,10 @@ impl<'pass, 'frame: 'pass, 'global: 'frame> CommandRenderer<'pass, 'frame, 'glob
|
|||
}
|
||||
}
|
||||
|
||||
render_pass.set_pipeline(pipelines.bitmap.pipeline_for(mask_state));
|
||||
render_pass.set_pipeline(pipelines.blend.pipeline_for(mask_state));
|
||||
|
||||
render_pass.set_bind_group(1, texture_buffers.whole_frame_bind_group(), &[0]);
|
||||
render_pass.set_bind_group(2, &bitmap_bind_group, &[]);
|
||||
render_pass.set_bind_group(2, &blend_bind_group, &[]);
|
||||
|
||||
render_pass.set_vertex_buffer(0, descriptors.quad.vertices.slice(..));
|
||||
render_pass.set_index_buffer(
|
||||
|
|
|
@ -4,6 +4,9 @@ use crate::shaders::Shaders;
|
|||
use crate::{create_buffer_with_data, BitmapSamplers, Pipelines, TextureTransforms, Vertex};
|
||||
use fnv::FnvHashMap;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use swf::BlendMode;
|
||||
|
||||
const MAX_BLEND_MODES: usize = 13;
|
||||
|
||||
pub struct Descriptors {
|
||||
pub adapter: wgpu::Adapter,
|
||||
|
@ -13,6 +16,7 @@ pub struct Descriptors {
|
|||
pub bitmap_samplers: BitmapSamplers,
|
||||
pub bind_layouts: BindLayouts,
|
||||
pub quad: Quad,
|
||||
blend_buffers: [wgpu::Buffer; MAX_BLEND_MODES],
|
||||
copy_pipeline: Mutex<FnvHashMap<wgpu::TextureFormat, Arc<wgpu::RenderPipeline>>>,
|
||||
copy_srgb_pipeline: Mutex<FnvHashMap<wgpu::TextureFormat, Arc<wgpu::RenderPipeline>>>,
|
||||
shaders: Shaders,
|
||||
|
@ -27,6 +31,15 @@ impl Descriptors {
|
|||
let shaders = Shaders::new(&device);
|
||||
let quad = Quad::new(&device);
|
||||
|
||||
let blend_buffers = core::array::from_fn(|blend_id| {
|
||||
create_buffer_with_data(
|
||||
&device,
|
||||
bytemuck::cast_slice(&[blend_id, 0, 0, 0]),
|
||||
wgpu::BufferUsages::UNIFORM,
|
||||
create_debug_label!("Blend mode {:?}", blend_id),
|
||||
)
|
||||
});
|
||||
|
||||
Self {
|
||||
adapter,
|
||||
device,
|
||||
|
@ -35,6 +48,7 @@ impl Descriptors {
|
|||
bitmap_samplers,
|
||||
bind_layouts,
|
||||
quad,
|
||||
blend_buffers,
|
||||
copy_pipeline: Default::default(),
|
||||
copy_srgb_pipeline: Default::default(),
|
||||
shaders,
|
||||
|
@ -182,6 +196,26 @@ impl Descriptors {
|
|||
})
|
||||
.clone()
|
||||
}
|
||||
|
||||
pub fn blend_buffer(&self, blend_mode: BlendMode) -> &wgpu::Buffer {
|
||||
let index = match blend_mode {
|
||||
BlendMode::Normal => 0,
|
||||
BlendMode::Layer => 0,
|
||||
BlendMode::Multiply => 1,
|
||||
BlendMode::Screen => 2,
|
||||
BlendMode::Lighten => 3,
|
||||
BlendMode::Darken => 4,
|
||||
BlendMode::Difference => 5,
|
||||
BlendMode::Add => 6,
|
||||
BlendMode::Subtract => 7,
|
||||
BlendMode::Invert => 8,
|
||||
BlendMode::Alpha => 9,
|
||||
BlendMode::Erase => 10,
|
||||
BlendMode::Overlay => 11,
|
||||
BlendMode::HardLight => 12,
|
||||
};
|
||||
&self.blend_buffers[index]
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Quad {
|
||||
|
|
|
@ -4,6 +4,7 @@ pub struct BindLayouts {
|
|||
pub transforms: wgpu::BindGroupLayout,
|
||||
pub bitmap: wgpu::BindGroupLayout,
|
||||
pub gradient: wgpu::BindGroupLayout,
|
||||
pub blend: wgpu::BindGroupLayout,
|
||||
}
|
||||
|
||||
impl BindLayouts {
|
||||
|
@ -71,6 +72,49 @@ impl BindLayouts {
|
|||
label: bitmap_bind_layout_label.as_deref(),
|
||||
});
|
||||
|
||||
let blend_bind_layout_label = create_debug_label!("Blend bind group layout");
|
||||
let blend = 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: false },
|
||||
view_dimension: wgpu::TextureViewDimension::D2,
|
||||
},
|
||||
count: None,
|
||||
},
|
||||
wgpu::BindGroupLayoutEntry {
|
||||
binding: 1,
|
||||
visibility: wgpu::ShaderStages::FRAGMENT,
|
||||
ty: wgpu::BindingType::Texture {
|
||||
multisampled: false,
|
||||
sample_type: wgpu::TextureSampleType::Float { filterable: false },
|
||||
view_dimension: wgpu::TextureViewDimension::D2,
|
||||
},
|
||||
count: None,
|
||||
},
|
||||
wgpu::BindGroupLayoutEntry {
|
||||
binding: 2,
|
||||
visibility: wgpu::ShaderStages::FRAGMENT,
|
||||
ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::NonFiltering),
|
||||
count: None,
|
||||
},
|
||||
wgpu::BindGroupLayoutEntry {
|
||||
binding: 3,
|
||||
visibility: wgpu::ShaderStages::FRAGMENT,
|
||||
ty: wgpu::BindingType::Buffer {
|
||||
ty: wgpu::BufferBindingType::Uniform,
|
||||
has_dynamic_offset: false,
|
||||
min_binding_size: None,
|
||||
},
|
||||
count: None,
|
||||
},
|
||||
],
|
||||
label: blend_bind_layout_label.as_deref(),
|
||||
});
|
||||
|
||||
let gradient_bind_layout_label = create_debug_label!("Gradient shape bind group");
|
||||
let gradient = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
||||
entries: &[
|
||||
|
@ -107,6 +151,7 @@ impl BindLayouts {
|
|||
transforms,
|
||||
bitmap,
|
||||
gradient,
|
||||
blend,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ pub struct Pipelines {
|
|||
pub color: ShapePipeline,
|
||||
pub bitmap: ShapePipeline,
|
||||
pub gradient: ShapePipeline,
|
||||
pub blend: ShapePipeline,
|
||||
}
|
||||
|
||||
impl ShapePipeline {
|
||||
|
@ -64,6 +65,7 @@ impl Pipelines {
|
|||
msaa_sample_count,
|
||||
&VERTEX_BUFFERS_DESCRIPTION,
|
||||
&[&bind_layouts.globals, &bind_layouts.transforms],
|
||||
wgpu::BlendState::PREMULTIPLIED_ALPHA_BLENDING,
|
||||
);
|
||||
|
||||
let bitmap_pipelines = create_shape_pipeline(
|
||||
|
@ -78,6 +80,7 @@ impl Pipelines {
|
|||
&bind_layouts.transforms,
|
||||
&bind_layouts.bitmap,
|
||||
],
|
||||
wgpu::BlendState::PREMULTIPLIED_ALPHA_BLENDING,
|
||||
);
|
||||
|
||||
let gradient_pipelines = create_shape_pipeline(
|
||||
|
@ -92,12 +95,29 @@ impl Pipelines {
|
|||
&bind_layouts.transforms,
|
||||
&bind_layouts.gradient,
|
||||
],
|
||||
wgpu::BlendState::PREMULTIPLIED_ALPHA_BLENDING,
|
||||
);
|
||||
|
||||
let blend_pipeline = create_shape_pipeline(
|
||||
"Blend",
|
||||
device,
|
||||
format,
|
||||
&shaders.blend_shader,
|
||||
msaa_sample_count,
|
||||
&VERTEX_BUFFERS_DESCRIPTION,
|
||||
&[
|
||||
&bind_layouts.globals,
|
||||
&bind_layouts.transforms,
|
||||
&bind_layouts.blend,
|
||||
],
|
||||
wgpu::BlendState::REPLACE,
|
||||
);
|
||||
|
||||
Self {
|
||||
color: color_pipelines,
|
||||
bitmap: bitmap_pipelines,
|
||||
gradient: gradient_pipelines,
|
||||
blend: blend_pipeline,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -145,6 +165,7 @@ fn create_pipeline_descriptor<'a>(
|
|||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn create_shape_pipeline(
|
||||
name: &'static str,
|
||||
device: &wgpu::Device,
|
||||
|
@ -153,6 +174,7 @@ fn create_shape_pipeline(
|
|||
msaa_sample_count: u32,
|
||||
vertex_buffers_layout: &[wgpu::VertexBufferLayout<'_>],
|
||||
bind_group_layouts: &[&wgpu::BindGroupLayout],
|
||||
blend: wgpu::BlendState,
|
||||
) -> ShapePipeline {
|
||||
let pipeline_layout_label = create_debug_label!("{} shape pipeline layout", name);
|
||||
let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
||||
|
@ -181,7 +203,7 @@ fn create_shape_pipeline(
|
|||
}),
|
||||
&[Some(wgpu::ColorTargetState {
|
||||
format,
|
||||
blend: Some(wgpu::BlendState::PREMULTIPLIED_ALPHA_BLENDING),
|
||||
blend: Some(blend),
|
||||
write_mask,
|
||||
})],
|
||||
vertex_buffers_layout,
|
||||
|
|
|
@ -5,6 +5,7 @@ pub struct Shaders {
|
|||
pub gradient_shader: wgpu::ShaderModule,
|
||||
pub copy_srgb_shader: wgpu::ShaderModule,
|
||||
pub copy_shader: wgpu::ShaderModule,
|
||||
pub blend_shader: wgpu::ShaderModule,
|
||||
}
|
||||
|
||||
impl Shaders {
|
||||
|
@ -23,6 +24,7 @@ impl Shaders {
|
|||
include_str!("../shaders/copy_srgb.wgsl"),
|
||||
);
|
||||
let copy_shader = create_shader(device, "copy", include_str!("../shaders/copy.wgsl"));
|
||||
let blend_shader = create_shader(device, "blend", include_str!("../shaders/blend.wgsl"));
|
||||
|
||||
Self {
|
||||
color_shader,
|
||||
|
@ -30,6 +32,7 @@ impl Shaders {
|
|||
gradient_shader,
|
||||
copy_srgb_shader,
|
||||
copy_shader,
|
||||
blend_shader,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -98,6 +98,7 @@ impl FrameBuffer {
|
|||
#[derive(Debug)]
|
||||
pub struct BlendBuffer {
|
||||
texture: wgpu::Texture,
|
||||
view: wgpu::TextureView,
|
||||
}
|
||||
|
||||
impl BlendBuffer {
|
||||
|
@ -117,8 +118,13 @@ impl BlendBuffer {
|
|||
format,
|
||||
usage,
|
||||
});
|
||||
let view = texture.create_view(&Default::default());
|
||||
|
||||
Self { texture }
|
||||
Self { texture, view }
|
||||
}
|
||||
|
||||
pub fn view(&self) -> &wgpu::TextureView {
|
||||
&self.view
|
||||
}
|
||||
|
||||
pub fn texture(&self) -> &wgpu::Texture {
|
||||
|
|
Loading…
Reference in New Issue