It is estimated that usually about 60-70% of the time that Flash is working it is because of rendering operations such as animating or drawing display objects. One of the most effective ways to increase the Flash Player rendering speed is caching the display objects. In fact, Flash allows caching as bitmap since Flash 8. Up until FP 10.1 the Flash Player has used software to render all the vector graphic and animation.
The way it works is that the player converts the content into a bitmap representation and stores the display object with all of it’s children in memory, so you end up with both: the original vector graphic and bitmap image representation.
It allows the player to use the bitmap image instead of to keep updating the display object on each frame when animating. However, in case visual change occurs, it will re-render the bitmap in memory again and again on every frame before displaying, which will tax the user’s machine.
So as you can imagine, working with bitmap caching is a double edge sword. When used correctly, you get to enjoy faster rendering of your movie clip with little updates for every frame, which will result in an increase of the application’s performance and smoothness of your animation. But when used in places where change to the visual occurs, it can often result in large memory footprint and abuse of the CPU. When used in conjunction with mobile devices where you have limited resources, that can be even more significant and noticeable and will result in draining the battery, especially when building games or animations.
A good usage for cacheAsBitmap
cacheAsBitmap should be used where the visual content doesn’t change often or doesn’t change at all. You will see the impact especially where there is a complex vector graphic involved.
Not a good usage
It should not be used when you need your visual appearance to change or expect the browser window to be resized often. For instance, cases when you need to change: alpha, rotate, scale, change width/height, filter, blendMode, opaqueBackground, transform or add more vector graphic or an image. These cases will result in the bitmap getting re-rendered in memory before displayed.
Additionally, the cached content can only be in the size of up to 2880×2880 pixels, since having a large bitmap dump stored in memory will defeat the purpose of increasing performance.
In some cases where it doesn’t makes much sense to cache as bitmap you can cache your display object manually. Additionally, Flash Player 10.1 offers new methods to help with content where the display object needs to change some visual properties often.
BitmapCaching API can be useful when you need to cache bitmap. Not only does it take into account the new features in Flash Player 10.1, it adds utilities that can help for a quick manual caching. The API includes the following static operations:
- Manually cache as BitmapData
- Manually cache as Bitmap
- Cache all children as Bitmap
- Cache UIComponent
- Cache as BitmapMatrix
It also includes small utility methods to help you get your content cached and protect innocent bystanders.
Additionally, the API is cross platform and will work with Pure AS3, Flex, AIR as well as different versions of the player including FP 9 or FP 10.1, so it will allow you to use what you need based on the platform and FP version you’re using.
Manual cache as BitmapData or Bitmap
Using the cacheAsBitmap method instructs the Player to cached the content as a bitmap and to render it on screen. In case a change to the visual occurs, then each frame will result in updating the cached copy on memory before updating the screen.
The solution is to use a technique where you rasterized and create different bitmap references using the same BitmapData. Then, when you change the visual of the original BitmapData in memory the Flash Player will avoid redrawing, which will reduce CPU usage while keeping a smooth animation.
Take a look at the API. It allows you to quickly do a manual caching using the following static method manualCacheAsBitmapData.
We create a new matrix and then generate the BitmapData that will be used to create different bitmap objects.
public static function manualCacheAsBitmapData( source:Sprite, main:Object = null, finalStageQuality:String = StageQuality.LOW ):BitmapData
{
var sourceRectangle:Rectangle = source.getBounds(source);
var matrix:Matrix = new Matrix();
matrix.translate( -sourceRectangle.x, -sourceRectangle.y );
var bitmapData:BitmapData = new BitmapData( source.width + 1, source.height + 1, true, 0);
if ( main != null )
main.stage.quality = StageQuality.HIGH;
bitmapData.draw( source, matrix );
if ( main != null )
main.stage.quality = finalStageQuality;
return bitmapData;
}
Notice that I am using StageQuality.HIGH and then StageQuality.LOW. It can help the vector graphic appearance, since as the BitmapData is created we care about anti-aliasing and we want our bitmaps to be smoothed while the player creates the BitmapData. Then we can switch to StageQuality.LOW, since at that point we don’t want to tax the user’s CPU at all times. Once we are back at LOW our graphics won’t care that much about anti-aliased or bitmaps smoothed and we usually care about keeping performance and CPU usage low. Keep in mind that StageQuality.LOW is not available in AIR and the stage is always in HIGH or BEST quality.
This technique wont work well for the out of the box cacheAsBitmap, since the frame rate will get reduced as the player will redraw all the display objects that are set as cacheAsBitmap true.
You can then use manualCacheAsBitmap to wrap the display object as Bitmap when needed.
public static function manualCacheAsBitmap( source:Sprite, main:Object = null ):Bitmap
{
return new Bitmap( manualCacheAsBitmapData( source, main ) );
}
Before I show you the implementation example, take a look at these two handy utilities that can help you create a MovieClip based on Sprite and create a Bitmap clip. You pass the graphic source (which is the sprite with the vector graphic or bitmap representation) and you pass a method that will set the event handlers (in case they are needed). These methods will come handy and will help us create the implementation examples. In case you use Flash Professional and the MovieClip is created, you wont need these methods.
public static function createMovieClipBasedOnSprite(spriteSource:Sprite,
isCacheAsBitmap:Boolean = false,
setClipEventHandlers:Function = null):MovieClip
{
var movieClip:MovieClip = new MovieClip();
var source:Sprite = spriteSource;
movieClip.addChild( source );
movieClip.cacheAsBitmap = isCacheAsBitmap;
if (setClipEventHandlers != null)
setClipEventHandlers( movieClip );
return movieClip;
}
public static function createBitmapClip(bitmapData:BitmapData,
setClipEventHandlers:Function = null):Bitmap
{
var movieClip:Bitmap = new Bitmap(bitmapData);
movieClip.smoothing = true;
if (setClipEventHandlers != null)
setClipEventHandlers( movieClip );
return movieClip;
}
Now for the implementation. I will be creating 500 display objects that will move and change alpha, one using the built in bitmap caching mechanism and the other using the manual caching.
private static const NUM_OF_DISPLAY_OBJECTS:int = 100;
private function bitmapCache():void
{
var destination:Sprite = new Sprite();
var uicom:UIComponent = new UIComponent();
uicom.addChild(destination);
group.addElement( uicom );
var movieClip:MovieClip;
for (var i:int = 0; i < NUM_OF_DISPLAY_OBJECTS; i++ )
{
movieClip = BitmapCaching.createMovieClipBasedOnSprite( spriteSource, true, setClipEventHandlers);
destination.addChild( movieClip );
}
}
protected function manualCache():void
{
var source:Sprite = spriteSource;
var destination:Sprite = new Sprite();
var uicom:UIComponent = new UIComponent();
uicom.addChild(destination);
group.addElement( uicom );
var bitmap:Bitmap;
var bitmapData:BitmapData = BitmapCaching.manualCacheAsBitmapData( source, FlexGlobals.topLevelApplication );
for (var i:int = 0; i < NUM_OF_DISPLAY_OBJECTS; i++)
{
bitmap = BitmapCaching.createBitmapClip( bitmapData, setClipEventHandlers );
destination.addChild( bitmap );
}
}
private function get spriteSource():Sprite
{
var source:Sprite = new Sprite();
source.graphics.beginFill(0xFF88CC);
source.graphics.drawRect(0, 0, 80, 80);
source.filters = [new DropShadowFilter()];
return source;
}
private function setClipEventHandlers(clip:*):void
{
var newX:int;
var newY:int;
clip.addEventListener( Event.ADDED_TO_STAGE, function( event:Event ):void {
newX = Math.random() * ( stage.stageWidth - ( clip.width / 2 ) );
newY = Math.random() * ( stage.stageHeight - (clip.height / 2 ) );
clip.addEventListener( Event.ENTER_FRAME, function( event:Event ):void {
clip.x -= ( clip.x - newX ) * 0.25;
clip.y -= ( clip.y - newY ) * 0.25;
clip.alpha = Math.random();
});
});
}
When you run the example and switch you’ll notice that the difference is night and day. As you can see from the screenshots below, using the built-in Bitmap caching produced a jagged animation while the manual one runs smooth and in much better fps.
Bitmap caching:
Manual caching:
Bitmap caching turn off:

