Flash AS3: Garbage collection and the importance of recycling

February 21, 2009

Today I am optimizing my portfolio site. It combines both dynamically generated- and static items. Now the static items pose no problem. The dynamically generated do.

Garbage collection

First of all: the garbage collector of Flash 9 sucks. Objects/movieclips with either/ or

  1. Events
  2. External references to-

Will sustain in memory. Apparently the next release of Flash will deal with this issue with some kind of “Kill” or “Die” or “Dispose” function. For now, we have to be creative.

The problem of Flash’s lazy/sloppy garbage collection is not the only thing that bugged me.

“Memory leaks”

In my portfolio site I create tiles with an image inside the tile. The tile itself is a button. Each time I switched to another Panel to present other data, the memory use of that Flash movie increased with 20MB to 24MB. First I blamed the images I loaded inside the MovieClips. But when I did NOT load the images into the movieclips, the increase/leakage stayed the same.

The culprits: 4 tiny tiles x 5MB each
4 tiles = 20MB memory leak

The tiles are the only variable element in the site. As 4 new tiles are created with every new page loaded and are hard to dispose of, the increase in memory use will be at least 20MB with each new page view, since old pages are not stored but (should be) disposed of. Since the garbage collection of Flash is crap anyway I decided to drop Plan A (write a garbage cleaner) and go for plan B.

Plan B: The short term solution for Memory Leaks: Recycling

When the garbage truck does not come along and rubbish piles up around your house what can you do? Recycling the old crap to avoid more rubbish to come into your house is one of the easiest ways.

Once I create my objects on stage, I keep tabs on them via an Array. When I “destroy” them, I simply remove them from stage and make them available for re-use. Instead of creating new instances of my interactive objects I run through the array with already created ones, clear their settings and call them back on stage. Only when more movieclips are required I create them.

My current solution is rather straight forward since the array is per  Content Pane (3 in total) and there is only one stip of tiles.

The memory leakage is now “only” 1 MB per new view.

Next step

The next step to take is to create a solid pattern for recycling assets (including images and SWFs). When time allows I will improve my current solution and release it as open source.


Shoutcasting, event models and AS3 – basics

February 21, 2009

Today I am redesigning the event structure of my new portfolio site. Instead of sketching things out on paper and have things ending up in some stack of paper I decided to put it in a blog post. This is part 1, explaining the basics ot two principles I use:

  1. Shoutcasting – “I do not know where you are or who you are so I simply shout your name and when you hear me you will respond”
  2. Pointcasting – “I do not know nor care where you are, but I know your name and you are in my list, so I call you directly”

Starting points

ORIGINS

In the past year I have done several projects where flash-content was created by others. In contrast to code-driven sites (where all assets are placed on stage- and are stitched together via code) these sites were design driven (where all assets are placed on stage and have to be stitched together via code).

SHOUTCASTING

Let’s look at the possible strategies to find and address someone within a venue:

  1. Address the person directly. You do need to know what this person looks like and where he or she is, or cal him or her via their cellphone (which is easier). See “pointcasting” for the implementation of that scenario.
  2. Ask around. You only need to know the persons name, but you are dependent of a chain of people and connections. If your strategy is fuzzy, it might take a lot of asking around. If it is focused it will take less time. You are dependent of a chain of people though. When the chain is broken, you will not reach the person you are looking for.
  3. Shoutcasting via an centralized announcement system. It is a bit crude, but you are not dependent of a chain of people nor do you need to know where the person is within the venue.

Setup

  1. There is a Central Handler (named CENTRAL in my code)
  2. This Central Handler is a singleton and provides the shoutcast eventlistener model using “subscribe” and “broadcast” to subscribe to and broadcast the occurrence of an event
  3. There is a Smart MovieClip (Flash / actionscript). This “smart movieclip” carries a toolset of handy things I can use for 96% of all smart objects in my proejcts, including: loading and mappng XML data to elements in a Page/Panel/Button, event handlers and shared objects.
  4. There are Panels/Pages and Buttons
  5. Panels/Pages and Buttons use the Smart MovieClip as their basis via inheritance / extension
  6. The Smart MovieClip imports and instantiates the Central Handler
  7. Via the Central Handler, Smart MovieClips have direct access to the eventlistener

