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:
Kamil Jarosz 2024-06-23 18:45:21 +02:00
parent 23f01e0dd9
commit 23931ed863
2 changed files with 292 additions and 236 deletions

View File

@ -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,

View File

@ -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)
}
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,58 +524,60 @@ 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) => {
impl<'a> CommandHandler for WgpuCommandHandler<'a> {
fn blend(&mut self, commands: CommandList, blend_mode: RenderBlendMode) {
let mut surface = Surface::new(
descriptors,
quality,
width,
height,
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 {
nearest_layer
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),
descriptors,
meshes,
self.descriptors,
self.meshes,
commands,
staging_belt,
dynamic_transforms,
draw_encoder,
self.staging_belt,
self.dynamic_transforms,
self.draw_encoder,
target_layer,
texture_pool,
self.texture_pool,
);
target.ensure_cleared(draw_encoder);
target.ensure_cleared(self.draw_encoder);
match blend_type {
BlendType::Trivial(blend_mode) => {
@ -499,42 +587,33 @@ pub fn chunk_blends<'a>(
};
let texture = target.take_color_texture();
let bind_group =
descriptors
self.descriptors
.device
.create_bind_group(&wgpu::BindGroupDescriptor {
layout: &descriptors.bind_layouts.bitmap,
layout: &self.descriptors.bind_layouts.bitmap,
entries: &[
wgpu::BindGroupEntry {
binding: 0,
resource: descriptors
resource: self
.descriptors
.quad
.texture_transforms
.as_entire_binding(),
},
wgpu::BindGroupEntry {
binding: 1,
resource: wgpu::BindingResource::TextureView(
texture.view(),
),
resource: wgpu::BindingResource::TextureView(texture.view()),
},
wgpu::BindGroupEntry {
binding: 2,
resource: wgpu::BindingResource::Sampler(
descriptors
.bitmap_samplers
.get_sampler(false, false),
self.descriptors.bitmap_samplers.get_sampler(false, false),
),
},
],
label: None,
});
add_to_current(
&mut result,
&mut current,
&mut transforms,
dynamic_transforms,
needs_stencil,
descriptors,
self.add_to_current(
transform.matrix,
transform.color_transform,
|transform_buffer| DrawCommand::RenderTexture {
@ -546,37 +625,40 @@ pub fn chunk_blends<'a>(
);
}
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),
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),
),
));
}
transforms.set_buffer_limit(dynamic_transforms.buffer.size());
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!(),
};
result.push(Chunk::Blend(
self.result.push(Chunk::Blend(
target.take_color_texture(),
chunk_blend_mode,
num_masks > 0,
self.num_masks > 0,
));
needs_stencil = num_masks > 0;
self.needs_stencil = self.num_masks > 0;
}
}
}
Command::RenderBitmap {
bitmap,
transform,
smoothing,
pixel_snapping,
} => {
fn render_bitmap(
&mut self,
bitmap: BitmapHandle,
transform: Transform,
smoothing: bool,
pixel_snapping: PixelSnapping,
) {
let mut matrix = transform.matrix;
{
let texture = as_texture(&bitmap);
@ -586,25 +668,17 @@ pub fn chunk_blends<'a>(
texture.texture.height() as f32,
);
}
add_to_current(
&mut result,
&mut current,
&mut transforms,
dynamic_transforms,
needs_stencil,
descriptors,
matrix,
transform.color_transform,
|transform_buffer| DrawCommand::RenderBitmap {
self.add_to_current(matrix, transform.color_transform, |transform_buffer| {
DrawCommand::RenderBitmap {
bitmap,
transform_buffer,
smoothing,
blend_mode: TrivialBlend::Normal,
render_stage3d: false,
},
);
}
Command::RenderStage3D { bitmap, transform } => {
});
}
fn render_stage3d(&mut self, bitmap: BitmapHandle, transform: Transform) {
let mut matrix = transform.matrix;
{
let texture = as_texture(&bitmap);
@ -613,73 +687,55 @@ pub fn chunk_blends<'a>(
texture.texture.height() as f32,
);
}
add_to_current(
&mut result,
&mut current,
&mut transforms,
dynamic_transforms,
needs_stencil,
descriptors,
matrix,
transform.color_transform,
|transform_buffer| DrawCommand::RenderBitmap {
self.add_to_current(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,
});
}
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,
},
),
Command::DrawRect { color, matrix } => add_to_current(
&mut result,
&mut current,
&mut transforms,
dynamic_transforms,
needs_stencil,
descriptors,
);
}
fn draw_rect(&mut self, color: Color, matrix: Matrix) {
self.add_to_current(
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);
}
}
);
}
if !current.is_empty() {
result.push(Chunk::Draw(current, needs_stencil, transforms));
fn push_mask(&mut self) {
self.needs_stencil = true;
self.num_masks += 1;
self.current.push(DrawCommand::PushMask);
}
result
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);
}
}