Wayback Machinekoobas.hobune.stream
May JUN Jul
Previous capture 14 Next capture
2021 2022 2023
2 captures
13 Jun 22 - 14 Jun 22
sparklines
Close Help
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
  • Asset Store
  • Get Unity

UNITY ACCOUNT

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account
  • Blog
  • Forums
  • Answers
  • Evangelists
  • User Groups
  • Beta Program
  • Advisory Panel

Navigation

  • Home
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
    • Blog
    • Forums
    • Answers
    • Evangelists
    • User Groups
    • Beta Program
    • Advisory Panel

Unity account

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account

Language

  • Chinese
  • Spanish
  • Japanese
  • Korean
  • Portuguese
  • Ask a question
  • Spaces
    • Default
    • Help Room
    • META
    • Moderators
    • Topics
    • Questions
    • Users
    • Badges
  • Home /
avatar image
1
Question by hangarter · Mar 08 at 02:00 PM · performance optimizationdebuggingterraindatathreading

How could I optimize this terrain generation?

Hi All,

Out of curiosity I was reading the documentation about IJobParallelFor and decided to try updating the terrain realtime with a generated perlin noise texture. I've tweaked the code quite a bit, and it shows good 30FPS but I feel it could be improved somehow. Any thoughts?


Below I'm sharing a video and the results of the logs in the code:

  • Job elapsed time: 12

  • List item

  • Loop elapsed time: 17

  • Time elapsed for adding up layers: 30ms (both above)

  • SetHeights ellapsed time: 58


https://www.youtube.com/watch?v=OteB1mUGEqE

 using System.Diagnostics;
 using Unity.Jobs;
 using UnityEngine;
 using Unity.Collections;
 using Debug = UnityEngine.Debug;
 using Unity.Mathematics;
 using Unbegames.Noise;
 using System.Collections;
 using Unity.Burst;
 using System.Threading.Tasks;
 
 public class TerrainGenerator : MonoBehaviour
 {
     public float speed = 1f;
     public float translationX = 0f;
     public float translationY = 0f;
 
     public float layer1HeightMultiplier = 3;
     public float layer2HeightMultiplier = 3;
 
     public float layer1ScaleMultiplier = 0.5f;
     public float layer2ScaleMultiplier = 3f;
 
     public float scale = 0.5f;
 
     private Terrain terrain;
     private Coroutine _coroutine;
 
     // Start is called before the first frame update
     void Start()
     {
         _coroutine = StartCoroutine(DrawTerrain());
     }
 
     private IEnumerator DrawTerrain()
     {
         terrain = GetComponent<Terrain>();
         var detailWidth = terrain.terrainData.detailWidth;
         var detailHeight = terrain.terrainData.detailHeight;
 
         while (true)
         {
             var heights = GenerateTerrain(detailWidth, detailHeight);
 
             var sw = new Stopwatch();
             sw.Start();
             //terrain.terrainData.SetHeightsDelayLOD(0, 0, heights);
 
             //terrain.terrainData.SyncHeightmap();
             terrain.terrainData.SetHeights(0, 0, heights);
             sw.Stop();
             Debug.Log($"SetHeights ellapsed time: {sw.ElapsedMilliseconds}");
             translationY += Time.deltaTime * speed;
             translationX += Time.deltaTime * speed;
 
             yield return new WaitForSeconds(0.10f);
         }
     }
 
     private void OnDestroy()
     {
         StopCoroutine(_coroutine);
     }
 
     private float[,] GenerateTerrain(int detailWidth, int detailHeight)
     {
         var sw = new Stopwatch();
         sw.Reset();
 
         sw.Start();
         var heightsLayer1 = layer(detailWidth, detailHeight, layer1ScaleMultiplier, layer1HeightMultiplier);
         sw.Stop();
         Debug.Log($"Time elapsed for adding up layers: {sw.ElapsedMilliseconds}ms");
         return heightsLayer1;
     }
 
     private float[,] layer(int detailWidth, int detailHeight, float scaleMultiplier, float heightMultiplier)
     {
         var sw = new Stopwatch();
         sw.Start();
         float[,] result = new float[detailWidth, detailHeight];
 
         var heights = new NativeArray<float3>(detailHeight * detailWidth, Allocator.Persistent);
         var modifiers = new NativeArray<Vector2>(1, Allocator.Persistent);
         modifiers[0] = new Vector2(scale, heightMultiplier);
 
         var dimensions = new NativeArray<Vector2>(1, Allocator.Persistent);
         dimensions[0] = new Vector2(detailWidth, detailHeight);
 
         var translation = new NativeArray<Vector2>(1, Allocator.Persistent);
         translation[0] = new Vector2(translationX, translationY);
 
         var job = new LayerJob
         {
             dimensions = dimensions,
             heights = heights,
             modifiers = modifiers,
             translation = translation
         };
 
         int size = heights.Length;
         JobHandle jobHandle = job.Schedule(size, 32);
 
         jobHandle.Complete();
         sw.Stop();
         Debug.Log($"Job elapsed time: {sw.ElapsedMilliseconds}");
 
         sw.Reset();
         sw.Start();
         for (var i = 0; i < size; i++)
         {
             result[(int)heights[i].x, (int)heights[i].y] = heights[i].z;
         }
         sw.Stop();
         Debug.Log($"Loop elapsed time: {sw.ElapsedMilliseconds}");
         //foreach (var height in heights)
         //{
         //    result[(int)height.x, (int)height.y] = height.z;
         //};
 
         heights.Dispose();
         dimensions.Dispose();
         translation.Dispose();
         modifiers.Dispose();
         
         return result;
     }
 
     [BurstCompile]
 
     struct LayerJob : IJobParallelFor
     {
         [ReadOnly]
         public NativeArray<Vector2> dimensions;
         [ReadOnly]
         public NativeArray<Vector2> modifiers;
         [ReadOnly]
         internal NativeArray<Vector2> translation;
 
         public NativeArray<float3> heights;
 
         public void Execute(int index)
         {
             var detailWidth = dimensions[0].x;
             var detailHeight = dimensions[0].y;
             var scale = modifiers[0].x;
             var heightMultiplier = modifiers[0].y;
             var translationX = translation[0].x;
             var translationY = translation[0].y;
 
             var x = (int)Mathf.Floor(index % detailWidth);
             var y = (int)Mathf.Floor(index / detailWidth);
 
             //var perlin = new Perlin3D();
             //heights[index] = new float3(x, y, perlin.GetValue(0, new float3(x * scale + translationX, y * scale + translationY, 0)) * heightMultiplier);
             heights[index] = new float3(x, y, noise.srnoise(new float2(x * scale + translationX, y * scale)) * heightMultiplier);
             //heights[index] = new float3(x, y, Mathf.PerlinNoise(x * scale + translationX, y * scale + translationY) * heightMultiplier);
         }
     }
 
 }
 




