[solved] How to efficiently set RenderMesh to Entities?

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?

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:

public class BatchSpawn : SystemBase {
    private struct SpawnComponent : IComponentData {
    }

    EntityArchetype spawnedArchetype;
    EntityQuery initializeQuery;

    // Start is called before the first frame update
    protected override void OnCreate() {
        spawnedArchetype = EntityManager.CreateArchetype(typeof(RenderMesh), typeof(SpawnComponent));
        initializeQuery = EntityManager.CreateEntityQuery(typeof(RenderMesh), typeof(SpawnComponent));
    }

    protected override void OnUpdate() {
        if (!Input.GetKeyDown(KeyCode.Space)) {
            return;
        }

        var entities = new NativeArray<Entity>(1000, Allocator.TempJob);
        EntityManager.CreateEntity(spawnedArchetype, entities);

        EntityManager.SetSharedComponentData(initializeQuery, new RenderMesh() {
            mesh = ...
            material = ...
        });
        EntityManager.RemoveComponent<SpawnComponent>(initializeQuery);

        entities.Dispose();
    }
}
2 Likes

May or may not be helpful in your case.

Thanks! I actually totally overlooked the batch variants of SetSharedComponentData(). I actually solved my problem like this:

class ParticleBatchSetRenderMeshSystem : ComponentSystem
{
    EntityQuery query;

    protected override void OnCreate()
    {
        query = GetEntityQuery(
            typeof(Particle),
            ComponentType.Exclude<RenderMesh>());
    }

    protected override void OnUpdate()
    {
        EntityManager.SetSharedComponentData(query, ParticleSpawner.e.renderMesh);
    }
}

…and that was it! Takes like 0.15ms to set RenderMesh to ~6000 entities :slight_smile:

3 Likes

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)