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 num_masks = 0;
let mut mask_state = MaskState::NoMask; let mut mask_state = MaskState::NoMask;
let chunks = chunk_blends( let chunks = chunk_blends(
commands.commands, commands,
descriptors, descriptors,
staging_belt, staging_belt,
dynamic_transforms, dynamic_transforms,

View File

@ -9,13 +9,14 @@ use crate::surface::target::CommandTarget;
use crate::surface::Surface; use crate::surface::Surface;
use crate::{as_texture, Descriptors, MaskState, Pipelines, Transforms}; use crate::{as_texture, Descriptors, MaskState, Pipelines, Transforms};
use ruffle_render::backend::ShapeHandle; use ruffle_render::backend::ShapeHandle;
use ruffle_render::bitmap::BitmapHandle; use ruffle_render::bitmap::{BitmapHandle, PixelSnapping};
use ruffle_render::commands::{Command, RenderBlendMode}; use ruffle_render::commands::{CommandHandler, CommandList, RenderBlendMode};
use ruffle_render::matrix::Matrix; use ruffle_render::matrix::Matrix;
use ruffle_render::pixel_bender::PixelBenderShaderHandle; use ruffle_render::pixel_bender::PixelBenderShaderHandle;
use ruffle_render::quality::StageQuality; use ruffle_render::quality::StageQuality;
use ruffle_render::transform::Transform; use ruffle_render::transform::Transform;
use swf::{BlendMode, ColorTransform, Fixed8}; use std::mem;
use swf::{BlendMode, Color, ColorTransform};
use super::target::PoolOrArcTexture; 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 /// Every complex blend will be its own item, but every other draw will be chunked together
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
pub fn chunk_blends<'a>( pub fn chunk_blends<'a>(
commands: Vec<Command>, commands: CommandList,
descriptors: &'a Descriptors, descriptors: &'a Descriptors,
staging_belt: &'a mut wgpu::util::StagingBelt, staging_belt: &'a mut wgpu::util::StagingBelt,
dynamic_transforms: &'a DynamicTransforms, dynamic_transforms: &'a DynamicTransforms,
@ -404,21 +405,106 @@ pub fn chunk_blends<'a>(
nearest_layer: LayerRef, nearest_layer: LayerRef,
texture_pool: &mut TexturePool, texture_pool: &mut TexturePool,
) -> Vec<Chunk> { ) -> Vec<Chunk> {
let mut result = vec![]; WgpuCommandHandler::new(
let mut current = vec![]; descriptors,
let mut needs_stencil = false; staging_belt,
let mut num_masks = 0; dynamic_transforms,
let mut transforms = BufferBuilder::new_for_uniform(&descriptors.limits); 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.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( fn add_to_current(
result: &mut Vec<Chunk>, &mut self,
current: &mut Vec<DrawCommand>,
transforms: &mut BufferBuilder,
dynamic_transforms: &DynamicTransforms,
needs_stencil: bool,
descriptors: &Descriptors,
matrix: Matrix, matrix: Matrix,
color_transform: ColorTransform, color_transform: ColorTransform,
command_builder: impl FnOnce(wgpu::DynamicOffset) -> DrawCommand, command_builder: impl FnOnce(wgpu::DynamicOffset) -> DrawCommand,
@ -438,58 +524,60 @@ pub fn chunk_blends<'a>(
mult_color: color_transform.mult_rgba_normalized(), mult_color: color_transform.mult_rgba_normalized(),
add_color: color_transform.add_rgba_normalized(), add_color: color_transform.add_rgba_normalized(),
}; };
if let Ok(transform_range) = transforms.add(&[transform]) { if let Ok(transform_range) = self.transforms.add(&[transform]) {
current.push(command_builder( self.current.push(command_builder(
transform_range.start as wgpu::DynamicOffset, transform_range.start as wgpu::DynamicOffset,
)); ));
} else { } else {
result.push(Chunk::Draw( self.result.push(Chunk::Draw(
std::mem::take(current), mem::take(&mut self.current),
needs_stencil, self.needs_stencil,
std::mem::replace( mem::replace(
transforms, &mut self.transforms,
BufferBuilder::new_for_uniform(&descriptors.limits), BufferBuilder::new_for_uniform(&self.descriptors.limits),
), ),
)); ));
transforms.set_buffer_limit(dynamic_transforms.buffer.size()); self.transforms
let transform_range = transforms .set_buffer_limit(self.dynamic_transforms.buffer.size());
let transform_range = self
.transforms
.add(&[transform]) .add(&[transform])
.expect("Buffer must be able to fit a new thing, it was just emptied"); .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, transform_range.start as wgpu::DynamicOffset,
)); ));
} }
} }
}
for command in commands { impl<'a> CommandHandler for WgpuCommandHandler<'a> {
match command { fn blend(&mut self, commands: CommandList, blend_mode: RenderBlendMode) {
Command::Blend(commands, blend_mode) => {
let mut surface = Surface::new( let mut surface = Surface::new(
descriptors, self.descriptors,
quality, self.quality,
width, self.width,
height, self.height,
wgpu::TextureFormat::Rgba8Unorm, wgpu::TextureFormat::Rgba8Unorm,
); );
let target_layer = if let RenderBlendMode::Builtin(BlendMode::Layer) = &blend_mode { let target_layer = if let RenderBlendMode::Builtin(BlendMode::Layer) = &blend_mode {
LayerRef::Current LayerRef::Current
} else { } else {
nearest_layer self.nearest_layer
}; };
let blend_type = BlendType::from(blend_mode); let blend_type = BlendType::from(blend_mode);
let clear_color = blend_type.default_color(); let clear_color = blend_type.default_color();
let target = surface.draw_commands( let target = surface.draw_commands(
RenderTargetMode::FreshWithColor(clear_color), RenderTargetMode::FreshWithColor(clear_color),
descriptors, self.descriptors,
meshes, self.meshes,
commands, commands,
staging_belt, self.staging_belt,
dynamic_transforms, self.dynamic_transforms,
draw_encoder, self.draw_encoder,
target_layer, target_layer,
texture_pool, self.texture_pool,
); );
target.ensure_cleared(draw_encoder); target.ensure_cleared(self.draw_encoder);
match blend_type { match blend_type {
BlendType::Trivial(blend_mode) => { BlendType::Trivial(blend_mode) => {
@ -499,42 +587,33 @@ pub fn chunk_blends<'a>(
}; };
let texture = target.take_color_texture(); let texture = target.take_color_texture();
let bind_group = let bind_group =
descriptors self.descriptors
.device .device
.create_bind_group(&wgpu::BindGroupDescriptor { .create_bind_group(&wgpu::BindGroupDescriptor {
layout: &descriptors.bind_layouts.bitmap, layout: &self.descriptors.bind_layouts.bitmap,
entries: &[ entries: &[
wgpu::BindGroupEntry { wgpu::BindGroupEntry {
binding: 0, binding: 0,
resource: descriptors resource: self
.descriptors
.quad .quad
.texture_transforms .texture_transforms
.as_entire_binding(), .as_entire_binding(),
}, },
wgpu::BindGroupEntry { wgpu::BindGroupEntry {
binding: 1, binding: 1,
resource: wgpu::BindingResource::TextureView( resource: wgpu::BindingResource::TextureView(texture.view()),
texture.view(),
),
}, },
wgpu::BindGroupEntry { wgpu::BindGroupEntry {
binding: 2, binding: 2,
resource: wgpu::BindingResource::Sampler( resource: wgpu::BindingResource::Sampler(
descriptors self.descriptors.bitmap_samplers.get_sampler(false, false),
.bitmap_samplers
.get_sampler(false, false),
), ),
}, },
], ],
label: None, label: None,
}); });
add_to_current( self.add_to_current(
&mut result,
&mut current,
&mut transforms,
dynamic_transforms,
needs_stencil,
descriptors,
transform.matrix, transform.matrix,
transform.color_transform, transform.color_transform,
|transform_buffer| DrawCommand::RenderTexture { |transform_buffer| DrawCommand::RenderTexture {
@ -546,37 +625,40 @@ pub fn chunk_blends<'a>(
); );
} }
blend_type => { blend_type => {
if !current.is_empty() { if !self.current.is_empty() {
result.push(Chunk::Draw( self.result.push(Chunk::Draw(
std::mem::take(&mut current), mem::take(&mut self.current),
needs_stencil, self.needs_stencil,
std::mem::replace( mem::replace(
&mut transforms, &mut self.transforms,
BufferBuilder::new_for_uniform(&descriptors.limits), 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 { let chunk_blend_mode = match blend_type {
BlendType::Complex(complex) => ChunkBlendMode::Complex(complex), BlendType::Complex(complex) => ChunkBlendMode::Complex(complex),
BlendType::Shader(shader) => ChunkBlendMode::Shader(shader), BlendType::Shader(shader) => ChunkBlendMode::Shader(shader),
_ => unreachable!(), _ => unreachable!(),
}; };
result.push(Chunk::Blend( self.result.push(Chunk::Blend(
target.take_color_texture(), target.take_color_texture(),
chunk_blend_mode, 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, fn render_bitmap(
transform, &mut self,
smoothing, bitmap: BitmapHandle,
pixel_snapping, transform: Transform,
} => { smoothing: bool,
pixel_snapping: PixelSnapping,
) {
let mut matrix = transform.matrix; let mut matrix = transform.matrix;
{ {
let texture = as_texture(&bitmap); let texture = as_texture(&bitmap);
@ -586,25 +668,17 @@ pub fn chunk_blends<'a>(
texture.texture.height() as f32, texture.texture.height() as f32,
); );
} }
add_to_current( self.add_to_current(matrix, transform.color_transform, |transform_buffer| {
&mut result, DrawCommand::RenderBitmap {
&mut current,
&mut transforms,
dynamic_transforms,
needs_stencil,
descriptors,
matrix,
transform.color_transform,
|transform_buffer| DrawCommand::RenderBitmap {
bitmap, bitmap,
transform_buffer, transform_buffer,
smoothing, smoothing,
blend_mode: TrivialBlend::Normal, blend_mode: TrivialBlend::Normal,
render_stage3d: false, render_stage3d: false,
},
);
} }
Command::RenderStage3D { bitmap, transform } => { });
}
fn render_stage3d(&mut self, bitmap: BitmapHandle, transform: Transform) {
let mut matrix = transform.matrix; let mut matrix = transform.matrix;
{ {
let texture = as_texture(&bitmap); let texture = as_texture(&bitmap);
@ -613,73 +687,55 @@ pub fn chunk_blends<'a>(
texture.texture.height() as f32, texture.texture.height() as f32,
); );
} }
add_to_current( self.add_to_current(matrix, transform.color_transform, |transform_buffer| {
&mut result, DrawCommand::RenderBitmap {
&mut current,
&mut transforms,
dynamic_transforms,
needs_stencil,
descriptors,
matrix,
transform.color_transform,
|transform_buffer| DrawCommand::RenderBitmap {
bitmap, bitmap,
transform_buffer, transform_buffer,
smoothing: false, smoothing: false,
blend_mode: TrivialBlend::Normal, blend_mode: TrivialBlend::Normal,
render_stage3d: true, render_stage3d: true,
},
);
} }
Command::RenderShape { shape, transform } => add_to_current( });
&mut result, }
&mut current,
&mut transforms, fn render_shape(&mut self, shape: ShapeHandle, transform: Transform) {
dynamic_transforms, self.add_to_current(
needs_stencil,
descriptors,
transform.matrix, transform.matrix,
transform.color_transform, transform.color_transform,
|transform_buffer| DrawCommand::RenderShape { |transform_buffer| DrawCommand::RenderShape {
shape, shape,
transform_buffer, transform_buffer,
}, },
), );
Command::DrawRect { color, matrix } => add_to_current( }
&mut result,
&mut current, fn draw_rect(&mut self, color: Color, matrix: Matrix) {
&mut transforms, self.add_to_current(
dynamic_transforms,
needs_stencil,
descriptors,
matrix, matrix,
ColorTransform::multiply_from(color), ColorTransform::multiply_from(color),
|transform_buffer| DrawCommand::DrawRect { transform_buffer }, |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() { fn push_mask(&mut self) {
result.push(Chunk::Draw(current, needs_stencil, transforms)); 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);
}
} }