Posted on July 4, 02008
Filed Under DaVinci, Development, Flash, Journal | Leave a Comment
I've been working with the AbstractComponent base class for about a week or so and it seems to be working out okay so far. Along the way I've moved some things around, added some more comments and cleared a bug in the suspend resume system. I've revised the code in the earlier posts to reflect these fixes and updates. Once all the features are in and it's looking stable I'll put a demo together with some source code.
The two big features I'm keen to add next are support for managing children and granular invalidation. These could both get quite complicated so I might try out a few ideas for posting those up. I have an idea at the back of my head to spin off the state management into a separate manager class that is included in the base class using composition. It will make the base class a bit cleaner.
Posted on July 2, 02008
Filed Under DaVinci, Development, Flash | Leave a Comment
Every component framework I’ve ever seen implements deferred rendering. The aim of this feature is to optimize the redrawing of a component so that it does not perform any unnecessary operations.
The Problem
Component properties can change many times during a frame and redrawing them every time this happens is wasteful. The optimal solution is to redraw the component once per frame and only if one or more properties have changed. This process of batching state changes for later rendering is called 'invalidation'.
There is a second issue to be aware of when dealing with redrawing and it’s of critical importance during the instantiation process. The component does not have access to the stage until it has been added to the active DisplayList.
The Solution
Every component has a draw() function which when called renders the component based upon its current internal state. Instead of calling draw() every time a property changes, the component caches the value of the property and calls invalidate() instead. This function schedules a draw() event at the next available opportunity. With this technique the properties can be changed hundreds of times a frame without any significant drop in performance.
There is two common strategies for implementing invalidation in AS3. I compared both of these when I looked ‘Inside the MinimalComps’ and concluded that unless I can find a fail-safe way of using stage.invalidate() then I'm going to have to go with the old ENTER_FRAME event. Here’s the process flow for a typical component: It can be instantiated by either placing it on the timeline or by using the new() command in code.
1. Firstly the component constructor runs. This calls the init() function which initializes the start-up state of the component.
2. When the component is added to the stage the resume() function gets called. This function starts up any internal listeners then calls the draw() function which redraws the component based upon its internal state.
3. Removing a component from the stage (by code or by moving the timeline playhead forces the suspend() function to be called. This freezes the component whilst it is not on the DisplayList. That means no more realtime 3D hogging the processor and no more mp3s playing from nowhere!
The invalidate() and draw() functions are currently both protected. I can’t think of a good reason for making them public right now. They are both housekeeping functions that get used by the public properties and methods of the individual component.
The memory management methods however, suspend(), resume() and destroy() are all public so they can be called by parents or managers at any time.
The Benefits
Deferred Rendering will greatly improve the overall performance of your application.
The Code
package org.computus.core
{
import flash.events.Event;
import flash.display.MovieClip;
public class AbstractComponent extends MovieClip
{
// ------------------------------------------
// PROPERTIES
// isSuspended starts as true as the component is off stage
protected var _isSuspended:Boolean = true;
// ------------------------------------------
// CONSTRUCTOR
public function AbstractComponent():void
{
super();
init();
if (stage) { resume() }
}
// ------------------------------------------
// MEMORY MANAGEMENT
protected function init():void
{
// concrete classes that override this function should call super.init()
// n.b. These events require Flash Player 9.0.28.0
addEventListener(Event.ADDED_TO_STAGE, onAddedToStage, false, 0, true);
addEventListener(Event.REMOVED_FROM_STAGE, onRemovedFromStage, false, 0, true);
// initialise component here.
// no need to call draw() as it will be called when component is added to the stage.
}
public function destroy():void
{
// concrete classes that override this function should call super.destroy()
// remove all listeners here
suspend();
removeEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
removeEventListener(Event.REMOVED_FROM_STAGE, onRemovedFromStage);
}
public function suspend():void
{
// concrete classes that override this function should call super.suspend()
// suspend all processes
removeEventListener(Event.ENTER_FRAME, onInvalidate)
_isSuspended = true;
}
public function resume():void
{
// concrete classes that override this function should call super.resume()
// resume all processes
_isSuspended = false;
draw();
}
public function get isSuspended():Boolean
{
return _isSuspended;
}
// ------------------------------------------
// EVENTS
private function onInvalidate(e:Event):void
{
removeEventListener(Event.ENTER_FRAME, onInvalidate);
draw();
}
private function onAddedToStage(e:Event):void
{
resume()
}
private function onRemovedFromStage(e:Event):void
{
suspend()
}
// ------------------------------------------
// DRAW
protected function invalidate():void
{
addEventListener(Event.ENTER_FRAME, onInvalidate, false, 0, true);
}
protected function draw():void
{
// concrete classes should override this function
// redraw component state
}
}
}
« go back