Cache Me If You Can


At Infrared5, one thing we do to give us a good boost of speed in Flash is using a technique called (at least by me) Bitmap Injection.  The idea behind bitmap injection is to swap out pre-rendered frames of an animated clip into a bitmap object.  This removes the extra draw call of a MovieClip, thus speeding things up.  Since you can simply change the bitmapData property of a Bitmap, it simply reassigns what it is drawing, rather than having to copyPixels or do any of those performance killers.  Since we do it at work, and since Papervision is driven by bitmapData objects – I decided to whip up a quick material which could save your life.

A normal MovieMaterial, when set to animated, redraws the bitmap it is using for every frame of your rendering.  As you can imagine, this costs serious CPU, and if you have too many animated materials, your application will slow to a crawl.

So, I wrote up MovieCacheMaterial.  The name says it all really.  The material draws every frame of the movie into a global array for that particular asset.  Then, if the movie is ever on that same frame again, instead of redrawing the movie, it simply draws the 3D content using the already cached bitmap.  Then, any other DisplayObject3Ds using that same MovieClip will use the same cache as well.  So, your performance on the first loop will be the same as a normal MovieMaterial, but after that, the performance is equivalent to a normal BitmapMaterial – even though there is animation on it!

Check out the Demo

I wrote a hack work-around for the first loop issue, which might be of use to you.  You can call material.ripMovie(stage, frameRate, completeHandler).  You pass in the stage, how fast you want the movie to be ripped into the cache, and what function to call when it is complete.  The material will set your stage FPS to your desired number, then when it has ripped every frame of the MovieClip that was assigned to it, it will call the function you pass in as completeHandler.  This will make it so when you start your application you can have the best performance right off the bat.  Obviously it will take it longer to start up, however, since it has to rip your animation before beginning.

You are amazing Andy – What’s the catch?

Unfortunately, there are some limitations to using this material.  The first limitation is that it is a memory hog.  Since you draw every frame of your Movie into memory, that much more memory is going to be used.  To give you an idea of how much that equals:  the animation in the demo above is ~150 frames, and is 512×512.  When it is fully parsed it uses about 140 megs of memory.  Smaller movies will obviously use less.  It is definitely something you need to be aware of however.

Second limitation: it doesn’t work with real time dynamic movies or interactions.  Remember, it simply draws every frame of a movieclip.  That content can be dynamic the first loop, but after that, you are going to see exactly what happened the first time over and over again.  This obviously rules out any Virtual Mouse behaviors on a movie as well.  But, if you have a simple animation you want to loop – go for it!

And In the End

One other final thing to point out is that you can control your animation by changing your currentFrame on the MovieClip just like normal.  It won’t mess up the caching, so you can pause, play, skip, etc. just like normal.  In the demo, you can press “F” to jump to the middle of the animation – using a gotoAndPlay.

Anyways – thats about all there is to say about it.  You can see the performance difference on the demo.  For me, on 49 planes, i get about 25 FPS with MovieCacheMaterial, and about 5 FPS with regular animated MovieMaterial.

See the Demo Again

and

Get the Source

Feel free to report any bugs.  I probably won’t fix any, but it might help others :)   Also, you might have noticed I bolded certain words throughout the post.  If you can guess why I will give you a free copy of my upcoming top secret application (still a little ways off).

Enjoy!


