Avm2: Implement Stage.invalidate

This commit is contained in:
golfinq 2023-01-28 23:38:59 -05:00 committed by kmeisthax
parent a818dd0918
commit 37ec94f95b
8 changed files with 149 additions and 1 deletions

View File

@ -66,7 +66,7 @@ pub use crate::avm2::value::Value;
use self::scope::Scope;
const BROADCAST_WHITELIST: [&str; 3] = ["enterFrame", "exitFrame", "frameConstructed"];
const BROADCAST_WHITELIST: [&str; 4] = ["enterFrame", "exitFrame", "frameConstructed", "render"];
/// The state of an AVM2 interpreter.
#[derive(Collect)]

View File

@ -726,6 +726,21 @@ pub fn stage3ds<'gc>(
Ok(Value::Undefined)
}
/// Implement `invalidate`
pub fn invalidate<'gc>(
activation: &mut Activation<'_, 'gc>,
this: Option<Object<'gc>>,
_args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> {
if let Some(stage) = this
.and_then(|this| this.as_display_object())
.and_then(|this| this.as_stage())
{
stage.set_invalidated(activation.context.gc_context, true);
}
Ok(Value::Undefined)
}
/// Stage.fullScreenSourceRect's getter
pub fn full_screen_source_rect<'gc>(
activation: &mut Activation<'_, 'gc>,
@ -863,5 +878,8 @@ pub fn create_class<'gc>(mc: MutationContext<'gc, '_>) -> GcCell<'gc, Class<'gc>
];
write.define_public_builtin_instance_properties(mc, PUBLIC_INSTANCE_PROPERTIES);
const PUBLIC_INSTANCE_METHODS: &[(&str, NativeMethodImpl)] = &[("invalidate", invalidate)];
write.define_public_builtin_instance_methods(mc, PUBLIC_INSTANCE_METHODS);
class
}

View File

