Hi!
We are looking into dynamically modifying mesh geometry at runtime, in particular cases where vertices and indices may be added or removed frequently.
MeshAccessor looks useful for editing existing mesh data, especially since it abstracts away the actual vertex buffer layout and formats. However, as far as we understand, its buffers have a fixed size once allocated with AllocateBuffer(). The underlying BytesContainer/DataContainer does not appear to be designed for dynamic resizing either.
This raises a question about the recommended approach when the topology itself changes frequently.
For example, suppose we have a mesh containing N vertices and some indices, and over time we need to add or remove vertices:
1000 vertices
↓
1050 vertices
↓
1100 vertices
↓
1030 vertices
↓
...
Reallocating the buffers to exactly match the new vertex/index count every time seems potentially wasteful, especially if those changes happen frequently.
While looking at the implementation, we also noticed that MeshAccessor::UpdateMesh() calls MeshBase::Init(), which in turn creates the vertex/index GPUBuffer objects through GPUDevice::CreateBuffer().
So, unless we are missing another code path, it looks like updating a mesh through MeshAccessor::UpdateMesh() creates new GPU buffer objects rather than updating the existing ones in place.
This surprised us a little, particularly because the documentation recommends MeshAccessor when “performing real-time mesh editing”. We assume there may be implementation details or another intended update path that we are overlooking.
We have considered a few approaches:
-
Allocate some spare capacity
We could use
MeshAccessor::AllocateBuffer()to allocate buffers larger than currently required, similarly to the capacity of a dynamic array.Conceptually:
Vertex capacity: 2048 Vertices in use: 1350We could then add/remove vertices without reallocating until the capacity is exceeded.
However, we haven’t found a way to tell a
Meshthat only the first N vertices/indices of those buffers are currently in use. Also, sinceMeshAccessor::UpdateMesh()goes throughMeshBase::Init(), we are not sure that over-allocating the accessor buffers would help with reusing the GPU buffers anyway. -
Split the geometry into multiple meshes
Another possibility would be to use several smaller meshes, so that a local topology change only requires rebuilding one of them instead of reallocating one large mesh.
Depending on the geometry, hardware geometry instancing might also be useful when some of those meshes share the same geometry, although that obviously does not apply to arbitrary topology changes.
So our questions are:
- What is the recommended Flax approach for a runtime mesh whose vertex/index count changes frequently?
- Is
MeshAccessor::UpdateMesh()expected to recreate the GPU buffer objects on each update, including for real-time mesh editing, or is there another intended path for updating their contents in place? - Is there a supported way to allocate a
Mesh/GPU buffer with some spare capacity while specifying a smaller number of vertices/indices to actually use for rendering? - If such spare capacity can be allocated, can the existing vertex/index GPU buffers then be updated and reused until that capacity is exceeded?
- Or is splitting dynamic geometry into multiple smaller meshes generally the preferred approach in this situation?
We are mainly trying to understand the intended Flax architecture before building our own solution around it.
Thanks!