Search Unity

Tile Map 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:
    23
    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
    christides11 and foxnne like this.
  2. ChuanXin

    ChuanXin

    Unity Technologies

    Joined:
    Apr 7, 2015
    Posts:
    283
    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:
    23
    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
    christides11 likes this.
  4. ChuanXin

    ChuanXin

    Unity Technologies

    Joined:
    Apr 7, 2015
    Posts:
    283
    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:
    23
    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:
    283
    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:
    23
    glad to read it, hope it will be done some day...
     
  8. Akhilya

    Akhilya

    Joined:
    Sep 4, 2017
    Posts:
    3
    same issue, i have procedural map generation script for my game. The map is 125x125 size and have placement algorithm for environmental elements (such as trees, bushes, rocks etc...) , so it can take for my machine about ~7 seconds. In profiler i can see, what 85 % of this time is SetTile function. It would be great if Unity team will improve their SetTile algorithm, otherwise my game in mobile devices will take about half minute to generate world... even compute shaders won`t help me... upload_2019-5-11_11-12-48.png upload_2019-5-11_11-6-46.png upload_2019-5-11_11-14-25.png
     
  9. o1o1o1o1o2

    o1o1o1o1o2

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

    DavidNLN

    Joined:
    Sep 27, 2018
    Posts:
    53
    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
  11. ChuanXin

    ChuanXin

    Unity Technologies

    Joined:
    Apr 7, 2015
    Posts:
    283
    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.
     
  12. o1o1o1o1o2

    o1o1o1o1o2

    Joined:
    May 22, 2014
    Posts:
    23

    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
    Akhilya likes this.