Search Unity

ECS RenderMeshSystemV2 performance seems to be much slower in Unity 2019.3

Discussion in 'Graphics for ECS' started by rchenmirada, Jan 31, 2020.

  1. rchenmirada

    rchenmirada

    Joined:
    Apr 26, 2016
    Posts:
    1
    While running a simple ECS + Jobs performance test with 90,000 entities, I noticed my frame rate dropped from 33 fps to 22 fps after updating from Unity 2019.2.12f1 to Unity 2019.3.0f5, without any other changes.

    Looking inside Entity Debugger, it looks like RenderMeshSystemV2 now takes around 24.5 ms, instead of 8.5 ms (screenshots attached).
    Unity_2019_2_12f1.PNG Unity_2019_3_0f5.PNG

    The ECS packages I'm using are: Entities 0.1.1, Hybrid Renderer 0.1.1, Burst 1.2.1

    I tried a few things (one at a time) to fix this including:
    - updating to the latest ECS packages (Entities 0.5.1, Hybrid Renderer 0.3.3, Burst 1.2.1)
    - updating the systems job scheduling code to use the new Entities.ForEach statement
    - also tried using Entities.WithoutBurst().ForEach (as mentioned in https://forum.unity.com/threads/my-hybrid-ecs-performance-test.792585/)
    - turning off safety checks and leak detection

    Yet I'm still unable to achieve the same performance as I had seen previously before upgrading to 2019.3. RenderMeshSystemV2 is still around 3 times as slow.

    Is there something else I should be looking into?

    Unitypackage with my test scene and scripts attached, but also pasted here for reference:

    Monobehavior spawns the entities and places them in a grid formation:
    Code (CSharp):
    1. public class ECSReactivePointSpawner : MonoBehaviour
    2. {
    3.     public GameObject spawn;
    4.     public int rows;
    5.     public int cols;
    6.     public float step;
    7.  
    8.     // Start is called before the first frame update
    9.     void Start()
    10.     {
    11.  
    12.         // For use with latest ECS PACKAGES
    13.         //EntityManager entityManager = World.DefaultGameObjectInjectionWorld.EntityManager;
    14.         //GameObjectConversionSettings settings = new GameObjectConversionSettings(World.DefaultGameObjectInjectionWorld, GameObjectConversionUtility.ConversionFlags.AssignName);
    15.  
    16.         // For older ECS Packages
    17.         EntityManager entityManager = World.Active.EntityManager;
    18.         GameObjectConversionSettings settings = new GameObjectConversionSettings(World.Active, GameObjectConversionUtility.ConversionFlags.AssignName);
    19.  
    20.         Entity prefab = GameObjectConversionUtility.ConvertGameObjectHierarchy(spawn, settings);
    21.  
    22.         NativeArray<Entity> entities = new NativeArray<Entity>((int)(rows * cols), Allocator.Temp);
    23.         entityManager.Instantiate(prefab, entities);
    24.  
    25.         int index = 0;
    26.         for (int i = -(int)(rows/2f); i < (int)Mathf.Ceil(rows/2f); i++)
    27.         {
    28.             for (int j=-(int)(cols/2f); j< (int) Mathf.Ceil(cols/2f); j++)
    29.             {
    30.                 float3 pos = new float3(j * step, i * step, 0f);
    31.                 float mass = 1f;
    32.                 entityManager.SetComponentData<Translation>(entities[index], new Translation{Value = pos });
    33.  
    34.  
    35.                 // For Newer ECS Packages
    36.                 //entityManager.AddComponentData<NonUniformScale>(entities[index], new NonUniformScale{Value = new float3(0.1f,0.1f,0.1f) * mass });
    37.  
    38.                 // For older ECS Packages
    39.                 entityManager.SetComponentData<NonUniformScale>(entities[index], new NonUniformScale{Value = new float3(0.1f,0.1f,0.1f) * mass });
    40.                  
    41.                 index ++;
    42.             }
    43.         }
    44.         entities.Dispose();
    45.         return;
    46.     }
    47.  
    48.     // Update is called once per frame
    49.     void Update()
    50.     {
    51.      
    52.     }
    53. }
    The JobComponentSystem class that updates Translation components (moves them in a sinusoidal wave pattern):
    Code (CSharp):
    1. public class ECSReactivePointSystem : JobComponentSystem
    2. {
    3.     //protected override JobHandle OnUpdate(JobHandle inputDeps)
    4.     //{
    5.     //    float amplitude = 1f;
    6.     //    float freq = 1f;
    7.     //    float speed = 5f;
    8.     //    float time = UnityEngine.Time.time;
    9.  
    10.  
    11.  
    12.     //    return Entities.ForEach((ref Translation translation) =>
    13.     //    {
    14.     //        float3 pos = translation.Value;
    15.  
    16.  
    17.     //        pos.z = amplitude * math.sin((pos.x + speed * time) * freq);
    18.     //        translation.Value = pos;
    19.  
    20.     //    }).Schedule(inputDeps);
    21.  
    22.     //}
    23.  
    24.     struct ECSReactionJob : IJobForEach<Translation>
    25.     {
    26.         public float amplitude;
    27.         public float freq;
    28.         public float speed;
    29.         public float time;
    30.  
    31.         public void Execute(ref Translation translation)
    32.         {
    33.             float3 pos = translation.Value;
    34.  
    35.  
    36.             pos.z = amplitude * math.sin((pos.x + speed * time) * freq);
    37.  
    38.             translation.Value = pos;
    39.         }
    40.     }
    41.  
    42.  
    43.     protected override JobHandle OnUpdate(JobHandle inputDeps)
    44.     {
    45.  
    46.  
    47.         ECSReactionJob reactionJob = new ECSReactionJob()
    48.         {
    49.             amplitude = 1f,
    50.             freq = 1f,
    51.             speed = 5f,
    52.             time = UnityEngine.Time.time,
    53.         };
    54.  
    55.         JobHandle reactionHandle = reactionJob.Schedule(this, inputDeps);
    56.  
    57.         return reactionHandle;
    58.     }
    59. }
    The Prefab input for the spawner is just a basic cube prefab, with a few modifications: GPU Instancing turned on, box collider removed, and lighting/shadow/probe rendering options turned off.
     

    Attached Files:

    Last edited: Jan 31, 2020
  2. Radu392

    Radu392

    Joined:
    Jan 6, 2016
    Posts:
    210
    Another rendering solution. No, really. Stay away from the Hybrid Renderer, it's been the scourge of ECS and it looks like it will only get worse before (maybe?) getting better.
     
    rchenmirada likes this.
  3. nicolasgramlich

    nicolasgramlich

    Joined:
    Sep 21, 2017
    Posts:
    231
    Do you mean: Use ECS to do the heavy computation under the hood, sync back transforms to gameobjects and render using standard URP?
     
    rchenmirada likes this.
  4. Radu392

    Radu392

    Joined:
    Jan 6, 2016
    Posts:
    210
    Yes, or any version of the Graphics.Draw methods. But now I’m not so sure anymore. They announced that a ‘massive’ performance boost to the hybrid renderer is coming in 2020.1. We’ll see just how massive it is and if it will justify building your own rendering solutions or not. If youcan wait until then, my advice is to do so.

    However, having your own rendering systems gives you control over not just rendering but animation as well, among other things, something that the dots stack doesn’t have builtin yet.