Simple Optimization – Finding Graphics Bottlenecks - selene tan
selene tan

Simple Optimization – Finding Graphics Bottlenecks

by on Jan.28, 2013, under Blog

This post is about a time when I was able to use a profiler to very quickly find and fix the cause of a noticeable frame hiccup.

I was working on an isometric Flash game with turn-based battles on a grid. When you mouse over or select a movement or ability, the game highlights valid target tiles. After each battle, units can move freely about the battle map before continuing, without the per-turn movement limits. On larger maps, especially with ranged units or at the end of battle, there was a noticeable pause before the highlights appeared.

Since the symptoms were straightforward and easy to trigger, I broke out the Flash Builder profiler to see what exactly was going on. I loaded up a large map and defeated the enemies. I started capturing profiling data, then switched the active character–which recalculates the movement highlights–and stopped the capture after the highlights appeared.

Sorted by cumulative time, these were the first few results:

Method Calls Cumulative Time (ms)
[enterFrameEvent] 1 9855 (23.44%)
Grid.highlightForAbility() 10 6668 (15.86%)
Animation.update() 1960 5051 (12.01%)
PSprite.update() 1764 5048 (12%)
[render] 1 4846 (11.52%)
Tile.highlight() 770 4829 (11.48%)

[enterFrameEvent] and [render] are internal Flash constructs, as indicated by the bracketed names. [enterFrameEvent] is the root for all listeners attached to Event.ENTER_FRAME . [render] is what it sounds like, and is when objects on the display list are drawn to the screen.

Animation.update() and PSprite.update() are related functions that play through the sprite animations. They’re pretty high on the cumulative time list, but they’re a constant load–not what we’re looking for, which is a noticeable pause.

Grid.highlightForAbility() and Tile.highlight() are clearly relevant, and they’re ranked very high in terms of time usage. A closer look at Grid.highlightForAbility() reveals that it’s spending most of its time in various highlighting functions.

Method Cumulative Time (ms)
Grid.highlightWalkableTiles() 3316 (49.73%)
Grid.highlightAttackMoveTiles() 2823 (42.34%)
Grid.unhighlightAll() 317 (4.75%)
Grid.highlightAbilityTargetableTiles() 212 (3.18%)

Drilling down further, each of those highlighting functions seem to be spending most of their time in Tile.highlight()

  • Grid.highlightWalkableTiles() – 3316 ms
    • Tile.highlightWalkable() – 2598 ms (78.35%)
      • Tile.highlight() – 2596 ms(99.92%)

Time to take a look at Tile.highlight(). Here are all its callees with more than 10 ms cumulative time:

Method Cumulative Time (ms)
Node.addChild() 693 (26.69%)
IsoRectangle.as3isolib.display.primitive.IsoRectangle() 397 (15.29%)
AssetManager.createInstanceFromAsset() 303 (11.67%) 45 (1.73%)
IsoPrimitive.set stroke() 18 (0.69%)
IsoPrimitve.set fill() 16 (0.62%)

All of these methods except AssetManager.createInstanceFromAsset() come from as3isolib, the graphics library we use for isometric display. It looks like every time a tile’s highlight is set, a new isometric object is created for the highlight and added to the scene. The instantiation and adding account for about 50% of the total time spent in Tile.highlight(). Multiplied over our 770 calls to Tile.highlight(), that adds up! Looks like we found our culprit.

My solution was to remove instantiation entirely. I created a single isometric object for the overlay, with one BitmapFill. When highlighting a tile, I used BitmapData.copyPixels() to update the relevant section of the BitmapFill. Since I now only needed one master instance of each of the tile highlight textures, I had them pre-load during initialization. After these changes, the highlight functions spent a tiny fraction of their time in BaseTile.highlight():

  • Grid.highlightAttackMoveTiles() – 1064 ms
    • Tile.highlightAttackMoveable() – 3ms (0.28%)
      • Tile.highlight() – 3 ms (100%)
:, ,

1 Trackback or Pingback for this entry

Leave a Reply

Looking for something?

Use the form below to search the site:

Still not finding what you're looking for? Drop a comment on a post or contact us so we can take care of it!