Comment
Add comment
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users

1 Reply

· Add your reply
  • Sort: 
avatar image
0

Answer by andrew-lukasik · Mar 10 at 06:10 PM


alt text

Ignore absolute time (old i3 cpu) but note that terrainData.SetHeights becomes most of the cpu cost here. This is good news because it means our code is efficient. SetHeights is slow^2 because it forces Terrain to recalculate everything at once.


This is how to do that:

 using System.Collections;
 using UnityEngine;
 using UnityEngine.Assertions;
 using Unity.Mathematics;
 using Unity.Jobs;
 using Unity.Collections;
 using Unity.Collections.LowLevel.Unsafe;
 using Unity.Profiling;
 
 using BurstCompile = Unity.Burst.BurstCompileAttribute;
 
 public class TerrainGenerator : MonoBehaviour
 {
     [SerializeField] Terrain _terrain = null;
     [SerializeField] float _speed = 1f;
     [SerializeField] float2 _translation = 0f;
     [SerializeField] float _heightMultiplier = 0.1f;
     [SerializeField] float _noiseScale = 5f;
     NativeArray<float> _heightsNative;
     float[,] _heights;
     ProfilerMarker ___tick = new ProfilerMarker("tick");
     ProfilerMarker ___set_heights = new ProfilerMarker("set_heights");
     ProfilerMarker ___generate_layer = new ProfilerMarker("generate_layer");
     ProfilerMarker ___native_to_managed = new ProfilerMarker("native_to_managed");
 
     IEnumerator Start ()
     {
         var terrainData = _terrain.terrainData;
         var width = terrainData.detailWidth;
         var height = terrainData.detailHeight;
 
         _heightsNative = new NativeArray<float>( height * width , Allocator.Persistent );
         _heights = new float[ width , height ];
 
         var step = new WaitForSeconds( 0.1f );
         while( true )
         {
             ___tick.Begin();
 
             ___generate_layer.Begin();
             new LayerJob
             {
                 Width                = width ,
                 Height                = height ,
                 NoiseCoordScale        = _noiseScale ,
                 HeightMultiplier    = _heightMultiplier ,
                 Translation            = _translation ,
                 Heightmap            = _heightsNative ,
             }.Schedule( _heightsNative.Length , 32 ).Complete();
 
             ___native_to_managed.Begin();
             MemCpy( _heightsNative , _heights );
             ___native_to_managed.End();
             ___generate_layer.End();
             
             ___set_heights.Begin();
             terrainData.SetHeights( 0 , 0 , _heights );
             ___set_heights.End();
 
             _translation += Time.deltaTime * _speed;
 
             ___tick.End();
             yield return step;
         }
     }
 
     void OnDestroy ()
     {
         if( _heightsNative.IsCreated ) _heightsNative.Dispose();
     }
 
     unsafe void MemCpy <SRC,DST> ( NativeArray<SRC> src , DST[,] dst )
         where SRC : unmanaged
         where DST : struct
     {
         int srcSize = src.Length * UnsafeUtility.SizeOf<SRC>();
         int dstSize = dst.Length * UnsafeUtility.SizeOf<DST>();
         if( srcSize==dstSize )
         {
             void* srcPtr = NativeArrayUnsafeUtility.GetUnsafePtr( src );
             void* dstPtr = UnsafeUtility.PinGCArrayAndGetDataAddress( dst , out ulong dstHandle );
             UnsafeUtility.MemCpy( destination:dstPtr , source:srcPtr , size:srcSize );
             UnsafeUtility.ReleaseGCObject( dstHandle );
         }
         else Debug.LogError( $"<b>src</b> ({srcSize}[b]) and <b>dst</b> ({dstSize}[b]) must be of equal size. MemCpy aborted." );
     }
 
     [BurstCompile]
     struct LayerJob : IJobParallelFor
     {
         public float Width, Height, HeightMultiplier;
         public float2 NoiseCoordScale, Translation;
         [WriteOnly] public NativeArray<float> Heightmap;
         void IJobParallelFor.Execute ( int index )
         {
             float tx = ( index % Width )/Width;
             float ty = ( index / Width )/Height;
             float2 pos = new float2(tx,ty) * NoiseCoordScale + Translation;
             Heightmap[index] = noise.cnoise(pos) * HeightMultiplier;
         }
     }
 
 }



