Migrating to Starling 2: Manage Your Texture Memory

Quizzical Starling by BrianScott

Welcome back. This is part 2 of migrating and existing project to Flash’s Stage3d. You want to do this because Stage3d uses the graphics card to display graphics which is way faster than doing everything in software.

Starling is an excellent 2d framework that makes Adobe’s Stage3d code more manageable. We’ll use that to make the migration way easier. You can find the basics of getting your project to compile with starling in part 1. Done that already? Awesome.

So your code is now compiling but you’re probably seeing a lot of this runtime error: “Error #3691: Resource limit for this resource type exceeded.”

You are out of space
You are out of space

That means you are out of texture memory.

This is because you aren’t managing your texture memory at all. Every time you call the imageFromSprite method from the last post you add a texture to memory but you never remove textures.

Starling really wants to you to define all the graphics you use in sprite sheets and then manage texture memory by loading and unloading those sheets. It’s a good idea and I’m slowly moving Incredipede over to this method of texture management. But I don’t want to do that all at once. The project is too big to do a massive overhaul of the whole graphical system all at once. Instead I need an intermediate stage where I can keep instantiating graphics wherever I damn well please. So I needs a central place to manage my textures. This is where the TextureManager class comes in handy.

I’ve started fleshing out a TextureManager that helps you keep all the textures that might be comming from anywhere under control. It’s still a work in progress so it’s not genius but at least it will give you some structure.

TextureManager.as

 

The most important part of the TextureManager is:

protected var _textures:Dictionary;

The TextureManager keeps track of every texture you instantiate. Now you always know the state of your texture memory and finding leaking textures is just a matter of calling TextureManager.i.traceAllocatedTextures().

To make this work we have to provide a little more information to the TextureManager whenever we instantiate a new texture so the central imageFromSprite method has gained a few new parameters.

public function imageFromSprite
 (textureName:String, sprite:DisplayObject, replaceTexture:Boolean = false)
 :Image

In addition to the sprite we want to texturize we have to give it a textureName. This is an English readable handle for the texture. This is what pops out of traceAllocatedTextures and it also lets us refrence the texture later.

Say we have a “saveButton” texture we use all over the place. Now we can just call TextureManager.i.imageFromSprite(“savebutton”, new SprSaveButton()); whenever we need a save button and the TextureManager will upload it to texture memory if it needs it and it will just reuse the texture if it already has it.

Very rarely you will want to be constantly replacing a texture with a new version. A lot of the temp art in Incredipede is just graphics.drawRect calls. Eventually this will be replaced by real art but in the mean time those rects are changing in shape and size a lot so it’s easier for me to just do this:

var sp:Sprite = new Sprite();
sp.graphics.beginFill(_muscle.colourContract.hex);
...
 sp.graphics.endFill();
 _canvas = TextureManager.i.imageFromSprite("muscleSkin:"+Utils.memAddess(this), sp, true);

This code will replace the given muscle texture every time it’s called. A couple things are going on here. Most importantly I’m passing in “true” for the replaceTexture parameter of imageFromSprite. This tells the textureManager to replace any texture that has the same name. It will deallocate the old texture and upload the new one. Uploading new textures all the time like this is slow and a bad idea but as an intermediate step it works. Now I have an ever-changing muscle that isn’t leaking into texture memory every time it changes.

But now every muscle needs a unique name or all muscles will share one texture. I append the memory address of the object to make the name unique. Flash doesn’t give you convenient access to the memory address but you can pull it out of an invalid cast error message:

public static function memAddess(o:Object):String
{
  try{
    ByteArray(o);
  }catch (e:Error){
    return String(e).replace(/.*([@|\$].*?) to .*$/gi, '$1');
  }
  return "Unkown";
}

Some Textures in Memory

Now when you want to start freeing textures you have a few options. You can set up a bunch of deconstructors and call TextureManager.i. freeTexture(“cherry”). Or you can free all those terrible textures that are constantly being replaced (like the muscle example above) with TextureManager.i. freeVolatileTextures(). Or you can free all the textures beginning with a certain prefix with TextureManager.i. freePrefix(“optionsMenu”).

What’s important is that you’re keeping track of all your textures and not leaking them uncontrollably into memory.

 

Now your project is working! Unless you use any MovieClips of course… those are just being displayed as stills.

Well, very quickly, I’ve written a wrapper to convert flash MovieClips to starling MovieClips on the fly and add in “stops”. You should be converting your MovieClips to sprite sheets. That will be faster, but in the mean time this will upload all the frames to texture memory and then let you gotoAndPlay whatever you like.

ConvertedMovieClip.as

Pretty easy to use:

_startButton = new ConvertedMovieClip(new MovStartButton());
_startButton.addStop(18);
_startButton.addStop(38);
addChild(_startButton);

The addStop method is the equivalent of putting a stop(); command on that frame in the MovieClip. It’s functionality I always end up using so I threw it into the ConvertedMovieClip class. This class might mess up the positioning of your MovieClips slightly. Sorry about that.

 

Now you really are done! Your game is running just like it was before you started! Except now it’s blindingly fast! Congratulations!

You also have access to cool stuff like UV mapped triangle meshes and pixel shaders! In fact you can find my UV Mapping tutorial right here.

Central Park Avians by Professor Bop
Share

6 thoughts on “Migrating to Starling 2: Manage Your Texture Memory

    1. Hey Chris

      ConvertedMovieClip extends Starling’s MovieClip class which works a little differently from Adobe’s MovieClip.

      Instead of gotoAndPlay you can:

      clip.currentFrame = 10
      clip.play()

  1. Thanks you for helpfull utils
    And i improve them a lttle bit by introduce a stack for textures
    // ==========================
    static public var textureStack:Vector.;

    public function ConvertedMovieClip(flashClip:flash.display.MovieClip, recenter:Boolean = true, useStack:Boolean = true)
    {
    if (useStack)
    {

    textureStack = new Vector.;
    .
    .
    .

    textureStack.push(TextureManager …

    }

    super(textureStack, 24);

    // ==========================

    And it increase a initialization speed for complex animation by 200%

    Best Regards,
    Vladimir Minkin

Leave a Reply

Your email address will not be published.