Changing FDynamicMesh3 allocation size? (C++)

I’m currently trying to integrate the ModelingTools with the plugin I am working on. I’m storing procedurally generated mesh data as FDynamicMesh3 and running into performance issues that I think could be solved by allowing custom allocation block sizes.

Currently, FDynamicMesh3 uses TDynamicVector s to store its data. This creates arrays in large blocks to avoid constant memory reallocation in interactive editing. Unfortunately, the size of these blocks is set at compile time to be 2048. This is fine for larger meshes that are converted to FDynamicMesh3 infrequently, but in my editor plugin, I may convert 1000+ small meshes at once. This is a relatively massive drain on performance and memory.

My current workaround has been to create a custom mesh type and convert to FDynamicMesh3 when needed, but if I need to work with any of the modelling tools, I’m still going to get hit with the cost of the data allocation.

The ideal solution would be to be able to set the allocation size for any given FDynamicMesh3. Are there plans to add this functionality at any point? Any other solution ideas are welcome too.

Changing the block size to a variable, ie non-compile-time constant, would significantly increase the cost of the TDynamicVector operator[]. Using a (power-of-two) compile-time constant means the modulo and divide operations in the operator[] implementation become constant bit-shift instructions, instead of an expensive memory lookup (for the variable) and then full modulo and divides. And in some cases that more expensive code will reduce the likelihood that the operator[] inlines, which is significant. Of course it’s small in context, but it becomes a drag on virtually everything in the GeometryProcessing library. So, we don’t intend to make this runtime-configurable in TDynamicVector.

In UE5.0 we have reduced the standard block size to 512, which might help somewhat, but at some point making the blocks smaller starts to seriously affect performance on large meshes.

FDynamicMesh3 is not really a memory-efficient way to store a mesh - beyond this block size question, it also stores a lot of topological information (ie ref counts, edge lists, etc) that are redundant and/or can be recomputed from more compact representations. So I would say your strategy of storing the meshes as a separate type is actually recommended if you are memory-constrained. This is similar to what we do in the UE Editor, eg StaticMesh assets are stored via FMeshDescription which is more compact and memory-efficient.

Thanks for the quick reply! I love what you guys are doing with these modeling tool btw.

I’m not really concerned with the memory footprint but with the time it takes to allocate large numbers of small FDynamicMesh3’s. I totally understand why the block size is fine tuned for larger meshes in terms of the Modeling Tools, but its really killing my performance when creating meshes ~30 verts in my plugin. I have yet to experiment with larger sizes but I will rarely if ever be allocating more than 1000 verts. Using the separate mesh type is helpful but to use tools like FMeshNormals on each mesh, I still incur cost when converting back and forth.

I’m wondering if the nShiftBits couldn’t just be a template argument to TDynamicVector? That way it stays a compile time constant and is configurable. I’m not sure what affect that would have on inlines and I totally understand if this is simply not the intended use.

well, TDynamicVector is buried inside quite a few places, so for you to be able to specify that value at the level of your code, FDynamicMesh3 and several other classes would also have to be templated on that value. And then any function that takes a FDynamicMesh3 would have to also become a template function, and be in a header. This kind of strategy was taken in the OpenVDB library and it is extremely bad for compile times (and makes the code extremely hard to read…), and putting all the code in headers means we can’t fix bugs in hotfixes. And ultimately UObjects can’t be templated, so any UThing that contains a FDynamicMesh3 would only work w/ a mesh using the fixed/default size. So, it would be quite a big mess :slight_smile:

One strategy I have used in the past for temporary mesh processing of many small meshes, is to not create a new FDynamicMesh3 every time, and instead reuse a single instance. If you call Reset(), it will empty the mesh but not free the internal buffers, so they will not be reallocated.

Ultimately though I would say that “large number of small FDynamicMesh3” is basically not a use case we support right now. I will think about it but there is not an easy solution.

Yeah I figured that might be the case, I was just hoping it wouldn’t be tooo sloppy :yum:. I hadn’t thought about the UObject implications either. After some testing last night the savings wouldn’t be enormous anyways since the number of allocations is a bigger concern than the sizes.

I’ll definitely look into reusing the FDyamicMesh3 though, that seems like it could be very useful. Thanks again for the help!