Cache as bitmap matrix
Everything up to here is applicable since Flash Player 8; nothing really new other than a cool API to help you implement quicker. Now let’s fast forward to 2009. As Adobe created the packager for the iPhone it was soon clear that cacheAsBitmap is not enough since it was possible to take into account hardware acceleration (OpenGL ES 1.1 and soon OpenGL ES 2) and move the rendering of the display object from the CPU to the GPU. Using cacheAsBitmap the display matrix is used rather than the matrix itself - that’s where cacheAsBitmapMatrix comes into place.
A display object can be set with the cacheAsBitmapMatrix property to redraw only when the value of cacheAsBitmapMatrix changes. Once you set matrix at the cacheAsBitmapMatrix method, the GPU can use the matrix that created the transformation instead of the display object matrix. That makes a big difference since it allows the GPU to compose and animate even in cases where the object has changed. For instance, you can rotate, scale or use alpha and avoid the expensive process of re-rendering the display object on the CPU. So while you set cacheAsBitmapMatrix, you should set the cacheAsBitmap flag to false to avoid the display object to re-render. Even if you can’t use the GPU this method will speed things up.
Now back to the API. The cacheAsBitmapMatrix will allow you to pass a display object and cache a matrix as BitmapMatrix. You can also set the scale property, which will allow you to scale up or down the display object in case you don’t need a matrix representation in the same size. Note that just like cacheAsBitmap, cacheAsBitmapMatrix limits the content to be not more than 1020×1020 pixels and/or about four million pixels.
public static function cacheAsBitmapMatrix(displayObject:*,
scaleX:Number = 1, scaleY:Number = 1,
isCacheAsBitmapMatrix:Boolean = true,
isCacheAsBitmap:Boolean = false,
isDebugMode:Boolean = false):void
{
var matrix:Matrix;
if( displayObject.hasOwnProperty("cacheAsBitmapMatrix") )
{
if ( !isCacheAsBitmapMatrix )
{
if (isDebugMode)
trace("remove cacheAsBitmapMatrix");
displayObject.cacheAsBitmapMatrix = null;
displayObject.cacheAsBitmap = isCacheAsBitmap;
return;
}
if (scaleX != 1 && scaleY != 1 )
{
if (isDebugMode)
trace("set cacheAsBitmapMatrix and scale");
matrix = new Matrix()
matrix.scale(scaleX, scaleY);
}
else
{
if (isDebugMode)
trace("set cacheAsBitmapMatrix");
matrix = displayObject.transform.concatenatedMatrix;
}
displayObject.cacheAsBitmapMatrix = matrix;
displayObject.cacheAsBitmap = isCacheAsBitmap;
}
else
{
if (isDebugMode)
trace("warning :: method cacheAsBitmapMatrix is only avaliable in FP10.1");
}
}
You will need to set the renderMode to GPU in the application’s descriptor initialWindow tag in order to improve performance and take advantage of GPU (where it’s supported). The GPU rendering engines are based on the device drivers so as Adobe is working with manufactures you should check which device offers support.