25 Comments, Comment or Ping

  1. sike

    The bolded text for SEO?

    January 11th, 2009

  2. great news andy.
    the technique you called “bitmap injection” is a must in performance based apps such as games or…yes…3D, so thanx a lot to have put a ready to use solution to this problem.
    anyway i think a good solution to bitmap caching memory cost is using bytearrays. i spent a lot of time at work figuring out how to do something like a “light weight bitmap cache” and that’s the only way i found both performing and easy to do: just cache as bytearray a bitmapdata which is not currently used on your app, then put again the data from the bytearray to the bitmapdata and you’re done. actually, from here you can introduce a “next use cache” in which you store bitmapdata you’re going to use… it all depends on how many bitmaps you’re using ,this solution with a fine tune is optimal for a many bmps app switching not too fast. in mine i’m saving 3\4 of the amount of ram required by bitmap data.

    good job :)

    January 11th, 2009

  3. This is great !
    I think I found what is your top secret application : an (AIR ?) app to pre-render the frames of a MovieClip and store them in a bytearray in a file you can load runtime.
    Anyway thanks for all the great work you put in Papervision3D !

    January 11th, 2009

  4. Awesome. We used a similar technique that Mr Doob came up with, but more specifically geared for FLVs:

    http://code.google.com/p/mrdoob/source/browse/trunk/libs/net/hires/utils/display/BitmapDataSequence.as

    for this site:

    http://www.walteesquest.com/

    Once you get into the game, all the “worlds” are in PV3D; and we used a lot of animations cached as bitmapdata to increase performance 2000%

    January 11th, 2009

  5. I’ve added the AnimatedBitmapMaterial for Away3d back in 2007. Very similar to this one. Nobody but game devs and animators saw the power of this method back then. Combined with some bitmapdata magic, it allows much more than the obvious.
    Well done Andy!

    January 11th, 2009

  6. Macaca

    Has it a render/rip queue? While using a solution similar like this one we encountered a limit on the amount of animations one can render in a single frame (with gotoAndPlay) or the amount of concurrent animations you can render (If you let them play at normal speed).

    January 12th, 2009

  7. If you can guess why I will give you a free copy of my upcoming top secret application (still a little ways off). –
    I doubt it is SEO since “won’t mess up”, “regular” and other are senseless in that context

    however i wish i had this caching while ago – a good usage is for loading spinners. I had 10 planes and other animations were stopping when putting the spinners

    January 12th, 2009

  8. sike

    Insightful view.
    And does the bolded text is testing the strong tag for SEO purpose?

    January 12th, 2009

  9. D

    Cool. Can the swf that is being drawn (Anim.swf) be created in any version of Flash or is a particular version required ?

    January 13th, 2009

  10. saul

    Nice one Andy. Again!

    On a different note would you consider providing an Augmented Reality example using the FLARtoolkit and some effects.

    It would be ultra cool!

    January 13th, 2009

  11. Derek

    Hey Andy,

    thanks a lot for your great tutorial!! I’ve spent like all last nights trying to figure out how to make my SWF (about 15 text-displaying Planes) run at more than 5FPS..With MovieCacheMaterial it runs at about 40!!

    Okay, i’d like to have a alpha Tween on each of those Planes, but
    Tweener.addTween(MovieCacheMaterial(Plane.material).movie, {alpha:1, time:.5});
    doesn’t work..

    What do I do wrong?

    January 14th, 2009

  12. @D – anything you can load in can be used. It just needs to be a movieclip.

    @Derek – since the movie is cached you can’t change properties of it on the movie itself. Instead, you will need to use layers to get things like alpha to work. Try this:

    viewport.getChildLayer(plane);
    Tweener.addTween(plane.container, {alpha:0, time:0.5});

    January 14th, 2009

  13. Derek

    Thanks a lot Andy!! The code basically works when i refer directly to the plane like you did. But accually my plane is stored in an Array and theres a event calling the Tween function.

    Have a look:
    viewport.getChildLayer(event.target);
    Tweener.addTween(event.target.container, {alpha:1, time:0.5});

    Flash returns this:
    Implicit coercion of a value with static type Object to a possibly unrelated type org.papervision3d.objects:DisplayObject3D

    I have looked the whole web up to figure out how to find a solution but nothing…please help me Andy!

    January 15th, 2009

  14. Adam

    Is there a way to do something similar to this using cs4s built in 3d stuff. From what i understand when you rotate a movieclip in 3d, it caches the movieclip as a bitmap. Is there a way to turn this off once the clip has done its animation? There is dynamic text in the clip and it is blurry when it gets to its static state.

    January 15th, 2009

  15. Hi,

    Cool stuff!
    I’ve tried it and ran into a bit of trouble playing with the sample.

    If I have my animation straight in the timeline it will play, but if I have an animation in a movieclip it won’t play.
    I’ve tried calling play() from the movieClip I’m assigning to the MovieCacheMaterial and calling from MovieClip(movieCacheMat.movie).innerClip.play();
    and nothing.

    Is there a way to play nested animations with a MovieCacheMaterial ?
    If so, how ?

    January 16th, 2009

  16. Fantastic. I played around with it and it is working quite well. Great job, and thanks yet again, Andy.

    -JD5

    January 20th, 2009

  17. Wow — great stuff!

    This really increases the performance quite a bit, keep up the great work!

    January 22nd, 2009

  18. Nice performance boost, especially in FPS. But this simple demo took 300MB of my RAM :) When working with more complex projects a bitmapData clearing procedure is needed.

    February 4th, 2009

  19. Hey Andy,

    This is really great work. We just used a modified cache material for a National Geographic Channel game on the USS Macon(http://www.neo-pangea.com/clients/natgeo/ussmacon/). It’s great to be able to have a sprite-based graphics strategy for Papervision so you only need to use meshes where there’s no getting around it. I can’t wait until Flash gets the hardware support to do full 3D without bringing an average person’s computer to it’s knees, but that said, it’s fun coming up with ways to get around existing limitations. Thanks a ton for all of your hard work man!

    February 6th, 2009

  20. JJ

    This was used in Pillage the Village for the 2D character animations; ad yes, the memory trade off is unfortunate.

    February 6th, 2009

  21. Alessandro

    HI Andy, thanks for all this nice stuff you got here.
    I have te same issue as George pointed out: if the movie clip used has an animation, or has some interactive controls, the animation returns at frame 1.
    So it seems to me that material is cached even when interactive controls are run, is that possible ? Thanks

    February 10th, 2009

  22. Alessandro

    Well, I have read better your post and it was just written there ! :-)

    February 10th, 2009

  23. Jeff

    Any chance that someone can help me out with a question about Planes in Papervision3D?

    Here is my post:
    http://www.kirupa.com/forum/showthread.php?t=320412

    February 20th, 2009

  24. Zlydak

    Hi Andy,
    First off thanks for all of your work, everything here has helped me out tremendously with learning pv3d. I was curious on how you came up with getting the center coords for the grid. I’m fairly new to 3D and I tried a 2D approach to centering the grid items but it was offset to the right. Is there a common formula to placing objects in 3D (x, y) based on their z value? Or is there another approach? Thanks

    June 22nd, 2009

Reply to “Cache Me If You Can”