Creating meshes with native containers and burst

So what I am trying to achieve is voxel greedy mesh generation in C# Job System with Burst on.
Mesh itself contains bunch of submeshes (for different materials).

So I am passing NativeList for vertices, indices, uvs, etc. It works just fine for IJob, but… it’s too slow.
And I can’t use IJobParallelFor because it is forbidden to add to list in parallel. My idea was to create NativeList<NativeList> for every submesh but it’s also impossible.

What I am doing wrong? Maybe you have any ideas how I can generate my meshes suuuuper fast correctly?

You c a n not nest containers ith jobs, for safety resins.
You could execute each mesh however, on own thread.
So you could run either parallel for, or foreach per mesh.

Here is how I am doing this kind of thing - first step, generate the vertex, index, normals and uv in parallel, and store them in Buffer components

Entities
        .WithAll<DisplaySector>()
        .WithNone<SectorMeshVert>()
        .WithNone<SectorMeshGenerated_Tag>()
        .WithReadOnly(bufferFromEntity)
        .WithName("GenerateGeometry")
        .ForEach(
            (Entity entity, int entityInQueryIndex, in DisplayEntity de) => {
                var groundData = bufferFromEntity[de.source];

                var vertices = buff.AddBuffer<SectorMeshVert>(entityInQueryIndex, entity)
                    .Reinterpret<float3>();
                var indices = buff.AddBuffer<SectorMeshIndex>(entityInQueryIndex, entity)
                    .Reinterpret<int>();
                var normals = buff.AddBuffer<SectorMeshNormal>(entityInQueryIndex, entity)
                    .Reinterpret<float3>();
                var uvs = buff.AddBuffer<SectorMeshUV>(entityInQueryIndex, entity)
                    .Reinterpret<float2>();

                CreateMesh(vertices, indices, normals, uvs, groundData);
            }
        )
        .ScheduleParallel();

After that we make the collider (which can be done in a job)

And then we can make the mesh (which needs Burst off on on main thread)

Entities
                .WithAll<DisplaySector, PhysicsCollider>()
                .WithNone<SectorMeshGenerated_Tag>()
                .WithName("CreateAndAttachMesh")
                .WithoutBurst()
                .ForEach(
                    (Entity entity,
                        ref DynamicBuffer<SectorMeshVert> verts,
                        ref DynamicBuffer<SectorMeshIndex> indices,
                        ref DynamicBuffer<SectorMeshNormal> normals,
                        ref DynamicBuffer<SectorMeshUV> uvs,
                        in RenderMesh rm
                    ) => {
                        var mesh = BuildMesh(verts, indices, normals, uvs);

                        var rm2 = rm;

                        rm2.mesh = mesh;
                        buff.SetSharedComponent(entity, rm2);

                        verts.Clear();
                        indices.Clear();
                        normals.Clear();
                        uvs.Clear();

                        buff.AddComponent<SectorMeshGenerated_Tag>(entity);
                    }
                )
                .Run();

Buildmesh is just a simple mesh creation

private static Mesh BuildMesh(DynamicBuffer<SectorMeshVert> verts, DynamicBuffer<SectorMeshIndex> indices,
            DynamicBuffer<SectorMeshNormal> normals, DynamicBuffer<SectorMeshUV> uvs)
        {
            var mesh = new Mesh();
            mesh.SetVertices(
                verts.Reinterpret<float3>().AsNativeArray()
            );
            mesh.SetIndices(
                indices.Reinterpret<int>().AsNativeArray(),
                MeshTopology.Triangles,
              0
            );
            mesh.SetNormals(
                normals.Reinterpret<float3>().AsNativeArray()
            );
            mesh.SetUVs(
                0,
                uvs.Reinterpret<float2>().AsNativeArray()
            );

            mesh.RecalculateBounds();
            return mesh;
        }

Keep as much as you can off the main thread.

You can actually use Mesh.MeshData and the new mesh API to simplify things a bit. So you can do something along the following lines:

var meshDataArray = Mesh.AllocateWritableMeshData(_targetEntities.CalculateEntityCount());

// Update the meshData in a job here

Mesh.ApplyAndDisposeWritableMeshData(meshDataArray, meshes);