Search Unity

  1. Unity Asset Manager is now available in public beta. Try it out now and join the conversation here in the forums.
    Dismiss Notice
  2. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  3. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

SetTile is slow... Plans to implement ECS Pattern to tilemap?

Discussion in '2D Experimental Preview' started by o1o1o1o1o2, Mar 5, 2019.

  1. o1o1o1o1o2

    o1o1o1o1o2

    Joined:
    May 22, 2014
    Posts:
    34
    In my case i need to rebuild tilemap often, not in update, but several times in a minute. SetTile to tilemap of a size 128*128 it takes about 80ms on my machine, I tried SetTiles, but it is also slow. I think it will be good to implement ability of setting array of tiles or sprites in parallel using Jobs for example...or may be ability to refresh all tiles on a different tilemaps in parallel
     
    Last edited: Mar 7, 2019
    tonytopper, chrisall76 and foxnne like this.
  2. ChuanXin

    ChuanXin

    Unity Technologies

    Joined:
    Apr 7, 2015
    Posts:
    1,068
    Could you share a little about why and how you are rebuilding your Tilemap, for example with any particular types of custom Tiles or components on the Tilemap GameObject? That would be very helpful for us to optimize the problem you have.
     
  3. o1o1o1o1o2

    o1o1o1o1o2

    Joined:
    May 22, 2014
    Posts:
    34
    Im making an isometric map that represent voxel based terrain (like minecraft) (xsize = 128 zsize = 128 and ysize = 64 (height)), so i want to have an ability to switch between horizontal layers, im not using custom tile types, just Tile. All my tiles have different z coordinate, based on position of voxel (because sorting not work properly in chunk mode when tiles on a same z(i use shader with ZWrite On)). So when l need to show layer that below current for example i need to delete all tiles that represent higher voxels and draw new ones that was not visible before. I use several tilemaps as chunks, so when l switch layer, in a job I prepare sprites id arrays for a new layer and then in a simple loop i set tiles (only that has changed) and refresh them, and it is slow. Even just
    Code (CSharp):
    1.  
    2. for (int z = 0; z < 128; z++)
    3. {
    4.      for(int x = 0; x < 128; x++)
    5.      {
    6.          tilemap.SetTile(new Vector3Int(x, z, 0), _tile);
    7.       }
    8. }
    9.  
    is slow
     
    Last edited: Mar 12, 2019
    chrisall76 likes this.
  4. ChuanXin

    ChuanXin

    Unity Technologies

    Joined:
    Apr 7, 2015
    Posts:
    1,068
    It is indeed slow enough to not be able to do this in a per frame basis. We will look into this for improvements.

    In the meantime, would staggering the setting of Tiles and showing that layers when done help?
     
  5. o1o1o1o1o2

    o1o1o1o1o2

    Joined:
    May 22, 2014
    Posts:
    34
    i dont think i understand a question (not so good in english), If you mean something like async or coroutine, it will not help,actually to such a little map 128*128 80ms is not so slow several times in a minute, but for 256*256 it four time slower,let alone anything bigger... no one wants to wait longer than 500ms)) the problem is that settile (of refreshtile) can be only made on a mainthread
     
    Last edited: Mar 15, 2019
  6. ChuanXin

    ChuanXin

    Unity Technologies

    Joined:
    Apr 7, 2015
    Posts:
    1,068
    Currently, due to the way TileBase.GetTileData and TileBase.RefreshTile can be extended, it is not safe to assume that they can be run on jobs.

    We are investigating on an entities based solution for 2D, which may be more suitable for this when it becomes available in the future.
     
    Sylmerria likes this.
  7. o1o1o1o1o2

    o1o1o1o1o2

    Joined:
    May 22, 2014
    Posts:
    34
    glad to read it, hope it will be done some day...
     
  8. o1o1o1o1o2

    o1o1o1o1o2

    Joined:
    May 22, 2014
    Posts:
    34
    You something do wrong, it is slow, but not so, you can improve it to at least 100-200ms a such a small map
     
  9. DavidNLN

    DavidNLN

    Joined:
    Sep 27, 2018
    Posts:
    90
    Hya, I've been working on something similar, my world consists of 144000000 tiles and I'm loading fine never had an issue with setTile per say.

    Wasn't easy tho, I'm loading the data from the disk and caching it using weak refs, I then render the data 1000 tiles per frame, it holds fine, 600+ fps while not moving, 300+ while running around.

    That said, getting some optimization for the tiling system would be good, it took a lot of work to get things running fast, could have been easier...

    @ChuanXin glad to hear you're working on an ecs solution, where could we get updates on tileMap improvements that are coming up ?

     
    Last edited: Jun 16, 2019
    Djayp, NotaNaN and ph3rin like this.
  10. ChuanXin

    ChuanXin

    Unity Technologies

    Joined:
    Apr 7, 2015
    Posts:
    1,068
    When setting multiple Tiles, it would be faster to use SetTiles or SetTilesBlock instead of individually calling SetTile for each Tile, especially for scripted Tiles.

    The ECS solution for Tilemaps will not be available for a while, I'm afraid.
     
  11. o1o1o1o1o2

    o1o1o1o1o2

    Joined:
    May 22, 2014
    Posts:
    34

    I wonder why there is no ability to call RefreshTile() from other thread, even with [burstdiscard] attribute. For example if I had a setup like this:
    1) Map divided into Tilemap chunks represented by separate Tilemaps.
    2) I fill each Tilemap chunk with array of Tiles created by ScriptableObject.CreateInstance<Tile>() and set by SetTilesBlock().
    3) Then I cache all tiles on Tilemaps chunks in a managed array MyTile[TilemapIndex][TileIndex] using GetTile(). where each Tile cached in a struct MyTile{Tile tile}
    4) Then I cache refs to each element (MyTile) of a managed tiles array in a DynamicBuffer or NativeArray of a special struct TileRef{void* tileRef} using UnsafeUtility.AddressOf<MyTile>
    5) Now from IJobParallelFor using UnsafeUtility.ReadArrayElement<MyTile> I can get my cached Tile and set their sprite using [burstdiscard] attribute, all in parallel.

    One thing I can't do is RefreshTile(), cause it can be made only on a main thread. I'm interested in what kind of race condition I can get in my approach (if there is an ability to refresh tile from other threads) when I do not touch any Tilemap functionality from a main thread? Is there an ability to add method in api that will work from threads in some cases and setups, for example in a case that there was no extensions to GetTile of RefreshTile, if I use just Tile class?
     
    Last edited: Jun 27, 2019
    Deleted User likes this.
  12. _Eyesgood_

    _Eyesgood_

    Joined:
    Apr 19, 2013
    Posts:
    55
    I created a chunking engine in my game to handle very large maps -- as in millions of tiles. I initialize my tilemaps one time (I have about a dozen) to be the size of the game world and then use SetTile as I iterate through the tiles on each chunk, which are 20x20 in size = 400 tiles. I am loading 400 tiles in 7-8 milliseconds. All 9 chunks around the player load in 750 milliseconds (that's 3600 tiles). Unloading the chunks is also very fast. My tile data is stored on a Scriptable Object where I keep a reference to the tile objects. But my chunked game data about each tile is stored in Json files. I have found it quite performant since rebuilding my tile engine using Scriptable Objects. You can read about it on my blog.

    https://surviveandthrivegame.blogspot.com/
     
    NotaNaN likes this.
  13. Lo-renzo

    Lo-renzo

    Joined:
    Apr 8, 2018
    Posts:
    1,503
    Especially if these are chunked tiles, SetTiles and SetTilesBlock are more performant than using SetTile. From my own rough tests it becomes "worth it" around 6 tiles. When chunked, it can speed things up exponentially. For a 200x200 map, switching to these methods reduced an 8-second map generation process down to 600 ms. 300x300 went from 23 seconds to 1400 ms. 20x20 is smaller than that, but it's likely worthwhile to try the more performant methods.
     
  14. _Eyesgood_

    _Eyesgood_

    Joined:
    Apr 19, 2013
    Posts:
    55
    I should probably mention another reason I use SetTile is because I wrote my own rule-tile engine. This has to be done because as of this writing I have 47 different tile types in my game, each with its own tilesheet of 15 possible tiles to choose from based on the surrounding tiles. The above numbers include the time it takes to work out this logic. But as any developer does, I will continue to look at optimizing my code and approach in any way I can.
     
  15. o1o1o1o1o2

    o1o1o1o1o2

    Joined:
    May 22, 2014
    Posts:
    34
    700ms is very slow for such small size (9 chunks 20×20) i came to implement my own tilemap based on ecs multithreading chunk mesh combining and all new burst unity features with full control about sorting.. so 256x256 map load time took about 3-4 ms on my pc. (12 cores) but ofcourse ot is not unity tilemap at all, it is just chunked mesh of sprites. Now i redo this system to work with 3d chunks, cause i came to idea than 3d is better than 2d in my case.
     
  16. _Eyesgood_

    _Eyesgood_

    Joined:
    Apr 19, 2013
    Posts:
    55
    I am happy to hear you achieved such performance. But you are comparing apples to oranges. Try that with the tilemap and let me know if you can get better performance than what I have done with the tilemap. Cheers.
     
    lukewebby, Qusdrok and NotaNaN like this.
  17. Qusdrok

    Qusdrok

    Joined:
    Jun 28, 2020
    Posts:
    24
    wow i'm having problems with it i'm glad to know you did about it i hope you will come up with videos or articles faster :3 thanks a lot
     
  18. bre_dev

    bre_dev

    Joined:
    Sep 14, 2015
    Posts:
    5
    Is there any update on this matter?
    I am trying to manage big tilemap in chunks, but loading the next chunk sometimes takes up to 500ms. Is there any reference where I can see how better performances can be achieved?
     
  19. Lo-renzo

    Lo-renzo

    Joined:
    Apr 8, 2018
    Posts:
    1,503
    Use tilemap.SetTiles and tilemap.SetTilesBlock. It's MUCH faster. Besides that, load in smaller chunks. Avoiding complex RuleTiles can also make things faster, if possible. Also, avoid colliders where unneeded because this step takes a while. In worst case, Individual mode will avoid the Chunking process but this probably isn't worth it because it would increase per frame rendering time.
     
    Last edited: Mar 4, 2022