Search Unity

Question Creating meshes with native containers and burst

Discussion in 'Burst' started by UniOwl, Nov 10, 2020.

  1. UniOwl

    UniOwl

    Joined:
    May 31, 2018
    Posts:
    24
    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<T>> 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?
     
  2. Antypodish

    Antypodish

    Joined:
    Apr 29, 2014
    Posts:
    10,770
    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.
     
  3. nobeerleft

    nobeerleft

    Joined:
    Mar 29, 2012
    Posts:
    27
    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

    Code (CSharp):
    1. Entities
    2.         .WithAll<DisplaySector>()
    3.         .WithNone<SectorMeshVert>()
    4.         .WithNone<SectorMeshGenerated_Tag>()
    5.         .WithReadOnly(bufferFromEntity)
    6.         .WithName("GenerateGeometry")
    7.         .ForEach(
    8.             (Entity entity, int entityInQueryIndex, in DisplayEntity de) => {
    9.                 var groundData = bufferFromEntity[de.source];
    10.  
    11.                 var vertices = buff.AddBuffer<SectorMeshVert>(entityInQueryIndex, entity)
    12.                     .Reinterpret<float3>();
    13.                 var indices = buff.AddBuffer<SectorMeshIndex>(entityInQueryIndex, entity)
    14.                     .Reinterpret<int>();
    15.                 var normals = buff.AddBuffer<SectorMeshNormal>(entityInQueryIndex, entity)
    16.                     .Reinterpret<float3>();
    17.                 var uvs = buff.AddBuffer<SectorMeshUV>(entityInQueryIndex, entity)
    18.                     .Reinterpret<float2>();
    19.  
    20.                 CreateMesh(vertices, indices, normals, uvs, groundData);
    21.             }
    22.         )
    23.         .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)

    Code (CSharp):
    1. Entities
    2.                 .WithAll<DisplaySector, PhysicsCollider>()
    3.                 .WithNone<SectorMeshGenerated_Tag>()
    4.                 .WithName("CreateAndAttachMesh")
    5.                 .WithoutBurst()
    6.                 .ForEach(
    7.                     (Entity entity,
    8.                         ref DynamicBuffer<SectorMeshVert> verts,
    9.                         ref DynamicBuffer<SectorMeshIndex> indices,
    10.                         ref DynamicBuffer<SectorMeshNormal> normals,
    11.                         ref DynamicBuffer<SectorMeshUV> uvs,
    12.                         in RenderMesh rm
    13.                     ) => {
    14.                         var mesh = BuildMesh(verts, indices, normals, uvs);
    15.  
    16.                         var rm2 = rm;
    17.  
    18.                         rm2.mesh = mesh;
    19.                         buff.SetSharedComponent(entity, rm2);
    20.  
    21.                         verts.Clear();
    22.                         indices.Clear();
    23.                         normals.Clear();
    24.                         uvs.Clear();
    25.  
    26.                         buff.AddComponent<SectorMeshGenerated_Tag>(entity);
    27.                     }
    28.                 )
    29.                 .Run();
    Buildmesh is just a simple mesh creation
    Code (CSharp):
    1. private static Mesh BuildMesh(DynamicBuffer<SectorMeshVert> verts, DynamicBuffer<SectorMeshIndex> indices,
    2.             DynamicBuffer<SectorMeshNormal> normals, DynamicBuffer<SectorMeshUV> uvs)
    3.         {
    4.             var mesh = new Mesh();
    5.             mesh.SetVertices(
    6.                 verts.Reinterpret<float3>().AsNativeArray()
    7.             );
    8.             mesh.SetIndices(
    9.                 indices.Reinterpret<int>().AsNativeArray(),
    10.                 MeshTopology.Triangles,
    11.               0
    12.             );
    13.             mesh.SetNormals(
    14.                 normals.Reinterpret<float3>().AsNativeArray()
    15.             );
    16.             mesh.SetUVs(
    17.                 0,
    18.                 uvs.Reinterpret<float2>().AsNativeArray()
    19.             );
    20.  
    21.             mesh.RecalculateBounds();
    22.             return mesh;
    23.         }
    Keep as much as you can off the main thread.
     
  4. SteveSync

    SteveSync

    Joined:
    Sep 25, 2013
    Posts:
    27
    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:

    Code (CSharp):
    1. var meshDataArray = Mesh.AllocateWritableMeshData(_targetEntities.CalculateEntityCount());
    2.  
    3. // Update the meshData in a job here
    4.  
    5. Mesh.ApplyAndDisposeWritableMeshData(meshDataArray, meshes);