Frame rate optimization
There has been a lot of articles written about how to optimize your flash / flex application frameRate. In most cases, you only need to have full frameRate when some animation is playing, or tweening etc., in other words when some visual changes are going on. FrameRateOptimizator class does exactly what you need. In default, it uses full frameRate when visual changes and when it stops, optimizator automatically switches to lower frameRate. Optimizator uses bitmapData to compare last state with actual state to catch any visual changes. By default snapshot is executed two times per second in low frameRate (inactive state), or one time per second in full frameRate state (active state), taking approximately not more than 10ms per execution (fast enough).
- sk.yoz.optimization.FrameRateOptimizator
- Application
FrameRateOptimizator.as
package sk.yoz.optimization
{
import flash.display.BitmapData;
import flash.display.DisplayObject;
import flash.events.Event;
import flash.events.EventDispatcher;
import flash.events.MouseEvent;
import flash.events.TimerEvent;
import flash.utils.Timer;
public class FrameRateOptimizator extends EventDispatcher
{
protected var snapshot:BitmapData;
protected var fpsp:uint;
protected var _fps:uint;
protected var fpsTimer:Timer;
protected var root:DisplayObject;
protected var _forceActive:Boolean = false;
protected var _forceInactive:Boolean = false;
protected var _mouseMoveActivation:Boolean = false;
public var frameRateActive:uint = 24;
public var frameRateInactive:uint = 2;
protected var inactivityDelay:uint = 1000;
protected var activityTimer:Timer;
public var debug:Boolean = false;
public function FrameRateOptimizator(root:DisplayObject, frameRateActive:uint = 24, frameRateInactive:uint = 2,
inactivityDelay:uint = 1000)
{
super();
this.root = root;
this.frameRateActive = frameRateActive;
this.frameRateInactive = frameRateInactive;
this.inactivityDelay = inactivityDelay;
root.addEventListener(Event.ENTER_FRAME, rootEnterFrame);
fpsTimer = new Timer(1000);
fpsTimer.addEventListener(TimerEvent.TIMER, fpsTimerHandler);
fpsTimer.start();
activityTimer = new Timer(inactivityDelay, 1);
activityTimer.addEventListener(TimerEvent.TIMER_COMPLETE, activityTimerComplete);
isActive = true;
}
[Bindable(event="fpsChanged")]
public function get fps():uint
{
return _fps;
}
protected function set fps(value:uint):void
{
if(_fps == value)
return;
_fps = value;
dispatchEvent(new Event("fpsChanged"));
}
public function set forceActive(value:Boolean):void
{
_forceActive = value;
if(forceActive)
isActive = true;
}
public function get forceActive():Boolean
{
return _forceActive;
}
public function set forceInactive(value:Boolean):void
{
_forceInactive = value;
if(forceInactive)
isActive = false;
}
public function get forceInactive():Boolean
{
return _forceInactive;
}
public function set mouseMoveActivation(value:Boolean):void
{
if(value == _mouseMoveActivation)
return;
_mouseMoveActivation = value;
if(value)
root.addEventListener(MouseEvent.MOUSE_MOVE, rootMouseMove);
else
root.removeEventListener(MouseEvent.MOUSE_MOVE, rootMouseMove);
}
public function get mouseMoveActivation():Boolean
{
return _mouseMoveActivation;
}
public function set isActive(value:Boolean):void
{
if(value && !forceActive && !forceInactive)
{
activityTimer.reset();
activityTimer.start();
}
frameRate = suggestFrameRate(value);
}
protected function suggestFrameRate(active:Boolean):uint
{
if(forceActive)
return frameRateActive;
if(forceInactive)
return frameRateInactive;
return active ? frameRateActive : frameRateInactive;
}
protected function set frameRate(value:uint):void
{
if(value == frameRate)
return;
root.stage.frameRate = value;
if(debug)
trace("FrameRateOptimizator -> frameRate = " + value);
}
protected function get frameRate():uint
{
return root.stage.frameRate;
}
public function get isActive():Boolean
{
return frameRate == frameRateActive;
}
protected function rootEnterFrame(event:Event):void
{
fpsp++;
if(!isActive && rootChanged)
isActive = true;
}
protected function rootMouseMove(event:MouseEvent):void
{
isActive = true;
}
protected function get rootChanged():Boolean
{
var changed:Boolean = true;
var bitmapData:BitmapData;
try
{
bitmapData = new BitmapData(root.width, root.height);
bitmapData.draw(root);
if(snapshot)
changed = bitmapData.compare(snapshot) != 0;
snapshot = bitmapData;
}
catch(error:Error)
{
snapshot = null;
if(debug)
trace("FrameRateOptimizator -> rootChanged Error: " + error.message);
}
return changed;
}
protected function fpsTimerHandler(event:TimerEvent):void
{
protected::fps = fpsp;
fpsp = 0;
}
protected function activityTimerComplete(event:TimerEvent):void
{
isActive = rootChanged;
}
}
}
Application
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
applicationComplete="init()">
<mx:Script>
<![CDATA[
import sk.yoz.optimization.FrameRateOptimizator;
[Bindable]
private var frameRateOptimizator:FrameRateOptimizator;
private function init():void
{
frameRateOptimizator = new FrameRateOptimizator(this, 30);
// trace new frameRate on change
frameRateOptimizator.debug = true;
// makes active frameRate on mouse move
frameRateOptimizator.mouseMoveActivation = true;
// always use active frameRate
// frameRateOptimizator.forceActive = true;
// always use inactive frameRate
// frameRateOptimizator.forceInactive = true;
// use active frameRate immediately
//frameRateOptimizator.isActive = true;
}
]]>
</mx:Script>
<mx:VSlider/>
</mx:Application>
Export code in debug mode and watch how frameRate changes (in trace console) when playing with slider. Try (un)comment mouseMoveActivation and see the difference.
Optimizer will not work if application contains any source object and (in the case of a Sprite or MovieClip object) all of its child objects do not come from the same domain as the caller, or are not in a content that is accessible to the caller by having called the Security.allowDomain() method. Read more about BitmapData.draw() SecurityError.

What would be the easiest way to use this class in a flash movie? Could you possibly provide an example?
easiest use in flash
import sk.yoz.optimization.FrameRateOptimizator;
var fro:FrameRateOptimizator = new FrameRateOptimizator(this);
fro.debug = true;
the debug line is just to trace when fps changed. there has to be some graphic content in stage