Here is an example using AIR 2.5:
<s:WindowedApplication xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
creationComplete="creationCompleteHandler()">
<fx:Script>
<![CDATA[
import com.elad.optimize.cache.BitmapCaching;
import com.elad.optimize.cache.GarbageCollector;
import flash.filters.DropShadowFilter;
import mx.core.FlexGlobals;
import mx.core.UIComponent;
private static const NUM_OF_DISPLAY_OBJECTS:int = 100;
protected function creationCompleteHandler():void
{
var statsSprite:Stats = new Stats();
var componenent:UIComponent = new UIComponent();
componenent.addChild( statsSprite );
stats.addElement( componenent );
bitmapCache();
}
private function bitmapCache():void
{
var destination:Sprite = new Sprite();
var uicom:UIComponent = new UIComponent();
uicom.addChild(destination);
group.addElement( uicom );
var movieClip:MovieClip;
for (var i:int = 0; i< NUM_OF_DISPLAY_OBJECTS; i++ )
{
movieClip = BitmapCaching.createMovieClipBasedOnSprite( spriteSource, true, setClipEventHandlers);
destination.addChild( movieClip );
}
}
private function get spriteSource():Sprite
{
var source:Sprite = new Sprite();
source.graphics.beginFill(0xFF88CC);
source.graphics.drawRect(0, 0, 80, 80);
source.filters = [new DropShadowFilter()];
return source;
}
private function setClipEventHandlers(clip:*):void
{
var newX:int;
var newY:int;
clip.addEventListener( Event.ADDED_TO_STAGE, function( event:Event ):void {
newX = Math.random() * ( stage.stageWidth - ( clip.width / 2 ) );
newY = Math.random() * ( stage.stageHeight - (clip.height / 2 ) );
clip.addEventListener( Event.ENTER_FRAME, function( event:Event ):void {
clip.x -= ( clip.x - newX ) * 0.25;
clip.y -= ( clip.y - newY ) * 0.25;
clip.alpha = Math.random();
});
});
}
]]>
</fx:Script>
<s:Group id="group" />
<s:Group id="stats" />
<s:CheckBox id="checkBox" x="109" y="6" label="bitmap matrix cache all"
click="GarbageCollector.forceGarbageCollector();
BitmapCaching.setAllChildrenCachingPolicy( group.getElementAt(0), checkBox.selected, checkBox.selected, true );"
selected="true"/>
</s:WindowedApplication>

