Search Unity

MeshDataArray and ScheduleParallel

Discussion in 'Entity Component System' started by andywatts, Apr 5, 2020.

  1. andywatts

    andywatts

    Joined:
    Sep 19, 2015
    Posts:
    112
    Can you write to a MeshDataArray in a ForEach+ScheduleParallel?

    Ive many entities with custom meshes.
    I use a ComponentSystem that computes a dynamicBuffer<CustomFaceStruct> for matching entities....and a second ComponentSystem that converts these to Mesh+RenderMesh SCDs.

    I want to refactor this to write MeshData in parallel, before applying RenderMesh SCD on main thread.

    @Aras combineMesh sample shows passing a _single_ writable MeshData object in an ijobparallelfor. It extracts nativearray from the MeshData and writes to them in parallel. Good stuff, but I want to write to multiple MeshData.
     
    Last edited: Apr 5, 2020
  2. Fribur

    Fribur

    Joined:
    Jan 5, 2019
    Posts:
    136
    Using IJobParallelFor: First allocate an MeshDataArray, and set the Vertex and Index parameters of the individual MeshData according to the size of the final meshes (I wish we did not have to know this beforehand, would make jobified Mesh generation with unknown outcome much easier):
    Code (CSharp):
    1. Mesh.MeshDataArray _meshDataArray = Mesh.AllocateWritableMeshData(NumberOfEntities);
    2. for (int i = 0; i<NumberOfEntities; i++)
    3. {
    4.     _meshDataArray[i].SetIndexBufferParams(indexCount[i], IndexFormat.UInt32);
    5.     _meshDataArray[i].SetVertexBufferParams(vertexCount[i],
    6.         new VertexAttributeDescriptor(VertexAttribute.Position),
    7.         new VertexAttributeDescriptor(VertexAttribute.Normal, stream: 1));
    8. }
    Then I pass the whole MeshDataArray into an IJobParallelFor; accessing the individual MeshData by job index:
    Code (CSharp):
    1. var outputVerts = _jobMeshDataArray[index].GetVertexData<Vector3>();
    2. var outputNormals = _jobMeshDataArray[index].GetVertexData<Vector3>(stream: 1);
    3. var tris = _jobMeshDataArray[index].GetIndexData<int>();
    4. for (var i = 0; i<vertices.Length; ++i)
    5. {
    6.     var pos = new float3(vertices[i].x, depth, vertices[i].y);
    7.     outputVerts[i] = pos;
    8.     outputNormals[i] = new float3(0, 1, 0);  
    9. }
    10. tris.CopyFrom(indices);
    And finally I create a receiving MeshArray, set the individual submeshes, and apply the MeshDataArray to the MeshArray. That last step is happening BTW on the main thread, and takes the longest. And makes me actually wonder if the whole procedure is worth it for me.
    Code (CSharp):
    1. Mesh[] _meshArray = new Mesh[NumberOfEntities];
    2. for (int i = 0; i<NumberOfEntities; i++)
    3. {
    4.     _meshArray[i] = new Mesh();
    5.     var sm = new SubMeshDescriptor(0, indexCount[i], MeshTopology.Triangles);
    6.     sm.firstVertex = 0;
    7.     sm.vertexCount = vertexCount[i];
    8.     //sm.bounds = new Bounds(bounds[i].c0, bounds[i].c1);
    9.     _meshArray[i].bounds= new Bounds(bounds[i].c0, bounds[i].c1);
    10.     var currentMeshData = _meshDataArray[i];
    11.     currentMeshData.subMeshCount = 1;
    12.     currentMeshData.SetSubMesh(0, sm, MeshUpdateFlags.DontRecalculateBounds | MeshUpdateFlags.DontValidateIndices | MeshUpdateFlags.DontNotifyMeshUsers);
    13. }
    14. Mesh.ApplyAndDisposeWritableMeshData(_meshDataArray, _meshArray, MeshUpdateFlags.DontRecalculateBounds | MeshUpdateFlags.DontValidateIndices | MeshUpdateFlags.DontNotifyMeshUsers);
     
    Last edited: Apr 5, 2020
    Shinyclef, Nyanpas and andywatts like this.
  3. Nyanpas

    Nyanpas

    Joined:
    Dec 29, 2016
    Posts:
    406
    Cannot one create NativeLists of the verticen, uvs, and triangles, in the job, and then have a WriteOnly MeshData that you set with the index values of the created NativeLists?
     
    Last edited: Dec 24, 2020
  4. Dinamytes

    Dinamytes

    Joined:
    Nov 3, 2016
    Posts:
    51
    Setting vertex data with ushort.MaxValue length and then appending the count after vertexData was assigned is working for me:
    (inside job code)
    Code (CSharp):
    1.  
    2.            meshData.SetIndexBufferParams(ushort.MaxValue, IndexFormat.UInt16);
    3.             meshData.SetVertexBufferParams(ushort.MaxValue, VertexAttributeDescriptors);
    4.  
    5.             var buildingData = new BuildingData()
    6.             {
    7.                 Vertices = meshData.GetVertexData<float3>(0),
    8.                 Normals = meshData.GetVertexData<float3>(1),
    9.                 VertexColors = meshData.GetVertexData<Color32>(2)
    10.             };
    11.  
    12.             // sets array from GetVertexData...
    13.             ComputeMesh(ref buildingData);
    14.  
    15.             var indicesCount= buildingData.IndicesCount; // increment count eg: Vertices[VerticesCount++]
    16.             meshData.SetIndexBufferParams(indexCount, IndexFormat.UInt16);
    17.             meshData.SetVertexBufferParams(buildingData.VerticesCount, VertexAttributeDescriptors);
    18.             meshData.GetIndexData<ushort>().MemCpyFrom(Triangles, indexCount);
    19.  
    20.             meshData.subMeshCount = 1;
    21.             SubMeshDescriptor subMeshDescriptor = new SubMeshDescriptor()
    22.             {
    23.                 indexCount = indexCount,
    24.                 topology = MeshTopology.Triangles,
    25.                 vertexCount = buildingData.VerticesCount
    26.             };
    27.  
    28.             meshData.SetSubMesh(0, subMeshDescriptor, UnityEngine.Rendering.MeshUpdateFlags.DontRecalculateBounds | UnityEngine.Rendering.MeshUpdateFlags.DontResetBoneBounds |
    29.                   UnityEngine.Rendering.MeshUpdateFlags.DontNotifyMeshUsers);
    Edit: I'll check in RenderDoc if the buffer sent to the gpu will be as big as the first length setted.
    Edit2: according to RenderDoc the byte length of the buffers were the same, with initial bigger vertex data length and then resizing and with pre calculated vertex data length
     
    Last edited: Jan 6, 2021
    Nyanpas likes this.
  5. Bovine

    Bovine

    Joined:
    Oct 13, 2010
    Posts:
    195
    I want to be able to generate a relatively small mesh each frame ideally on mobile so I can render a LOS texture I can use as a kind of shadow map. It might be 15K verts at the very most.

    I won't know the number of triangles beforehand but naturally I want to avoid a lot of unnecessary operations - most times it will likely be a few hundred triangles tops, but I'd like to have the room to flex without having to allocate a buffer each time? Can I only copy the verts I need and flex the number consumed without making the buffer smaller, or would I need to always draw 15K verts and perhaps always zero the unused verts.

    Does anyone know how performant this is likely to be?

    Thanks
    Ian H
     
  6. iamarugin

    iamarugin

    Joined:
    Dec 17, 2014
    Posts:
    883
    I don't quite understand why normals go to stream 1?

    Code (CSharp):
    1. new VertexAttributeDescriptor(VertexAttribute.Normal, stream: 1));
    What if I have tangents and colors, in what stream I should put them into?
     
  7. MostHated

    MostHated

    Joined:
    Nov 29, 2015
    Posts:
    1,235
    I have been using:

    Code (CSharp):
    1. var descriptor = new NativeArray<VertexAttributeDescriptor>(
    2.     3, Allocator.Temp, NativeArrayOptions.UninitializedMemory
    3. );
    4.  
    5. descriptor[0] = new VertexAttributeDescriptor(VertexAttribute.Position, dimension: 3, stream: 0);
    6. descriptor[1] = new VertexAttributeDescriptor(VertexAttribute.Normal, dimension: 3, stream: 1);
    7. descriptor[2] = new VertexAttributeDescriptor(VertexAttribute.TexCoord0, dimension: 2, stream: 2);
    I believe Tangents can go in stream 3, based on seeing other examples prior.