-
-
Notifications
You must be signed in to change notification settings - Fork 776
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
AVM2: RemoveObject
runs at the start of a frame & frame phase tracking
#7048
Closed
Closed
Changes from all commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
8df594c
core: `enter_frame` should be an event that all display objects get a…
kmeisthax 44a56c6
core: In AVM2, movie clips process `RemoveObject` before any other fr…
kmeisthax 9c028d7
core: Frame actions should start with `enterFrame`; not `exitFrame`.
kmeisthax 966d0f2
tests: Fix the test output for `as3_movieclip_displayevents_dblhandler`.
kmeisthax 53d8a2a
core: Track what part of the frame processing loop we're in
kmeisthax dddfb09
core: Add a new frame phase to represent non-frame work such as input…
kmeisthax 5806956
core: `Player` should start in the `Idle` phase
kmeisthax a7c5d3b
core: Centralize all the various calls to `enter_frame`/`construct_fr…
kmeisthax 20f0b0d
tests: Ignore `simplebutton_constr_childevents` until other PRs cut f…
kmeisthax File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,157 @@ | ||
//! Frame events management | ||
//! | ||
//! This module aids in keeping track of which frame execution phase we are in. | ||
//! | ||
//! For AVM2 code, display objects execute a series of discrete phases, and | ||
//! each object is notified about the current frame phase in rendering order. | ||
//! When objects are created, they are 'caught up' to the current frame phase | ||
//! to ensure correct order of operations. | ||
//! | ||
//! AVM1 code (presumably, either on an AVM1 stage or within an `AVM1Movie`) | ||
//! runs in one phase, with timeline operations executing with all phases | ||
//! inline in the order that clips were originally created. | ||
|
||
use crate::context::UpdateContext; | ||
use crate::display_object::{DisplayObject, TDisplayObject}; | ||
use crate::vminterface::AvmType; | ||
|
||
/// Which phase of the frame we're currently in. | ||
/// | ||
/// AVM2 frames exist in one of five phases: `Enter`, `Construct`, `Update`, | ||
/// `FrameScripts`, or `Exit`. An additional `Idle` phase covers rendering and | ||
/// event processing. | ||
#[derive(Copy, Clone, PartialEq, Eq, Debug)] | ||
pub enum FramePhase { | ||
/// We're entering the next frame. | ||
/// | ||
/// When movie clips enter a new frame, they must do two things: | ||
/// | ||
/// - Remove all children that should not exist on the next frame. | ||
/// - Increment their current frame number. | ||
/// | ||
/// Once this phase ends, we fire `enterFrame` on the broadcast list. | ||
Enter, | ||
|
||
/// We're constructing children of existing display objects. | ||
/// | ||
/// All `PlaceObject` tags should execute at this time. | ||
/// | ||
/// Once we construct the frame, we fire `frameConstructed` on the | ||
/// broadcast list. | ||
Construct, | ||
|
||
/// We're updating all display objects on the stage. | ||
/// | ||
/// This roughly corresponds to `run_frame`; and should encompass all time | ||
/// based display object changes that are not encompassed by the other | ||
/// phases. | ||
/// | ||
/// This frame phase also exists in AVM1 frames. In AVM1, it does the work | ||
/// of `Enter`, `FrameScripts` (`DoAction` tags), and `Construct`. | ||
Update, | ||
|
||
/// We're running all queued frame scripts. | ||
/// | ||
/// Frame scripts are the AS3 equivalent of old-style `DoAction` tags. They | ||
/// are queued in the `Update` phase if the current timeline frame number | ||
/// differs from the prior frame's one. | ||
FrameScripts, | ||
|
||
/// We're finishing frame processing. | ||
/// | ||
/// When we exit a completed frame, we fire `exitFrame` on the broadcast | ||
/// list. | ||
Exit, | ||
|
||
/// We're not currently executing any frame code. | ||
/// | ||
/// At this point in time, event handlers are expected to run. No frame | ||
/// catch-up work should execute. | ||
Idle, | ||
} | ||
|
||
impl Default for FramePhase { | ||
fn default() -> Self { | ||
FramePhase::Idle | ||
} | ||
} | ||
|
||
/// Run one frame according to AVM1 frame order. | ||
pub fn run_all_phases_avm1<'gc>(context: &mut UpdateContext<'_, 'gc, '_>) { | ||
// In AVM1, we only ever execute the update phase, and all the work that | ||
// would ordinarily be phased is instead run all at once in whatever order | ||
// the SWF requests it. | ||
*context.frame_phase = FramePhase::Update; | ||
|
||
// AVM1 execution order is determined by the global execution list, based on instantiation order. | ||
for clip in context.avm1.clip_exec_iter() { | ||
if clip.removed() { | ||
// Clean up removed objects from this frame or a previous frame. | ||
// Can be safely removed while iterating here, because the iterator advances | ||
// to the next node before returning the current node. | ||
context.avm1.remove_from_exec_list(context.gc_context, clip); | ||
} else { | ||
clip.run_frame(context); | ||
} | ||
} | ||
|
||
// Fire "onLoadInit" events. | ||
context | ||
.load_manager | ||
.movie_clip_on_load(context.action_queue); | ||
|
||
*context.frame_phase = FramePhase::Idle; | ||
} | ||
|
||
/// Run one frame according to AVM2 frame order. | ||
pub fn run_all_phases_avm2<'gc>(context: &mut UpdateContext<'_, 'gc, '_>) { | ||
let stage = context.stage; | ||
|
||
*context.frame_phase = FramePhase::Enter; | ||
stage.enter_frame(context); | ||
|
||
*context.frame_phase = FramePhase::Construct; | ||
stage.construct_frame(context); | ||
stage.frame_constructed(context); | ||
|
||
*context.frame_phase = FramePhase::Update; | ||
stage.run_frame_avm2(context); | ||
|
||
*context.frame_phase = FramePhase::FrameScripts; | ||
stage.run_frame_scripts(context); | ||
|
||
*context.frame_phase = FramePhase::Exit; | ||
stage.exit_frame(context); | ||
|
||
*context.frame_phase = FramePhase::Idle; | ||
} | ||
|
||
/// Run all previously-executed frame phases on a newly-constructed display | ||
/// object. | ||
/// | ||
/// This is a no-op on AVM1, which has it's own catch-up logic. | ||
pub fn catchup_display_object_to_frame<'gc>( | ||
context: &mut UpdateContext<'_, 'gc, '_>, | ||
dobj: DisplayObject<'gc>, | ||
) { | ||
match (*context.frame_phase, context.avm_type()) { | ||
(_, AvmType::Avm1) => {} | ||
//NOTE: We currently do not have test coverage to justify `Enter` | ||
//running `construct_frame`. However, `Idle` *does* need frame | ||
//construction to happen, because event handlers expect to be able to | ||
//construct new movie clips and see their grandchildren. So I suspect | ||
//that constructing symbols in `enterFrame` works the same way. | ||
(FramePhase::Enter, AvmType::Avm2) | (FramePhase::Construct, AvmType::Avm2) => { | ||
dobj.enter_frame(context); | ||
dobj.construct_frame(context); | ||
} | ||
(FramePhase::Update, AvmType::Avm2) | ||
| (FramePhase::FrameScripts, AvmType::Avm2) | ||
| (FramePhase::Exit, AvmType::Avm2) | ||
| (FramePhase::Idle, AvmType::Avm2) => { | ||
dobj.enter_frame(context); | ||
dobj.construct_frame(context); | ||
dobj.run_frame_avm2(context); | ||
} | ||
} | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is talking about looping back to the first frame, not 'looping' on the current frame, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes.