Replacing ENTER_FRAME
Today an interesting discussion took place on Twitter, in which I was involved. It caught my attention for a variety of reasons, and I wanted to document the conversation, the problem and the solution here. It started when it was identified that Flash’s event dispatcher is sub-optimal in performance terms, and revolved around trying to find a solution.
Why replace the ENTER_FRAME?
Every event that is dispatched creates an object. An ENTER_FRAME creates as many objects per second as there are frames. If you have mutliple objects dispatching ENTER_FRAME events, that’s many objects creating event objects many times per second. Computationally, chains of “many” are one of those things you want to avoid. So, can we do better?
Doing Better
Robert Penner proposed creating an as3signal and using a MovieClip with two frames to dispatch that signal on each frame. He posted his idea to this GitHub gist.
One drawback to this approach is that it requires a MovieClip symbol with two empty frames, to which the two event dispatcher methods could be attached. I remembered reading in this excellent article by Bit 101 how using a [Frame] metadata tag would force your application to have two frames. This seemed to be what Penner needed for his functionality to work correctly, so I came up with my own GitHub gist which extends the concept. The code is also added ‘below the fold‘, at the bottom of this article.
[Update, after Jackson's comment, below] The idea behind the approach is that a frame-loop is an internal Flash player structure. It sits underneath the AS3 functionality, and so it doesn’t produce ENTER_FRAME events. By hooking into them we can produce our own iterative ‘event’ (Penner proposes using an as3signal), which doesn’t construct an object each time it is dispatched. It is a lovely idea, and quite distinct from invoking one ENTER_FRAME dispatcher on the root or stage of the application.
[Update, after Joa Ebert tweeted test results] Whether this method is superior to the ‘one-event ticker’ is open to question. Joa Ebert applied the frameloop concept to AudioBox and found no memory nor performance improvements.
Background
It all started today with a statement on Twitter from Robert Penner:
Listening to a single #AS3 enterFrame will create over 1000 event instances per minute. @robpenner
In fact, at a decent frame rate, you’re looking at 1800 event instances per minute. Across the blogosphere and on Twitter, there has been a lot of talk about the cost of object construction in AS3. People are pushing the limitations of the player, and are looking for performance optimisation wherever they can find it. Strategies such as object pooling are becoming mainstream in order to avoid the creation and destruction of objects. In the meanwhile, the internal Flash event model is creating and destroying huge numbers of objects to furnish us with an event model.
At the same time however, there has been a murmur of discomfiture about the state of the event model. (Not that I have much of a voice, but I chimed in to this debate as well!). The most notable result of this murmur was Penner’s decision to create as3signals. It is an on-going project to which I have contributed in a small way, and which I whole-heartedly support.
After Penner’s tweet, the #as3 twitterati (please tweet if there are glaring omissions from this list) became very excited indeed.
My initial thoughts led to the idea that maybe the setInterval/setTimeout functions, which are hangovers from AS2 might offer a solution – perhaps they shortcut the event system? Not only ain’t it so, they’re even worse as this Action Script Viewer deconstruction of the setInterval methods demonstrate.
So what else? Mike Chambers (aka @Mesh) in conversation with @TheFlashBum pointed towards a solution using a ‘ticker’; a single ENTER_FRAME dispatcher, and @bengarney confirmed that he also uses this technique.
It seems that @scottjanousek and @robpenner had the idea of using the player frameloop (which the solution offered here exploits) at pretty much the same time. (@scottjanousek’s tweets are protected, but it was reported by this tweet.)
Whether and how this vein of thought will develop is really interesting. It is fascinating how a tweet from one of the best-known ActionScript developers can provoke such a lot of attention from the general community. It is interesting too to see the envelope of what is possible being pushed.
FrameLoop Example Source Code
To use this code, you will need the as3signals library. You will also need to compile through the MXML compiler. I have tested it with the Flex 4 SDK – if you try and discover that it does not work for other target platforms, please share the information by leaving a comment!.
Main.as
package
{
import org.osflash.signals.Signal;
import flash.display.Shape;
import flash.display.Sprite;
/**
* A Main application class that uses the [Frame] MetaData Tag to use the
* EventFrameDispatcher class to configure itself. This class contains a
* frame signal which is constructed and passed into it by the
* EventFrameDispatcher class before init() is called. Application
* functionality should be placed in the init() method
*
* @author Alec McEachran
*/
[Frame(factoryClass="EnterFrameDispatcher")]
public class Main extends Sprite
{
/** an as3signal that is dispatched on every ENTER_FRAME */
public var frame:Signal;
/**
* do interesting things in here...
*/
public function init():void
{
// functionality here...
}
}
}
EnterFrameDispatcher.as
package
{
import org.osflash.signals.Signal;
import flash.display.MovieClip;
import flash.events.Event;
import flash.utils.getDefinitionByName;
/**
* The EnterFrameDispatcher is injected into the Main class through
* the [Frame] MetaData tag.
*
* The class creates a frame signal. By being injected, two frames are
* generated, onto which the frameMethod are attached so that the
* frame signal is dispatched on every frame cycle. It then injects this
* object into the Main class and calls the Main class's init() method.
*
* @author Alec McEachran
*
* based on an idea by Robert Penner @see http://gist.github.com/243630
*/
public class EnterFrameDispatcher extends MovieClip
{
public var frame:Signal;
public function EnterFrameDispatcher()
{
frame = new Signal();
addFrameScript(0, frameMethod, 1, frameMethod);
addEventListener(Event.ENTER_FRAME, onEnterFrame);
nextFrame();
}
private function onEnterFrame(event:Event):void
{
removeEventListener(Event.ENTER_FRAME, onEnterFrame);
init();
play();
}
private function frameMethod():void
{
frame.dispatch();
}
private function init():void
{
var mainClass:Class = Class(getDefinitionByName("Main"));
if (mainClass)
{
var app:* = new mainClass();
app.frame = frame;
addChild(app);
app.init();
}
}
}
}
-
cambiata
-
og2t
-
alecmce
-
alecmce
-
alecmce
-
Derrick Grigg
-
jacksondunstan
-
alecmce
-
jacksondunstan
-
alecmce
-
jacksondunstan
