render: Don't use BitmapHandle in tessellator

Currently, we rely on ShapeTessellator being able to get a BitmapHandle
without a RenderBackend. With the upcoming BitmapData refactor,
we will always need a RenderBackend to get a BitmapHandle, which creates
borrow-checker issues in ShapeTessellator (which is stored in a
RenderBackend).

To solve this, we split BitmapSource.bitmap into two methods -
BitmapSource.bitmap and BitmapSource.bitmap_handle. ShapeTessellator
continues to use BitmapSource.bitmap, and uses the u16 bitmap id
instead of a BitmapHandle. The BitmapSource.bitmap_handle method
is used inside each render backend to convert the id to a BitmapHandle,
avoiding borrow-checker issues.
This commit is contained in:
Aaron Hill 2022-10-20 13:31:55 -05:00 committed by kmeisthax
parent 0aec23b468
commit bdadb17a95
9 changed files with 87 additions and 63 deletions

View File

@ -1,7 +1,7 @@
use crate::context::RenderContext; use crate::context::RenderContext;
use gc_arena::Collect; use gc_arena::Collect;
use ruffle_render::backend::ShapeHandle; use ruffle_render::backend::{RenderBackend, ShapeHandle};
use ruffle_render::bitmap::{BitmapInfo, BitmapSource}; use ruffle_render::bitmap::{BitmapHandle, BitmapInfo, BitmapSize, BitmapSource};
use ruffle_render::bounding_box::BoundingBox; use ruffle_render::bounding_box::BoundingBox;
use ruffle_render::commands::CommandHandler; use ruffle_render::commands::CommandHandler;
use ruffle_render::shape_utils::{DistilledShape, DrawCommand, DrawPath}; use ruffle_render::shape_utils::{DistilledShape, DrawCommand, DrawPath};
@ -402,8 +402,14 @@ impl Drawing {
} }
impl BitmapSource for Drawing { impl BitmapSource for Drawing {
fn bitmap(&self, id: u16) -> Option<BitmapInfo> { fn bitmap_size(&self, id: u16) -> Option<BitmapSize> {
self.bitmaps.get(id as usize).cloned() self.bitmaps.get(id as usize).map(|bm| BitmapSize {
width: bm.width,
height: bm.height,
})
}
fn bitmap_handle(&self, id: u16, _backend: &mut dyn RenderBackend) -> Option<BitmapHandle> {
self.bitmaps.get(id as usize).map(|bm| bm.handle)
} }
} }

View File

@ -8,6 +8,8 @@ use crate::prelude::*;
use crate::string::AvmString; use crate::string::AvmString;
use crate::tag_utils::SwfMovie; use crate::tag_utils::SwfMovie;
use gc_arena::{Collect, MutationContext}; use gc_arena::{Collect, MutationContext};
use ruffle_render::backend::RenderBackend;
use ruffle_render::bitmap::BitmapHandle;
use ruffle_render::utils::remove_invalid_jpeg_data; use ruffle_render::utils::remove_invalid_jpeg_data;
use std::collections::HashMap; use std::collections::HashMap;
use std::sync::{Arc, Weak}; use std::sync::{Arc, Weak};
@ -315,14 +317,16 @@ impl<'gc> MovieLibrary<'gc> {
} }
impl<'gc> ruffle_render::bitmap::BitmapSource for MovieLibrary<'gc> { impl<'gc> ruffle_render::bitmap::BitmapSource for MovieLibrary<'gc> {
fn bitmap(&self, id: u16) -> Option<ruffle_render::bitmap::BitmapInfo> { fn bitmap_size(&self, id: u16) -> Option<ruffle_render::bitmap::BitmapSize> {
self.get_bitmap(id).and_then(|bitmap| { self.get_bitmap(id)
Some(ruffle_render::bitmap::BitmapInfo { .map(|bitmap| ruffle_render::bitmap::BitmapSize {
handle: bitmap.bitmap_handle()?,
width: bitmap.width(), width: bitmap.width(),
height: bitmap.height(), height: bitmap.height(),
}) })
}) }
fn bitmap_handle(&self, id: u16, _backend: &mut dyn RenderBackend) -> Option<BitmapHandle> {
self.get_bitmap(id)
.and_then(|bitmap| bitmap.bitmap_handle())
} }
} }

