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 gc_arena::Collect;
use ruffle_render::backend::ShapeHandle;
use ruffle_render::bitmap::{BitmapInfo, BitmapSource};
use ruffle_render::backend::{RenderBackend, ShapeHandle};
use ruffle_render::bitmap::{BitmapHandle, BitmapInfo, BitmapSize, BitmapSource};
use ruffle_render::bounding_box::BoundingBox;
use ruffle_render::commands::CommandHandler;
use ruffle_render::shape_utils::{DistilledShape, DrawCommand, DrawPath};
@ -402,8 +402,14 @@ impl Drawing {
}
impl BitmapSource for Drawing {
fn bitmap(&self, id: u16) -> Option<BitmapInfo> {
self.bitmaps.get(id as usize).cloned()
fn bitmap_size(&self, id: u16) -> Option<BitmapSize> {
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::tag_utils::SwfMovie;
use gc_arena::{Collect, MutationContext};
use ruffle_render::backend::RenderBackend;
use ruffle_render::bitmap::BitmapHandle;
use ruffle_render::utils::remove_invalid_jpeg_data;
use std::collections::HashMap;
use std::sync::{Arc, Weak};
@ -315,14 +317,16 @@ impl<'gc> MovieLibrary<'gc> {
}
impl<'gc> ruffle_render::bitmap::BitmapSource for MovieLibrary<'gc> {
fn bitmap(&self, id: u16) -> Option<ruffle_render::bitmap::BitmapInfo> {
self.get_bitmap(id).and_then(|bitmap| {
Some(ruffle_render::bitmap::BitmapInfo {
handle: bitmap.bitmap_handle()?,
fn bitmap_size(&self, id: u16) -> Option<ruffle_render::bitmap::BitmapSize> {
self.get_bitmap(id)
.map(|bitmap| ruffle_render::bitmap::BitmapSize {
width: bitmap.width(),
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,
) -> ShapeHandle {
let handle = ShapeHandle(self.shapes.len());
let data =
swf_shape_to_canvas_commands(&shape, bitmap_source, &self.bitmaps, &self.context);
let data = swf_shape_to_canvas_commands(&shape, bitmap_source, self);
self.shapes.push(data);
handle
}
@ -435,8 +434,7 @@ impl RenderBackend for WebCanvasRenderBackend {
bitmap_source: &dyn BitmapSource,
handle: ShapeHandle,
) {
let data =
swf_shape_to_canvas_commands(&shape, bitmap_source, &self.bitmaps, &self.context);
let data = swf_shape_to_canvas_commands(&shape, bitmap_source, self);
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(
shape: &DistilledShape,
bitmap_source: &dyn BitmapSource,
bitmaps: &FnvHashMap<BitmapHandle, BitmapData>,
context: &CanvasRenderingContext2d,
backend: &mut WebCanvasRenderBackend,
) -> ShapeData {
use ruffle_render::shape_utils::DrawPath;
use swf::{FillStyle, LineCapStyle, LineJoinStyle};
@ -840,16 +837,21 @@ fn swf_shape_to_canvas_commands(
let fill_style = match style {
FillStyle::Color(color) => CanvasFillStyle::Color(color.into()),
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(
create_radial_gradient(context, gradient, 0.0, true).unwrap(),
create_radial_gradient(&backend.context, gradient, 0.0, true).unwrap(),
),
FillStyle::FocalGradient {
gradient,
focal_point,
} => CanvasFillStyle::Gradient(
create_radial_gradient(context, gradient, focal_point.to_f64(), true)
create_radial_gradient(
&backend.context,
gradient,
focal_point.to_f64(),
true,
)
.unwrap(),
),
FillStyle::Bitmap {
@ -864,8 +866,7 @@ fn swf_shape_to_canvas_commands(
*is_smoothed,
*is_repeating,
bitmap_source,
bitmaps,
context,
backend,
) {
bitmap
} else {
@ -915,8 +916,7 @@ fn swf_shape_to_canvas_commands(
*is_smoothed,
*is_repeating,
bitmap_source,
bitmaps,
context,
backend,
) {
bitmap
} else {
@ -1159,12 +1159,11 @@ fn create_bitmap_pattern(
is_smoothed: bool,
is_repeating: bool,
bitmap_source: &dyn BitmapSource,
bitmaps: &FnvHashMap<BitmapHandle, BitmapData>,
context: &CanvasRenderingContext2d,
backend: &mut WebCanvasRenderBackend,
) -> Option<CanvasBitmap> {
if let Some(bitmap) = bitmap_source
.bitmap(id)
.and_then(|bitmap| bitmaps.get(&bitmap.handle))
.bitmap_handle(id, backend)
.and_then(|handle| backend.bitmaps.get(&handle))
{
let repeat = if !is_repeating {
// NOTE: The WebGL backend does clamping in this case, just like
@ -1174,7 +1173,9 @@ fn create_bitmap_pattern(
"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,
_ => {

View File

@ -1,5 +1,5 @@
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::error::Error;
use crate::shape_utils::DistilledShape;
@ -8,7 +8,10 @@ use swf::Color;
pub struct 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
}
}

View File

@ -1,3 +1,5 @@
use crate::backend::RenderBackend;
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub struct BitmapHandle(pub usize);
@ -9,12 +11,19 @@ pub struct BitmapInfo {
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.
///
/// 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.
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.

View File

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

View File

@ -630,6 +630,8 @@ impl WebGlRenderBackend {
.enable_vertex_attrib_array(program.vertex_color_location);
}
let num_vertex_attributes = program.num_vertex_attributes;
draws.push(match draw.draw_type {
TessDrawType::Color => Draw {
draw_type: DrawType::Color,
@ -662,7 +664,7 @@ impl WebGlRenderBackend {
TessDrawType::Bitmap(bitmap) => Draw {
draw_type: DrawType::Bitmap(BitmapDraw {
matrix: bitmap.matrix,
handle: bitmap.bitmap,
handle: bitmap_source.bitmap_handle(bitmap.bitmap_id, self).unwrap(),
is_smoothed: bitmap.is_smoothed,
is_repeating: bitmap.is_repeating,
}),
@ -682,7 +684,8 @@ impl WebGlRenderBackend {
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);
}
}

View File

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

View File

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