We increased the memory usage from 17 to 23, however you get a better fps and smooth animation.
Set all children caching policy
It’s recommended that when using the cache bitmaps flag, only to use that for short periods of time
Notice that I used the setAllChildrenCachingPolicy in the example above. This method is part of the API and allows you to protect the poor innocent bystanders and remove the caching once you don’t need it any more or to set an entire display object and it’s children. The method iterate throws the class and sets every display object caching policy.
public static function setAllChildrenCachingPolicy( displayObject:*,
isCacheAsBitmap:Boolean = true,
isCacheAsBitmapMatrix:Boolean = false,
isDebugMode:Boolean = false,
scaleXBitmapMatrix:Number = 1,
scaleYBitmapMatrix:Number = 1 ):void
{
var len:int = 0;
var description:XML = describeType(displayObject);
var componentType:String = description.@name;
var isSpark:Boolean = false;
var isSprite:Boolean = false;
if( componentType == "mx.core::UIComponent" ||
componentType == "flash.display::Sprite" ||
componentType == "flash.display::MovieClip")
{
isSprite = true;
len = displayObject.numChildren;
}
else if ( componentType == "spark.components::Group" )
{
isSpark = true;
len = displayObject.numElements;
}
if (!isSpark && !isSprite)
{
if (isDebugMode)
trace("warning: " + componentType + " is not supported!");
return;
}
if( displayObject.hasOwnProperty("cacheAsBitmap") )
{
// Cache as bitmap
if (componentType == "mx.core::UIComponent")
cacheUIComponent(displayObject, isCacheAsBitmap, "auto", true, isDebugMode);
else
displayObject.cacheAsBitmap = isCacheAsBitmap;
// Cache as bitmapMatrix
if ( displayObject.hasOwnProperty("cacheAsBitmapMatrix") )
cacheAsBitmapMatrix(displayObject, scaleXBitmapMatrix, scaleYBitmapMatrix, isCacheAsBitmapMatrix, isCacheAsBitmap, isDebugMode);
if (isDebugMode)
{
var isBitmapMatrix:Boolean = BitmapCaching.isCacheAsBitmapMatrix( displayObject );
trace("cache status: " + displayObject.name + ", isCacheAsBitmap: " + isCacheAsBitmap + ", cacheAsBitmap: " +
displayObject.cacheAsBitmap + ", isCacheAsBitmapMatrix: " + isBitmapMatrix);
}
}
var dispalyObjectItem:*;
for (var i:int=0; i<len; i++)
{
if( isSprite )
dispalyObjectItem = displayObject.getChildAt(i);
else if ( isSpark )
dispalyObjectItem = displayObject.getElementAt(i);
if( dispalyObjectItem.hasOwnProperty("cacheAsBitmap") )
{
setAllChildrenCachingPolicy( dispalyObjectItem,
isCacheAsBitmap,
isCacheAsBitmapMatrix,
isDebugMode,
scaleXBitmapMatrix,
scaleYBitmapMatrix );
}
else
{
if (isDebugMode)
trace("warning: " + dispalyObjectItem.name + " doesn't support cacheAsBitmap");
}
}
}
Check the status of all children
In case you want to know the cache policy of each display object, you can use the following method:
private static function showAllChildrenCacheAsBitmapFlagStatus(displayObject:*):void
{
setAllChildrenCachingPolicy( displayObject, false, false, true );
}
Lastly, here is the same application running on the Android using AIR. Attach the following pure AS 3 class:
package {
import flash.display.MovieClip;
import com.elad.optimize.cache.BitmapCaching;
import flash.events.Event;
import flash.display.Sprite;
import flash.filters.DropShadowFilter;
import flash.ui.Mouse;
import flash.events.MouseEvent;
import com.elad.optimize.cache.GarbageCollector;
public class Sqr extends MovieClip {
private static const NUM_OF_DISPLAY_OBJECTS:int = 200;
private var destination:Sprite;
public function Sqr() {
checkBox.addEventListener(MouseEvent.CLICK, function(event:MouseEvent):void {
GarbageCollector.forceGarbageCollector();
BitmapCaching.setAllChildrenCachingPolicy( destination, checkBox.selected, checkBox.selected, true );
});
bitmapCache();
this.addChild( new Stats() );
}
private function bitmapCache():void
{
destination = new Sprite();
this.addChild(destination);
var movieClip:MovieClip;
for (var i:int = 0; i< NUM_OF_DISPLAY_OBJECTS; i++ )
{
movieClip = BitmapCaching.createMovieClipBasedOnSprite( spriteSource, true, setClipEventHandlers);
destination.addChild( movieClip );
}
}
private function setClipEventHandlers(clip:*):void
{
var newX:int;
var newY:int;
clip.addEventListener( Event.ADDED_TO_STAGE, function( event:Event ):void {
newX = Math.random() * ( stage.stageWidth - ( clip.width / 2 ) );
newY = Math.random() * ( stage.stageHeight - (clip.height / 2 ) );
clip.addEventListener( Event.ENTER_FRAME, function( event:Event ):void {
clip.x -= ( clip.x - newX ) * 0.25;
clip.y -= ( clip.y - newY ) * 0.25;
clip.alpha = Math.random();
});
});
}
private function get spriteSource():Sprite
{
var source:Sprite = new Sprite();
source.graphics.beginFill(0xFF88CC);
source.graphics.drawRect(0, 0, 80, 80);
source.filters = [new DropShadowFilter()];
return source;
}
}
}

This API is part of an ‘optimization tools’ project I started (see here). This project will provide tools that can help optimize a Flash application.
SWC:
http://github.com/EladElrom/Flash-Optimizing-Tools/tree/master/swc/
ASDOC:
http://github.com/EladElrom/Flash-Optimizing-Tools/tree/master/asdoc/
Project:
http://github.com/EladElrom/Flash-Optimizing-Tools
Cheers






















nyce!
thx.