render: Refactor chunk_blends to use CommandHandler
This refactor simplifies code, gets rid of the large number of parameters, and allows calling other rendering methods from themselves. The latter is useful for emulating lines on Dx12.
This commit is contained in:
parent
23f01e0dd9
commit
23931ed863
|
@ -132,7 +132,7 @@ impl Surface {
|
|||
let mut num_masks = 0;
|
||||
let mut mask_state = MaskState::NoMask;
|
||||
let chunks = chunk_blends(
|
||||
commands.commands,
|
||||
commands,
|
||||
descriptors,
|
||||
staging_belt,
|
||||
dynamic_transforms,
|
||||
|
|
|
@ -9,13 +9,14 @@ use crate::surface::target::CommandTarget;
|
|||
use crate::surface::Surface;
|
||||
use crate::{as_texture, Descriptors, MaskState, Pipelines, Transforms};
|
||||
use ruffle_render::backend::ShapeHandle;
|
||||
use ruffle_render::bitmap::BitmapHandle;
|
||||
use ruffle_render::commands::{Command, RenderBlendMode};
|
||||
use ruffle_render::bitmap::{BitmapHandle, PixelSnapping};
|
||||
use ruffle_render::commands::{CommandHandler, CommandList, RenderBlendMode};
|
||||
use ruffle_render::matrix::Matrix;
|
||||
use ruffle_render::pixel_bender::PixelBenderShaderHandle;
|
||||
use ruffle_render::quality::StageQuality;
|
||||
use ruffle_render::transform::Transform;
|
||||
use swf::{BlendMode, ColorTransform, Fixed8};
|
||||
use std::mem;
|
||||
use swf::{BlendMode, Color, ColorTransform};
|
||||
|
||||
use super::target::PoolOrArcTexture;
|
||||
|
||||
|
@ -392,7 +393,7 @@ pub enum LayerRef<'a> {
|
|||
/// Every complex blend will be its own item, but every other draw will be chunked together
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn chunk_blends<'a>(
|
||||
commands: Vec<Command>,
|
||||
commands: CommandList,
|
||||
descriptors: &'a Descriptors,
|
||||
staging_belt: &'a mut wgpu::util::StagingBelt,
|
||||
dynamic_transforms: &'a DynamicTransforms,
|
||||
|
@ -404,21 +405,106 @@ pub fn chunk_blends<'a>(
|
|||
nearest_layer: LayerRef,
|
||||
texture_pool: &mut TexturePool,
|
||||
) -> Vec<Chunk> {
|
||||
let mut result = vec![];
|
||||
let mut current = vec![];
|
||||
let mut needs_stencil = false;
|
||||
let mut num_masks = 0;
|
||||
let mut transforms = BufferBuilder::new_for_uniform(&descriptors.limits);
|
||||
WgpuCommandHandler::new(
|
||||
descriptors,
|
||||
staging_belt,
|
||||
dynamic_transforms,
|
||||
draw_encoder,
|
||||
meshes,
|
||||
quality,
|
||||
width,
|
||||
height,
|
||||
nearest_layer,
|
||||
texture_pool,
|
||||
)
|
||||
.chunk_blends(commands)
|
||||
}
|
||||
|
||||
transforms.set_buffer_limit(dynamic_transforms.buffer.size());
|
||||
struct WgpuCommandHandler<'a> {
|
||||
descriptors: &'a Descriptors,
|
||||
quality: StageQuality,
|
||||
width: u32,
|
||||
height: u32,
|
||||
nearest_layer: LayerRef<'a>,
|
||||
meshes: &'a Vec<Mesh>,
|
||||
staging_belt: &'a mut wgpu::util::StagingBelt,
|
||||
dynamic_transforms: &'a DynamicTransforms,
|
||||
draw_encoder: &'a mut wgpu::CommandEncoder,
|
||||
texture_pool: &'a mut TexturePool,
|
||||
|
||||
result: Vec<Chunk>,
|
||||
current: Vec<DrawCommand>,
|
||||
transforms: BufferBuilder,
|
||||
needs_stencil: bool,
|
||||
num_masks: i32,
|
||||
}
|
||||
|
||||
impl<'a> WgpuCommandHandler<'a> {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn new(
|
||||
descriptors: &'a Descriptors,
|
||||
staging_belt: &'a mut wgpu::util::StagingBelt,
|
||||
dynamic_transforms: &'a DynamicTransforms,
|
||||
draw_encoder: &'a mut wgpu::CommandEncoder,
|
||||
meshes: &'a Vec<Mesh>,
|
||||
quality: StageQuality,
|
||||
width: u32,
|
||||
height: u32,
|
||||
nearest_layer: LayerRef<'a>,
|
||||
texture_pool: &'a mut TexturePool,
|
||||
) -> Self {
|
||||
let transforms = Self::new_transforms(descriptors, dynamic_transforms);
|
||||
Self {
|
||||
descriptors,
|
||||
quality,
|
||||
width,
|
||||
height,
|
||||
nearest_layer,
|
||||
meshes,
|
||||
staging_belt,
|
||||
dynamic_transforms,
|
||||
draw_encoder,
|
||||
texture_pool,
|
||||
|
||||
result: vec![],
|
||||
current: vec![],
|
||||
transforms,
|
||||
needs_stencil: false,
|
||||
num_masks: 0,
|
||||
}
|
||||
}
|
||||
|
||||
fn new_transforms(
|
||||
descriptors: &'a Descriptors,
|
||||
dynamic_transforms: &'a DynamicTransforms,
|
||||
) -> BufferBuilder {
|
||||
let mut transforms = BufferBuilder::new_for_uniform(&descriptors.limits);
|
||||
transforms.set_buffer_limit(dynamic_transforms.buffer.size());
|
||||
transforms
|
||||
}
|
||||
|
||||
/// Replaces every blend with a RenderBitmap, with the subcommands rendered out to a temporary texture
|
||||
/// Every complex blend will be its own item, but every other draw will be chunked together
|
||||
fn chunk_blends(&mut self, commands: CommandList) -> Vec<Chunk> {
|
||||
commands.execute(self);
|
||||
|
||||
let current = mem::take(&mut self.current);
|
||||
let mut result = mem::take(&mut self.result);
|
||||
let needs_stencil = mem::take(&mut self.needs_stencil);
|
||||
let transforms = mem::replace(
|
||||
&mut self.transforms,
|
||||
Self::new_transforms(self.descriptors, self.dynamic_transforms),
|
||||
);
|
||||
|
||||
if !current.is_empty() {
|
||||
result.push(Chunk::Draw(current, needs_stencil, transforms));
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
fn add_to_current(
|
||||
result: &mut Vec<Chunk>,
|
||||
current: &mut Vec<DrawCommand>,
|
||||
transforms: &mut BufferBuilder,
|
||||
dynamic_transforms: &DynamicTransforms,
|
||||
needs_stencil: bool,
|
||||
descriptors: &Descriptors,
|
||||
&mut self,
|
||||
matrix: Matrix,
|
||||
color_transform: ColorTransform,
|
||||
command_builder: impl FnOnce(wgpu::DynamicOffset) -> DrawCommand,
|
||||
|
@ -438,248 +524,218 @@ pub fn chunk_blends<'a>(
|
|||
mult_color: color_transform.mult_rgba_normalized(),
|
||||
add_color: color_transform.add_rgba_normalized(),
|
||||
};
|
||||
if let Ok(transform_range) = transforms.add(&[transform]) {
|
||||
current.push(command_builder(
|
||||
if let Ok(transform_range) = self.transforms.add(&[transform]) {
|
||||
self.current.push(command_builder(
|
||||
transform_range.start as wgpu::DynamicOffset,
|
||||
));
|
||||
} else {
|
||||
result.push(Chunk::Draw(
|
||||
std::mem::take(current),
|
||||
needs_stencil,
|
||||
std::mem::replace(
|
||||
transforms,
|
||||
BufferBuilder::new_for_uniform(&descriptors.limits),
|
||||
self.result.push(Chunk::Draw(
|
||||
mem::take(&mut self.current),
|
||||
self.needs_stencil,
|
||||
mem::replace(
|
||||
&mut self.transforms,
|
||||
BufferBuilder::new_for_uniform(&self.descriptors.limits),
|
||||
),
|
||||
));
|
||||
transforms.set_buffer_limit(dynamic_transforms.buffer.size());
|
||||
let transform_range = transforms
|
||||
self.transforms
|
||||
.set_buffer_limit(self.dynamic_transforms.buffer.size());
|
||||
let transform_range = self
|
||||
.transforms
|
||||
.add(&[transform])
|
||||
.expect("Buffer must be able to fit a new thing, it was just emptied");
|
||||
current.push(command_builder(
|
||||
self.current.push(command_builder(
|
||||
transform_range.start as wgpu::DynamicOffset,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for command in commands {
|
||||
match command {
|
||||
Command::Blend(commands, blend_mode) => {
|
||||
let mut surface = Surface::new(
|
||||
descriptors,
|
||||
quality,
|
||||
width,
|
||||
height,
|
||||
wgpu::TextureFormat::Rgba8Unorm,
|
||||
);
|
||||
let target_layer = if let RenderBlendMode::Builtin(BlendMode::Layer) = &blend_mode {
|
||||
LayerRef::Current
|
||||
} else {
|
||||
nearest_layer
|
||||
impl<'a> CommandHandler for WgpuCommandHandler<'a> {
|
||||
fn blend(&mut self, commands: CommandList, blend_mode: RenderBlendMode) {
|
||||
let mut surface = Surface::new(
|
||||
self.descriptors,
|
||||
self.quality,
|
||||
self.width,
|
||||
self.height,
|
||||
wgpu::TextureFormat::Rgba8Unorm,
|
||||
);
|
||||
let target_layer = if let RenderBlendMode::Builtin(BlendMode::Layer) = &blend_mode {
|
||||
LayerRef::Current
|
||||
} else {
|
||||
self.nearest_layer
|
||||
};
|
||||
let blend_type = BlendType::from(blend_mode);
|
||||
let clear_color = blend_type.default_color();
|
||||
let target = surface.draw_commands(
|
||||
RenderTargetMode::FreshWithColor(clear_color),
|
||||
self.descriptors,
|
||||
self.meshes,
|
||||
commands,
|
||||
self.staging_belt,
|
||||
self.dynamic_transforms,
|
||||
self.draw_encoder,
|
||||
target_layer,
|
||||
self.texture_pool,
|
||||
);
|
||||
target.ensure_cleared(self.draw_encoder);
|
||||
|
||||
match blend_type {
|
||||
BlendType::Trivial(blend_mode) => {
|
||||
let transform = Transform {
|
||||
matrix: Matrix::scale(target.width() as f32, target.height() as f32),
|
||||
color_transform: Default::default(),
|
||||
};
|
||||
let blend_type = BlendType::from(blend_mode);
|
||||
let clear_color = blend_type.default_color();
|
||||
let target = surface.draw_commands(
|
||||
RenderTargetMode::FreshWithColor(clear_color),
|
||||
descriptors,
|
||||
meshes,
|
||||
commands,
|
||||
staging_belt,
|
||||
dynamic_transforms,
|
||||
draw_encoder,
|
||||
target_layer,
|
||||
texture_pool,
|
||||
);
|
||||
target.ensure_cleared(draw_encoder);
|
||||
|
||||
match blend_type {
|
||||
BlendType::Trivial(blend_mode) => {
|
||||
let transform = Transform {
|
||||
matrix: Matrix::scale(target.width() as f32, target.height() as f32),
|
||||
color_transform: Default::default(),
|
||||
};
|
||||
let texture = target.take_color_texture();
|
||||
let bind_group =
|
||||
descriptors
|
||||
.device
|
||||
.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||
layout: &descriptors.bind_layouts.bitmap,
|
||||
entries: &[
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 0,
|
||||
resource: descriptors
|
||||
.quad
|
||||
.texture_transforms
|
||||
.as_entire_binding(),
|
||||
},
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 1,
|
||||
resource: wgpu::BindingResource::TextureView(
|
||||
texture.view(),
|
||||
),
|
||||
},
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 2,
|
||||
resource: wgpu::BindingResource::Sampler(
|
||||
descriptors
|
||||
.bitmap_samplers
|
||||
.get_sampler(false, false),
|
||||
),
|
||||
},
|
||||
],
|
||||
label: None,
|
||||
});
|
||||
add_to_current(
|
||||
&mut result,
|
||||
&mut current,
|
||||
&mut transforms,
|
||||
dynamic_transforms,
|
||||
needs_stencil,
|
||||
descriptors,
|
||||
transform.matrix,
|
||||
transform.color_transform,
|
||||
|transform_buffer| DrawCommand::RenderTexture {
|
||||
_texture: texture,
|
||||
binds: bind_group,
|
||||
transform_buffer,
|
||||
blend_mode,
|
||||
},
|
||||
);
|
||||
}
|
||||
blend_type => {
|
||||
if !current.is_empty() {
|
||||
result.push(Chunk::Draw(
|
||||
std::mem::take(&mut current),
|
||||
needs_stencil,
|
||||
std::mem::replace(
|
||||
&mut transforms,
|
||||
BufferBuilder::new_for_uniform(&descriptors.limits),
|
||||
),
|
||||
));
|
||||
}
|
||||
transforms.set_buffer_limit(dynamic_transforms.buffer.size());
|
||||
let chunk_blend_mode = match blend_type {
|
||||
BlendType::Complex(complex) => ChunkBlendMode::Complex(complex),
|
||||
BlendType::Shader(shader) => ChunkBlendMode::Shader(shader),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
result.push(Chunk::Blend(
|
||||
target.take_color_texture(),
|
||||
chunk_blend_mode,
|
||||
num_masks > 0,
|
||||
));
|
||||
needs_stencil = num_masks > 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
Command::RenderBitmap {
|
||||
bitmap,
|
||||
transform,
|
||||
smoothing,
|
||||
pixel_snapping,
|
||||
} => {
|
||||
let mut matrix = transform.matrix;
|
||||
{
|
||||
let texture = as_texture(&bitmap);
|
||||
pixel_snapping.apply(&mut matrix);
|
||||
matrix *= Matrix::scale(
|
||||
texture.texture.width() as f32,
|
||||
texture.texture.height() as f32,
|
||||
);
|
||||
}
|
||||
add_to_current(
|
||||
&mut result,
|
||||
&mut current,
|
||||
&mut transforms,
|
||||
dynamic_transforms,
|
||||
needs_stencil,
|
||||
descriptors,
|
||||
matrix,
|
||||
let texture = target.take_color_texture();
|
||||
let bind_group =
|
||||
self.descriptors
|
||||
.device
|
||||
.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||
layout: &self.descriptors.bind_layouts.bitmap,
|
||||
entries: &[
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 0,
|
||||
resource: self
|
||||
.descriptors
|
||||
.quad
|
||||
.texture_transforms
|
||||
.as_entire_binding(),
|
||||
},
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 1,
|
||||
resource: wgpu::BindingResource::TextureView(texture.view()),
|
||||
},
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 2,
|
||||
resource: wgpu::BindingResource::Sampler(
|
||||
self.descriptors.bitmap_samplers.get_sampler(false, false),
|
||||
),
|
||||
},
|
||||
],
|
||||
label: None,
|
||||
});
|
||||
self.add_to_current(
|
||||
transform.matrix,
|
||||
transform.color_transform,
|
||||
|transform_buffer| DrawCommand::RenderBitmap {
|
||||
bitmap,
|
||||
|transform_buffer| DrawCommand::RenderTexture {
|
||||
_texture: texture,
|
||||
binds: bind_group,
|
||||
transform_buffer,
|
||||
smoothing,
|
||||
blend_mode: TrivialBlend::Normal,
|
||||
render_stage3d: false,
|
||||
blend_mode,
|
||||
},
|
||||
);
|
||||
}
|
||||
Command::RenderStage3D { bitmap, transform } => {
|
||||
let mut matrix = transform.matrix;
|
||||
{
|
||||
let texture = as_texture(&bitmap);
|
||||
matrix *= Matrix::scale(
|
||||
texture.texture.width() as f32,
|
||||
texture.texture.height() as f32,
|
||||
);
|
||||
blend_type => {
|
||||
if !self.current.is_empty() {
|
||||
self.result.push(Chunk::Draw(
|
||||
mem::take(&mut self.current),
|
||||
self.needs_stencil,
|
||||
mem::replace(
|
||||
&mut self.transforms,
|
||||
BufferBuilder::new_for_uniform(&self.descriptors.limits),
|
||||
),
|
||||
));
|
||||
}
|
||||
add_to_current(
|
||||
&mut result,
|
||||
&mut current,
|
||||
&mut transforms,
|
||||
dynamic_transforms,
|
||||
needs_stencil,
|
||||
descriptors,
|
||||
matrix,
|
||||
transform.color_transform,
|
||||
|transform_buffer| DrawCommand::RenderBitmap {
|
||||
bitmap,
|
||||
transform_buffer,
|
||||
smoothing: false,
|
||||
blend_mode: TrivialBlend::Normal,
|
||||
render_stage3d: true,
|
||||
},
|
||||
);
|
||||
}
|
||||
Command::RenderShape { shape, transform } => add_to_current(
|
||||
&mut result,
|
||||
&mut current,
|
||||
&mut transforms,
|
||||
dynamic_transforms,
|
||||
needs_stencil,
|
||||
descriptors,
|
||||
transform.matrix,
|
||||
transform.color_transform,
|
||||
|transform_buffer| DrawCommand::RenderShape {
|
||||
shape,
|
||||
transform_buffer,
|
||||
},
|
||||
),
|
||||
Command::DrawRect { color, matrix } => add_to_current(
|
||||
&mut result,
|
||||
&mut current,
|
||||
&mut transforms,
|
||||
dynamic_transforms,
|
||||
needs_stencil,
|
||||
descriptors,
|
||||
matrix,
|
||||
ColorTransform::multiply_from(color),
|
||||
|transform_buffer| DrawCommand::DrawRect { transform_buffer },
|
||||
),
|
||||
Command::PushMask => {
|
||||
needs_stencil = true;
|
||||
num_masks += 1;
|
||||
current.push(DrawCommand::PushMask);
|
||||
}
|
||||
Command::ActivateMask => {
|
||||
needs_stencil = true;
|
||||
current.push(DrawCommand::ActivateMask);
|
||||
}
|
||||
Command::DeactivateMask => {
|
||||
needs_stencil = true;
|
||||
current.push(DrawCommand::DeactivateMask);
|
||||
}
|
||||
Command::PopMask => {
|
||||
needs_stencil = true;
|
||||
num_masks -= 1;
|
||||
current.push(DrawCommand::PopMask);
|
||||
self.transforms
|
||||
.set_buffer_limit(self.dynamic_transforms.buffer.size());
|
||||
let chunk_blend_mode = match blend_type {
|
||||
BlendType::Complex(complex) => ChunkBlendMode::Complex(complex),
|
||||
BlendType::Shader(shader) => ChunkBlendMode::Shader(shader),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
self.result.push(Chunk::Blend(
|
||||
target.take_color_texture(),
|
||||
chunk_blend_mode,
|
||||
self.num_masks > 0,
|
||||
));
|
||||
self.needs_stencil = self.num_masks > 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !current.is_empty() {
|
||||
result.push(Chunk::Draw(current, needs_stencil, transforms));
|
||||
fn render_bitmap(
|
||||
&mut self,
|
||||
bitmap: BitmapHandle,
|
||||
transform: Transform,
|
||||
smoothing: bool,
|
||||
pixel_snapping: PixelSnapping,
|
||||
) {
|
||||
let mut matrix = transform.matrix;
|
||||
{
|
||||
let texture = as_texture(&bitmap);
|
||||
pixel_snapping.apply(&mut matrix);
|
||||
matrix *= Matrix::scale(
|
||||
texture.texture.width() as f32,
|
||||
texture.texture.height() as f32,
|
||||
);
|
||||
}
|
||||
self.add_to_current(matrix, transform.color_transform, |transform_buffer| {
|
||||
DrawCommand::RenderBitmap {
|
||||
bitmap,
|
||||
transform_buffer,
|
||||
smoothing,
|
||||
blend_mode: TrivialBlend::Normal,
|
||||
render_stage3d: false,
|
||||
}
|
||||
});
|
||||
}
|
||||
fn render_stage3d(&mut self, bitmap: BitmapHandle, transform: Transform) {
|
||||
let mut matrix = transform.matrix;
|
||||
{
|
||||
let texture = as_texture(&bitmap);
|
||||
matrix *= Matrix::scale(
|
||||
texture.texture.width() as f32,
|
||||
texture.texture.height() as f32,
|
||||
);
|
||||
}
|
||||
self.add_to_current(matrix, transform.color_transform, |transform_buffer| {
|
||||
DrawCommand::RenderBitmap {
|
||||
bitmap,
|
||||
transform_buffer,
|
||||
smoothing: false,
|
||||
blend_mode: TrivialBlend::Normal,
|
||||
render_stage3d: true,
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
result
|
||||
fn render_shape(&mut self, shape: ShapeHandle, transform: Transform) {
|
||||
self.add_to_current(
|
||||
transform.matrix,
|
||||
transform.color_transform,
|
||||
|transform_buffer| DrawCommand::RenderShape {
|
||||
shape,
|
||||
transform_buffer,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
fn draw_rect(&mut self, color: Color, matrix: Matrix) {
|
||||
self.add_to_current(
|
||||
matrix,
|
||||
ColorTransform::multiply_from(color),
|
||||
|transform_buffer| DrawCommand::DrawRect { transform_buffer },
|
||||
);
|
||||
}
|
||||
|
||||
fn push_mask(&mut self) {
|
||||
self.needs_stencil = true;
|
||||
self.num_masks += 1;
|
||||
self.current.push(DrawCommand::PushMask);
|
||||
}
|
||||
|
||||
fn activate_mask(&mut self) {
|
||||
self.needs_stencil = true;
|
||||
self.current.push(DrawCommand::ActivateMask);
|
||||
}
|
||||
|
||||
fn deactivate_mask(&mut self) {
|
||||
self.needs_stencil = true;
|
||||
self.current.push(DrawCommand::DeactivateMask);
|
||||
}
|
||||
|
||||
fn pop_mask(&mut self) {
|
||||
self.needs_stencil = true;
|
||||
self.num_masks -= 1;
|
||||
self.current.push(DrawCommand::PopMask);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue