Proposed Update - Broad-Phase SceneGraph Optimisation

All things HTML5 or text based engines, or really any web based engines.
Post Reply
User avatar
coolbloke1324
Posts: 181
Joined: Mon Jan 23, 2012 5:20 pm

Proposed Update - Broad-Phase SceneGraph Optimisation

Post by coolbloke1324 »

Hi all,

Over time the engine has grown and been updated to handle many different scenarios and game types. Once of the challenges of trying to support isometric, 2d, scenegraph-based data structures etc is that as each new feature is added the engine takes a small performance hit.

One of the areas I am currently actively working to optimise is the scenegraph update and render pipeline.

At the moment the engine will effectively update and render every item in the scenegraph every frame and this is mostly fine except for two intensive tasks:

1) Depth-sorting 3d bounds-based isometric objects
2) Rendering (blitting) graphics to the canvas

Obviously the ideal scenario here is that the engine should only depth-sort or render to the canvas entities that are currently visible on the screen (viewport). This sounds relatively easy but you must take into account complex entities (composite entities that are built up from other entities who's "on screen" bounds are not just the base entity's AABB but all of the composite entity's bounds combined), multiple viewports which may be looking at different sections of the world and therefore need to render different areas etc.

To solve this problem I am proposing an update and wanted to get your thoughts and opinions before I dive into coding it.

The main issue is around how to handle view checking (is an entity "on screen") and how to de-couple the depth-sorting system from the ._children array in each object since that array is used when doing almost everything with the scenegraph.

What I am thinking of doing is creating a "broad-phase" check where every entity is added to a broad grid, with each grid section sized to the size of the screen / canvas (ige._geometry). As entities move around the world they are re-organised into the correct grid section. An entity can potentially exist inside multiple grid sections depending on if their position and bounds overlap multiple sections.

Then what I propose is to get the grid sections that the current viewport overlaps (or is inside if the viewport is smaller than the grid section) and then loop only the entities inside that grid to see if they are "on screen". After this "broad-phase" check, any entities that were set to on-screen will be added to a new _depthSortAndRender array which is used to loop and depth sort ONLY entities inside that array. Any entities that are no longer on screen are removed from it.

Finally the depth-sort will loop the _depthSortAndRender array and assign correct depths before the whole rendering sequence is processed from that array.

You should note two things that will effectively change because of this process:

1) While all entity update() methods will be called regardless of if they are on screen, only entities that are on screen will have their tick() methods called. If you have logic in the tick() method that should be processed every frame it should really be in the update() method anyway.

2) A process called "view checking" will need to run against the scenegraph using this broad-phase grid system to narrow down the checking and because of this, the grid will need to be kept up to date so that entities that move are assigned to the correct grid section arrays. The data structure for this grid will look something like this for grid section 0 x 0 (the center of the world):

Code: Select all

ige._sceneGrid = [
    [
        [
            {anEntityObject},
            {anEntityObject},
            {anEntityObject}
        ]
    ]
];
Scene grid can then be accessed for grid section 0x0 via:

Code: Select all

var entitiesCurrentlyIntersectingGridSection0x0 = ige._sceneGrid[0][0];
As entities move around the world they will intersect different grid sections so we will have to:

1) Assign grid sections on entity mount
2) Update grid sections on any entity transform (translate, rotate, scale)
3) Update grid sections on any entity bounds update (size3d, width, height)
4) Remove from all grid sections on entity unmount or destroy

The only concern I have with this system is that items like particle emitters generate and destroy a lot of entities very quickly and adding overhead to them is a slowdown in performance. Particles are also transforming constantly so will require constant grid-section re-calculation. This all adds to memory usage as well as different bits of data are cached to speed up comparisons etc.

Hope that makes sense.

If anyone has any suggestions or ideas please raise them now! I could do with a sounding board on this one as it is complex and potentially a very VERY useful update to the engine.

P.S. Anyone who has seen my tech talk at the OnGameStart conference might find this proposal familiar - a similar system used to exist in the engine before it became scenegraph based and was used to clear distinct sections of the canvas instead of the whole thing so that only sections that had changed could be redrawn - before browsers had built-in GPU acceleration that made the process redundant).
CEO & Lead Developer
Irrelon Software Limited
http://www.isogenicengine.com
User avatar
GoldFire
Posts: 4
Joined: Wed Sep 11, 2013 8:31 pm

