diff --git a/core/src/backend/render.rs b/core/src/backend/render.rs index 9045b3051..d9a902db8 100644 --- a/core/src/backend/render.rs +++ b/core/src/backend/render.rs @@ -3,6 +3,7 @@ pub use crate::{transform::Transform, Color}; use downcast_rs::Downcast; use std::io::Read; pub use swf; +use swf::Matrix; pub trait RenderBackend: Downcast { fn set_viewport_dimensions(&mut self, width: u32, height: u32); @@ -34,6 +35,7 @@ pub trait RenderBackend: Downcast { fn begin_frame(&mut self, clear: Color); fn render_bitmap(&mut self, bitmap: BitmapHandle, transform: &Transform); fn render_shape(&mut self, shape: ShapeHandle, transform: &Transform); + fn draw_rect(&mut self, color: Color, matrix: &Matrix); fn end_frame(&mut self); fn draw_letterbox(&mut self, letterbox: Letterbox); fn push_mask(&mut self); @@ -137,6 +139,7 @@ impl RenderBackend for NullRenderer { fn end_frame(&mut self) {} fn render_bitmap(&mut self, _bitmap: BitmapHandle, _transform: &Transform) {} fn render_shape(&mut self, _shape: ShapeHandle, _transform: &Transform) {} + fn draw_rect(&mut self, _color: Color, _matrix: &Matrix) {} fn draw_letterbox(&mut self, _letterbox: Letterbox) {} fn push_mask(&mut self) {} fn activate_mask(&mut self) {} diff --git a/render/canvas/src/lib.rs b/render/canvas/src/lib.rs index c457cce50..6dab58e98 100644 --- a/render/canvas/src/lib.rs +++ b/render/canvas/src/lib.rs @@ -5,6 +5,7 @@ use ruffle_core::backend::render::{ }; use ruffle_core::color_transform::ColorTransform; use ruffle_core::shape_utils::{DistilledShape, DrawCommand}; +use ruffle_core::swf::Matrix; use ruffle_web_common::JsResult; use std::collections::HashMap; use std::convert::TryInto; @@ -305,9 +306,7 @@ impl WebCanvasRenderBackend { #[allow(clippy::float_cmp)] #[inline] - fn set_transform(&mut self, transform: &Transform) { - let matrix = transform.matrix; - + fn set_transform(&mut self, matrix: &Matrix) { self.context .set_transform( matrix.a.into(), @@ -559,7 +558,7 @@ impl RenderBackend for WebCanvasRenderBackend { } fn render_bitmap(&mut self, bitmap: BitmapHandle, transform: &Transform) { - self.set_transform(transform); + self.set_transform(&transform.matrix); self.set_color_filter(transform); if let Some(bitmap) = self.bitmaps.get(bitmap.0) { let _ = self @@ -570,7 +569,7 @@ impl RenderBackend for WebCanvasRenderBackend { } fn render_shape(&mut self, shape: ShapeHandle, transform: &Transform) { - self.set_transform(transform); + self.set_transform(&transform.matrix); if let Some(shape) = self.shapes.get(shape.0) { for command in shape.0.iter() { match command { @@ -629,6 +628,25 @@ impl RenderBackend for WebCanvasRenderBackend { } } + fn draw_rect(&mut self, color: Color, matrix: &Matrix) { + self.set_transform(matrix); + self.clear_color_filter(); + + self.context.set_fill_style( + &format!( + "rgba({},{},{},{})", + color.r, + color.g, + color.b, + f32::from(color.a) / 255.0 + ) + .into(), + ); + self.context.fill_rect(0.0, 0.0, 1.0, 1.0); + + self.clear_color_filter(); + } + fn draw_letterbox(&mut self, letterbox: Letterbox) { self.context.reset_transform().unwrap(); self.context.set_fill_style(&"black".into()); @@ -717,7 +735,6 @@ fn swf_shape_to_svg( ) -> ShapeData { use fnv::FnvHashSet; use ruffle_core::shape_utils::DrawPath; - use ruffle_core::swf::Matrix; use svg::node::element::{ path::Data, Definitions, Filter, Image, LinearGradient, Path as SvgPath, Pattern, RadialGradient, Stop, diff --git a/render/webgl/src/lib.rs b/render/webgl/src/lib.rs index 0ab587672..dc883d2f1 100644 --- a/render/webgl/src/lib.rs +++ b/render/webgl/src/lib.rs @@ -4,6 +4,7 @@ use ruffle_core::backend::render::{ RenderBackend, ShapeHandle, Transform, }; use ruffle_core::shape_utils::DistilledShape; +use ruffle_core::swf::Matrix; use ruffle_render_common_tess::{GradientSpread, GradientType, ShapeTessellator, Vertex}; use ruffle_web_common::JsResult; use wasm_bindgen::{JsCast, JsValue}; @@ -899,7 +900,6 @@ impl RenderBackend for WebGlRenderBackend { } // Scale the quad to the bitmap's dimensions. - use ruffle_core::swf::Matrix; let scale_transform = Transform { matrix: transform.matrix * Matrix { @@ -1074,6 +1074,76 @@ impl RenderBackend for WebGlRenderBackend { } } + fn draw_rect(&mut self, color: Color, matrix: &Matrix) { + let world_matrix = [ + [matrix.a, matrix.b, 0.0, 0.0], + [matrix.c, matrix.d, 0.0, 0.0], + [0.0, 0.0, 1.0, 0.0], + [ + matrix.tx.to_pixels() as f32, + matrix.ty.to_pixels() as f32, + 0.0, + 1.0, + ], + ]; + + let mult_color = [1.0, 1.0, 1.0, 1.0]; + + let add_color = [ + color.r as f32 * 255.0, + color.g as f32 * 255.0, + color.b as f32 * 255.0, + color.a as f32 * 255.0, + ]; + + self.set_stencil_state(); + + let program = &self.color_program; + let src_blend = Gl::SRC_ALPHA; + let dst_blend = Gl::ONE_MINUS_SRC_ALPHA; + + // Set common render state, while minimizing unnecessary state changes. + // TODO: Using designated layout specifiers in WebGL2/OpenGL ES 3, we could guarantee that uniforms + // are in the same location between shaders, and avoid changing them unless necessary. + if program as *const ShaderProgram != self.active_program { + self.gl.use_program(Some(&program.program)); + self.active_program = program as *const ShaderProgram; + + program.uniform_matrix4fv(&self.gl, ShaderUniform::ViewMatrix, &self.view_matrix); + + self.mult_color = None; + self.add_color = None; + + if (src_blend, dst_blend) != self.blend_func { + self.gl.blend_func(src_blend, dst_blend); + self.blend_func = (src_blend, dst_blend); + } + }; + + self.color_program + .uniform_matrix4fv(&self.gl, ShaderUniform::WorldMatrix, &world_matrix); + if Some(mult_color) != self.mult_color { + self.color_program + .uniform4fv(&self.gl, ShaderUniform::MultColor, &mult_color); + self.mult_color = Some(mult_color); + } + if Some(add_color) != self.add_color { + self.color_program + .uniform4fv(&self.gl, ShaderUniform::AddColor, &add_color); + self.add_color = Some(add_color); + } + + let quad = &self.meshes[self.quad_shape.0]; + self.bind_vertex_array(Some(&quad.draws[0].vao)); + + self.gl.draw_elements_with_i32( + Gl::TRIANGLES, + quad.draws[0].num_indices, + Gl::UNSIGNED_SHORT, + 0, + ); + } + fn draw_letterbox(&mut self, letterbox: Letterbox) { self.set_stencil_state(); @@ -1345,3 +1415,5 @@ impl ShaderProgram { ); } } + +impl WebGlRenderBackend {} diff --git a/render/wgpu/src/lib.rs b/render/wgpu/src/lib.rs index 4836ebac6..ca8ce75c3 100644 --- a/render/wgpu/src/lib.rs +++ b/render/wgpu/src/lib.rs @@ -37,6 +37,7 @@ mod pipelines; mod shapes; pub mod target; +use ruffle_core::swf::{Matrix, Twips}; pub use wgpu; pub struct WgpuRenderBackend { @@ -707,116 +708,6 @@ impl WgpuRenderBackend { pub fn device(&self) -> &wgpu::Device { &self.device } - - fn draw_rect(&mut self, x: f32, y: f32, width: f32, height: f32, color: Color) { - let (frame_output, encoder) = if let Some((frame_output, encoder)) = &mut self.current_frame - { - (frame_output, encoder) - } else { - return; - }; - - let world_matrix = [ - [width, 0.0, 0.0, 0.0], - [0.0, height, 0.0, 0.0], - [0.0, 0.0, 1.0, 0.0], - [x, y, 0.0, 1.0], - ]; - - let mult_color = [ - f32::from(color.r) / 255.0, - f32::from(color.g) / 255.0, - f32::from(color.b) / 255.0, - f32::from(color.a) / 255.0, - ]; - - let add_color = [0.0, 0.0, 0.0, 0.0]; - - let transforms_ubo = create_buffer_with_data( - &self.device, - bytemuck::cast_slice(&[Transforms { - view_matrix: self.view_matrix, - world_matrix, - }]), - wgpu::BufferUsage::UNIFORM, - create_debug_label!("Rectangle transfer buffer"), - ); - - let colors_ubo = create_buffer_with_data( - &self.device, - bytemuck::cast_slice(&[ColorAdjustments { - mult_color, - add_color, - }]), - wgpu::BufferUsage::UNIFORM, - create_debug_label!("Rectangle colors transfer buffer"), - ); - - let bind_group_label = create_debug_label!("Rectangle bind group"); - let bind_group = self.device.create_bind_group(&wgpu::BindGroupDescriptor { - layout: &self.pipelines.color.bind_layout, - entries: &[ - wgpu::BindGroupEntry { - binding: 0, - resource: wgpu::BindingResource::Buffer( - transforms_ubo.slice(0..std::mem::size_of::() as u64), - ), - }, - wgpu::BindGroupEntry { - binding: 1, - resource: wgpu::BindingResource::Buffer( - colors_ubo.slice(0..std::mem::size_of::() as u64), - ), - }, - ], - label: bind_group_label.as_deref(), - }); - - let (color_attachment, resolve_target) = if self.msaa_sample_count >= 2 { - (&self.frame_buffer_view, Some(frame_output.view())) - } else { - (frame_output.view(), None) - }; - let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { - color_attachments: &[wgpu::RenderPassColorAttachmentDescriptor { - attachment: color_attachment, - resolve_target, - ops: wgpu::Operations { - load: wgpu::LoadOp::Load, - store: true, - }, - }], - depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachmentDescriptor { - attachment: &self.depth_texture_view, - depth_ops: Some(wgpu::Operations { - load: wgpu::LoadOp::Load, - store: true, - }), - stencil_ops: Some(wgpu::Operations { - load: wgpu::LoadOp::Load, - store: true, - }), - }), - }); - - render_pass.set_pipeline(&self.pipelines.color.pipeline_for( - self.num_masks, - self.num_masks_active, - self.test_stencil_mask, - self.write_stencil_mask, - )); - render_pass.set_bind_group(0, &bind_group, &[]); - render_pass.set_vertex_buffer(0, self.quad_vbo.slice(..)); - render_pass.set_index_buffer(self.quad_ibo.slice(..)); - - if self.num_masks_active < self.num_masks { - render_pass.set_stencil_reference(self.write_stencil_mask); - } else { - render_pass.set_stencil_reference(self.test_stencil_mask); - } - - render_pass.draw_indexed(0..6, 0, 0..1); - } } impl RenderBackend for WgpuRenderBackend { @@ -983,7 +874,6 @@ impl RenderBackend for WgpuRenderBackend { return; }; - use ruffle_core::swf::Matrix; let transform = Transform { matrix: transform.matrix * Matrix { @@ -1249,6 +1139,121 @@ impl RenderBackend for WgpuRenderBackend { } } + fn draw_rect(&mut self, color: Color, matrix: &Matrix) { + let (frame_output, encoder) = if let Some((frame_output, encoder)) = &mut self.current_frame + { + (frame_output, encoder) + } else { + return; + }; + + let world_matrix = [ + [matrix.a, matrix.b, 0.0, 0.0], + [matrix.c, matrix.d, 0.0, 0.0], + [0.0, 0.0, 1.0, 0.0], + [ + matrix.tx.to_pixels() as f32, + matrix.ty.to_pixels() as f32, + 0.0, + 1.0, + ], + ]; + + let mult_color = [ + f32::from(color.r) / 255.0, + f32::from(color.g) / 255.0, + f32::from(color.b) / 255.0, + f32::from(color.a) / 255.0, + ]; + + let add_color = [0.0, 0.0, 0.0, 0.0]; + + let transforms_ubo = create_buffer_with_data( + &self.device, + bytemuck::cast_slice(&[Transforms { + view_matrix: self.view_matrix, + world_matrix, + }]), + wgpu::BufferUsage::UNIFORM, + create_debug_label!("Rectangle transfer buffer"), + ); + + let colors_ubo = create_buffer_with_data( + &self.device, + bytemuck::cast_slice(&[ColorAdjustments { + mult_color, + add_color, + }]), + wgpu::BufferUsage::UNIFORM, + create_debug_label!("Rectangle colors transfer buffer"), + ); + + let bind_group_label = create_debug_label!("Rectangle bind group"); + let bind_group = self.device.create_bind_group(&wgpu::BindGroupDescriptor { + layout: &self.pipelines.color.bind_layout, + entries: &[ + wgpu::BindGroupEntry { + binding: 0, + resource: wgpu::BindingResource::Buffer( + transforms_ubo.slice(0..std::mem::size_of::() as u64), + ), + }, + wgpu::BindGroupEntry { + binding: 1, + resource: wgpu::BindingResource::Buffer( + colors_ubo.slice(0..std::mem::size_of::() as u64), + ), + }, + ], + label: bind_group_label.as_deref(), + }); + + let (color_attachment, resolve_target) = if self.msaa_sample_count >= 2 { + (&self.frame_buffer_view, Some(frame_output.view())) + } else { + (frame_output.view(), None) + }; + let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + color_attachments: &[wgpu::RenderPassColorAttachmentDescriptor { + attachment: color_attachment, + resolve_target, + ops: wgpu::Operations { + load: wgpu::LoadOp::Load, + store: true, + }, + }], + depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachmentDescriptor { + attachment: &self.depth_texture_view, + depth_ops: Some(wgpu::Operations { + load: wgpu::LoadOp::Load, + store: true, + }), + stencil_ops: Some(wgpu::Operations { + load: wgpu::LoadOp::Load, + store: true, + }), + }), + }); + + render_pass.set_pipeline(&self.pipelines.color.pipeline_for( + self.num_masks, + self.num_masks_active, + self.test_stencil_mask, + self.write_stencil_mask, + )); + render_pass.set_bind_group(0, &bind_group, &[]); + render_pass.set_vertex_buffer(0, self.quad_vbo.slice(..)); + render_pass.set_index_buffer(self.quad_ibo.slice(..)); + + if self.num_masks_active < self.num_masks { + render_pass.set_stencil_reference(self.write_stencil_mask); + } else { + render_pass.set_stencil_reference(self.test_stencil_mask); + } + + render_pass.draw_indexed(0..6, 0, 0..1); + } + fn end_frame(&mut self) { if let Some((_frame, encoder)) = self.current_frame.take() { let register_encoder_label = create_debug_label!("Register encoder"); @@ -1272,54 +1277,66 @@ impl RenderBackend for WgpuRenderBackend { Letterbox::None => {} Letterbox::Letterbox(margin) => { self.draw_rect( - 0.0, - 0.0, - self.viewport_width, - margin, Color { r: 0, g: 0, b: 0, a: 255, }, + &Matrix::create_box( + self.viewport_width, + margin, + 0.0, + Twips::zero(), + Twips::zero(), + ), ); self.draw_rect( - 0.0, - self.viewport_height - margin, - self.viewport_width, - margin, Color { r: 0, g: 0, b: 0, a: 255, }, + &Matrix::create_box( + self.viewport_width, + margin, + 0.0, + Twips::zero(), + Twips::from_pixels((self.viewport_height - margin) as f64), + ), ); } Letterbox::Pillarbox(margin) => { self.draw_rect( - 0.0, - 0.0, - margin, - self.viewport_height, Color { r: 0, g: 0, b: 0, a: 255, }, + &Matrix::create_box( + margin, + self.viewport_height, + 0.0, + Twips::zero(), + Twips::zero(), + ), ); self.draw_rect( - self.viewport_width - margin, - 0.0, - margin, - self.viewport_height, Color { r: 0, g: 0, b: 0, a: 255, }, + &Matrix::create_box( + margin, + self.viewport_height, + 0.0, + Twips::from_pixels((self.viewport_width - margin) as f64), + Twips::zero(), + ), ); } }