Implementation

  1. Each panel/page is given an unique name.
  2. Each panel/page/button subscribes to one or more events using the following setup:
    CENTRAL.subscribe(“eventtype_”+myPagePanelName , myOnEventDoAction). myPagePanelName will personalize the “eventtype_” event to a specific page/panel or button.
  3. Via XML definitions, the objects recieve “targets” to shoutcast to. “sources” are passed to child objects (like buttons) via code. For instance:
    <XML><button action=”open_panel3″ src=”mySource.xml”></XML> can be shoutcasted as CENTRAL.broadcast(“open_panel3″)

When the user presses the button, only objects named: “panel3″ with the subscription “open_” (“open_panel3″) will respond. The code can look like this:

CENTRAL.subscribe(“open_”+this.name, onOpenEvent)
// When whatever component fires a “broadcast” with “open_”+ myPagePanelName the onOpenEvent function will be called.

CENTRAL.subscribe(“hideallpanels”, onHideEvent)
// Since it is not named to a specific panel, all panels will respond and hide when this event occurs.

function onHideEvent(evt)
{
this.visible=false;
// Hide this panel (and all other panels: on the general “hideallpanels” event)
}

function onOpenEvent(evt)
{
CENTRAL.broadcast(“hideallpanels”) // All panels – including this one – listens to this event and will be set to invisible
this.visible=true; // After every panel is hdden:  show this panel
}

Advantages

Since there is no need for objects to be aware of each other, the event model stays resonably simple. With only 6 lines of code you hide all panels and show only the panel whose name was “shoutcasted” with an instruction.

POINTCASTING

With pointcasting, you only address very specific items. In some cases, when loading data into another object, pointcasting might be preferrable above shoutcasting. Again I used a shortcut, by registering every object of relevance into a list, like a phone directory named: CENTRAL.objects.

When using the example of people, again I want to avoid too many dependencies. I do NOT WANT or NEED to know WHERE this person is or what he or she LOOKS like. Like with my mobile phone: when I dial the number I simply want a response, regardless if this person is at home or on the road somewhere.

To get there I use three steps:

Registering a page/panel
CENTRAL.objects[this.name]=this; // Using the name of the (visual) object to register the object reference to it.

Using XML to tie things together
<button action=”loaddata” target=”myTarget” src=”myXML.xml”>

Calling the target object using the settings in XML
myTarget=CENTRAL.objects[button.@target]
myTarget.loadData(button.@src)

How it works:

  1. Each panel/page registers itself in the objects list, using their unique name.
  2. Via XML I tell objects like my buttons where to pointcast their actions to.
  3. Within the Button-class I simply use the objects list in CENTRAL with the XML definitions bound to the button to perform a specific action
  4. When the target object is there, the XML file will be loaded within that object

LIMITATIONS AND DISADVANTAGES

Using a loosely based structure like this also carries its risks. “What if there is no panel with the name I called?”. In the sample given above all panels are closed and none will be opened. Also, when multiple elements carry the same name there will be conflicts of references will be overwritten by other references.

SUMMERY

Shoutcast and Pointcast as described here are two simple methods to address objects within my Flash projects.


Snow! AS3 filters and performance tests

February 18, 2009

Inspired by the falling snow in the third epsode of Mushishi: “Tender horns” I decided that a snow generator might be a nice benchmarker for several items:

  1. Using filters
  2. Addressing a LOT of particles
  3. Exploring the boundries of AS3 performance and looping through arrays

Why snow?

Snow is reasonably limited in its behaviour and nice to look at. It falls down. It can be moved by wind. It can flutter when there is hardly no wind. It is white and has this semi-transparency to it.

Screenshots:

Source of inspration: Mushishi – episode 3 – snow fall

picture-67

Snow using Blendmode

picture-651

Snow using Blur filter