Re: Proposed Update - Broad-Phase SceneGraph Optimisation

Post by GoldFire »

Glad to see some more effort going into performance, we have certainly been looking for ways to squeeze out a few more FPS on CasinoRPG. From a quick read-through this sounds interesting. Depth sorting is definitely the area that slows things down the most for us. Luke has been playing around with the depth sorting a lot more than I have lately, so I think he'll have some more feedback on this soon.
Luke
Posts: 3
Joined: Wed Sep 11, 2013 9:01 pm

Re: Proposed Update - Broad-Phase SceneGraph Optimisation

Post by Luke »

Hi Rob, sounds like a pretty cool update.

We've created a customized entity manager for CasinoRPG that automatically unmounts any static offscreen entities (buildings and the like) so their updates aren't called. Anything that isn't, such as players and npcs, are marked as "_inView = false" so that they're ignored by depth sorting.

Will entities only be depth sorted within their respective grid sections? Or will they still be depth sorted within a single array?

We've put a lot of work into ensuring offscreen entities aren't reducing performance, so I'm not sure if we'd see an improvement with this update. Otherwise, I bet it'd make a big impact. Even so, I'd love to test it out and see what it does in CasinoRPG.
foolmoron
Posts: 25
Joined: Wed Sep 11, 2013 3:34 pm

Re: Proposed Update - Broad-Phase SceneGraph Optimisation

Post by foolmoron »

coolbloke1324 wrote:The only concern I have with this system is that items like particle emitters generate and destroy a lot of entities very quickly and adding overhead to them is a slowdown in performance. Particles are also transforming constantly so will require constant grid-section re-calculation. This all adds to memory usage as well as different bits of data are cached to speed up comparisons etc.
You could have an option for entities to "opt-out" of this grid-based optimization. So they would still be drawn if they're off-screen, but would avoid all this overhead. Then developers can choose which option to use depending on which is preferable.
signature
ChrisEt
Posts: 1
Joined: Thu Sep 12, 2013 8:39 pm

Re: Proposed Update - Broad-Phase SceneGraph Optimisation

Post by ChrisEt »

Have you thought about how this would interact with network streaming? I have been thinking about the same problem, performance issues with many entities. While prohibiting rendering of off-screen entities will most certainly bring a big performance gain on the client side, a big number of entities is still an issue when their changes are constantly streamed to all clients.

So my idea was basically that the server does the filtering, similar to what you described above. As a result the server only sends these entities to the client which are "nearby" (visible or nearly visible). Then all depth sorting and blitting would only happen on these and also memory would be saved. I would only need to come up with a mechanism to destroy entities client-side which have moved out of sight.

This would basically permit an almost infinite number of entities (provided the server is powerful enough).

What you described as "grid sections" I would try to implement as simple mathematical calculations. Instead of trying to maintain a 2d-array of lists of entities you could simply determine the respective grid section by a coarse division. And to be sure, in case the entity overlaps a grid section boundary, just send the adjacent grid sections as well.

These were my ideas, but I'm not sure if they are implementable though... I will have to actually try that, then I can say if some unsolvable problem comes up or not.
User avatar
coolbloke1324
Posts: 181
Joined: Mon Jan 23, 2012 5:20 pm

Re: Proposed Update - Broad-Phase SceneGraph Optimisation

Post by coolbloke1324 »

ChrisEt wrote:Have you thought about how this would interact with network streaming? I have been thinking about the same problem, performance issues with many entities. While prohibiting rendering of off-screen entities will most certainly bring a big performance gain on the client side, a big number of entities is still an issue when their changes are constantly streamed to all clients.

So my idea was basically that the server does the filtering, similar to what you described above. As a result the server only sends these entities to the client which are "nearby" (visible or nearly visible). Then all depth sorting and blitting would only happen on these and also memory would be saved. I would only need to come up with a mechanism to destroy entities client-side which have moved out of sight.

This would basically permit an almost infinite number of entities (provided the server is powerful enough).

