Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

[solved] How to efficiently set RenderMesh to Entities?

Discussion in 'Graphics for ECS' started by Nothke, Mar 4, 2020.

  1. Nothke

    Nothke

    Joined:
    Dec 2, 2012
    Posts:
    112
    I know there's a couple of threads on this issue, I've searched through them, but I just don't seem to find a clear answer.

    I need to spawn 1000s of particles every frame which live for a short time. The particles use RenderMesh for drawing, but the problem is that since RenderMesh is an ISharedComponentData that has references to managed objects (Mesh and Material), you can't set them in a burst compiled job. I can add all other components blazingly fast through a burst compiled ForEach job and concurrent EntityCommandBuffer. But, I need to have a separate system that only adds RenderMeshes, and it's a ComponentSystem that is incredibly slow.

    I can probably think of doing some kind of pooling instead (actually off the top of my head since pooling will require looping through N entities I also fear it could be a bottleneck), or reinventing the Hybrid Renderer, which I don't want to do since Hybrid Renderer is already there...

    So, is there some way to efficiently set RenderMeshes I'm not aware of, or perhaps an unsafe way?
     
  2. thebanjomatic

    thebanjomatic

    Joined:
    Nov 13, 2016
    Posts:
    36
    You could probably use the batch api's to set the data on all entities matching an EntityQuery at once. Not sure if I'm doing this right (I'm using other peoples questions as a way of exploring/learning ECS myself as I'm not currently working on a project with it myself.

    For example:

    Code (CSharp):
    1. public class BatchSpawn : SystemBase {
    2.     private struct SpawnComponent : IComponentData {
    3.     }
    4.  
    5.     EntityArchetype spawnedArchetype;
    6.     EntityQuery initializeQuery;
    7.  
    8.     // Start is called before the first frame update
    9.     protected override void OnCreate() {
    10.         spawnedArchetype = EntityManager.CreateArchetype(typeof(RenderMesh), typeof(SpawnComponent));
    11.         initializeQuery = EntityManager.CreateEntityQuery(typeof(RenderMesh), typeof(SpawnComponent));
    12.     }
    13.  
    14.     protected override void OnUpdate() {
    15.         if (!Input.GetKeyDown(KeyCode.Space)) {
    16.             return;
    17.         }
    18.  
    19.         var entities = new NativeArray<Entity>(1000, Allocator.TempJob);
    20.         EntityManager.CreateEntity(spawnedArchetype, entities);
    21.  
    22.         EntityManager.SetSharedComponentData(initializeQuery, new RenderMesh() {
    23.             mesh = ...
    24.             material = ...
    25.         });
    26.         EntityManager.RemoveComponent<SpawnComponent>(initializeQuery);
    27.  
    28.         entities.Dispose();
    29.     }
    30. }
     
    deus0 and Nothke like this.
  3. RoughSpaghetti3211

    RoughSpaghetti3211

    Joined:
    Aug 11, 2015
    Posts:
    1,694
  4. Nothke

    Nothke

    Joined:
    Dec 2, 2012
    Posts:
    112
    Thanks! I actually totally overlooked the batch variants of SetSharedComponentData(). I actually solved my problem like this:

    Code (CSharp):
    1. class ParticleBatchSetRenderMeshSystem : ComponentSystem
    2. {
    3.     EntityQuery query;
    4.  
    5.     protected override void OnCreate()
    6.     {
    7.         query = GetEntityQuery(
    8.             typeof(Particle),
    9.             ComponentType.Exclude<RenderMesh>());
    10.     }
    11.  
    12.     protected override void OnUpdate()
    13.     {
    14.         EntityManager.SetSharedComponentData(query, ParticleSpawner.e.renderMesh);
    15.     }
    16. }
    ...and that was it! Takes like 0.15ms to set RenderMesh to ~6000 entities :)
     
  5. liiir1985

    liiir1985

    Joined:
    Jul 30, 2014
    Posts:
    147
    My solution was to create an prefab entity, and use instantiate method to spawn the actual entities. So no need for extra SetSharedComponentData(which is also how unity did it in various demos)