Search Unity

SyncHeightmaps() and ApplyDelayedHeightmapModification() performance

Discussion in 'World Building' started by crysicle, Apr 9, 2019.

  1. crysicle

    crysicle

    Joined:
    Oct 24, 2018
    Posts:
    95
    Hello, i am currently making runtime terraformations. With the 2018.3 TerrainPaintUtility i am able to set the heightmap information way more rapidly than the previous SetHeight() or SetHeightsDelayLOD() methods, however syncing the heightmaps still remains as the main bottleneck of this system.

    I was wondering if there were going to be any changes made to the heightmap update functions to either be moved to GPU just like the TerrainPaintUtility moved from the previous CPU based functions or if there is a multicore job rework coming for these syncing methods.
     
  2. wyattt_

    wyattt_

    Unity Technologies

    Joined:
    May 9, 2018
    Posts:
    424
    The heightmap update functions already deal with the GPU. The bottleneck would be, as you stated, the syncing of the GPU copy to CPU copy. That's mostly what those functions are doing. They may also be flagging the Terrain Collider as needing to be rebuilt.

    How often are you syncing the heightmaps?
     
  3. crysicle

    crysicle

    Joined:
    Oct 24, 2018
    Posts:
    95
    As hinted in the https://forum.unity.com/threads/the-new-terrain-system-questions.567529/page-2#post-4419520, pretty much any time i update the terrain. For the system i'm creating it wouldn't be all the time, however, once the terraformations would begin happening, i'd be changing roughly 1024-20480 heightmaps each FixedUpdate frame from 5-120 seconds. I've done performance checks already with the TerrainPaintUtility tool and was able to change 16777216 heightmaps at runtime with only losing 50 FPS on my system. After crunching the numbers, i figured it was roughly a 100-257 time increase in performance with the old way that i was making the terraformation system. The bottleneck right now is converting the changes into a mesh collider, however i find this impossible to do it each frame with the way the SyncHeightmap() works.
     
  4. crysicle

    crysicle

    Joined:
    Oct 24, 2018
    Posts:
    95
    I've done some more testing on the mesh collider syncing. These are my findings:

    I've completely resolved the lag coming from Terrain.RecomputeInvalidPatches() by dividing my terrain of 1000x1000 vector size by 513x513 heightmap size into much smaller chunks of 64x64 vector size by 33x33 heightmap size. By parsing the terrain in this manner, most of the lag from this method comes from the actual manipulated heightmaps, which is excellent.

    I probably figured out why RenderTexture.SetActive() stalls the GPU so much after some testing. Not sure if this is precisely what the heighmap syncing functions are doing, but as i understood it, Graphics.CopyTexture(RenderTexture var1, Texture2D var2) is most likely used to convert a texture. The same footprint is in the profiler when i use this function and it makes sense that the RenderTexture should be converted. With this information however, the stall times still don't make much sense. I've troubleshooted the Graphics.CopyTexture() performance and here are the figures:

    1000x1000 copy - GPU 0.1ms
    5000x5000 copy - GPU 2.3ms
    7000x7000 copy - GPU 4.5ms

    GPU starts stalling heavily and FPS drops drasticly at this point
    8000x8000 copy - GPU 5.8 followed by 40ms after some time.
    8750x8750 copy - GPU 50ms
    10000x10000 copy - GPU 64ms

    The thing that doesn't make sense in the syncing functions is that even if you manipulate a single terrain that has 33x33 heightmap resolution by changing 8 heightmaps and then syncing the terrain, you still get GPU stalling times of 15-50ms. I'm not sure what additional data it's parsing, but it seems to be hitting a fly with a huge hammer when it does it.
     
  5. wyattt_

    wyattt_

    Unity Technologies

    Joined:
    May 9, 2018
    Posts:
    424
    It has to bus the texture data from GPU to CPU so that's going to be pretty slow, to begin with. That's what Graphics.CopyTexture is doing if it's from RenderTexture to Texture.

    You could try not syncing the heightmap to CPU so often or only do a few per frame. You might run into clipping issues depending on how close the camera is to the Terrain rendering with those non-synced heightmaps. In that case, you could sync based on proximity to camera.

    As an alternative to frequently using SyncHeightmap(), you could also try

    terrainData.DirtyHeightmapRegion(..., TerrainHeightmapSyncControl.None);

    or

    terrainData.DirtyHeightmapRegion(..., TerrainHeightmapSyncControl.HeightOnly);

    Documentation:
    https://docs.unity3d.com/2019.2/Documentation/ScriptReference/TerrainData.DirtyHeightmapRegion.html
     
  6. crysicle

    crysicle

    Joined:
    Oct 24, 2018
    Posts:
    95
    Thanks, i overlooked these new methods after upgrading to 2019.2. I'll have to check them out.
     
  7. crysicle

    crysicle

    Joined:
    Oct 24, 2018
    Posts:
    95
    The previous methods have the same issue. However, I found a pretty ungly workaround which is to make a physics extension and use a method to calculate heights information from particular coordinates around units, derive collission data from them and apply physics behaviour. Still, shame that there's no way to quickly both manipulate the heightmap and apply collissions.
     
    Last edited: Apr 25, 2019