View File

@ -423,8 +423,7 @@ impl RenderBackend for WebCanvasRenderBackend {
bitmap_source: &dyn BitmapSource, bitmap_source: &dyn BitmapSource,
) -> ShapeHandle { ) -> ShapeHandle {
let handle = ShapeHandle(self.shapes.len()); let handle = ShapeHandle(self.shapes.len());
let data = let data = swf_shape_to_canvas_commands(&shape, bitmap_source, self);
swf_shape_to_canvas_commands(&shape, bitmap_source, &self.bitmaps, &self.context);
self.shapes.push(data); self.shapes.push(data);
handle handle
} }
@ -435,8 +434,7 @@ impl RenderBackend for WebCanvasRenderBackend {
bitmap_source: &dyn BitmapSource, bitmap_source: &dyn BitmapSource,
handle: ShapeHandle, handle: ShapeHandle,
) { ) {
let data = let data = swf_shape_to_canvas_commands(&shape, bitmap_source, self);
swf_shape_to_canvas_commands(&shape, bitmap_source, &self.bitmaps, &self.context);
self.shapes[handle.0] = data; self.shapes[handle.0] = data;
} }
@ -810,8 +808,7 @@ fn draw_commands_to_path2d(commands: &[DrawCommand], is_closed: bool) -> Path2d
fn swf_shape_to_canvas_commands( fn swf_shape_to_canvas_commands(
shape: &DistilledShape, shape: &DistilledShape,
bitmap_source: &dyn BitmapSource, bitmap_source: &dyn BitmapSource,
bitmaps: &FnvHashMap<BitmapHandle, BitmapData>, backend: &mut WebCanvasRenderBackend,
context: &CanvasRenderingContext2d,
) -> ShapeData { ) -> ShapeData {
use ruffle_render::shape_utils::DrawPath; use ruffle_render::shape_utils::DrawPath;
use swf::{FillStyle, LineCapStyle, LineJoinStyle}; use swf::{FillStyle, LineCapStyle, LineJoinStyle};
@ -840,16 +837,21 @@ fn swf_shape_to_canvas_commands(
let fill_style = match style { let fill_style = match style {
FillStyle::Color(color) => CanvasFillStyle::Color(color.into()), FillStyle::Color(color) => CanvasFillStyle::Color(color.into()),
FillStyle::LinearGradient(gradient) => CanvasFillStyle::Gradient( FillStyle::LinearGradient(gradient) => CanvasFillStyle::Gradient(
create_linear_gradient(context, gradient, true).unwrap(), create_linear_gradient(&backend.context, gradient, true).unwrap(),
), ),
FillStyle::RadialGradient(gradient) => CanvasFillStyle::Gradient( FillStyle::RadialGradient(gradient) => CanvasFillStyle::Gradient(
create_radial_gradient(context, gradient, 0.0, true).unwrap(), create_radial_gradient(&backend.context, gradient, 0.0, true).unwrap(),
), ),
FillStyle::FocalGradient { FillStyle::FocalGradient {
gradient, gradient,
focal_point, focal_point,
} => CanvasFillStyle::Gradient( } => CanvasFillStyle::Gradient(
create_radial_gradient(context, gradient, focal_point.to_f64(), true) create_radial_gradient(
&backend.context,
gradient,
focal_point.to_f64(),
true,
)
.unwrap(), .unwrap(),
), ),
FillStyle::Bitmap { FillStyle::Bitmap {
@ -864,8 +866,7 @@ fn swf_shape_to_canvas_commands(
*is_smoothed, *is_smoothed,
*is_repeating, *is_repeating,
bitmap_source, bitmap_source,
bitmaps, backend,
context,
) { ) {
bitmap bitmap
} else { } else {
@ -915,8 +916,7 @@ fn swf_shape_to_canvas_commands(
*is_smoothed, *is_smoothed,
*is_repeating, *is_repeating,
bitmap_source, bitmap_source,
bitmaps, backend,
context,
) { ) {
bitmap bitmap
} else { } else {
@ -1159,12 +1159,11 @@ fn create_bitmap_pattern(
is_smoothed: bool, is_smoothed: bool,
is_repeating: bool, is_repeating: bool,
bitmap_source: &dyn BitmapSource, bitmap_source: &dyn BitmapSource,
bitmaps: &FnvHashMap<BitmapHandle, BitmapData>, backend: &mut WebCanvasRenderBackend,
context: &CanvasRenderingContext2d,
) -> Option<CanvasBitmap> { ) -> Option<CanvasBitmap> {
if let Some(bitmap) = bitmap_source if let Some(bitmap) = bitmap_source
.bitmap(id) .bitmap_handle(id, backend)
.and_then(|bitmap| bitmaps.get(&bitmap.handle)) .and_then(|handle| backend.bitmaps.get(&handle))
{ {
let repeat = if !is_repeating { let repeat = if !is_repeating {
// NOTE: The WebGL backend does clamping in this case, just like // NOTE: The WebGL backend does clamping in this case, just like
@ -1174,7 +1173,9 @@ fn create_bitmap_pattern(
"repeat" "repeat"
}; };
let pattern = match context.create_pattern_with_html_canvas_element(&bitmap.canvas, repeat) let pattern = match backend
.context
.create_pattern_with_html_canvas_element(&bitmap.canvas, repeat)
{ {
Ok(Some(pattern)) => pattern, Ok(Some(pattern)) => pattern,
_ => { _ => {

View File

@ -1,5 +1,5 @@
use crate::backend::{RenderBackend, ShapeHandle, ViewportDimensions}; use crate::backend::{RenderBackend, ShapeHandle, ViewportDimensions};
use crate::bitmap::{Bitmap, BitmapHandle, BitmapInfo, BitmapSource}; use crate::bitmap::{Bitmap, BitmapHandle, BitmapSize, BitmapSource};
use crate::commands::CommandList; use crate::commands::CommandList;
use crate::error::Error; use crate::error::Error;
use crate::shape_utils::DistilledShape; use crate::shape_utils::DistilledShape;
@ -8,7 +8,10 @@ use swf::Color;
pub struct NullBitmapSource; pub struct NullBitmapSource;
impl BitmapSource for NullBitmapSource { impl BitmapSource for NullBitmapSource {
fn bitmap(&self, _id: u16) -> Option<BitmapInfo> { fn bitmap_size(&self, _id: u16) -> Option<BitmapSize> {
None
}
fn bitmap_handle(&self, _id: u16, _renderer: &mut dyn RenderBackend) -> Option<BitmapHandle> {
None None
} }
} }

View File

@ -1,3 +1,5 @@
use crate::backend::RenderBackend;
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub struct BitmapHandle(pub usize); pub struct BitmapHandle(pub usize);
@ -9,12 +11,19 @@ pub struct BitmapInfo {
pub height: u16, pub height: u16,
} }
#[derive(Copy, Clone, Debug)]
pub struct BitmapSize {
pub width: u16,
pub height: u16,
}
/// An object that returns a bitmap given an ID. /// An object that returns a bitmap given an ID.
/// ///
/// This is used by render backends to get the bitmap used in a bitmap fill. /// This is used by render backends to get the bitmap used in a bitmap fill.
/// For movie libraries, this will return the bitmap with the given character ID. /// For movie libraries, this will return the bitmap with the given character ID.
pub trait BitmapSource { pub trait BitmapSource {
fn bitmap(&self, id: u16) -> Option<BitmapInfo>; fn bitmap_size(&self, id: u16) -> Option<BitmapSize>;
fn bitmap_handle(&self, id: u16, renderer: &mut dyn RenderBackend) -> Option<BitmapHandle>;
} }
/// Decoded bitmap data from an SWF tag. /// Decoded bitmap data from an SWF tag.

View File

@ -1,4 +1,4 @@
use crate::bitmap::{BitmapHandle, BitmapSource}; use crate::bitmap::BitmapSource;
use crate::shape_utils::{DistilledShape, DrawCommand, DrawPath}; use crate::shape_utils::{DistilledShape, DrawCommand, DrawPath};
use lyon::path::Path; use lyon::path::Path;
use lyon::tessellation::{ use lyon::tessellation::{
@ -90,7 +90,7 @@ impl ShapeTessellator {
is_smoothed, is_smoothed,
is_repeating, is_repeating,
} => { } => {
if let Some(bitmap) = bitmap_source.bitmap(*id) { if let Some(bitmap) = bitmap_source.bitmap_size(*id) {
( (
DrawType::Bitmap(Bitmap { DrawType::Bitmap(Bitmap {
matrix: swf_bitmap_to_gl_matrix( matrix: swf_bitmap_to_gl_matrix(
@ -98,7 +98,7 @@ impl ShapeTessellator {
bitmap.width.into(), bitmap.width.into(),
bitmap.height.into(), bitmap.height.into(),
), ),
bitmap: bitmap.handle, bitmap_id: *id,
is_smoothed: *is_smoothed, is_smoothed: *is_smoothed,
is_repeating: *is_repeating, is_repeating: *is_repeating,
}), }),
@ -264,7 +264,7 @@ pub struct Vertex {
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct Bitmap { pub struct Bitmap {
pub matrix: [[f32; 3]; 3], pub matrix: [[f32; 3]; 3],
pub bitmap: BitmapHandle, pub bitmap_id: u16,
pub is_smoothed: bool, pub is_smoothed: bool,
pub is_repeating: bool, pub is_repeating: bool,
} }

View File

@ -630,6 +630,8 @@ impl WebGlRenderBackend {
.enable_vertex_attrib_array(program.vertex_color_location); .enable_vertex_attrib_array(program.vertex_color_location);
} }
let num_vertex_attributes = program.num_vertex_attributes;
draws.push(match draw.draw_type { draws.push(match draw.draw_type {
TessDrawType::Color => Draw { TessDrawType::Color => Draw {
draw_type: DrawType::Color, draw_type: DrawType::Color,
@ -662,7 +664,7 @@ impl WebGlRenderBackend {
TessDrawType::Bitmap(bitmap) => Draw { TessDrawType::Bitmap(bitmap) => Draw {
draw_type: DrawType::Bitmap(BitmapDraw { draw_type: DrawType::Bitmap(BitmapDraw {
matrix: bitmap.matrix, matrix: bitmap.matrix,
handle: bitmap.bitmap, handle: bitmap_source.bitmap_handle(bitmap.bitmap_id, self).unwrap(),
is_smoothed: bitmap.is_smoothed, is_smoothed: bitmap.is_smoothed,
is_repeating: bitmap.is_repeating, is_repeating: bitmap.is_repeating,
}), }),
@ -682,7 +684,8 @@ impl WebGlRenderBackend {
self.bind_vertex_array(None); self.bind_vertex_array(None);
for i in program.num_vertex_attributes..NUM_VERTEX_ATTRIBUTES { // Don't use 'program' here in order to satisfy the borrow checker
for i in num_vertex_attributes..NUM_VERTEX_ATTRIBUTES {
self.gl.disable_vertex_attrib_array(i); self.gl.disable_vertex_attrib_array(i);
} }
} }

View File

@ -200,13 +200,7 @@ impl<T: RenderTarget> WgpuRenderBackend<T> {
let mut draws = Vec::with_capacity(lyon_mesh.len()); let mut draws = Vec::with_capacity(lyon_mesh.len());
for draw in lyon_mesh { for draw in lyon_mesh {
let draw_id = draws.len(); let draw_id = draws.len();
draws.push(Draw::new( draws.push(Draw::new(self, bitmap_source, draw, shape_id, draw_id));
&self.descriptors,
draw,
shape_id,
draw_id,
&self.bitmap_registry,
));
} }
Mesh { draws } Mesh { draws }
@ -223,6 +217,10 @@ impl<T: RenderTarget> WgpuRenderBackend<T> {
pub fn device(&self) -> &wgpu::Device { pub fn device(&self) -> &wgpu::Device {
&self.descriptors.device &self.descriptors.device
} }
pub fn bitmap_registry(&self) -> &FnvHashMap<BitmapHandle, RegistryData> {
&self.bitmap_registry
}
} }
impl<T: RenderTarget + 'static> RenderBackend for WgpuRenderBackend<T> { impl<T: RenderTarget + 'static> RenderBackend for WgpuRenderBackend<T> {

View File

@ -1,9 +1,11 @@
use crate::backend::WgpuRenderBackend;
use crate::target::RenderTarget;
use crate::{ use crate::{
create_buffer_with_data, Descriptors, GradientStorage, GradientUniforms, RegistryData, create_buffer_with_data, Descriptors, GradientStorage, GradientUniforms, TextureTransforms,
TextureTransforms, Vertex, Vertex,
}; };
use fnv::FnvHashMap;
use ruffle_render::bitmap::BitmapHandle; use ruffle_render::bitmap::BitmapSource;
use ruffle_render::tessellator::{Bitmap, Draw as LyonDraw, DrawType as TessDrawType, Gradient}; use ruffle_render::tessellator::{Bitmap, Draw as LyonDraw, DrawType as TessDrawType, Gradient};
use swf::CharacterId; use swf::CharacterId;
@ -22,14 +24,15 @@ pub struct Draw {
} }
impl Draw { impl Draw {
pub fn new( pub fn new<T: RenderTarget>(
descriptors: &Descriptors, backend: &mut WgpuRenderBackend<T>,
source: &dyn BitmapSource,
draw: LyonDraw, draw: LyonDraw,
shape_id: CharacterId, shape_id: CharacterId,
draw_id: usize, draw_id: usize,
bitmap_registry: &FnvHashMap<BitmapHandle, RegistryData>,
) -> Self { ) -> Self {
let vertices: Vec<_> = draw.vertices.into_iter().map(Vertex::from).collect(); let vertices: Vec<_> = draw.vertices.into_iter().map(Vertex::from).collect();
let descriptors = backend.descriptors();
let vertex_buffer = create_buffer_with_data( let vertex_buffer = create_buffer_with_data(
&descriptors.device, &descriptors.device,
bytemuck::cast_slice(&vertices), bytemuck::cast_slice(&vertices),
@ -61,13 +64,7 @@ impl Draw {
num_mask_indices: draw.mask_index_count, num_mask_indices: draw.mask_index_count,
}, },
TessDrawType::Bitmap(bitmap) => Draw { TessDrawType::Bitmap(bitmap) => Draw {
draw_type: DrawType::bitmap( draw_type: DrawType::bitmap(backend, source, bitmap, shape_id, draw_id),
&descriptors,
&bitmap_registry,
bitmap,
shape_id,
draw_id,
),
vertex_buffer, vertex_buffer,
index_buffer, index_buffer,
num_indices: index_count, num_indices: index_count,
@ -179,20 +176,23 @@ impl DrawType {
} }
} }
pub fn bitmap( pub fn bitmap<T: RenderTarget>(
descriptors: &Descriptors, backend: &mut WgpuRenderBackend<T>,
bitmap_registry: &FnvHashMap<BitmapHandle, RegistryData>, source: &dyn BitmapSource,
bitmap: Bitmap, bitmap: Bitmap,
shape_id: CharacterId, shape_id: CharacterId,
draw_id: usize, draw_id: usize,
) -> Self { ) -> Self {
let entry = bitmap_registry.get(&bitmap.bitmap).unwrap(); let bitmap_handle = source.bitmap_handle(bitmap.bitmap_id, backend).unwrap();
let entry = backend.bitmap_registry().get(&bitmap_handle).unwrap();
let descriptors = backend.descriptors();
let texture_view = entry let texture_view = entry
.texture_wrapper .texture_wrapper
.texture .texture
.create_view(&Default::default()); .create_view(&Default::default());
let texture_transforms = create_texture_transforms( let texture_transforms = create_texture_transforms(
&descriptors.device, &backend.descriptors().device,
&bitmap.matrix, &bitmap.matrix,
create_debug_label!( create_debug_label!(
"Shape {} draw {} textransforms ubo transfer buffer", "Shape {} draw {} textransforms ubo transfer buffer",