building small GUI engine: visible vs. addChild/removeChild

Go To StackoverFlow.com

5

Currently, i'm experimenting with a very simple GUI drawing ... "engine" (i guess you could call it that). The gist of it:

  1. there is a FrontController that gets hit by user requests; each request has a uid
  2. each uid (read "page") has a declaration of the components ("modules") that are present on it
  3. components are Sprite subclasses and, in essence, are unique

Naturally, i need a way of hiding/showing these sprites. Currently, i have it pretty much like Flex has it by default - in the way "if we are in a place where the comp is visible, create it, cache it and reuse it every time it's visible again".

The question is - which would be the more appropriate and efficient way of hiding and showing - via addChild/removeChild or toggling visible.

The way i see it is that:

  • visible is quick and dirty (on first tests)
  • visible does not create a chain of bubbling events like Event.ADDED or Event.REMOVED
  • invisible components don't get mouse events

So removeChild would be something i'd call when i'm sure, that the component will no longer be necessary on the screen (or the cache is too big, for instance)

What do stackoverflow'ers / AS3-crazed people think?

Update: Here's a good read (forgot about google).

i will be sticking to visible; it seems to suit my task better; the manual "OPTIMIZING PERFORMANCE FOR THE FLASH PLATFORM" by Adobe on p. 69 gave me even more confidence.

here's a code snippet i put up to test things for those that are interested:

package 
{
import flash.display.Sprite;
import flash.display.Stage;
import flash.events.Event;
import flash.events.KeyboardEvent;
import flash.ui.Keyboard;
import flash.utils.getTimer;

/**
 * Simple benchmark to test alternatives for hiding and showing
 * DisplayObject.
 * 
 * Use:
 * <code>
 * new DisplayBM(stage);
 * </code>
 * 
 * Hit:
 * - "1" to addChild (note that hitting it 2 times is expensive; i think
 * this is because the player has to check whether or not the comp is
 * used elsewhere)
 * - "q" to removeChild (2 times in a row will throw an exception) 
 * - "2" to set visible to true
 * - "w" to set visible to false
 * 
 * @author Vasi Grigorash
 */    
public class DisplayBM{
    public function DisplayBM(stage:Stage){
        super();

        var insts:uint = 5000;
        var v:Vector.<Sprite> = new Vector.<Sprite>(insts);
        var i:Number = v.length, s:Sprite
        while (i--){
            s = new Sprite;
            s.graphics.beginFill(Math.random() * 0xFFFFFF);
            s.graphics.drawRect(
                Math.random() * stage.stageWidth, 
                Math.random() * stage.stageHeight,
                10, 
                10
            );
            s.graphics.endFill();
            v[i] = s;
        }

        var store:Object = {};
        store[Event.ADDED] = null;
        store[Event.REMOVED] = null;
        var count:Function = function(e:Event):void{
            store[e.type]++;
        }
        var keydown:Function = function (e:KeyboardEvent):void{
            var key:String
            //clear event counts from last run
            for (key in store){
                store[key] = 0;
            }

            stage.addEventListener(Event.ADDED, count);
            stage.addEventListener(Event.REMOVED, count);

            var s0:uint = getTimer(), op:String;
            var i:Number = v.length;
            if (e.keyCode === Keyboard.NUMBER_1){
                op = 'addChild';
                while (i--){
                    stage.addChild(v[i]);
                }
            }
            if (e.keyCode === Keyboard.Q){
                op = 'removeChild';
                while (i--){
                    stage.removeChild(v[i]);
                }
            }
            if (e.keyCode === Keyboard.NUMBER_2){
                op = 'visibile';
                while (i--){
                    v[i].visible = true;
                }
            }
            if (e.keyCode === Keyboard.W){
                op = 'invisibile';
                while (i--){
                    v[i].visible = false;
                }
            }
            if (op){
                //format events
                var events:Array = [];
                for (key in store){
                    events.push(key + ' : ' + store[key])
                }

                trace(op + ' took ' + (getTimer() - s0) + ' ' + events.join(','));
            }

            stage.removeEventListener(Event.ADDED, count);
            stage.removeEventListener(Event.REMOVED, count);
        }

        //autodispatch
        stage.addEventListener(KeyboardEvent.KEY_DOWN, keydown);
    }
}
}
2012-04-05 23:08
by Grigorash Vasilij
If what I need to hide/show uses a lot of resources, typically I'll removeChild/addChild over settings its visibility. If its something minor or less complex like a graphic or text field, I'll set its visibility or alpha. It all depends on what it is you need to hide/sho - Ronnie 2012-04-05 23:18
@Ronnie removeChild only removes the item from the display list - nothing more - Pixel Elephant 2012-04-05 23:30
sorry, I set it to null too and recreate when I need i - Ronnie 2012-04-05 23:37
I never really put any thought into removeChild vs visible before, but one thing comes to mind. If it's still on stage IE visible=false then it might still be getting events/enterFrame not sure about it and not tested. It might be worth creating a test to see if enterframe still gets fired(I don't think it will) and see if any stage events are triggered - The_asMan 2012-04-06 00:18
@Ronnie if there is something that needs a lot of resources - it makes sense not to touch its display list and recreate it O-). Currently, I'm finding visual component creation to be the heaviest operation of all (although the comps are still quite simple). @The_asMan visible does not affect stage. Because, stage is still there, i assume all stage-related listeners get fired. That is why besides setting visible i'm thinking about a function that (de)activates the particular component - Grigorash Vasilij 2012-04-06 06:40
im referring to heave animations and such. Honestly, you probably aren't going to notice that big of a difference between the two unless like I said, something that is going to use the CPU heavily like say an onEnterFram - Ronnie 2012-04-06 16:29


