Gamua / Starling-Framework

The Cross Platform Game Engine
http://www.starling-framework.org
Other
2.82k stars 821 forks source link

Optimize display object ENTER_FRAME event #1016

Closed hardcoremore closed 6 years ago

hardcoremore commented 6 years ago

https://github.com/Gamua/Starling-Framework/blob/7918257f0051d8e2f51d6cb0cf418aa7cba671a8/starling/src/starling/display/DisplayObjectContainer.as#L511

It seams like it is a good idea to check here if object is visible and eventType is ENTER_FRAME and if both are true skip dispatching ENTER_FRAME event for this object. Why would invisible objects need ENTER_FRAME event.

This is killing performance as currently in my game I have around 1000 objects and I am making them invisible as they are not in the viewport and that seams to not matter because starling is allocating around 50-60MB of memory for about 2 minutes of play and then my frame rate drops heavily while GC cleans it.

It all happens because every display object is dispatching ENTER_FRAME event on every frame even though they are invisible and memory keeps growing like crazy.

I guess only solution is that I remove object from stage if is not visible and add it again when it becomes visible. It looks like setting visible to false alone is not enough.

What do you think Daniel?

PrimaryFeather commented 6 years ago

This method is already optimized in the override inside Stage.as. It manages a special list for all objects that are listening to ENTER_FRAME events. It does not take visibility into account, though — because even objects that are invisible might still be interested in those events.

So if you've got many objects that are invisible and listen to ENTER_FRAME events, I recommend you remove that listener while they are invisible or you remove them from the display list altogether during that time. That would have some other advantages, too; e.g. Starling wouldn't need to iterate over them in each frame to check for their visibility.

hardcoremore commented 6 years ago

I have already done that but Starling keeps allocating massive amounts of memory.

Take a look at this. I have modified Starling DisplayObject class dispatchEvent method to this:

public override function dispatchEvent(event:Event):void
{
    if (event.type == Event.REMOVED_FROM_STAGE && stage == null)
    {
        return; // special check to avoid double-dispatch of RfS-event.
    }
    else
    {
        if(c < 20000)
        {
            super.dispatchEvent(event);
            c++;
        }
        else if(this is Stage)
        {
            super.dispatchEvent(event);
        }
    }
}

This 20000 gives me enough time to load the level and play it.

So ENTER_FRAME is being dispatched only for Stage object. I have removed all feathers components from stage as well. Game logic is not run as well, Box2D is not run also. This is just Starling running and is limited to run ENTER_FRAME only for Stage object nothing more. No other code is run.

For about 2 minutes of playing 30MB is generated and when GC collects it it drops frame rate heavily.

Take a look at this Scout profile: https://goo.gl/Xgaqdj

Select more than 1 second on the timeline and look at Trace Log tab on the right there will be printed standard memory usage (NOT GPU memory). I trace memory usage on every second.

If you look at around 30 second of play memory is 144MB and if you look at the end memory is 174, Why is so much memory being generated.

This is how my game looks during this test. You can see no ui is being present. I am not controlling anything I have just played level. When level is played display and physics objects are created. However Box2D and game logic is not running only Starling is running. I am not moving and display objects. All filters are cached. I am not triggering any touch events as well.

image

PrimaryFeather commented 6 years ago

I still don't understand what's happening here, and why you think that the EnterFrameEvent has something to do with it.

In your Scout file, I selected 300 frames and had a look at the "Memory Allocations" tab:

screen shot 2018-01-19 at 09 51 05

As you can see, the majority of memory is allocated by StardustStarlingRenderer. The 600 events you're seeing are actually those of the classic display list.

I also can't see any garbage collection spikes in your Scout file.

I don't know why System.totalMemory is rising like this, but it doesn't seem to be ActionScript allocations — Scout says that between frame 300 and 6000, only < 800 kB of memory are allocated (most of which are native EnterFrameEvents).

screen shot 2018-01-19 at 10 01 39

In general, I wouldn't put too much trust into System.totalMemory. I remember several cases in the past where it simply reported misleading values.

hardcoremore commented 6 years ago

Hi Daniel,

Sorry this is not because of ENTER_FRAME event. The thing is that I do not know what to fix since Adobe Scout is not giving me any useful info.

I have opened a new thread on the forum with detailed info here:

https://forum.starling-framework.org/topic/help-unable-to-reduce-memory-allocation#post-110570

I would like to hear your opinion on the Scout profile I have posted there.

You can close this issue.

Thanks,

Caslav

PrimaryFeather commented 6 years ago

Okay, thanks for the clarification! I added some comments in the forum thread.