Performance Zone is brought to you in partnership with:

Stephen Chin is a technical expert in RIA technologies, and Chief Agile Methodologist at GXS. He coauthored the Apress Pro JavaFX Platform title, which is the leading reference for JavaFX, and is lead author of the Pro Android Flash title. In addition, Stephen runs the very successful Silicon Valley JavaFX User Group, which has hundreds of members and tens of thousands of online viewers, and also is co-organizer for the Flash on Devices User Group. Finally, he is a Java Champion, chair of the OSCON Java Conference and an internationally recognized speaker featured at Devoxx, Jazoon and JavaOne, where he received a Rock Star Award. Steve can be followed on twitter @steveonjava and reached via his blog http://steveonjava.com Stephen is a DZone MVB and is not an employee of DZone and has posted 29 posts at DZone. You can read more from them at their website. View Full User Profile

Turbocharging Performance with Caching

08.21.2011
| 4272 views |
  • submit to reddit

This is the third installation of my Flex Mobile series.  In my first post I talked about multitouch, and my second post I dug in on how to control the soft keyboard.  This post goes into detail on performance, specifically caching.

Flash applications tend to use a lot more vector graphics than other UI platforms. This is a great thing for designers who can directly use all their path art and graphics.  However, on mobile devices it increases the challenge to build high performing applications.

Fortunately, there is a feature of the Flash platform called cacheAsBitmap (and its newer sibling, cacheAsBitmapMatrix) that lets you speed up rendering performance at the expense of memory.

Cache as Bitmap

CacheAsBitmap is a boolean property of DisplayObject, and by extension all the visual elements you use in Flash and Flex includings Sprites and UIComponents, have access to this variable. When set to true, each time the DisplayObject or one of its children changes it will take a snapshot of the current state and save it to an offscreen buffer. Then for future rendering operations it will redraw off the saved offscreen buffer, which can be orders of magnitude faster for a complicated portion of the scene.

To enable cacheAsBitmap on a DisplayObject you would do the following:

cacheAsBitmap = true;

Flex UIComponents have a cache policy that will automatically enable cacheAsBitmap based on a heuristic. You can override this behavior and force cacheAsBitmap to be enabled by doing the following:

cachePolicy = UIComponentCachePolicy.ON;

While cacheAsBitmap is a very powerful tool for optimizing the redraw of your application, it is a double-edged sword if not used properly. A full size screen buffer is kept and refreshed for each DisplayObject with cacheAsBitmap set to true, which can consume a lot of device memory or exhaust the limited GPU memory if you are running in graphics accelerated mode.

Also, if you have an object that updates frequently or has a transform applied, then cacheAsBitmap will simply slow down your application with unnecessary buffering operations.

Cache as Bitmap Matrix

CacheAsBitmapMatrix is also a property on DisplayObject, and works together with cacheAsBitmap. For cacheAsBitmapMatrix to have any effect cacheAsBitmap must also be turned on.

CacheAsBitmap does not work when a transformation, such as a rotation or a skew, is applied to the object. The reason for this is that applying such a transformation to a saved bitmap produces scaling artifacts that would degrade the appearance of the final image. Therefore, if you would like to have caching applied to objects with a transform applied, Flash requires that you also specify a transformation matrix for the bitmap that is stored in the cacheAsBitmapMatrix property.

For most purposes, setting cacheAsBitmapMatrix to the identify matrix will do what you expect. The offscreen bitmap will be saved in the untransformed position and any subsequent transforms on the DisplayObject will be applied to that bitmap. The following code shows how to set cacheAsBitmapMatrix to the identify transform:

cacheAsBitmap = true;
cacheAsBitmapMatrix = new Matrix();

Tip: If you plan on setting cacheAsBitmapMatrix on multiple objects, you can reuse the same matrix to get rid of the cost of the matrix creation.

The downside to this is that the final image may show some slight aliasing, especially if the image is enlarged or straight lines are rotated. To account for this, you can specify a transform matrix that scales the image up prior to buffering it. Similarly, if you know that the final graphic will always be rendered at a reduced size you can specify a transform matrix that scales down the buffered image to save on memory usage.

If you are using cacheAsBitmapMatrix to scale the image size down you need to be careful that you never show the DisplayObject at the original size. The following figure shows an example of what happens if you set a cache matrix that reduces and rotates the image first, and then try to render the object at its original size:

Notice that the final image has quite a bit of aliasing from being scaled up. Even though you are displaying it with a one-to-one transform from the original, Flash will upscale the cached version resulting in a low fidelity image.

The optimal use of cacheAsBitmapMatrix is to set it slightly larger than the expected transform so you have enough pixel information to produce high quality transformed images.

Flash Mobile Bench

The Flash Mobile Bench is a simple application that lets you test the affect of different settings on the performance of your deployed mobile application.

The functionality that it lets you test includes the following:

  • Addition of a large number of shapes to the display list
  • Animation speed of a simple x/y translation
  • Animation speed of a simple clockwise rotation
  • Impact of cacheAsBitmap on performance
  • Impact of cacheAsBitmapMatrix on performance
  • Impact of the automatic Flex cache heuristic on performance

The code that updates the cache behavior of the shape group is shown below:

private var identityMatrix:Matrix = new Matrix();
 
private function cacheOff():void {
  shapeGroup.cachePolicy = UIComponentCachePolicy.OFF;
}
 
private function cacheAuto():void {
  shapeGroup.cachePolicy = UIComponentCachePolicy.AUTO;
}
 
private function cacheAsBitmapX():void {
  shapeGroup.cachePolicy = UIComponentCachePolicy.ON;
  shapeGroup.cacheAsBitmapMatrix = null;
}
 
private function cacheAsBitmapMatrixX():void {
  shapeGroup.cachePolicy = UIComponentCachePolicy.ON;
  shapeGroup.cacheAsBitmapMatrix = identityMatrix;
}

Even though we have only one instance of an object to apply the cacheAsBitmapMatrix on, we follow the best practice of reusing a common identity matrix to avoid extra memory and garbage collection overhead.

Upon running the Flash Mobile Bench, you will immediately see the FPS counter max out on your given device. Click on the buttons to add some shapes to the scene, set the cache to your desired setting, and see how your device performs. The following figure shows the Flash Mobile Bench application running on a Motorola Droid 2 with 300 circles rendered using cacheAsBitmapMatrix:

How does the performance of your device compare?

 

From http://flash.steveonjava.com/turbocharging-performance-with-caching/

Published at DZone with permission of Stephen Chin, author and DZone MVB.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Tags: