As many users of Adobe's Scout have been able to notice, decompressing images is probably the biggest performance hit on applications that are frame-rate sensitive, such as a games.
I have been having problems in my game (a 2D side-scroller) with this. The framerate is very smooth (50+) except when scout shows the image-decompression events, then it drops to 2 - 15 fps. It's terrible. The more images I add (tiles on the screen) the more this will happen.
If you are not familiar with this, please simply note that flash player removes from memory the images that are not "needed", not shown on the screen, and re-decompresses them once you move them to the screen again. This reportedly happens about every 10 seconds.
I want to share my solutions and ask of some of your own, since I think the state of things is far from satisfying at the moment.
Let's begin with the obvious and my comments:
- On Flash Pro, any imported (embedded) images, should be set on their properties panel for no decompression / lossless PNG/GIF
Comments: This DOES help, but the decompression still occurs causing the usual lag problems.
- Instead of having several tiles or images, consolidate them in larger tiles or, a single image per "level" of your game or app.
Comments: This also helps tremendously, for obvious reasons. The image is larger, therefore shown on all of the screen, or more of it at any given time and therefore Flash can't get rid of it. This is really an ugly solution, for obvious reasons: Flash crashes when importing large images, there are dimension limitations, and in general is very difficult to deal with big images when designing a level or a graphic app, instead of tiles. However, sometimes this could work.
- Have several images or tiles, but add them on the screen (must have .visible = true) in places where Flash is forced to keep them in memory.
Comments: It does work also, but is even uglier than the solution above (if it can even be called that), and it's really just a workaround to keep Flash from crashing when importing large images or if you hit the maximum dimensions. It does the trick though. You could hide these images underneath other flash layers or images, sometimes with very little performance hits, if you only use the bitmap underneath as a filler of a shape object.
- Use ImageDecodingPolicy.ON_LOAD.
Comments: In my game, this barely made any difference on its own, but when I mixed it with solution one, it seemed to marginally help. Here's the code:
var loaderContext:LoaderContext = new LoaderContext();
loaderContext.imageDecodingPolicy = ImageDecodingPolicy.ON_LOAD
var loader:Loader = new Loader();
loader.load(new URLRequest("gameLevel.swf"), loaderContext);
I know this is normally used for loading bitmaps, but it worked on the swf, albeit only marginally.
- Use getPixel() on the bitmapData object to keep the object on memory.
Comments: I have read that this has worked for some people, but did absolutely nothing for me. Here's the most important part of the code for a non-embedded asset (Keeping Bitmaps Decompressed):
var bmd:BitmapData;
var timer:Timer = new Timer(1000);
timer.start();
timer.addEventListener(TimerEvent.TIMER, onTimer);
var loader:Loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onLoaded);
loader.load(new URLRequest("Adobe_Flash_Professional_CS5_icon.png"))
function onLoaded(ev:Event): void
{
bmd = ((ev.target as LoaderInfo).content as Bitmap).bitmapData;
}
function onTimer(ev:TimerEvent): void
{
bmd.getPixel(0, 0);
}
If you are like me, and use embedded images, you can adapt the code with something like this:
//be sure to set the library item on Flash Pro to be exported to ActionScript 3,
//and give it a class name, in this case Artwork.
var Artwork:Class;
Artwork = getDefinitionByName(Artwork) as Class;
bmd = new Artwork();
and then simply call every 10 seconds or so the getPixel() function to reclaim the image into memory.
Too bad it doesn't to a thing for me!!
- Cache as Bitmap the Bitmap, even though it already is a bitmap, to force the flash player algorithm to keep it in memory.
Comments: This works very well, almost as well as "pseudo solutions" 2-3. The frame rate is not as good, but it eliminates the hiccup due to decompression very smoothly for me. This can of course be done through code as in instance.cacheAsBitmap = true; or simply from Flash Pro directly under the rendering option.
- Copy the bitmapData into a new BitmapData instance and then leaving the original asset to the garbage collector. Take a relatively small one-time CPU hit from copying the data, and you’d briefly use extra memory to hold two copies of the decompressed bitmap. BUT, once you’re done, the compressed data can also be released.
Comments: Didn't do a thing for me either. I also don't understand why it should:
var loadedBitmapData = event.target.content.bitmapData;
var newBitmapData:BitmapData = new BitmapData(loadedBitmapData.width,loadedBitmapData.height);
newBitmapData.copyPixels(loadedBitmapData,new Rectangle(0,0,loadedBitmapData.width, loadedBitmapData.height),new Point(0,0));
And that's all I have tried, but as you can imagine, I am far from satisfied with these duct-tape hacks. These all seem like mediocre patches to me, perhaps with the exception of solutions 4 and 5, except they don't really do a thing for me.
So, here are my questions:
Can you help me exploring answers as to why solutions 5 and 7 wont' work for me? I am using the embedded image option in the code and instead of using a timer, calling it after 50 ticks on a counter++ variable on an enterFrame call.
Can you provide another workaround?
1) You're obviously not using Stage3D in your project, otherwise you could make use of Adobe Texture Format, which is much more convenient when it comes to UI freezing problem.
2) "If you are not familiar with this, please simply note that flash player removes from memory the images that are not "needed", not shown on the screen, and re-decompresses them once you move them to the screen again. This reportedly happens about every 10 seconds." This seems weird. Once you have your compressed image being decompressed into BitmapData, and since you have reference to that BitmapData so GC is not able to collect it, nothing in the world can make you to decompress it against your will. Never heard of Flash Player doing that.
3) If you're loading images from local file system and not from remote file server, try FileStream.openAsync to load your files as bytearray and then decode them using 3rd-party PNGDecoder. Not sure about embedded assets.