screenshot-2022-03-10-191529.jpg (7.1 kB)
Comment
Add comment · Show 4 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image hangarter · Mar 11 at 07:33 PM 1
Share

Damn! That MemCpy blew my mind! In a way I'm happy as my job was efficient and really I was losing 20ms copying the result with a for loop. What a code! Also I'm really glad you showed me how to use the profiler, I was always debugging times of execution with Debug.Log messages! Would you mind telling me your reasoning about how to approach this code for optimizing it? I can see there's some float magic going on as you multiply float by float2 types and changed also the types used in the job. Thank you so much!

avatar image andrew-lukasik hangarter · Mar 11 at 09:23 PM 0
Share

Thanks. You're welcome!

  • Allocating 3 x NativeArray<Vector2> of length 1 just to store 3 sets of 2xfloat solves nothing and decreases performance by reducing memory locality (reading values close to each other in address space)

  • noise.srnoise is considerably slower than noise.cnoise while producing similar results

  • Unity.Mathematics + Burst introduces SIMD. SIMD is vector multiplication (etc) for the same price as multiplication of two floats.

avatar image hangarter andrew-lukasik · Mar 12 at 09:31 AM 0
Share

Could you point me to a reference where I can study about SIMD? It seems quite powerful and useful

Show more comments

Your answer

Hint: You can notify a user about this post by typing @username

Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

Follow this Question

Answers Answers and Comments

776 People are following this question.

avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image

Related Questions

Lowered general performance with Threads 1 Answer

Getting a stack trace of a specific thread 1 Answer

Help with Optimizing Voxel Code? 1 Answer

Visual Studio Code Debugger Throws Random Exception 0 Answers

Passing a variable to thread function in Javascript 1 Answer


Enterprise
Social Q&A

Social
Subscribe on YouTube social-youtube Follow on LinkedIn social-linkedin Follow on Twitter social-twitter Follow on Facebook social-facebook Follow on Instagram social-instagram

Footer

  • Purchase
    • Products
    • Subscription
    • Asset Store
    • Unity Gear
    • Resellers
  • Education
    • Students
    • Educators
    • Certification
    • Learn
    • Center of Excellence
  • Download
    • Unity
    • Beta Program
  • Unity Labs
    • Labs
    • Publications
  • Resources
    • Learn platform
    • Community
    • Documentation
    • Unity QA
    • FAQ
    • Services Status
    • Connect
  • About Unity
    • About Us
    • Blog
    • Events
    • Careers
    • Contact
    • Press
    • Partners
    • Affiliates
    • Security
Copyright © 2020 Unity Technologies
  • Legal
  • Privacy Policy
  • Cookies
  • Do Not Sell My Personal Information
  • Cookies Settings
"Unity", Unity logos, and other Unity trademarks are trademarks or registered trademarks of Unity Technologies or its affiliates in the U.S. and elsewhere (more info here). Other names or brands are trademarks of their respective owners.
  • Anonymous
  • Sign in
  • Create
  • Ask a question
  • Spaces
  • Default
  • Help Room
  • META
  • Moderators
  • Explore
  • Topics
  • Questions
  • Users
  • Badges