Procedural meshes without garbage

My game builds procedural meshes every frame, but it seems like with the current C# API we kind of forced to use a static List<> to build the meshes since using an array requires Array.Resize to the correct length before setting the vertices, UV’s and indices in the mesh. Array.Resize creates garbage and I’d like to avoid expensive memory allocations every frame. List seems ideal, but the problem is that all List<> methods are 4x or more slower than arrays in my IL2CPP profiles. Even just indexing a list (e.g. vertices[cur] ) is surprisingly slow compared to arrays. When you’re pushing tens of thousands of vertices into a mesh every frame, that adds up.

So what I need is a method to pass an array of data to Mesh and include a count of items. So that way you can fill an array to, say 100 vertices, but the array has capacity (Length) for much more. Or perhaps there’s another way I haven’t considered?

Thanks for the advice.

Keep the array the same size, and zero out the vertices that you’re not using. (Or rather, the actual value doesn’t matter, just that it’s the same for all the “unused” vertices, since degenerate triangles aren’t rendered.) It’s still not perfect since the whole array always has to be uploaded, but it seems to be the best compromise for now.

–Eric

True, if the indices don’t refer to the vertices I’m not using then it won’t matter what value they have. At least then I’d only be reallocating indices array whereas the vertices and UVs can remain static. And like you said I can point all the remaining triangles at a single vertex then they too would get ignored.

You don’t have to reallocate any arrays though. Leaving the triangles pointing to “unused” vertices is fine (where the vertices all have the same value such as Vector3.zero); like I said, degenerate triangles aren’t rendered.

–Eric

Thank you, yes and I believe they can just point to any single vertex, regardless of value, as long as it’s the same vertex for all points of the triangle. I’m currently reallocating (shrinking) the indices array when the number of degenerate triangles passes a certain (fairly high) threshold to avoid unnecessary work. This reallocation is rare as you might expect as long as the dynamic mesh doesn’t change too radically over time.

You can also keep a few differently-sized arrays (small, medium, large) and use the appropriate ones, to avoid allocations entirely.

–Eric

I have a similar garbage collection issue with dynamic/procedural 2D edge colliders. I haven’t tried, but I wonder how the physics system would handle degenerate points at the end of the point list?

Sorry to bring up an old thread, but I am having this same problem, did you ever find a solution? It seems impossible to dynamically create an array without producing garbage when working with 2d procedural colliders. The physics system seems to set all points at the end of the list to (0, 0), so it looks like the only solution is to set all the end points to your last used point, but that doesn’t seem like the best solution.

I also have this issue when trying to set the materials on a mesh renderer, I need to dynamically choose which materials to put in the mesh, and I can’t include degenerate materials at the end since they will be applied to the last submesh.

Yes, I just pad the unused points with duplicates of the last point. If it notice more than X number of padded points, then I Array.Resize() the points array. Occasional reallocation is OK - in my case the procedural collider has fairly good temporal stability.

I just keep a static cache of material arrays. It’s an array of material arrays with 1, 2, 3, 4, 5, 6, etc. materials in it. You pick the array you want to pass to the mesh, depending on how many materials/submeshes you need. That way nothing is allocated if you reuse the same mesh and mark it dynamic. My mesh does not change the number of submeshes every frame, so it’s fairly stable, only changing every few seconds.

Alternatively, you can also have submeshes with no indices so they just don’t render when a material isn’t used. This retains the same number of submeshes. Might not be ideal for your application.

Thanks for the help, I’m going to implement the static material arrays

No problem, good luck!