picture-661

I only loosely based my snowfall on the Mushishi thing. (Mainly the sheer amount.) The snow created with Blur (the third image) is somehow less convincing to me than using the “add” Blendmode as they lack transparency. Beautiful snow has its price however. With only 1000 particles the Blendmode snow already make Flash consume 100% processor power on my MackBook Pro.

My assumptions

I assumed that the Blending filters (“add” and “screen”) would have a better performance than the Blur filter and Alpha. In theory the Blend-filters are binary manipulations like “add” “OR” “XOR” “bit shift left” and “bit shift right”. These are instructions the processor (old school types) only needs one or two clock cycles to perform. Blur and transparency require “snapshots” of the underlying layers on which the blur and transparency effects are placed. Many more clock cycles to do that, you would think.

My goal

I strive to keep my flash sites below 20% processor usage when the user is doing nothing. When you add something extra like snow, 50% constant processor usage to run an effect like snow is already a lot.

What I did

I tried single and multiple “snow flakes” per  particle, compared different settings in my particle count, and tried different approaces to animate the particles via code. For performance with the flash-render engine there is hardly no difference in 1000 individual snowflakes of a 1000 snowflakes in 250 particles. Both approaches make the render engine bog down.

The setup of the showflake engine:

  1. Each particle is pushed into an array
  2. There are four arrays for four layers of snow (more distant layers move slower and contain smaller particles)
  3. Updating particles is done by cycling through the arrays and adding values to the current X/Y coordinates

What I found

Processor usage ADD / BLUR – 3000 particles

  1. ADD: processor to 130%
  2. BLUR: processor to 75%

Checking X/Y position of each particle

To see whether the particles are out of sight I simply check if the Y-coordinate is larger than a specific number. In my case: 500. Then I subtract 700 to place the flakes “in the sky” again. A simple “perpetual fall” scenario.

  1. If/then: every cycle: Processor to 70%. Snow seems to fall less smooth. More “hiccups” in the animation
  2. If/then: only every 20th cycle: Processor to 75%. Snow seems to fall a bit smoother

Calling a function in the object versus updating via references in the “for” loop

When the snow falls, the X/Y coordinates of each particle is updated by adding a value to the current X/Y coordinates. When doing this in the “for/next” loop I choose stick my test to two things:

  1. Assign the reference of the object to a variable. Update the settings of the object via the object reference in the variable. The assumption is that this is cheaper than constantly referring to the object via pointers in the array.
  2. Call an “update” function in each particle. I assumed that calling a function would be more expensive than assigning the object reference from the Array to a variable. It turned out that calling the function and let the particle take care of itself is a bit cheaper.

Again: snow seems to be falling smoother when the second scenario is followed.

Many particles with one flake versus few particles with many flakes

For the Flash render engine, the number or size (I did not check that yet) of elements is one of the determing elements for performance.

I found no niticable performance difference by creating less particles with more flakes per particle.

No (blur) filter

Surprisingly, when I let the engine animate snowflakes without any filter, the processor use leaps to 100% compared to the 75% it takes when Blur is applied. Apparently Flash alreay performs some optimization on blurred objects. Very likely by rendering them as bitmaps.

Only when “Cache as bitmap” is switched on the “unfiltered” flakes are in the same usage-range as blurred flakes.

Cache as bitmap on/off

There is no measurabel difference between Chace as bitmap on/off.

Blur applied to all flakes via parent clip

When the blur is applied via the parent clip on all flakes, no gain is gotten. Apart from that the snow becomes one unclear blob.

Conclusions on blur and add-blendmode

Based on the above tests and finding and with no warrenties (as these tests were limited in their range)

  1. Blur and Alpha are way more cheap than Blendmode filters (for snowflakes)
  2. There is no significant loss when particles are asked to take care of their own state (by calling the “update” function of the particle)
  3. There is no significant loss when each particle has its own blur
  4. If/Then statements are costly. Although the processor usage seems to remain stable, the animation seems to be more sluggish
  5. Caching as bitmaps makes no difference for snow particles