2

Visible makes more sense to me (since removing a child indicates a finality) and is what I tend to use in my own projects when showing/hiding.

I'd also assume that addChild is slightly less performant but I haven't done any tests.

EDIT: I just came across this Adobe article http://help.adobe.com/en_US/as3/mobile/WS5d37564e2b3bb78e5247b9e212ea639b4d7-8000.html which specifies that when using GPU rendering mode just setting visible = false can have a performance impact since there is a cost for drawing overlapping objects (even though they are not visible). Instead, removing the child entirely is advised:

Avoid overdrawing whenever possible. Overdrawing is layering multiple graphical elements so that they obscure each other. Using the software renderer, each pixel is drawn only once. Therefore, for software rendering, the application incurs no performance penalty regardless how many graphical elements are covering each other at that pixel location. By contrast, the hardware renderer draws each pixel for each element whether other elements obscure that region or not. If two rectangles overlap each other, the hardware renderer draws the overlapped region twice while the software renderer draws the region only once.

Therefore, on the desktop, which use the software renderer, you typically do not notice a performance impact of overdraw. However, many overlapping shapes can adversely affect performance on devices using GPU rendering. A best practice is to remove objects from the display list rather than hiding them.

2012-04-05 23:28
by Pixel Elephant
This is what i tend to think as well. It's easier for the player to work in terms of "draw/not draw" than "add to display list, update the display list, spawn and bubble events, draw" and the way back. First simple tests (not unit test) show that the UI is more responsive when manipulating it via visible; i'll have to study it a bit more in depth though - Grigorash Vasilij 2012-04-06 07:08


1

Remove child is better to reduce instances,events and free up memory from your flash movie, You may find after time the sprites may effect each other.From how they are drawn or there listneres,Also Garbage collection generaly comes into play when this method is implemented wich can ultimatly screw around with your application

Visible still has the sprite in memory, its just currently not drawn.you could also save the sprite and then remove it, then reload it when needed would be an ideal over all solution. using arrays to store data is another solution aswell depends on how your application is implemented, hard to say as we dont know ,lol

Adding the child performance i would say is less stress as its still only item adding vs multiples that are hidden.Also in these hidden children"s" there properties are stored im memory along with listeners.

2012-04-06 03:14
by joshua
As noted by @Pixel Elephant - removeChild will not reduce your instances, it will remove references. If it happens that those are the only refs pointing to the object - they will be gc'ed and, thus, free up memory. I haven't tested it thoroughly, but from the user's point of view - visible works faster than addChild and removeChild. No events are spawned either. Listeners can be unset along with the visibility (just like restored) - Grigorash Vasilij 2012-04-06 06:56
so your telling me if i was to creat a loop adding 500 objects to stage with listners,when they are invisible the listners just vanish?they are still referenced in memory until they are made visible again, but removing items will destroy all its assets from memor - joshua 2012-04-06 07:29
not exactly; removeChild or visible = false on 500 objects - the listeners will still be there. The only difference in visible = false and removeChilded comp is that the first gets stage events; the second - doesn't. What i meant by "no events are spawned" - is that invisible = false does not spawn anything. addChild and removeChild - do spawn events - Grigorash Vasilij 2012-04-06 12:58
If you are to truly destroy an object - you will have it remove all the listeners it has attached on external objects meaning any listeners that are NOT set via this.addEventListener(...). If you don't do that, it will never get gc'ed - Grigorash Vasilij 2012-04-06 13:01


0

Here's some hard data on the subject by Moock: http://www.developria.com/2008/11/visible-false-versus-removechi.html

                  Children on the                     Single-frame 
                  Display List    .visible   .alpha   Elapsed Time (ms)
No Children       0               --         --       4
Non-visible       1000            false      1        4
Zero Alpha        1000            true       0        85
Fully Visible     1000            true       1        1498
90% Transparent   1000            true       .1       1997
2013-07-24 00:07
by Clintm
Ads