Is there any downside with spawning around 10.000 display groups?

Hi all,

Long story short, I have created my own tile engine some time ago and want to extend its functionality by supporting multiple tile sizes within the same map (eg. map-tilesize = 16x16,  tileset1 = 16x16, tileset2 = 32x64, etc…)

This introduces a new “functionality”: being able to have tiles overlap each other. In the screenshot below you can see tree tiles, they are all placed on the same tile-layer. The display order of these tiles are apparently defined in Tiled, as tiles placed at a higher x-index are placed above tiles with a lower x-index, same with the y-index.

Previously I have already implemented this for just the y-index, by making some kind of depth buffer. This consists of a displayGroup, with a table of displayGroups inside of it. (Pseudo code found below)

local layer = display.newGroup() for i=1, #amountOfVerticalTilesInMap, 1 do layer[i] = display.newGroup(layer) end

Now when I spawn a tile, I would insert it in the correct y-index as defined in Tiled. This makes all tiles with a higher y-index overlap the tiles with a lower y-index. In a map of 100x100 tiles this would create just 100 displayGroups, which is not that big of a number for what it is worth.

When I’d also create a depth buffer for the x-index, the number of displayGroups that would need to be created is a lot higher: 10.000 (100x100). I did check the memory impact and it was not that high, around 2.7MB system memory and 0 texture memory. But i’m afraid this could cause some other issues I am not overlooking at the moment? Could any developer verify me how this would affect the performance of the Corona engine other than memory usage? For example, when moving the complete layer, would the ~9000 empty layers use a lot of processing power? 

 

There is no screenshot attached.

Sorry my bad, fixed now  :slight_smile:

run it on a device, find out:  can you maintain your fps?  my guess is that 10K “anythings” may become a burden.  internally Corona probably at least needs to check .isVisible, then possibly a bounds check, before it could decide to cull a group – which can’t happen entirely for free.  do you really need to do this for your whole map, rather than just the visible portion?

I also though 10K of anything would become a burden, but as of now everything looks fine.
Memory usage is relatively small, FPS stays the same, also did a test :translate() 100.000 times, this is with a simple displayGroup vs a displayGroup with 10.000 groups nested. There is just a slight difference, test executed is listed below (Although I use an internal debugHelper, it stills shows what I did)

local debugHelper = require("debugHelper") local simpleMap = display.newGroup() local map = display.newGroup() for i = 1, 10000, 1 do map:insert(display.newGroup()) end timer.performWithDelay(3000, function() local function test1() map:translate(1,1) end local function test2() simpleMap:translate(1,1) end debugHelper:compareExecutionTime(test1, test2, 100000) end) ---------EXECUTION TIME--------- 100000x Function 1 took: 54.793 ms 100000x Function 2 took: 53.223 ms Function 2 = 1.0294985250737 faster than Function 1 ------------------------------------------

Regarding the question if I really need to do this, I think I do. The reason I need to do this for the whole map is because of the defined layer order. Every tile in the game gets culled when it is outside of the screen boundaries, let’s take the bottom trees in my main post as example. If I would walk from the east side, I would first encounter the most right tree and it would be rendered as it becomes inside of the screen boundaries, after that every tree to the left will be spawned. This would make every tree on the left spawn on top of the tree on the right, while it should be below as seen in the screenshot. Spawning it in one of my ‘10.000’ display groups, it would spawn correctly as the most left displayGroups are spawned earlier. Having some difficulties explaining why I need this, but I hope it’s clear :slight_smile:

so even if you did  nothing else  in your game, the best you could manage would be ~19fps, which might not be enough to convey smooth scrolling, right?

The execution time in my test shows the time it takes to do :translate() a  100.000 times. (normally I would just do this once per frame, but I choose to do this many executions for the sake of comparing)

So the ~54 ms can be divided by 100.000 for a single execution time, which is 0,00054 ms.

The test was meant to show the minor performance loss when translating an empty displayGroup vs one with 10.000 displayGroups nested.

then i’d suggest you already have your answer – if there’s no (or negligible, as demonstrated) difference, then there’s no difference.  :)

There is no screenshot attached.

Sorry my bad, fixed now  :slight_smile:

run it on a device, find out:  can you maintain your fps?  my guess is that 10K “anythings” may become a burden.  internally Corona probably at least needs to check .isVisible, then possibly a bounds check, before it could decide to cull a group – which can’t happen entirely for free.  do you really need to do this for your whole map, rather than just the visible portion?

I also though 10K of anything would become a burden, but as of now everything looks fine.
Memory usage is relatively small, FPS stays the same, also did a test :translate() 100.000 times, this is with a simple displayGroup vs a displayGroup with 10.000 groups nested. There is just a slight difference, test executed is listed below (Although I use an internal debugHelper, it stills shows what I did)

local debugHelper = require("debugHelper") local simpleMap = display.newGroup() local map = display.newGroup() for i = 1, 10000, 1 do map:insert(display.newGroup()) end timer.performWithDelay(3000, function() local function test1() map:translate(1,1) end local function test2() simpleMap:translate(1,1) end debugHelper:compareExecutionTime(test1, test2, 100000) end) ---------EXECUTION TIME--------- 100000x Function 1 took: 54.793 ms 100000x Function 2 took: 53.223 ms Function 2 = 1.0294985250737 faster than Function 1 ------------------------------------------

Regarding the question if I really need to do this, I think I do. The reason I need to do this for the whole map is because of the defined layer order. Every tile in the game gets culled when it is outside of the screen boundaries, let’s take the bottom trees in my main post as example. If I would walk from the east side, I would first encounter the most right tree and it would be rendered as it becomes inside of the screen boundaries, after that every tree to the left will be spawned. This would make every tree on the left spawn on top of the tree on the right, while it should be below as seen in the screenshot. Spawning it in one of my ‘10.000’ display groups, it would spawn correctly as the most left displayGroups are spawned earlier. Having some difficulties explaining why I need this, but I hope it’s clear :slight_smile:

so even if you did  nothing else  in your game, the best you could manage would be ~19fps, which might not be enough to convey smooth scrolling, right?

The execution time in my test shows the time it takes to do :translate() a  100.000 times. (normally I would just do this once per frame, but I choose to do this many executions for the sake of comparing)

So the ~54 ms can be divided by 100.000 for a single execution time, which is 0,00054 ms.

The test was meant to show the minor performance loss when translating an empty displayGroup vs one with 10.000 displayGroups nested.

then i’d suggest you already have your answer – if there’s no (or negligible, as demonstrated) difference, then there’s no difference.  :)