render: Introduced render commands, moved to a command list model instead of direct rendering
This commit is contained in:
parent
c7f420dde5
commit
267ea0fd13
|
@ -11,6 +11,7 @@ use crate::display_object::TDisplayObject;
|
|||
use bitflags::bitflags;
|
||||
use ruffle_render::backend::RenderBackend;
|
||||
use ruffle_render::bitmap::{Bitmap, BitmapFormat, BitmapHandle};
|
||||
use ruffle_render::commands::{CommandHandler, CommandList};
|
||||
use ruffle_render::transform::Transform;
|
||||
use std::ops::Range;
|
||||
|
||||
|
@ -971,54 +972,50 @@ impl<'gc> BitmapData<'gc> {
|
|||
let mut transform_stack = ruffle_render::transform::TransformStack::new();
|
||||
transform_stack.push(&transform);
|
||||
let handle = self.bitmap_handle(context.renderer).unwrap();
|
||||
let mut commands = CommandList::new();
|
||||
|
||||
let mut render_context = RenderContext {
|
||||
renderer: context.renderer,
|
||||
commands: &mut commands,
|
||||
gc_context: context.gc_context,
|
||||
ui: context.ui,
|
||||
library: &context.library,
|
||||
transform_stack: &mut transform_stack,
|
||||
is_offscreen: true,
|
||||
stage: context.stage,
|
||||
clip_depth_stack: vec![],
|
||||
allow_mask: true,
|
||||
};
|
||||
|
||||
// Make the screen opacity match the opacity of this bitmap
|
||||
let initial_alpha = if self.transparency { 0 } else { 0xFF };
|
||||
render_context.commands.push_blend_mode(blend_mode);
|
||||
match &mut source {
|
||||
IBitmapDrawable::BitmapData(data) => {
|
||||
let source_handle = data
|
||||
.write(context.gc_context)
|
||||
.bitmap_handle(render_context.renderer)
|
||||
.unwrap();
|
||||
render_context.commands.render_bitmap(
|
||||
source_handle,
|
||||
render_context.transform_stack.transform(),
|
||||
smoothing,
|
||||
);
|
||||
}
|
||||
IBitmapDrawable::DisplayObject(object) => {
|
||||
// Note that we do *not* use `render_base`,
|
||||
// as we want to ignore the object's mask and normal transform
|
||||
object.render_self(&mut render_context);
|
||||
}
|
||||
}
|
||||
render_context.commands.pop_blend_mode();
|
||||
|
||||
let image = context.renderer.render_offscreen(
|
||||
handle,
|
||||
bitmapdata_width,
|
||||
bitmapdata_height,
|
||||
&mut |renderer| {
|
||||
let mut render_context = RenderContext {
|
||||
renderer,
|
||||
gc_context: context.gc_context,
|
||||
ui: context.ui,
|
||||
library: &context.library,
|
||||
transform_stack: &mut transform_stack,
|
||||
is_offscreen: true,
|
||||
stage: context.stage,
|
||||
clip_depth_stack: vec![],
|
||||
allow_mask: true,
|
||||
};
|
||||
|
||||
// Make the screen opacity match the opacity of this bitmap
|
||||
let initial_alpha = if self.transparency { 0 } else { 0xFF };
|
||||
|
||||
render_context
|
||||
.renderer
|
||||
.begin_frame(swf::Color::from_rgb(0x000000, initial_alpha));
|
||||
render_context.renderer.push_blend_mode(blend_mode);
|
||||
match &mut source {
|
||||
IBitmapDrawable::BitmapData(data) => {
|
||||
let source_handle = data
|
||||
.write(context.gc_context)
|
||||
.bitmap_handle(render_context.renderer)
|
||||
.unwrap();
|
||||
render_context.renderer.render_bitmap(
|
||||
source_handle,
|
||||
render_context.transform_stack.transform(),
|
||||
smoothing,
|
||||
);
|
||||
}
|
||||
IBitmapDrawable::DisplayObject(object) => {
|
||||
// Note that we do *not* use `render_base`,
|
||||
// as we want to ignore the object's mask and normal transform
|
||||
object.render_self(&mut render_context);
|
||||
}
|
||||
}
|
||||
render_context.renderer.pop_blend_mode();
|
||||
render_context.renderer.end_frame();
|
||||
|
||||
Ok(())
|
||||
},
|
||||
commands,
|
||||
swf::Color::from_rgb(0x000000, initial_alpha),
|
||||
);
|
||||
|
||||
match image {
|
||||
|
|
|
@ -27,6 +27,7 @@ use gc_arena::{Collect, MutationContext};
|
|||
use instant::Instant;
|
||||
use rand::rngs::SmallRng;
|
||||
use ruffle_render::backend::RenderBackend;
|
||||
use ruffle_render::commands::CommandList;
|
||||
use ruffle_render::transform::TransformStack;
|
||||
use ruffle_video::backend::VideoBackend;
|
||||
use std::collections::{HashMap, VecDeque};
|
||||
|
@ -428,9 +429,12 @@ impl<'gc> Default for ActionQueue<'gc> {
|
|||
/// Shared data used during rendering.
|
||||
/// `Player` creates this when it renders a frame and passes it down to display objects.
|
||||
pub struct RenderContext<'a, 'gc, 'gc_context> {
|
||||
/// The renderer, used by the display objects to draw themselves.
|
||||
/// The renderer, used by the display objects to register themselves.
|
||||
pub renderer: &'a mut dyn RenderBackend,
|
||||
|
||||
/// The command list, used by the display objects to draw themselves.
|
||||
pub commands: &'a mut CommandList,
|
||||
|
||||
/// The GC MutationContext, used to perform any GcCell writes
|
||||
/// that must occur during rendering.
|
||||
pub gc_context: MutationContext<'gc, 'gc_context>,
|
||||
|
|
|
@ -47,6 +47,7 @@ pub use interactive::{InteractiveObject, TInteractiveObject};
|
|||
pub use loader_display::LoaderDisplay;
|
||||
pub use morph_shape::{MorphShape, MorphShapeStatic};
|
||||
pub use movie_clip::{MovieClip, Scene};
|
||||
use ruffle_render::commands::CommandHandler;
|
||||
pub use stage::{Stage, StageAlign, StageDisplayState, StageQuality, StageScaleMode, WindowMode};
|
||||
pub use text::Text;
|
||||
pub use video::Video;
|
||||
|
@ -483,7 +484,7 @@ pub fn render_base<'gc>(this: DisplayObject<'gc>, context: &mut RenderContext<'_
|
|||
context.transform_stack.push(this.base().transform());
|
||||
let blend_mode = this.blend_mode();
|
||||
if blend_mode != BlendMode::Normal {
|
||||
context.renderer.push_blend_mode(this.blend_mode());
|
||||
context.commands.push_blend_mode(this.blend_mode());
|
||||
}
|
||||
|
||||
let scroll_rect_matrix = if let Some(rect) = this.scroll_rect() {
|
||||
|
@ -518,13 +519,13 @@ pub fn render_base<'gc>(this: DisplayObject<'gc>, context: &mut RenderContext<'_
|
|||
if let Some(m) = mask {
|
||||
mask_transform.matrix = this.global_to_local_matrix();
|
||||
mask_transform.matrix *= m.local_to_global_matrix();
|
||||
context.renderer.push_mask();
|
||||
context.commands.push_mask();
|
||||
context.allow_mask = false;
|
||||
context.transform_stack.push(&mask_transform);
|
||||
m.render_self(context);
|
||||
context.transform_stack.pop();
|
||||
context.allow_mask = true;
|
||||
context.renderer.activate_mask();
|
||||
context.commands.activate_mask();
|
||||
}
|
||||
|
||||
// There are two parts to 'DisplayObject.scrollRect':
|
||||
|
@ -539,10 +540,10 @@ pub fn render_base<'gc>(this: DisplayObject<'gc>, context: &mut RenderContext<'_
|
|||
// lies in the intersection of the scroll rect and DisplayObject.mask,
|
||||
// which is exactly the behavior that we want.
|
||||
if let Some(rect_mat) = scroll_rect_matrix {
|
||||
context.renderer.push_mask();
|
||||
context.commands.push_mask();
|
||||
// The color doesn't matter, as this is a mask.
|
||||
context.renderer.draw_rect(Color::BLACK, &rect_mat);
|
||||
context.renderer.activate_mask();
|
||||
context.commands.draw_rect(Color::BLACK, &rect_mat);
|
||||
context.commands.activate_mask();
|
||||
}
|
||||
|
||||
this.render_self(context);
|
||||
|
@ -550,22 +551,22 @@ pub fn render_base<'gc>(this: DisplayObject<'gc>, context: &mut RenderContext<'_
|
|||
if let Some(rect_mat) = scroll_rect_matrix {
|
||||
// Draw the rectangle again after deactivating the mask,
|
||||
// to reset the stencil buffer.
|
||||
context.renderer.deactivate_mask();
|
||||
context.renderer.draw_rect(Color::BLACK, &rect_mat);
|
||||
context.renderer.pop_mask();
|
||||
context.commands.deactivate_mask();
|
||||
context.commands.draw_rect(Color::BLACK, &rect_mat);
|
||||
context.commands.pop_mask();
|
||||
}
|
||||
|
||||
if let Some(m) = mask {
|
||||
context.renderer.deactivate_mask();
|
||||
context.commands.deactivate_mask();
|
||||
context.allow_mask = false;
|
||||
context.transform_stack.push(&mask_transform);
|
||||
m.render_self(context);
|
||||
context.transform_stack.pop();
|
||||
context.allow_mask = true;
|
||||
context.renderer.pop_mask();
|
||||
context.commands.pop_mask();
|
||||
}
|
||||
if blend_mode != BlendMode::Normal {
|
||||
context.renderer.pop_blend_mode();
|
||||
context.commands.pop_blend_mode();
|
||||
}
|
||||
|
||||
if scroll_rect_matrix.is_some() {
|
||||
|
|
|
@ -11,6 +11,7 @@ use crate::prelude::*;
|
|||
use crate::vminterface::Instantiator;
|
||||
use gc_arena::{Collect, Gc, GcCell, MutationContext};
|
||||
use ruffle_render::bitmap::BitmapHandle;
|
||||
use ruffle_render::commands::CommandHandler;
|
||||
use std::cell::{Ref, RefMut};
|
||||
|
||||
/// A Bitmap display object is a raw bitamp on the stage.
|
||||
|
@ -272,7 +273,7 @@ impl<'gc> TDisplayObject<'gc> for Bitmap<'gc> {
|
|||
}
|
||||
}
|
||||
|
||||
context.renderer.render_bitmap(
|
||||
context.commands.render_bitmap(
|
||||
bitmap_handle,
|
||||
context.transform_stack.transform(),
|
||||
bitmap_data.smoothing,
|
||||
|
|
|
@ -11,6 +11,7 @@ use crate::string::WStr;
|
|||
use bitflags::bitflags;
|
||||
use gc_arena::{Collect, MutationContext};
|
||||
use ruffle_macros::enum_trait_object;
|
||||
use ruffle_render::commands::CommandHandler;
|
||||
use std::cmp::Ordering;
|
||||
use std::collections::BTreeMap;
|
||||
use std::fmt::Debug;
|
||||
|
@ -293,21 +294,21 @@ pub trait TDisplayObjectContainer<'gc>:
|
|||
// Clear the mask stencil and pop the mask.
|
||||
let (prev_clip_depth, clip_child) = clip_depth_stack.pop().unwrap();
|
||||
clip_depth = prev_clip_depth;
|
||||
context.renderer.deactivate_mask();
|
||||
context.commands.deactivate_mask();
|
||||
context.allow_mask = false;
|
||||
clip_child.render(context);
|
||||
context.allow_mask = true;
|
||||
context.renderer.pop_mask();
|
||||
context.commands.pop_mask();
|
||||
}
|
||||
if context.allow_mask && child.clip_depth() > 0 && child.allow_as_mask() {
|
||||
// Push and render the mask.
|
||||
clip_depth_stack.push((clip_depth, child));
|
||||
clip_depth = child.clip_depth();
|
||||
context.renderer.push_mask();
|
||||
context.commands.push_mask();
|
||||
context.allow_mask = false;
|
||||
child.render(context);
|
||||
context.allow_mask = true;
|
||||
context.renderer.activate_mask();
|
||||
context.commands.activate_mask();
|
||||
} else if child.visible() {
|
||||
// Normal child.
|
||||
child.render(context);
|
||||
|
@ -316,11 +317,11 @@ pub trait TDisplayObjectContainer<'gc>:
|
|||
|
||||
// Pop any remaining masks.
|
||||
for (_, clip_child) in clip_depth_stack.into_iter().rev() {
|
||||
context.renderer.deactivate_mask();
|
||||
context.commands.deactivate_mask();
|
||||
context.allow_mask = false;
|
||||
clip_child.render(context);
|
||||
context.allow_mask = true;
|
||||
context.renderer.pop_mask();
|
||||
context.commands.pop_mask();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@ use crate::tag_utils::SwfMovie;
|
|||
use crate::vminterface::{AvmObject, Instantiator};
|
||||
use chrono::Utc;
|
||||
use gc_arena::{Collect, Gc, GcCell, MutationContext};
|
||||
use ruffle_render::commands::CommandHandler;
|
||||
use ruffle_render::shape_utils::DrawCommand;
|
||||
use ruffle_render::transform::Transform;
|
||||
use std::{cell::Ref, cell::RefMut, sync::Arc};
|
||||
|
@ -882,7 +883,7 @@ impl<'gc> EditText<'gc> {
|
|||
x + Twips::from_pixels(-1.0),
|
||||
Twips::from_pixels(2.0),
|
||||
);
|
||||
context.renderer.draw_rect(Color::BLACK, &selection_box);
|
||||
context.commands.draw_rect(Color::BLACK, &selection_box);
|
||||
|
||||
// Set text color to white
|
||||
context.transform_stack.push(&Transform {
|
||||
|
@ -898,7 +899,7 @@ impl<'gc> EditText<'gc> {
|
|||
// Render glyph.
|
||||
let glyph_shape_handle = glyph.shape_handle(context.renderer);
|
||||
context
|
||||
.renderer
|
||||
.commands
|
||||
.render_shape(glyph_shape_handle, context.transform_stack.transform());
|
||||
context.transform_stack.pop();
|
||||
|
||||
|
@ -912,7 +913,7 @@ impl<'gc> EditText<'gc> {
|
|||
x + Twips::from_pixels(-1.0),
|
||||
Twips::from_pixels(2.0),
|
||||
);
|
||||
context.renderer.draw_rect(color.clone(), &caret);
|
||||
context.commands.draw_rect(color.clone(), &caret);
|
||||
} else if pos == length - 1 && caret_pos == length {
|
||||
let caret = context.transform_stack.transform().matrix
|
||||
* Matrix::create_box(
|
||||
|
@ -922,7 +923,7 @@ impl<'gc> EditText<'gc> {
|
|||
x + advance,
|
||||
Twips::from_pixels(2.0),
|
||||
);
|
||||
context.renderer.draw_rect(color.clone(), &caret);
|
||||
context.commands.draw_rect(color.clone(), &caret);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -1558,7 +1559,7 @@ impl<'gc> TDisplayObject<'gc> for EditText<'gc> {
|
|||
|
||||
edit_text.drawing.render(context);
|
||||
|
||||
context.renderer.push_mask();
|
||||
context.commands.push_mask();
|
||||
let mask = Matrix::create_box(
|
||||
edit_text.bounds.width().to_pixels() as f32,
|
||||
edit_text.bounds.height().to_pixels() as f32,
|
||||
|
@ -1566,11 +1567,11 @@ impl<'gc> TDisplayObject<'gc> for EditText<'gc> {
|
|||
Twips::ZERO,
|
||||
Twips::ZERO,
|
||||
);
|
||||
context.renderer.draw_rect(
|
||||
context.commands.draw_rect(
|
||||
Color::BLACK,
|
||||
&(context.transform_stack.transform().matrix * mask),
|
||||
);
|
||||
context.renderer.activate_mask();
|
||||
context.commands.activate_mask();
|
||||
|
||||
let scroll_offset = if edit_text.scroll > 1 {
|
||||
let line_data = &edit_text.line_data;
|
||||
|
@ -1612,7 +1613,7 @@ impl<'gc> TDisplayObject<'gc> for EditText<'gc> {
|
|||
Twips::from_pixels(-1.0),
|
||||
Twips::from_pixels(2.0),
|
||||
);
|
||||
context.renderer.draw_rect(Color::BLACK, &caret);
|
||||
context.commands.draw_rect(Color::BLACK, &caret);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -1623,12 +1624,12 @@ impl<'gc> TDisplayObject<'gc> for EditText<'gc> {
|
|||
|
||||
context.transform_stack.pop();
|
||||
|
||||
context.renderer.deactivate_mask();
|
||||
context.renderer.draw_rect(
|
||||
context.commands.deactivate_mask();
|
||||
context.commands.draw_rect(
|
||||
Color::BLACK,
|
||||
&(context.transform_stack.transform().matrix * mask),
|
||||
);
|
||||
context.renderer.pop_mask();
|
||||
context.commands.pop_mask();
|
||||
|
||||
context.transform_stack.pop();
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ use crate::tag_utils::SwfMovie;
|
|||
use crate::vminterface::Instantiator;
|
||||
use gc_arena::{Collect, GcCell, MutationContext};
|
||||
use ruffle_render::backend::ShapeHandle;
|
||||
use ruffle_render::commands::CommandHandler;
|
||||
use std::cell::{Ref, RefMut};
|
||||
use std::sync::Arc;
|
||||
|
||||
|
@ -169,7 +170,7 @@ impl<'gc> TDisplayObject<'gc> for Graphic<'gc> {
|
|||
drawing.render(context);
|
||||
} else if let Some(render_handle) = self.0.read().static_data.render_handle {
|
||||
context
|
||||
.renderer
|
||||
.commands
|
||||
.render_shape(render_handle, context.transform_stack.transform())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ use crate::prelude::*;
|
|||
use crate::tag_utils::SwfMovie;
|
||||
use gc_arena::{Collect, Gc, GcCell, MutationContext};
|
||||
use ruffle_render::backend::{RenderBackend, ShapeHandle};
|
||||
use ruffle_render::commands::CommandHandler;
|
||||
use std::cell::{Ref, RefCell, RefMut};
|
||||
use std::sync::Arc;
|
||||
use swf::{Fixed16, Fixed8, Twips};
|
||||
|
@ -94,7 +95,7 @@ impl<'gc> TDisplayObject<'gc> for MorphShape<'gc> {
|
|||
let static_data = this.static_data;
|
||||
let shape_handle = static_data.get_shape(context.renderer, context.library, ratio);
|
||||
context
|
||||
.renderer
|
||||
.commands
|
||||
.render_shape(shape_handle, context.transform_stack.transform());
|
||||
}
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@ use crate::vminterface::Instantiator;
|
|||
use bitflags::bitflags;
|
||||
use gc_arena::{Collect, GcCell, MutationContext};
|
||||
use ruffle_render::backend::ViewportDimensions;
|
||||
use ruffle_render::commands::CommandHandler;
|
||||
use std::cell::{Ref, RefMut};
|
||||
use std::fmt::{self, Display, Formatter};
|
||||
use std::str::FromStr;
|
||||
|
@ -512,7 +513,7 @@ impl<'gc> Stage<'gc> {
|
|||
if margin_top + margin_bottom > margin_left + margin_right {
|
||||
// Top + bottom
|
||||
if margin_top > 0.0 {
|
||||
context.renderer.draw_rect(
|
||||
context.commands.draw_rect(
|
||||
Color::BLACK,
|
||||
&Matrix::create_box(
|
||||
viewport_width,
|
||||
|
@ -524,7 +525,7 @@ impl<'gc> Stage<'gc> {
|
|||
);
|
||||
}
|
||||
if margin_bottom > 0.0 {
|
||||
context.renderer.draw_rect(
|
||||
context.commands.draw_rect(
|
||||
Color::BLACK,
|
||||
&Matrix::create_box(
|
||||
viewport_width,
|
||||
|
@ -538,7 +539,7 @@ impl<'gc> Stage<'gc> {
|
|||
} else {
|
||||
// Left + right
|
||||
if margin_left > 0.0 {
|
||||
context.renderer.draw_rect(
|
||||
context.commands.draw_rect(
|
||||
Color::BLACK,
|
||||
&Matrix::create_box(
|
||||
margin_left,
|
||||
|
@ -550,7 +551,7 @@ impl<'gc> Stage<'gc> {
|
|||
);
|
||||
}
|
||||
if margin_right > 0.0 {
|
||||
context.renderer.draw_rect(
|
||||
context.commands.draw_rect(
|
||||
Color::BLACK,
|
||||
&Matrix::create_box(
|
||||
margin_right,
|
||||
|
@ -700,22 +701,11 @@ impl<'gc> TDisplayObject<'gc> for Stage<'gc> {
|
|||
}
|
||||
|
||||
fn render(&self, context: &mut RenderContext<'_, 'gc, '_>) {
|
||||
let background_color =
|
||||
if self.window_mode() != WindowMode::Transparent || self.is_fullscreen() {
|
||||
self.background_color().unwrap_or(Color::WHITE)
|
||||
} else {
|
||||
Color::from_rgba(0)
|
||||
};
|
||||
|
||||
context.renderer.begin_frame(background_color);
|
||||
|
||||
render_base((*self).into(), context);
|
||||
|
||||
if self.should_letterbox() {
|
||||
self.draw_letterbox(context);
|
||||
}
|
||||
|
||||
context.renderer.end_frame();
|
||||
}
|
||||
|
||||
fn enter_frame(&self, context: &mut UpdateContext<'_, 'gc, '_>) {
|
||||
|
|
|
@ -4,6 +4,7 @@ use crate::font::TextRenderSettings;
|
|||
use crate::prelude::*;
|
||||
use crate::tag_utils::SwfMovie;
|
||||
use gc_arena::{Collect, GcCell, MutationContext};
|
||||
use ruffle_render::commands::CommandHandler;
|
||||
use ruffle_render::transform::Transform;
|
||||
use std::cell::{Ref, RefMut};
|
||||
use std::sync::Arc;
|
||||
|
@ -136,7 +137,7 @@ impl<'gc> TDisplayObject<'gc> for Text<'gc> {
|
|||
context.transform_stack.push(&transform);
|
||||
let glyph_shape_handle = glyph.shape_handle(context.renderer);
|
||||
context
|
||||
.renderer
|
||||
.commands
|
||||
.render_shape(glyph_shape_handle, context.transform_stack.transform());
|
||||
context.transform_stack.pop();
|
||||
transform.matrix.tx += Twips::new(c.advance);
|
||||
|
|
|
@ -12,6 +12,7 @@ use crate::vminterface::{AvmObject, Instantiator};
|
|||
use gc_arena::{Collect, GcCell, MutationContext};
|
||||
use ruffle_render::bitmap::BitmapInfo;
|
||||
use ruffle_render::bounding_box::BoundingBox;
|
||||
use ruffle_render::commands::CommandHandler;
|
||||
use ruffle_video::error::Error;
|
||||
use ruffle_video::frame::EncodedFrame;
|
||||
use ruffle_video::VideoStreamHandle;
|
||||
|
@ -465,7 +466,7 @@ impl<'gc> TDisplayObject<'gc> for Video<'gc> {
|
|||
};
|
||||
|
||||
context
|
||||
.renderer
|
||||
.commands
|
||||
.render_bitmap(bitmap.handle, &transform, smoothing);
|
||||
} else {
|
||||
log::warn!("Video has no decoded frame to render.");
|
||||
|
|
|
@ -3,6 +3,7 @@ use gc_arena::Collect;
|
|||
use ruffle_render::backend::ShapeHandle;
|
||||
use ruffle_render::bitmap::{BitmapInfo, BitmapSource};
|
||||
use ruffle_render::bounding_box::BoundingBox;
|
||||
use ruffle_render::commands::CommandHandler;
|
||||
use ruffle_render::shape_utils::{DistilledShape, DrawCommand, DrawPath};
|
||||
use std::cell::Cell;
|
||||
use swf::{FillStyle, LineStyle, Twips};
|
||||
|
@ -290,7 +291,7 @@ impl Drawing {
|
|||
|
||||
if let Some(handle) = self.render_handle.get() {
|
||||
context
|
||||
.renderer
|
||||
.commands
|
||||
.render_shape(handle, context.transform_stack.transform());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,6 +42,7 @@ use instant::Instant;
|
|||
use log::info;
|
||||
use rand::{rngs::SmallRng, SeedableRng};
|
||||
use ruffle_render::backend::{null::NullRenderer, RenderBackend, ViewportDimensions};
|
||||
use ruffle_render::commands::CommandList;
|
||||
use ruffle_render::transform::TransformStack;
|
||||
use ruffle_video::backend::VideoBackend;
|
||||
use std::cell::RefCell;
|
||||
|
@ -1350,24 +1351,38 @@ impl Player {
|
|||
pub fn render(&mut self) {
|
||||
let (renderer, ui, transform_stack) =
|
||||
(&mut self.renderer, &mut self.ui, &mut self.transform_stack);
|
||||
let mut commands = CommandList::new();
|
||||
let mut background_color = Color::WHITE;
|
||||
|
||||
self.gc_arena.borrow().mutate(|gc_context, gc_root| {
|
||||
let root_data = gc_root.data.read();
|
||||
let stage = root_data.stage;
|
||||
|
||||
let mut render_context = RenderContext {
|
||||
renderer: renderer.deref_mut(),
|
||||
commands: &mut commands,
|
||||
gc_context,
|
||||
ui: ui.deref_mut(),
|
||||
library: &root_data.library,
|
||||
transform_stack,
|
||||
is_offscreen: false,
|
||||
stage: root_data.stage,
|
||||
stage,
|
||||
clip_depth_stack: vec![],
|
||||
allow_mask: true,
|
||||
};
|
||||
|
||||
root_data.stage.render(&mut render_context);
|
||||
stage.render(&mut render_context);
|
||||
|
||||
background_color =
|
||||
if stage.window_mode() != WindowMode::Transparent || stage.is_fullscreen() {
|
||||
stage.background_color().unwrap_or(Color::WHITE)
|
||||
} else {
|
||||
Color::from_rgba(0)
|
||||
};
|
||||
});
|
||||
|
||||
renderer.submit_frame(background_color, commands);
|
||||
|
||||
self.needs_render = false;
|
||||
}
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ use ruffle_render::backend::null::NullBitmapSource;
|
|||
use ruffle_render::backend::{RenderBackend, ShapeHandle, ViewportDimensions};
|
||||
use ruffle_render::bitmap::{Bitmap, BitmapFormat, BitmapHandle, BitmapSource};
|
||||
use ruffle_render::color_transform::ColorTransform;
|
||||
use ruffle_render::commands::{CommandHandler, CommandList};
|
||||
use ruffle_render::error::Error;
|
||||
use ruffle_render::matrix::Matrix;
|
||||
use ruffle_render::shape_utils::{DistilledShape, DrawCommand, LineScaleMode, LineScales};
|
||||
|
@ -377,6 +378,28 @@ impl WebCanvasRenderBackend {
|
|||
.set_global_composite_operation(mode)
|
||||
.expect("Failed to update BlendMode");
|
||||
}
|
||||
|
||||
fn begin_frame(&mut self, clear: Color) {
|
||||
// Reset canvas transform in case it was left in a dirty state.
|
||||
self.context.reset_transform().unwrap();
|
||||
|
||||
let width = self.canvas.width();
|
||||
let height = self.canvas.height();
|
||||
|
||||
if clear.a > 0 {
|
||||
let color = format!("rgba({}, {}, {}, {})", clear.r, clear.g, clear.b, clear.a);
|
||||
self.context.set_fill_style(&color.into());
|
||||
let _ = self.context.set_global_composite_operation("copy");
|
||||
self.context
|
||||
.fill_rect(0.0, 0.0, width.into(), height.into());
|
||||
let _ = self.context.set_global_composite_operation("source-over");
|
||||
} else {
|
||||
self.context
|
||||
.clear_rect(0.0, 0.0, width.into(), height.into());
|
||||
}
|
||||
|
||||
self.mask_state = MaskState::DrawContent;
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderBackend for WebCanvasRenderBackend {
|
||||
|
@ -427,37 +450,53 @@ impl RenderBackend for WebCanvasRenderBackend {
|
|||
_handle: BitmapHandle,
|
||||
_width: u32,
|
||||
_height: u32,
|
||||
_f: &mut dyn FnMut(&mut dyn RenderBackend) -> Result<(), ruffle_render::error::Error>,
|
||||
_commands: CommandList,
|
||||
_clear_color: Color,
|
||||
) -> Result<Bitmap, ruffle_render::error::Error> {
|
||||
Err(ruffle_render::error::Error::Unimplemented)
|
||||
Err(Error::Unimplemented)
|
||||
}
|
||||
|
||||
fn begin_frame(&mut self, clear: Color) {
|
||||
// Reset canvas transform in case it was left in a dirty state.
|
||||
self.context.reset_transform().unwrap();
|
||||
|
||||
let width = self.canvas.width();
|
||||
let height = self.canvas.height();
|
||||
|
||||
if clear.a > 0 {
|
||||
let color = format!("rgba({}, {}, {}, {})", clear.r, clear.g, clear.b, clear.a);
|
||||
self.context.set_fill_style(&color.into());
|
||||
let _ = self.context.set_global_composite_operation("copy");
|
||||
self.context
|
||||
.fill_rect(0.0, 0.0, width.into(), height.into());
|
||||
let _ = self.context.set_global_composite_operation("source-over");
|
||||
} else {
|
||||
self.context
|
||||
.clear_rect(0.0, 0.0, width.into(), height.into());
|
||||
}
|
||||
|
||||
self.mask_state = MaskState::DrawContent;
|
||||
fn submit_frame(&mut self, clear: Color, commands: CommandList) {
|
||||
self.begin_frame(clear);
|
||||
commands.execute(self);
|
||||
}
|
||||
|
||||
fn end_frame(&mut self) {
|
||||
// Noop
|
||||
fn get_bitmap_pixels(&mut self, bitmap: BitmapHandle) -> Option<Bitmap> {
|
||||
let bitmap = &self.bitmaps[&bitmap];
|
||||
bitmap.get_pixels()
|
||||
}
|
||||
|
||||
fn register_bitmap(&mut self, bitmap: Bitmap) -> Result<BitmapHandle, Error> {
|
||||
let handle = self.next_bitmap_handle;
|
||||
self.next_bitmap_handle = BitmapHandle(self.next_bitmap_handle.0 + 1);
|
||||
let bitmap_data = BitmapData::new(bitmap).map_err(Error::JavascriptError)?;
|
||||
self.bitmaps.insert(handle, bitmap_data);
|
||||
Ok(handle)
|
||||
}
|
||||
|
||||
fn unregister_bitmap(&mut self, bitmap: BitmapHandle) {
|
||||
self.bitmaps.remove(&bitmap);
|
||||
}
|
||||
|
||||
fn update_texture(
|
||||
&mut self,
|
||||
handle: BitmapHandle,
|
||||
width: u32,
|
||||
height: u32,
|
||||
rgba: Vec<u8>,
|
||||
) -> Result<BitmapHandle, Error> {
|
||||
// TODO: Could be optimized to a single put_image_data call
|
||||
// in case it is already stored as a canvas+context.
|
||||
self.bitmaps.insert(
|
||||
handle,
|
||||
BitmapData::new(Bitmap::new(width, height, BitmapFormat::Rgba, rgba))
|
||||
.map_err(Error::JavascriptError)?,
|
||||
);
|
||||
Ok(handle)
|
||||
}
|
||||
}
|
||||
|
||||
impl CommandHandler for WebCanvasRenderBackend {
|
||||
fn render_bitmap(&mut self, bitmap: BitmapHandle, transform: &Transform, smoothing: bool) {
|
||||
if self.mask_state == MaskState::ClearMask {
|
||||
return;
|
||||
|
@ -739,40 +778,6 @@ impl RenderBackend for WebCanvasRenderBackend {
|
|||
self.apply_blend_mode(current);
|
||||
}
|
||||
}
|
||||
|
||||
fn get_bitmap_pixels(&mut self, bitmap: BitmapHandle) -> Option<Bitmap> {
|
||||
let bitmap = &self.bitmaps[&bitmap];
|
||||
bitmap.get_pixels()
|
||||
}
|
||||
|
||||
fn register_bitmap(&mut self, bitmap: Bitmap) -> Result<BitmapHandle, Error> {
|
||||
let handle = self.next_bitmap_handle;
|
||||
self.next_bitmap_handle = BitmapHandle(self.next_bitmap_handle.0 + 1);
|
||||
let bitmap_data = BitmapData::new(bitmap).map_err(Error::JavascriptError)?;
|
||||
self.bitmaps.insert(handle, bitmap_data);
|
||||
Ok(handle)
|
||||
}
|
||||
|
||||
fn unregister_bitmap(&mut self, bitmap: BitmapHandle) {
|
||||
self.bitmaps.remove(&bitmap);
|
||||
}
|
||||
|
||||
fn update_texture(
|
||||
&mut self,
|
||||
handle: BitmapHandle,
|
||||
width: u32,
|
||||
height: u32,
|
||||
rgba: Vec<u8>,
|
||||
) -> Result<BitmapHandle, Error> {
|
||||
// TODO: Could be optimized to a single put_image_data call
|
||||
// in case it is already stored as a canvas+context.
|
||||
self.bitmaps.insert(
|
||||
handle,
|
||||
BitmapData::new(Bitmap::new(width, height, BitmapFormat::Rgba, rgba))
|
||||
.map_err(Error::JavascriptError)?,
|
||||
);
|
||||
Ok(handle)
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert a series of `DrawCommands` to a `Path2d` shape.
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
pub mod null;
|
||||
|
||||
use crate::bitmap::{Bitmap, BitmapHandle, BitmapInfo, BitmapSource};
|
||||
use crate::commands::CommandList;
|
||||
use crate::error::Error;
|
||||
use crate::matrix::Matrix;
|
||||
use crate::shape_utils::DistilledShape;
|
||||
use crate::transform::Transform;
|
||||
use crate::utils;
|
||||
use downcast_rs::{impl_downcast, Downcast};
|
||||
use swf;
|
||||
use swf::Color;
|
||||
|
||||
pub trait RenderBackend: Downcast {
|
||||
fn viewport_dimensions(&self) -> ViewportDimensions;
|
||||
|
@ -50,7 +50,8 @@ pub trait RenderBackend: Downcast {
|
|||
handle: BitmapHandle,
|
||||
width: u32,
|
||||
height: u32,
|
||||
f: &mut dyn FnMut(&mut dyn RenderBackend) -> Result<(), Error>,
|
||||
commands: CommandList,
|
||||
clear_color: Color,
|
||||
) -> Result<Bitmap, Error>;
|
||||
|
||||
fn register_bitmap_jpeg_2(&mut self, data: &[u8]) -> Result<BitmapInfo, Error> {
|
||||
|
@ -96,18 +97,7 @@ pub trait RenderBackend: Downcast {
|
|||
})
|
||||
}
|
||||
|
||||
fn begin_frame(&mut self, clear: swf::Color);
|
||||
fn render_bitmap(&mut self, bitmap: BitmapHandle, transform: &Transform, smoothing: bool);
|
||||
fn render_shape(&mut self, shape: ShapeHandle, transform: &Transform);
|
||||
fn draw_rect(&mut self, color: swf::Color, matrix: &Matrix);
|
||||
fn end_frame(&mut self);
|
||||
fn push_mask(&mut self);
|
||||
fn activate_mask(&mut self);
|
||||
fn deactivate_mask(&mut self);
|
||||
fn pop_mask(&mut self);
|
||||
|
||||
fn push_blend_mode(&mut self, blend: swf::BlendMode);
|
||||
fn pop_blend_mode(&mut self);
|
||||
fn submit_frame(&mut self, clear: swf::Color, commands: CommandList);
|
||||
|
||||
fn get_bitmap_pixels(&mut self, bitmap: BitmapHandle) -> Option<Bitmap>;
|
||||
fn register_bitmap(&mut self, bitmap: Bitmap) -> Result<BitmapHandle, Error>;
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
use crate::backend::{RenderBackend, ShapeHandle, ViewportDimensions};
|
||||
use crate::bitmap::{Bitmap, BitmapHandle, BitmapInfo, BitmapSource};
|
||||
use crate::commands::CommandList;
|
||||
use crate::error::Error;
|
||||
use crate::matrix::Matrix;
|
||||
use crate::shape_utils::DistilledShape;
|
||||
use crate::transform::Transform;
|
||||
use swf::Color;
|
||||
|
||||
pub struct NullBitmapSource;
|
||||
|
@ -54,23 +53,13 @@ impl RenderBackend for NullRenderer {
|
|||
_handle: BitmapHandle,
|
||||
_width: u32,
|
||||
_height: u32,
|
||||
_f: &mut dyn FnMut(&mut dyn RenderBackend) -> Result<(), Error>,
|
||||
_commands: CommandList,
|
||||
_clear_color: Color,
|
||||
) -> Result<Bitmap, Error> {
|
||||
Err(Error::Unimplemented)
|
||||
}
|
||||
|
||||
fn begin_frame(&mut self, _clear: Color) {}
|
||||
fn render_bitmap(&mut self, _bitmap: BitmapHandle, _transform: &Transform, _smoothing: bool) {}
|
||||
fn render_shape(&mut self, _shape: ShapeHandle, _transform: &Transform) {}
|
||||
fn draw_rect(&mut self, _color: Color, _matrix: &Matrix) {}
|
||||
fn end_frame(&mut self) {}
|
||||
fn push_mask(&mut self) {}
|
||||
fn activate_mask(&mut self) {}
|
||||
fn deactivate_mask(&mut self) {}
|
||||
fn pop_mask(&mut self) {}
|
||||
|
||||
fn push_blend_mode(&mut self, _blend_mode: swf::BlendMode) {}
|
||||
fn pop_blend_mode(&mut self) {}
|
||||
fn submit_frame(&mut self, _clear: Color, _commands: CommandList) {}
|
||||
|
||||
fn get_bitmap_pixels(&mut self, _bitmap: BitmapHandle) -> Option<Bitmap> {
|
||||
None
|
||||
|
|
|
@ -0,0 +1,120 @@
|
|||
use crate::backend::ShapeHandle;
|
||||
use crate::bitmap::BitmapHandle;
|
||||
use crate::matrix::Matrix;
|
||||
use crate::transform::Transform;
|
||||
use swf::{BlendMode, Color};
|
||||
|
||||
pub trait CommandHandler {
|
||||
fn render_bitmap(&mut self, bitmap: BitmapHandle, transform: &Transform, smoothing: bool);
|
||||
fn render_shape(&mut self, shape: ShapeHandle, transform: &Transform);
|
||||
fn draw_rect(&mut self, color: Color, matrix: &Matrix);
|
||||
fn push_mask(&mut self);
|
||||
fn activate_mask(&mut self);
|
||||
fn deactivate_mask(&mut self);
|
||||
fn pop_mask(&mut self);
|
||||
|
||||
fn push_blend_mode(&mut self, blend: BlendMode);
|
||||
fn pop_blend_mode(&mut self);
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct CommandList(Vec<Command>);
|
||||
|
||||
impl CommandList {
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
pub fn execute(self, handler: &mut impl CommandHandler) {
|
||||
for command in self.0 {
|
||||
match command {
|
||||
Command::RenderBitmap {
|
||||
bitmap,
|
||||
transform,
|
||||
smoothing,
|
||||
} => handler.render_bitmap(bitmap, &transform, smoothing),
|
||||
Command::RenderShape { shape, transform } => {
|
||||
handler.render_shape(shape, &transform)
|
||||
}
|
||||
Command::DrawRect { color, matrix } => handler.draw_rect(color, &matrix),
|
||||
Command::PushMask => handler.push_mask(),
|
||||
Command::ActivateMask => handler.activate_mask(),
|
||||
Command::DeactivateMask => handler.deactivate_mask(),
|
||||
Command::PopMask => handler.pop_mask(),
|
||||
Command::PushBlendMode(blend) => handler.push_blend_mode(blend),
|
||||
Command::PopBlendMode => handler.pop_blend_mode(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CommandHandler for CommandList {
|
||||
fn render_bitmap(&mut self, bitmap: BitmapHandle, transform: &Transform, smoothing: bool) {
|
||||
self.0.push(Command::RenderBitmap {
|
||||
bitmap,
|
||||
transform: transform.clone(),
|
||||
smoothing,
|
||||
});
|
||||
}
|
||||
|
||||
fn render_shape(&mut self, shape: ShapeHandle, transform: &Transform) {
|
||||
self.0.push(Command::RenderShape {
|
||||
shape,
|
||||
transform: transform.clone(),
|
||||
});
|
||||
}
|
||||
|
||||
fn draw_rect(&mut self, color: Color, matrix: &Matrix) {
|
||||
self.0.push(Command::DrawRect {
|
||||
color,
|
||||
matrix: *matrix,
|
||||
});
|
||||
}
|
||||
|
||||
fn push_mask(&mut self) {
|
||||
self.0.push(Command::PushMask);
|
||||
}
|
||||
|
||||
fn activate_mask(&mut self) {
|
||||
self.0.push(Command::ActivateMask);
|
||||
}
|
||||
|
||||
fn deactivate_mask(&mut self) {
|
||||
self.0.push(Command::DeactivateMask);
|
||||
}
|
||||
|
||||
fn pop_mask(&mut self) {
|
||||
self.0.push(Command::PopMask);
|
||||
}
|
||||
|
||||
fn push_blend_mode(&mut self, blend: BlendMode) {
|
||||
self.0.push(Command::PushBlendMode(blend));
|
||||
}
|
||||
|
||||
fn pop_blend_mode(&mut self) {
|
||||
self.0.push(Command::PopBlendMode);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Command {
|
||||
RenderBitmap {
|
||||
bitmap: BitmapHandle,
|
||||
transform: Transform,
|
||||
smoothing: bool,
|
||||
},
|
||||
RenderShape {
|
||||
shape: ShapeHandle,
|
||||
transform: Transform,
|
||||
},
|
||||
DrawRect {
|
||||
color: Color,
|
||||
matrix: Matrix,
|
||||
},
|
||||
PushMask,
|
||||
ActivateMask,
|
||||
DeactivateMask,
|
||||
PopMask,
|
||||
PushBlendMode(BlendMode),
|
||||
PopBlendMode,
|
||||
}
|
|
@ -8,5 +8,6 @@ pub mod shape_utils;
|
|||
pub mod transform;
|
||||
pub mod utils;
|
||||
|
||||
pub mod commands;
|
||||
#[cfg(feature = "tessellator")]
|
||||
pub mod tessellator;
|
||||
|
|
|
@ -3,6 +3,7 @@ use fnv::FnvHashMap;
|
|||
use ruffle_render::backend::null::NullBitmapSource;
|
||||
use ruffle_render::backend::{RenderBackend, ShapeHandle, ViewportDimensions};
|
||||
use ruffle_render::bitmap::{Bitmap, BitmapFormat, BitmapHandle, BitmapSource};
|
||||
use ruffle_render::commands::{CommandHandler, CommandList};
|
||||
use ruffle_render::error::Error as BitmapError;
|
||||
use ruffle_render::shape_utils::DistilledShape;
|
||||
use ruffle_render::tessellator::{
|
||||
|
@ -736,81 +737,6 @@ impl WebGlRenderBackend {
|
|||
self.gl
|
||||
.blend_func_separate(src_rgb, dst_rgb, Gl::ONE, Gl::ONE_MINUS_SRC_ALPHA);
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderBackend for WebGlRenderBackend {
|
||||
fn viewport_dimensions(&self) -> ViewportDimensions {
|
||||
ViewportDimensions {
|
||||
width: self.renderbuffer_width as u32,
|
||||
height: self.renderbuffer_height as u32,
|
||||
scale_factor: self.viewport_scale_factor,
|
||||
}
|
||||
}
|
||||
|
||||
fn set_viewport_dimensions(&mut self, dimensions: ViewportDimensions) {
|
||||
// Build view matrix based on canvas size.
|
||||
self.view_matrix = [
|
||||
[1.0 / (dimensions.width as f32 / 2.0), 0.0, 0.0, 0.0],
|
||||
[0.0, -1.0 / (dimensions.height as f32 / 2.0), 0.0, 0.0],
|
||||
[0.0, 0.0, 1.0, 0.0],
|
||||
[-1.0, 1.0, 0.0, 1.0],
|
||||
];
|
||||
|
||||
// Setup GL viewport and renderbuffers clamped to reasonable sizes.
|
||||
// We don't use `.clamp()` here because `self.gl.drawing_buffer_width()` and
|
||||
// `self.gl.drawing_buffer_height()` return zero when the WebGL context is lost,
|
||||
// then an assertion error would be triggered.
|
||||
self.renderbuffer_width = (dimensions.width as i32)
|
||||
.max(1)
|
||||
.min(self.gl.drawing_buffer_width());
|
||||
self.renderbuffer_height = (dimensions.height as i32)
|
||||
.max(1)
|
||||
.min(self.gl.drawing_buffer_height());
|
||||
|
||||
// Recreate framebuffers with the new size.
|
||||
let _ = self.build_msaa_buffers();
|
||||
self.gl
|
||||
.viewport(0, 0, self.renderbuffer_width, self.renderbuffer_height);
|
||||
}
|
||||
|
||||
fn register_shape(
|
||||
&mut self,
|
||||
shape: DistilledShape,
|
||||
bitmap_source: &dyn BitmapSource,
|
||||
) -> ShapeHandle {
|
||||
let handle = ShapeHandle(self.meshes.len());
|
||||
let mesh = self.register_shape_internal(shape, bitmap_source);
|
||||
self.meshes.push(mesh);
|
||||
handle
|
||||
}
|
||||
|
||||
fn replace_shape(
|
||||
&mut self,
|
||||
shape: DistilledShape,
|
||||
bitmap_source: &dyn BitmapSource,
|
||||
handle: ShapeHandle,
|
||||
) {
|
||||
let mesh = self.register_shape_internal(shape, bitmap_source);
|
||||
self.meshes[handle.0] = mesh;
|
||||
}
|
||||
|
||||
fn register_glyph_shape(&mut self, glyph: &swf::Glyph) -> ShapeHandle {
|
||||
let shape = ruffle_render::shape_utils::swf_glyph_to_shape(glyph);
|
||||
let handle = ShapeHandle(self.meshes.len());
|
||||
let mesh = self.register_shape_internal((&shape).into(), &NullBitmapSource);
|
||||
self.meshes.push(mesh);
|
||||
handle
|
||||
}
|
||||
|
||||
fn render_offscreen(
|
||||
&mut self,
|
||||
_handle: BitmapHandle,
|
||||
_width: u32,
|
||||
_height: u32,
|
||||
_f: &mut dyn FnMut(&mut dyn RenderBackend) -> Result<(), ruffle_render::error::Error>,
|
||||
) -> Result<Bitmap, ruffle_render::error::Error> {
|
||||
Err(ruffle_render::error::Error::Unimplemented)
|
||||
}
|
||||
|
||||
fn begin_frame(&mut self, clear: Color) {
|
||||
self.active_program = std::ptr::null();
|
||||
|
@ -931,7 +857,174 @@ impl RenderBackend for WebGlRenderBackend {
|
|||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderBackend for WebGlRenderBackend {
|
||||
fn render_offscreen(
|
||||
&mut self,
|
||||
_handle: BitmapHandle,
|
||||
_width: u32,
|
||||
_height: u32,
|
||||
_commands: CommandList,
|
||||
_clear_color: Color,
|
||||
) -> Result<Bitmap, ruffle_render::error::Error> {
|
||||
Err(ruffle_render::error::Error::Unimplemented)
|
||||
}
|
||||
|
||||
fn viewport_dimensions(&self) -> ViewportDimensions {
|
||||
ViewportDimensions {
|
||||
width: self.renderbuffer_width as u32,
|
||||
height: self.renderbuffer_height as u32,
|
||||
scale_factor: self.viewport_scale_factor,
|
||||
}
|
||||
}
|
||||
|
||||
fn set_viewport_dimensions(&mut self, dimensions: ViewportDimensions) {
|
||||
// Build view matrix based on canvas size.
|
||||
self.view_matrix = [
|
||||
[1.0 / (dimensions.width as f32 / 2.0), 0.0, 0.0, 0.0],
|
||||
[0.0, -1.0 / (dimensions.height as f32 / 2.0), 0.0, 0.0],
|
||||
[0.0, 0.0, 1.0, 0.0],
|
||||
[-1.0, 1.0, 0.0, 1.0],
|
||||
];
|
||||
|
||||
// Setup GL viewport and renderbuffers clamped to reasonable sizes.
|
||||
// We don't use `.clamp()` here because `self.gl.drawing_buffer_width()` and
|
||||
// `self.gl.drawing_buffer_height()` return zero when the WebGL context is lost,
|
||||
// then an assertion error would be triggered.
|
||||
self.renderbuffer_width = (dimensions.width as i32)
|
||||
.max(1)
|
||||
.min(self.gl.drawing_buffer_width());
|
||||
self.renderbuffer_height = (dimensions.height as i32)
|
||||
.max(1)
|
||||
.min(self.gl.drawing_buffer_height());
|
||||
|
||||
// Recreate framebuffers with the new size.
|
||||
let _ = self.build_msaa_buffers();
|
||||
self.gl
|
||||
.viewport(0, 0, self.renderbuffer_width, self.renderbuffer_height);
|
||||
}
|
||||
|
||||
fn register_shape(
|
||||
&mut self,
|
||||
shape: DistilledShape,
|
||||
bitmap_source: &dyn BitmapSource,
|
||||
) -> ShapeHandle {
|
||||
let handle = ShapeHandle(self.meshes.len());
|
||||
let mesh = self.register_shape_internal(shape, bitmap_source);
|
||||
self.meshes.push(mesh);
|
||||
handle
|
||||
}
|
||||
|
||||
fn replace_shape(
|
||||
&mut self,
|
||||
shape: DistilledShape,
|
||||
bitmap_source: &dyn BitmapSource,
|
||||
handle: ShapeHandle,
|
||||
) {
|
||||
let mesh = self.register_shape_internal(shape, bitmap_source);
|
||||
self.meshes[handle.0] = mesh;
|
||||
}
|
||||
|
||||
fn register_glyph_shape(&mut self, glyph: &swf::Glyph) -> ShapeHandle {
|
||||
let shape = ruffle_render::shape_utils::swf_glyph_to_shape(glyph);
|
||||
let handle = ShapeHandle(self.meshes.len());
|
||||
let mesh = self.register_shape_internal((&shape).into(), &NullBitmapSource);
|
||||
self.meshes.push(mesh);
|
||||
handle
|
||||
}
|
||||
|
||||
fn submit_frame(&mut self, clear: Color, commands: CommandList) {
|
||||
self.begin_frame(clear);
|
||||
commands.execute(self);
|
||||
self.end_frame();
|
||||
}
|
||||
|
||||
fn get_bitmap_pixels(&mut self, bitmap: BitmapHandle) -> Option<Bitmap> {
|
||||
self.bitmap_registry.get(&bitmap).map(|e| e.bitmap.clone())
|
||||
}
|
||||
|
||||
fn register_bitmap(&mut self, bitmap: Bitmap) -> Result<BitmapHandle, BitmapError> {
|
||||
let format = match bitmap.format() {
|
||||
BitmapFormat::Rgb => Gl::RGB,
|
||||
BitmapFormat::Rgba => Gl::RGBA,
|
||||
};
|
||||
|
||||
let texture = self.gl.create_texture().unwrap();
|
||||
self.gl.bind_texture(Gl::TEXTURE_2D, Some(&texture));
|
||||
self.gl
|
||||
.tex_image_2d_with_i32_and_i32_and_i32_and_format_and_type_and_opt_u8_array(
|
||||
Gl::TEXTURE_2D,
|
||||
0,
|
||||
format as i32,
|
||||
bitmap.width() as i32,
|
||||
bitmap.height() as i32,
|
||||
0,
|
||||
format,
|
||||
Gl::UNSIGNED_BYTE,
|
||||
Some(bitmap.data()),
|
||||
)
|
||||
.into_js_result()
|
||||
.map_err(|e| BitmapError::JavascriptError(e.into()))?;
|
||||
|
||||
// You must set the texture parameters for non-power-of-2 textures to function in WebGL1.
|
||||
self.gl
|
||||
.tex_parameteri(Gl::TEXTURE_2D, Gl::TEXTURE_WRAP_S, Gl::CLAMP_TO_EDGE as i32);
|
||||
self.gl
|
||||
.tex_parameteri(Gl::TEXTURE_2D, Gl::TEXTURE_WRAP_T, Gl::CLAMP_TO_EDGE as i32);
|
||||
self.gl
|
||||
.tex_parameteri(Gl::TEXTURE_2D, Gl::TEXTURE_MIN_FILTER, Gl::LINEAR as i32);
|
||||
self.gl
|
||||
.tex_parameteri(Gl::TEXTURE_2D, Gl::TEXTURE_MAG_FILTER, Gl::LINEAR as i32);
|
||||
|
||||
let handle = self.next_bitmap_handle;
|
||||
self.next_bitmap_handle = BitmapHandle(self.next_bitmap_handle.0 + 1);
|
||||
self.bitmap_registry
|
||||
.insert(handle, RegistryData { bitmap, texture });
|
||||
|
||||
Ok(handle)
|
||||
}
|
||||
|
||||
fn unregister_bitmap(&mut self, bitmap: BitmapHandle) {
|
||||
self.bitmap_registry.remove(&bitmap);
|
||||
}
|
||||
|
||||
fn update_texture(
|
||||
&mut self,
|
||||
handle: BitmapHandle,
|
||||
width: u32,
|
||||
height: u32,
|
||||
rgba: Vec<u8>,
|
||||
) -> Result<BitmapHandle, BitmapError> {
|
||||
let texture = if let Some(entry) = self.bitmap_registry.get(&handle) {
|
||||
&entry.texture
|
||||
} else {
|
||||
log::warn!("Tried to replace nonexistent texture");
|
||||
return Ok(handle);
|
||||
};
|
||||
|
||||
self.gl.bind_texture(Gl::TEXTURE_2D, Some(&texture));
|
||||
|
||||
self.gl
|
||||
.tex_image_2d_with_i32_and_i32_and_i32_and_format_and_type_and_opt_u8_array(
|
||||
Gl::TEXTURE_2D,
|
||||
0,
|
||||
Gl::RGBA as i32,
|
||||
width as i32,
|
||||
height as i32,
|
||||
0,
|
||||
Gl::RGBA,
|
||||
Gl::UNSIGNED_BYTE,
|
||||
Some(&rgba),
|
||||
)
|
||||
.into_js_result()
|
||||
.map_err(|e| BitmapError::JavascriptError(e.into()))?;
|
||||
|
||||
Ok(handle)
|
||||
}
|
||||
}
|
||||
|
||||
impl CommandHandler for WebGlRenderBackend {
|
||||
fn render_bitmap(&mut self, bitmap: BitmapHandle, transform: &Transform, smoothing: bool) {
|
||||
self.set_stencil_state();
|
||||
if let Some(entry) = self.bitmap_registry.get(&bitmap) {
|
||||
|
@ -1285,89 +1378,6 @@ impl RenderBackend for WebGlRenderBackend {
|
|||
self.apply_blend_mode(current);
|
||||
}
|
||||
}
|
||||
|
||||
fn get_bitmap_pixels(&mut self, bitmap: BitmapHandle) -> Option<Bitmap> {
|
||||
self.bitmap_registry.get(&bitmap).map(|e| e.bitmap.clone())
|
||||
}
|
||||
|
||||
fn register_bitmap(&mut self, bitmap: Bitmap) -> Result<BitmapHandle, BitmapError> {
|
||||
let format = match bitmap.format() {
|
||||
BitmapFormat::Rgb => Gl::RGB,
|
||||
BitmapFormat::Rgba => Gl::RGBA,
|
||||
};
|
||||
|
||||
let texture = self.gl.create_texture().unwrap();
|
||||
self.gl.bind_texture(Gl::TEXTURE_2D, Some(&texture));
|
||||
self.gl
|
||||
.tex_image_2d_with_i32_and_i32_and_i32_and_format_and_type_and_opt_u8_array(
|
||||
Gl::TEXTURE_2D,
|
||||
0,
|
||||
format as i32,
|
||||
bitmap.width() as i32,
|
||||
bitmap.height() as i32,
|
||||
0,
|
||||
format,
|
||||
Gl::UNSIGNED_BYTE,
|
||||
Some(bitmap.data()),
|
||||
)
|
||||
.into_js_result()
|
||||
.map_err(|e| BitmapError::JavascriptError(e.into()))?;
|
||||
|
||||
// You must set the texture parameters for non-power-of-2 textures to function in WebGL1.
|
||||
self.gl
|
||||
.tex_parameteri(Gl::TEXTURE_2D, Gl::TEXTURE_WRAP_S, Gl::CLAMP_TO_EDGE as i32);
|
||||
self.gl
|
||||
.tex_parameteri(Gl::TEXTURE_2D, Gl::TEXTURE_WRAP_T, Gl::CLAMP_TO_EDGE as i32);
|
||||
self.gl
|
||||
.tex_parameteri(Gl::TEXTURE_2D, Gl::TEXTURE_MIN_FILTER, Gl::LINEAR as i32);
|
||||
self.gl
|
||||
.tex_parameteri(Gl::TEXTURE_2D, Gl::TEXTURE_MAG_FILTER, Gl::LINEAR as i32);
|
||||
|
||||
let handle = self.next_bitmap_handle;
|
||||
self.next_bitmap_handle = BitmapHandle(self.next_bitmap_handle.0 + 1);
|
||||
self.bitmap_registry
|
||||
.insert(handle, RegistryData { bitmap, texture });
|
||||
|
||||
Ok(handle)
|
||||
}
|
||||
|
||||
fn unregister_bitmap(&mut self, bitmap: BitmapHandle) {
|
||||
self.bitmap_registry.remove(&bitmap);
|
||||
}
|
||||
|
||||
fn update_texture(
|
||||
&mut self,
|
||||
handle: BitmapHandle,
|
||||
width: u32,
|
||||
height: u32,
|
||||
rgba: Vec<u8>,
|
||||
) -> Result<BitmapHandle, BitmapError> {
|
||||
let texture = if let Some(entry) = self.bitmap_registry.get(&handle) {
|
||||
&entry.texture
|
||||
} else {
|
||||
log::warn!("Tried to replace nonexistent texture");
|
||||
return Ok(handle);
|
||||
};
|
||||
|
||||
self.gl.bind_texture(Gl::TEXTURE_2D, Some(&texture));
|
||||
|
||||
self.gl
|
||||
.tex_image_2d_with_i32_and_i32_and_i32_and_format_and_type_and_opt_u8_array(
|
||||
Gl::TEXTURE_2D,
|
||||
0,
|
||||
Gl::RGBA as i32,
|
||||
width as i32,
|
||||
height as i32,
|
||||
0,
|
||||
Gl::RGBA,
|
||||
Gl::UNSIGNED_BYTE,
|
||||
Some(&rgba),
|
||||
)
|
||||
.into_js_result()
|
||||
.map_err(|e| BitmapError::JavascriptError(e.into()))?;
|
||||
|
||||
Ok(handle)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
|
|
|
@ -11,6 +11,7 @@ use crate::{
|
|||
use fnv::FnvHashMap;
|
||||
use ruffle_render::backend::{RenderBackend, ShapeHandle, ViewportDimensions};
|
||||
use ruffle_render::bitmap::{Bitmap, BitmapHandle, BitmapSource};
|
||||
use ruffle_render::commands::{CommandHandler, CommandList};
|
||||
use ruffle_render::error::Error as BitmapError;
|
||||
use ruffle_render::shape_utils::DistilledShape;
|
||||
use ruffle_render::tessellator::{DrawType as TessDrawType, ShapeTessellator};
|
||||
|
@ -550,6 +551,173 @@ impl<T: RenderTarget> WgpuRenderBackend<T> {
|
|||
&self.descriptors
|
||||
}
|
||||
|
||||
fn begin_frame(&mut self, clear: Color) {
|
||||
self.mask_state = MaskState::NoMask;
|
||||
self.num_masks = 0;
|
||||
self.uniform_buffers.reset();
|
||||
|
||||
let frame_output = match self.target.get_next_texture() {
|
||||
Ok(frame) => frame,
|
||||
Err(e) => {
|
||||
log::warn!("Couldn't begin new render frame: {}", e);
|
||||
// Attemp to recreate the swap chain in this case.
|
||||
self.target.resize(
|
||||
&self.descriptors.device,
|
||||
self.target.width(),
|
||||
self.target.height(),
|
||||
);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let label = create_debug_label!("Draw encoder");
|
||||
let draw_encoder =
|
||||
self.descriptors
|
||||
.device
|
||||
.create_command_encoder(&wgpu::CommandEncoderDescriptor {
|
||||
label: label.as_deref(),
|
||||
});
|
||||
let uniform_encoder_label = create_debug_label!("Uniform upload command encoder");
|
||||
let uniform_encoder =
|
||||
self.descriptors
|
||||
.device
|
||||
.create_command_encoder(&wgpu::CommandEncoderDescriptor {
|
||||
label: uniform_encoder_label.as_deref(),
|
||||
});
|
||||
let mut frame_data = Box::new((draw_encoder, frame_output, uniform_encoder));
|
||||
|
||||
self.globals
|
||||
.update_uniform(&self.descriptors.device, &mut frame_data.0);
|
||||
|
||||
// Use intermediate render targets when resolving MSAA or copying from linear-to-sRGB texture.
|
||||
let (color_view, resolve_target) = match (&self.frame_buffer_view, &self.copy_srgb_view) {
|
||||
(None, None) => (frame_data.1.view(), None),
|
||||
(None, Some(copy)) => (copy, None),
|
||||
(Some(frame_buffer), None) => (frame_buffer, Some(frame_data.1.view())),
|
||||
(Some(frame_buffer), Some(copy)) => (frame_buffer, Some(copy)),
|
||||
};
|
||||
|
||||
let render_pass = frame_data.0.begin_render_pass(&wgpu::RenderPassDescriptor {
|
||||
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
|
||||
view: color_view,
|
||||
ops: wgpu::Operations {
|
||||
load: wgpu::LoadOp::Clear(wgpu::Color {
|
||||
r: f64::from(clear.r) / 255.0,
|
||||
g: f64::from(clear.g) / 255.0,
|
||||
b: f64::from(clear.b) / 255.0,
|
||||
a: f64::from(clear.a) / 255.0,
|
||||
}),
|
||||
store: true,
|
||||
},
|
||||
resolve_target,
|
||||
})],
|
||||
depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
|
||||
view: &self.depth_texture_view,
|
||||
depth_ops: Some(wgpu::Operations {
|
||||
load: wgpu::LoadOp::Clear(0.0),
|
||||
store: false,
|
||||
}),
|
||||
stencil_ops: Some(wgpu::Operations {
|
||||
load: wgpu::LoadOp::Clear(0),
|
||||
store: true,
|
||||
}),
|
||||
}),
|
||||
label: None,
|
||||
});
|
||||
|
||||
// Since RenderPass holds a reference to the CommandEncoder, we cast the lifetime
|
||||
// away to allow for the self-referencing struct. draw_encoder is boxed so its
|
||||
// address should remain stable.
|
||||
self.current_frame = Some(Frame {
|
||||
render_pass: unsafe {
|
||||
std::mem::transmute::<_, wgpu::RenderPass<'static>>(render_pass)
|
||||
},
|
||||
frame_data,
|
||||
});
|
||||
}
|
||||
|
||||
fn end_frame(&mut self) {
|
||||
if let Some(frame) = self.current_frame.take() {
|
||||
let draw_encoder = frame.frame_data.0;
|
||||
let mut uniform_encoder = frame.frame_data.2;
|
||||
let render_pass = frame.render_pass;
|
||||
// Finalize render pass.
|
||||
drop(render_pass);
|
||||
|
||||
// If we have an sRGB surface, copy from our linear intermediate buffer to the sRGB surface.
|
||||
let command_buffers = if let Some(copy_srgb_bind_group) = &self.copy_srgb_bind_group {
|
||||
debug_assert!(self.copy_srgb_view.is_some());
|
||||
let mut copy_encoder = self.descriptors.device.create_command_encoder(
|
||||
&wgpu::CommandEncoderDescriptor {
|
||||
label: create_debug_label!("Frame copy command encoder").as_deref(),
|
||||
},
|
||||
);
|
||||
|
||||
let mut render_pass = copy_encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
||||
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
|
||||
view: frame.frame_data.1.view(),
|
||||
ops: wgpu::Operations {
|
||||
load: wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT),
|
||||
store: true,
|
||||
},
|
||||
resolve_target: None,
|
||||
})],
|
||||
depth_stencil_attachment: None,
|
||||
label: None,
|
||||
});
|
||||
|
||||
render_pass.set_pipeline(&self.descriptors.pipelines.copy_srgb_pipeline);
|
||||
render_pass.set_bind_group(0, self.globals.bind_group(), &[]);
|
||||
self.uniform_buffers.write_uniforms(
|
||||
&self.descriptors.device,
|
||||
&self.descriptors.uniform_buffers_layout,
|
||||
&mut uniform_encoder,
|
||||
&mut render_pass,
|
||||
1,
|
||||
&Transforms {
|
||||
world_matrix: [
|
||||
[self.target.width() as f32, 0.0, 0.0, 0.0],
|
||||
[0.0, self.target.height() as f32, 0.0, 0.0],
|
||||
[0.0, 0.0, 1.0, 0.0],
|
||||
[0.0, 0.0, 0.0, 1.0],
|
||||
],
|
||||
color_adjustments: ColorAdjustments {
|
||||
mult_color: [1.0, 1.0, 1.0, 1.0],
|
||||
add_color: [0.0, 0.0, 0.0, 0.0],
|
||||
},
|
||||
},
|
||||
);
|
||||
render_pass.set_bind_group(2, copy_srgb_bind_group, &[]);
|
||||
render_pass.set_bind_group(
|
||||
3,
|
||||
self.descriptors
|
||||
.bitmap_samplers
|
||||
.get_bind_group(false, false),
|
||||
&[],
|
||||
);
|
||||
render_pass.set_vertex_buffer(0, self.quad_vbo.slice(..));
|
||||
render_pass.set_index_buffer(self.quad_ibo.slice(..), wgpu::IndexFormat::Uint32);
|
||||
render_pass.draw_indexed(0..6, 0, 0..1);
|
||||
drop(render_pass);
|
||||
vec![
|
||||
uniform_encoder.finish(),
|
||||
draw_encoder.finish(),
|
||||
copy_encoder.finish(),
|
||||
]
|
||||
} else {
|
||||
vec![uniform_encoder.finish(), draw_encoder.finish()]
|
||||
};
|
||||
|
||||
self.uniform_buffers.finish();
|
||||
self.target.submit(
|
||||
&self.descriptors.device,
|
||||
&self.descriptors.queue,
|
||||
command_buffers,
|
||||
frame.frame_data.1,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn target(&self) -> &T {
|
||||
&self.target
|
||||
}
|
||||
|
@ -563,8 +731,9 @@ impl<T: RenderTarget> WgpuRenderBackend<T> {
|
|||
handle: BitmapHandle,
|
||||
width: u32,
|
||||
height: u32,
|
||||
f: &mut dyn FnMut(&mut dyn RenderBackend) -> Result<(), ruffle_render::error::Error>,
|
||||
) -> Result<(Self, ruffle_render::bitmap::Bitmap), ruffle_render::error::Error> {
|
||||
commands: CommandList,
|
||||
clear_color: Color,
|
||||
) -> Result<(Self, Bitmap), ruffle_render::error::Error> {
|
||||
// We need ownership of `Texture` to access the non-`Clone`
|
||||
// `wgpu` fields. At the end of this method, we re-insert
|
||||
// `texture` into the map.
|
||||
|
@ -659,7 +828,7 @@ impl<T: RenderTarget> WgpuRenderBackend<T> {
|
|||
blend_modes: vec![BlendMode::Normal],
|
||||
};
|
||||
|
||||
let f_res = f(&mut texture_backend);
|
||||
texture_backend.submit_frame(clear_color, commands);
|
||||
|
||||
// Capture with premultiplied alpha, which is what we use for all textures
|
||||
let image = texture_backend
|
||||
|
@ -667,7 +836,7 @@ impl<T: RenderTarget> WgpuRenderBackend<T> {
|
|||
.capture(&texture_backend.descriptors.device, true);
|
||||
|
||||
let image = image.map(|image| {
|
||||
ruffle_render::bitmap::Bitmap::new(
|
||||
Bitmap::new(
|
||||
image.dimensions().0,
|
||||
image.dimensions().1,
|
||||
ruffle_render::bitmap::BitmapFormat::Rgba,
|
||||
|
@ -694,9 +863,6 @@ impl<T: RenderTarget> WgpuRenderBackend<T> {
|
|||
texture.texture_wrapper.texture = texture_backend.target.texture;
|
||||
self.bitmap_registry.insert(handle, texture);
|
||||
|
||||
// Check result after restoring the backend fields
|
||||
f_res?;
|
||||
|
||||
Ok((self, image.unwrap()))
|
||||
}
|
||||
}
|
||||
|
@ -848,89 +1014,158 @@ impl<T: RenderTarget + 'static> RenderBackend for WgpuRenderBackend<T> {
|
|||
handle
|
||||
}
|
||||
|
||||
fn begin_frame(&mut self, clear: Color) {
|
||||
self.mask_state = MaskState::NoMask;
|
||||
self.num_masks = 0;
|
||||
self.uniform_buffers.reset();
|
||||
fn submit_frame(&mut self, clear: Color, commands: CommandList) {
|
||||
self.begin_frame(clear);
|
||||
commands.execute(self);
|
||||
self.end_frame();
|
||||
}
|
||||
|
||||
let frame_output = match self.target.get_next_texture() {
|
||||
Ok(frame) => frame,
|
||||
Err(e) => {
|
||||
log::warn!("Couldn't begin new render frame: {}", e);
|
||||
// Attemp to recreate the swap chain in this case.
|
||||
self.target.resize(
|
||||
&self.descriptors.device,
|
||||
self.target.width(),
|
||||
self.target.height(),
|
||||
);
|
||||
return;
|
||||
}
|
||||
fn get_bitmap_pixels(&mut self, bitmap: BitmapHandle) -> Option<Bitmap> {
|
||||
self.bitmap_registry.get(&bitmap).map(|e| e.bitmap.clone())
|
||||
}
|
||||
|
||||
fn register_bitmap(&mut self, bitmap: Bitmap) -> Result<BitmapHandle, BitmapError> {
|
||||
if bitmap.width() > self.descriptors.limits.max_texture_dimension_2d
|
||||
|| bitmap.height() > self.descriptors.limits.max_texture_dimension_2d
|
||||
{
|
||||
return Err(BitmapError::TooLarge);
|
||||
}
|
||||
|
||||
let bitmap = bitmap.to_rgba();
|
||||
let extent = wgpu::Extent3d {
|
||||
width: bitmap.width(),
|
||||
height: bitmap.height(),
|
||||
depth_or_array_layers: 1,
|
||||
};
|
||||
|
||||
let label = create_debug_label!("Draw encoder");
|
||||
let draw_encoder =
|
||||
self.descriptors
|
||||
.device
|
||||
.create_command_encoder(&wgpu::CommandEncoderDescriptor {
|
||||
label: label.as_deref(),
|
||||
});
|
||||
let uniform_encoder_label = create_debug_label!("Uniform upload command encoder");
|
||||
let uniform_encoder =
|
||||
self.descriptors
|
||||
.device
|
||||
.create_command_encoder(&wgpu::CommandEncoderDescriptor {
|
||||
label: uniform_encoder_label.as_deref(),
|
||||
});
|
||||
let mut frame_data = Box::new((draw_encoder, frame_output, uniform_encoder));
|
||||
let texture_label = create_debug_label!("Bitmap");
|
||||
let texture = self
|
||||
.descriptors
|
||||
.device
|
||||
.create_texture(&wgpu::TextureDescriptor {
|
||||
label: texture_label.as_deref(),
|
||||
size: extent,
|
||||
mip_level_count: 1,
|
||||
sample_count: 1,
|
||||
dimension: wgpu::TextureDimension::D2,
|
||||
format: wgpu::TextureFormat::Rgba8Unorm,
|
||||
usage: wgpu::TextureUsages::TEXTURE_BINDING
|
||||
| wgpu::TextureUsages::COPY_DST
|
||||
| wgpu::TextureUsages::RENDER_ATTACHMENT
|
||||
| wgpu::TextureUsages::COPY_SRC,
|
||||
});
|
||||
|
||||
self.globals
|
||||
.update_uniform(&self.descriptors.device, &mut frame_data.0);
|
||||
|
||||
// Use intermediate render targets when resolving MSAA or copying from linear-to-sRGB texture.
|
||||
let (color_view, resolve_target) = match (&self.frame_buffer_view, &self.copy_srgb_view) {
|
||||
(None, None) => (frame_data.1.view(), None),
|
||||
(None, Some(copy)) => (copy, None),
|
||||
(Some(frame_buffer), None) => (frame_buffer, Some(frame_data.1.view())),
|
||||
(Some(frame_buffer), Some(copy)) => (frame_buffer, Some(copy)),
|
||||
};
|
||||
|
||||
let render_pass = frame_data.0.begin_render_pass(&wgpu::RenderPassDescriptor {
|
||||
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
|
||||
view: color_view,
|
||||
ops: wgpu::Operations {
|
||||
load: wgpu::LoadOp::Clear(wgpu::Color {
|
||||
r: f64::from(clear.r) / 255.0,
|
||||
g: f64::from(clear.g) / 255.0,
|
||||
b: f64::from(clear.b) / 255.0,
|
||||
a: f64::from(clear.a) / 255.0,
|
||||
}),
|
||||
store: true,
|
||||
},
|
||||
resolve_target,
|
||||
})],
|
||||
depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
|
||||
view: &self.depth_texture_view,
|
||||
depth_ops: Some(wgpu::Operations {
|
||||
load: wgpu::LoadOp::Clear(0.0),
|
||||
store: false,
|
||||
}),
|
||||
stencil_ops: Some(wgpu::Operations {
|
||||
load: wgpu::LoadOp::Clear(0),
|
||||
store: true,
|
||||
}),
|
||||
}),
|
||||
label: None,
|
||||
});
|
||||
|
||||
// Since RenderPass holds a reference to the CommandEncoder, we cast the lifetime
|
||||
// away to allow for the self-referencing struct. draw_encoder is boxed so its
|
||||
// address should remain stable.
|
||||
self.current_frame = Some(Frame {
|
||||
render_pass: unsafe {
|
||||
std::mem::transmute::<_, wgpu::RenderPass<'static>>(render_pass)
|
||||
self.descriptors.queue.write_texture(
|
||||
wgpu::ImageCopyTexture {
|
||||
texture: &texture,
|
||||
mip_level: 0,
|
||||
origin: Default::default(),
|
||||
aspect: wgpu::TextureAspect::All,
|
||||
},
|
||||
frame_data,
|
||||
});
|
||||
bitmap.data(),
|
||||
wgpu::ImageDataLayout {
|
||||
offset: 0,
|
||||
bytes_per_row: NonZeroU32::new(4 * extent.width),
|
||||
rows_per_image: None,
|
||||
},
|
||||
extent,
|
||||
);
|
||||
|
||||
let handle = self.next_bitmap_handle;
|
||||
self.next_bitmap_handle = BitmapHandle(self.next_bitmap_handle.0 + 1);
|
||||
let width = bitmap.width();
|
||||
let height = bitmap.height();
|
||||
|
||||
// Make bind group for bitmap quad.
|
||||
let texture_view = texture.create_view(&Default::default());
|
||||
let bind_group = self
|
||||
.descriptors
|
||||
.device
|
||||
.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||
layout: &target_data!(self).pipelines.bitmap_layout,
|
||||
entries: &[
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 0,
|
||||
resource: wgpu::BindingResource::Buffer(wgpu::BufferBinding {
|
||||
buffer: &self.quad_tex_transforms,
|
||||
offset: 0,
|
||||
size: wgpu::BufferSize::new(
|
||||
std::mem::size_of::<TextureTransforms>() as u64
|
||||
),
|
||||
}),
|
||||
},
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 1,
|
||||
resource: wgpu::BindingResource::TextureView(&texture_view),
|
||||
},
|
||||
],
|
||||
label: create_debug_label!("Bitmap {} bind group", handle.0).as_deref(),
|
||||
});
|
||||
|
||||
if self
|
||||
.bitmap_registry
|
||||
.insert(
|
||||
handle,
|
||||
RegistryData {
|
||||
bitmap,
|
||||
texture_wrapper: Texture {
|
||||
width,
|
||||
height,
|
||||
texture,
|
||||
bind_group,
|
||||
texture_offscreen: None,
|
||||
},
|
||||
},
|
||||
)
|
||||
.is_some()
|
||||
{
|
||||
panic!("Overwrote existing bitmap {:?}", handle);
|
||||
}
|
||||
|
||||
Ok(handle)
|
||||
}
|
||||
|
||||
fn unregister_bitmap(&mut self, handle: BitmapHandle) {
|
||||
self.bitmap_registry.remove(&handle);
|
||||
}
|
||||
|
||||
fn update_texture(
|
||||
&mut self,
|
||||
handle: BitmapHandle,
|
||||
width: u32,
|
||||
height: u32,
|
||||
rgba: Vec<u8>,
|
||||
) -> Result<BitmapHandle, BitmapError> {
|
||||
let texture = if let Some(entry) = self.bitmap_registry.get(&handle) {
|
||||
&entry.texture_wrapper.texture
|
||||
} else {
|
||||
log::warn!("Tried to replace nonexistent texture");
|
||||
return Ok(handle);
|
||||
};
|
||||
|
||||
let extent = wgpu::Extent3d {
|
||||
width,
|
||||
height,
|
||||
depth_or_array_layers: 1,
|
||||
};
|
||||
|
||||
self.descriptors.queue.write_texture(
|
||||
wgpu::ImageCopyTexture {
|
||||
texture,
|
||||
mip_level: 0,
|
||||
origin: Default::default(),
|
||||
aspect: wgpu::TextureAspect::All,
|
||||
},
|
||||
&rgba,
|
||||
wgpu::ImageDataLayout {
|
||||
offset: 0,
|
||||
bytes_per_row: NonZeroU32::new(4 * extent.width),
|
||||
rows_per_image: None,
|
||||
},
|
||||
extent,
|
||||
);
|
||||
|
||||
Ok(handle)
|
||||
}
|
||||
|
||||
fn render_offscreen(
|
||||
|
@ -938,7 +1173,8 @@ impl<T: RenderTarget + 'static> RenderBackend for WgpuRenderBackend<T> {
|
|||
handle: BitmapHandle,
|
||||
width: u32,
|
||||
height: u32,
|
||||
f: &mut dyn FnMut(&mut dyn RenderBackend) -> Result<(), ruffle_render::error::Error>,
|
||||
commands: CommandList,
|
||||
clear_color: Color,
|
||||
) -> Result<Bitmap, ruffle_render::error::Error> {
|
||||
// Rendering to a texture backend requires us to use non-`Clone`
|
||||
// wgpu resources (e.g. `wgpu::Device`, `wgpu::Queue`.
|
||||
|
@ -965,11 +1201,13 @@ impl<T: RenderTarget + 'static> RenderBackend for WgpuRenderBackend<T> {
|
|||
// printed, and there's not really much point in attempting
|
||||
// to recover from a partially failed render operation, anyway.
|
||||
Ok(take_mut(self, |this| {
|
||||
this.render_offscreen_internal(handle, width, height, f)
|
||||
this.render_offscreen_internal(handle, width, height, commands, clear_color)
|
||||
.expect("Failed to render to offscreen backend")
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: RenderTarget> CommandHandler for WgpuRenderBackend<T> {
|
||||
fn render_bitmap(&mut self, bitmap: BitmapHandle, transform: &Transform, smoothing: bool) {
|
||||
let target_data = target_data!(self);
|
||||
if let Some(entry) = self.bitmap_registry.get(&bitmap) {
|
||||
|
@ -1249,88 +1487,6 @@ impl<T: RenderTarget + 'static> RenderBackend for WgpuRenderBackend<T> {
|
|||
frame.render_pass.draw_indexed(0..6, 0, 0..1);
|
||||
}
|
||||
|
||||
fn end_frame(&mut self) {
|
||||
if let Some(frame) = self.current_frame.take() {
|
||||
let draw_encoder = frame.frame_data.0;
|
||||
let mut uniform_encoder = frame.frame_data.2;
|
||||
let render_pass = frame.render_pass;
|
||||
// Finalize render pass.
|
||||
drop(render_pass);
|
||||
|
||||
// If we have an sRGB surface, copy from our linear intermediate buffer to the sRGB surface.
|
||||
let command_buffers = if let Some(copy_srgb_bind_group) = &self.copy_srgb_bind_group {
|
||||
debug_assert!(self.copy_srgb_view.is_some());
|
||||
let mut copy_encoder = self.descriptors.device.create_command_encoder(
|
||||
&wgpu::CommandEncoderDescriptor {
|
||||
label: create_debug_label!("Frame copy command encoder").as_deref(),
|
||||
},
|
||||
);
|
||||
|
||||
let mut render_pass = copy_encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
||||
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
|
||||
view: frame.frame_data.1.view(),
|
||||
ops: wgpu::Operations {
|
||||
load: wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT),
|
||||
store: true,
|
||||
},
|
||||
resolve_target: None,
|
||||
})],
|
||||
depth_stencil_attachment: None,
|
||||
label: None,
|
||||
});
|
||||
|
||||
render_pass.set_pipeline(&target_data!(self).pipelines.copy_srgb_pipeline);
|
||||
render_pass.set_bind_group(0, self.globals.bind_group(), &[]);
|
||||
self.uniform_buffers.write_uniforms(
|
||||
&self.descriptors.device,
|
||||
&self.descriptors.uniform_buffers_layout,
|
||||
&mut uniform_encoder,
|
||||
&mut render_pass,
|
||||
1,
|
||||
&Transforms {
|
||||
world_matrix: [
|
||||
[self.target.width() as f32, 0.0, 0.0, 0.0],
|
||||
[0.0, self.target.height() as f32, 0.0, 0.0],
|
||||
[0.0, 0.0, 1.0, 0.0],
|
||||
[0.0, 0.0, 0.0, 1.0],
|
||||
],
|
||||
color_adjustments: ColorAdjustments {
|
||||
mult_color: [1.0, 1.0, 1.0, 1.0],
|
||||
add_color: [0.0, 0.0, 0.0, 0.0],
|
||||
},
|
||||
},
|
||||
);
|
||||
render_pass.set_bind_group(2, copy_srgb_bind_group, &[]);
|
||||
render_pass.set_bind_group(
|
||||
3,
|
||||
self.descriptors
|
||||
.bitmap_samplers
|
||||
.get_bind_group(false, false),
|
||||
&[],
|
||||
);
|
||||
render_pass.set_vertex_buffer(0, self.quad_vbo.slice(..));
|
||||
render_pass.set_index_buffer(self.quad_ibo.slice(..), wgpu::IndexFormat::Uint32);
|
||||
render_pass.draw_indexed(0..6, 0, 0..1);
|
||||
drop(render_pass);
|
||||
vec![
|
||||
uniform_encoder.finish(),
|
||||
draw_encoder.finish(),
|
||||
copy_encoder.finish(),
|
||||
]
|
||||
} else {
|
||||
vec![uniform_encoder.finish(), draw_encoder.finish()]
|
||||
};
|
||||
|
||||
self.uniform_buffers.finish();
|
||||
self.target.submit(
|
||||
&self.descriptors.device,
|
||||
&self.descriptors.queue,
|
||||
command_buffers,
|
||||
frame.frame_data.1,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn push_mask(&mut self) {
|
||||
debug_assert!(
|
||||
self.mask_state == MaskState::NoMask || self.mask_state == MaskState::DrawMaskedContent
|
||||
|
@ -1366,154 +1522,6 @@ impl<T: RenderTarget + 'static> RenderBackend for WgpuRenderBackend<T> {
|
|||
fn pop_blend_mode(&mut self) {
|
||||
self.blend_modes.pop();
|
||||
}
|
||||
|
||||
fn get_bitmap_pixels(&mut self, bitmap: BitmapHandle) -> Option<Bitmap> {
|
||||
self.bitmap_registry.get(&bitmap).map(|e| e.bitmap.clone())
|
||||
}
|
||||
|
||||
fn register_bitmap(&mut self, bitmap: Bitmap) -> Result<BitmapHandle, BitmapError> {
|
||||
if bitmap.width() > self.descriptors.limits.max_texture_dimension_2d
|
||||
|| bitmap.height() > self.descriptors.limits.max_texture_dimension_2d
|
||||
{
|
||||
return Err(BitmapError::TooLarge);
|
||||
}
|
||||
|
||||
let bitmap = bitmap.to_rgba();
|
||||
let extent = wgpu::Extent3d {
|
||||
width: bitmap.width(),
|
||||
height: bitmap.height(),
|
||||
depth_or_array_layers: 1,
|
||||
};
|
||||
|
||||
let texture_label = create_debug_label!("Bitmap");
|
||||
let texture = self
|
||||
.descriptors
|
||||
.device
|
||||
.create_texture(&wgpu::TextureDescriptor {
|
||||
label: texture_label.as_deref(),
|
||||
size: extent,
|
||||
mip_level_count: 1,
|
||||
sample_count: 1,
|
||||
dimension: wgpu::TextureDimension::D2,
|
||||
format: wgpu::TextureFormat::Rgba8Unorm,
|
||||
usage: wgpu::TextureUsages::TEXTURE_BINDING
|
||||
| wgpu::TextureUsages::COPY_DST
|
||||
| wgpu::TextureUsages::RENDER_ATTACHMENT
|
||||
| wgpu::TextureUsages::COPY_SRC,
|
||||
});
|
||||
|
||||
self.descriptors.queue.write_texture(
|
||||
wgpu::ImageCopyTexture {
|
||||
texture: &texture,
|
||||
mip_level: 0,
|
||||
origin: Default::default(),
|
||||
aspect: wgpu::TextureAspect::All,
|
||||
},
|
||||
bitmap.data(),
|
||||
wgpu::ImageDataLayout {
|
||||
offset: 0,
|
||||
bytes_per_row: NonZeroU32::new(4 * extent.width),
|
||||
rows_per_image: None,
|
||||
},
|
||||
extent,
|
||||
);
|
||||
|
||||
let handle = self.next_bitmap_handle;
|
||||
self.next_bitmap_handle = BitmapHandle(self.next_bitmap_handle.0 + 1);
|
||||
let width = bitmap.width();
|
||||
let height = bitmap.height();
|
||||
|
||||
// Make bind group for bitmap quad.
|
||||
let texture_view = texture.create_view(&Default::default());
|
||||
let bind_group = self
|
||||
.descriptors
|
||||
.device
|
||||
.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||
layout: &target_data!(self).pipelines.bitmap_layout,
|
||||
entries: &[
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 0,
|
||||
resource: wgpu::BindingResource::Buffer(wgpu::BufferBinding {
|
||||
buffer: &self.quad_tex_transforms,
|
||||
offset: 0,
|
||||
size: wgpu::BufferSize::new(
|
||||
std::mem::size_of::<TextureTransforms>() as u64
|
||||
),
|
||||
}),
|
||||
},
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 1,
|
||||
resource: wgpu::BindingResource::TextureView(&texture_view),
|
||||
},
|
||||
],
|
||||
label: create_debug_label!("Bitmap {} bind group", handle.0).as_deref(),
|
||||
});
|
||||
|
||||
if self
|
||||
.bitmap_registry
|
||||
.insert(
|
||||
handle,
|
||||
RegistryData {
|
||||
bitmap,
|
||||
texture_wrapper: Texture {
|
||||
width,
|
||||
height,
|
||||
texture,
|
||||
bind_group,
|
||||
texture_offscreen: None,
|
||||
},
|
||||
},
|
||||
)
|
||||
.is_some()
|
||||
{
|
||||
panic!("Overwrote existing bitmap {:?}", handle);
|
||||
}
|
||||
|
||||
Ok(handle)
|
||||
}
|
||||
|
||||
fn unregister_bitmap(&mut self, handle: BitmapHandle) {
|
||||
self.bitmap_registry.remove(&handle);
|
||||
}
|
||||
|
||||
fn update_texture(
|
||||
&mut self,
|
||||
handle: BitmapHandle,
|
||||
width: u32,
|
||||
height: u32,
|
||||
rgba: Vec<u8>,
|
||||
) -> Result<BitmapHandle, BitmapError> {
|
||||
let texture = if let Some(entry) = self.bitmap_registry.get(&handle) {
|
||||
&entry.texture_wrapper.texture
|
||||
} else {
|
||||
log::warn!("Tried to replace nonexistent texture");
|
||||
return Ok(handle);
|
||||
};
|
||||
|
||||
let extent = wgpu::Extent3d {
|
||||
width,
|
||||
height,
|
||||
depth_or_array_layers: 1,
|
||||
};
|
||||
|
||||
self.descriptors.queue.write_texture(
|
||||
wgpu::ImageCopyTexture {
|
||||
texture,
|
||||
mip_level: 0,
|
||||
origin: Default::default(),
|
||||
aspect: wgpu::TextureAspect::All,
|
||||
},
|
||||
&rgba,
|
||||
wgpu::ImageDataLayout {
|
||||
offset: 0,
|
||||
bytes_per_row: NonZeroU32::new(4 * extent.width),
|
||||
rows_per_image: None,
|
||||
},
|
||||
extent,
|
||||
);
|
||||
|
||||
Ok(handle)
|
||||
}
|
||||
}
|
||||
|
||||
fn create_depth_texture_view(
|
||||
|
|
Loading…
Reference in New Issue