@ -90,6 +90,9 @@ pub struct StageData<'gc> {
/// The alignment of the stage.
align: StageAlign,
/// Whether or not a RENDER event should be dispatched on the next render
invalidated: bool,
/// Whether to use high quality downsampling for bitmaps.
///
/// This is usally implied by `quality` being `Best` or higher, but the AVM1
@ -149,6 +152,7 @@ impl<'gc> Stage<'gc> {
} else {
StageDisplayState::Normal
},
invalidated: false,
align: Default::default(),
use_bitmap_downsampling: false,
view_bounds: Default::default(),
@ -210,6 +214,16 @@ impl<'gc> Stage<'gc> {
self.0.write(gc_context).loader_info = loader_info;
}
// Get the invalidation state
pub fn invalidated(self) -> bool {
self.0.read().invalidated
}
// Set the invalidation state
pub fn set_invalidated(self, gc_context: MutationContext<'gc, '_>, value: bool) {
self.0.write(gc_context).invalidated = value;
}
/// Returns the quality setting of the stage.
///
/// In the Flash Player, the quality setting affects anti-aliasing and smoothing of bitmaps.
@ -617,6 +631,25 @@ impl<'gc> Stage<'gc> {
}
}
/// Broadcast the 'render' event
///
/// TODO: Need additional check as Flash Player does not
/// broadcast the 'render' event on the first render
pub fn broadcast_render(&self, context: &mut UpdateContext<'_, 'gc>) {
let render_evt = Avm2EventObject::bare_default_event(context, "render");
let dobject_constr = context.avm2.classes().display_object;
if let Err(e) = Avm2::broadcast_event(context, render_evt, dobject_constr) {
tracing::error!(
"Encountered AVM2 error when broadcasting render event: {}",
e
);
}
self.set_invalidated(context.gc_context, false);
}
/// Fires `Stage.onFullScreen` in AVM1 or `Event.FULLSCREEN` in AVM2.
pub fn fire_fullscreen_event(self, context: &mut UpdateContext<'_, 'gc>) {
if !context.is_action_script_3() {

View File

@ -1481,6 +1481,17 @@ impl Player {
#[instrument(level = "debug", skip_all)]
pub fn render(&mut self) {
let invalidated = self
.gc_arena
.borrow()
.mutate(|_, gc_root| gc_root.data.read().stage.invalidated());
if invalidated {
self.update(|context| {
let stage = context.stage;
stage.broadcast_render(context);
});
}
let (renderer, ui, transform_stack) =
(&mut self.renderer, &mut self.ui, &mut self.transform_stack);
let mut background_color = Color::WHITE;

View File

@ -0,0 +1,47 @@
package {
import flash.display.Sprite;
import flash.events.Event;
public class Test extends Sprite {
private var test_stage: int = 0;
public function Test() {
addEventListener(Event.ENTER_FRAME, enterFrameListener);
addEventListener(Event.FRAME_CONSTRUCTED, frameConstructedListener);
addEventListener(Event.EXIT_FRAME, exitFrameListener);
addEventListener(Event.RENDER, renderListener);
test_stage = 0;
}
public function enterFrameListener(e: Event) : void {
trace("ENTER_FRAME ( test_stage = ", test_stage, " )");
if ((test_stage == 2) || (test_stage == 14) || (test_stage == 17)) {
trace("Invalidate called inside enterFrameListener");
stage.invalidate();
}
test_stage = (test_stage + 1) % 21;
}
public function frameConstructedListener(e: Event) : void {
trace("FRAME_CONSTRUCTED ( test_stage = ", test_stage, " )");
if ((test_stage == 6) || (test_stage == 12) || (test_stage == 15)) {
trace("Invalidate called inside frameConstructedListener");
stage.invalidate();
}
test_stage = (test_stage + 1) % 21;
}
public function exitFrameListener(e: Event) : void {
trace("EXIT_FRAME ( test_stage = ", test_stage, " )");
if ((test_stage == 10) || (test_stage == 13) || (test_stage == 19)) {
trace("Invalidate called inside exitFrameListener");
stage.invalidate();
}
test_stage = (test_stage + 1) % 21;
}
public function renderListener(e: Event) : void {
trace("RENDER ( test_stage = ", test_stage, " )");
}
}
}

View File

@ -0,0 +1,38 @@
FRAME_CONSTRUCTED ( test_stage = 0 )
EXIT_FRAME ( test_stage = 1 )
ENTER_FRAME ( test_stage = 2 )
Invalidate called inside enterFrameListener
FRAME_CONSTRUCTED ( test_stage = 3 )
EXIT_FRAME ( test_stage = 4 )
RENDER ( test_stage = 5 )
ENTER_FRAME ( test_stage = 5 )
FRAME_CONSTRUCTED ( test_stage = 6 )
Invalidate called inside frameConstructedListener
EXIT_FRAME ( test_stage = 7 )
RENDER ( test_stage = 8 )
ENTER_FRAME ( test_stage = 8 )
FRAME_CONSTRUCTED ( test_stage = 9 )
EXIT_FRAME ( test_stage = 10 )
Invalidate called inside exitFrameListener
RENDER ( test_stage = 11 )
ENTER_FRAME ( test_stage = 11 )
FRAME_CONSTRUCTED ( test_stage = 12 )
Invalidate called inside frameConstructedListener
EXIT_FRAME ( test_stage = 13 )
Invalidate called inside exitFrameListener
RENDER ( test_stage = 14 )
ENTER_FRAME ( test_stage = 14 )
Invalidate called inside enterFrameListener
FRAME_CONSTRUCTED ( test_stage = 15 )
Invalidate called inside frameConstructedListener
EXIT_FRAME ( test_stage = 16 )
RENDER ( test_stage = 17 )
ENTER_FRAME ( test_stage = 17 )
Invalidate called inside enterFrameListener
FRAME_CONSTRUCTED ( test_stage = 18 )
EXIT_FRAME ( test_stage = 19 )
Invalidate called inside exitFrameListener
RENDER ( test_stage = 20 )
ENTER_FRAME ( test_stage = 20 )
FRAME_CONSTRUCTED ( test_stage = 0 )
EXIT_FRAME ( test_stage = 1 )

Binary file not shown.

View File

@ -0,0 +1 @@
num_frames = 8