What you described as "grid sections" I would try to implement as simple mathematical calculations. Instead of trying to maintain a 2d-array of lists of entities you could simply determine the respective grid section by a coarse division. And to be sure, in case the entity overlaps a grid section boundary, just send the adjacent grid sections as well.

These were my ideas, but I'm not sure if they are implementable though... I will have to actually try that, then I can say if some unsolvable problem comes up or not.
The problem with just using a calculation to do it is that you still have to loop through all entities each frame and calculate their grid intersections. The idea behind the sections data array is that you basically have a cached list of entities and no need to scan the entire scenegraph to find ones in a particular grid section.

That said, every entity will need to have it's update() method called if it exists on the scenegraph anyway, regardless of it is on screen or not since it will still need to process things like components, transform updates etc.

Regarding network streaming, there is already a streamMode() that enables an advanced streaming based on settings returned by a callback so that you can decide if an entity is streamed to a client or not. The main issue will be tracking where a client is looking or what the visible area is. Once you have that information, the stream is already looping the entities and if the entity is not inside the visible bounds area of a player's view then don't send the data.

@goldfire - Your approach seems to be pretty well thought out... can you explain how you went about it and what pitfalls you have encountered along the way?
CEO & Lead Developer
Irrelon Software Limited
http://www.isogenicengine.com
User avatar
coolbloke1324
Posts: 181
Joined: Mon Jan 23, 2012 5:20 pm

Re: Proposed Update - Broad-Phase SceneGraph Optimisation

Post by coolbloke1324 »

Perhaps the best way to handle this is not to worry about a grid at all and instead just create an entity manager that umounts entities when they are out of bounds and re-mount them when they are back in bounds.

There will then need to be entities that are un-mountable - such as those that represent AI, players or other items whose update() method must be called to handle game logic.
CEO & Lead Developer
Irrelon Software Limited
http://www.isogenicengine.com
User avatar
coolbloke1324
Posts: 181
Joined: Mon Jan 23, 2012 5:20 pm

Re: Proposed Update - Broad-Phase SceneGraph Optimisation

Post by coolbloke1324 »

coolbloke1324 wrote:Perhaps the best way to handle this is not to worry about a grid at all and instead just create an entity manager that umounts entities when they are out of bounds and re-mount them when they are back in bounds.

There will then need to be entities that are un-mountable - such as those that represent AI, players or other items whose update() method must be called to handle game logic.
Although this of course... assumes that only one viewport exists (I suspect this is the majority of games though).
CEO & Lead Developer
Irrelon Software Limited
http://www.isogenicengine.com
User avatar
coolbloke1324
Posts: 181
Joined: Mon Jan 23, 2012 5:20 pm

Re: Proposed Update - Broad-Phase SceneGraph Optimisation

Post by coolbloke1324 »

OK so thinking more about this, it makes a lot of sense to have an active entity manager handle auto-mount / unmount based on visible area.

We could have a flag set on entities, something like: entity.managed(true) - which defaults to true.

If the entity is set to managed, it will automatically be unmounted when out of sight. If it is unmanaged, it will not be mounted / unmounted from the scenegraph automatically.

This is a relatively easy system to produce so I can build and test it quickly to determine if it will massively increase rendering capability.
CEO & Lead Developer
Irrelon Software Limited
http://www.isogenicengine.com
User avatar
coolbloke1324
Posts: 181
Joined: Mon Jan 23, 2012 5:20 pm

Re: Proposed Update - Broad-Phase SceneGraph Optimisation

Post by coolbloke1324 »

New version with preliminary entity manager built in available on separate branch "dev-1.2.0".

You can check example 15.2 for the performance test. That example loads 3000 3d-bounds isometric entities onto a single tile map and enables the entity manager component on that map via:

Code: Select all

tileMap1.addComponent(IgeEntityManager);
The result is 30 fps. When I tested against 6000 I got 18 fps.

There are numerous further optimisations I can make to this component.

If you have NPCs / AIs or other non-static objects that need to have their update() method called each frame, you can exclude them from the auto-mount/unmount via:

Code: Select all

entity.managed(false);
Entities start with managed mode enabled by default.

Let me know your results!
CEO & Lead Developer
Irrelon Software Limited
http://www.isogenicengine.com
Post Reply

Return to “HTML5/Web Engines”