I've been working on improving DynTopo performance and fixing some editmode multires reprojection issues. I've now merged both sets of changes into temp_multires_bmesh. I initially wrote a special-purpose triangle mesh library to replace bmesh in DynTopo but ultimately decided to abandon it. In many ways DynTopo already is a special-purpose triangle mesh library that happens to reuse the BMesh data structures, and while I think a special-purpose library would be a good idea in terms of code maintainability it would also preclude us from ever adding support for quads and/or ngons to DynTopo.
Speedups When Topologizer Isn't Running
I discovered that even without the topologizer DynTopo was quite slow. I sped it up in several ways, listed below.
Increased pbvh->leaf_limit from 100 to 3000
This sped things up quite a bit.
Wrote a wrapper around GSet that stores elements in pointer arrays for faster iteration.
Profiling revealed that iterating over GSets (specifically PBVHNode->bm_unique_verts and bm_other_verts) was a significant performance loss. I initially solved the problem by inlining all of GHash, but I figured that wasn't appropriate and might have unexpected consequences elsewhere. My solution was to write a wrapper that maintains a flat pointer array for fast iteration (which is done via macro inlining instead of function calls). Note that this wrapper is still not indexable, when elements are removed from the set their entries in the flat pointer array is simply set to NULL. Note however that the array is compacted when it is resized.
Refactored sculpt vertex "indices" into vertex references (SculptVertRef) and indices.
Profiling revealed a large amount of time was being spent maining BMesh element arrays. By creating an abstract SculptVertRef type and storing a pointer to a BMVert inside of it (instead of an index) I was able to drastically cut down the calls to BM_elem_table_ensure. SculptVertRef and indices can be converted to/from each other:
SculptVertRef *vertex = BKE_pbvh_table_index_to_vertex(pbvh, index); int index = BKE_pbvh_vertex_index_to_table(pbvh, vertex);
PBVHVertexIter and SculptNeighborIterator now have .vertex members that refer to the currently active SculptVertRef, as well as .index.
For meshes and grid PBVHs these store the same values, but BMesh puts a pointer to a BMVert in the former.
Original Data Now Stored In Customdata Layers
Original vertex coordinates, normals and colors for bmesh PBVH are now cached as customdata layers to avoid fetching them from BMLog (which requires a GHash lookup).
== Topologizer Speedup ==
I made the topologizer faster by time limiting it. The core topologizer loops now execute for a fixed amount of time, and in addition there is a limit to how often the topologizer may be called per second. This helped quite a bit and seems to be in line with how other software behaves.
DynTopo now performs customdata interpolation. Right now it interpolates face and vertex data, and also propagates edge flags. It attempts to preserve edge seams, though it's not quite perfect yet. The hard part is the BMLog undo system. I've refactored it to save vertex customdata blocks. The hard part is not letting performance suffer from saving blocks too often and also keeping the customdata layouts of BMLog and the mesh in sync.
Here is a demo video I made:
I added support for sculpt colors to DynTopo. To do this I had to fix problems in the PBVH drawing code. PBVH can now draw vertex colors (both MLoopCol and MPropCol layers) and properly handles UVs. To make this work I added a new function to the draw engine API:
It adds the various aliases EEVEE needs for vertex attributes. I patterned it after extract_uvs in draw_cache_extract_mesh.c; I would appreciate
it if someone from the draw engine team took a look at it. Here's a link to the code. Note that it doesn't properly display sculpt colors in Solid mode in vertex colors mode. I'm not quite sure what the vertex attribute name for that is.
One thing I found is the PBVH drawing code is much *much* faster for sculpt colors. It's a pretty huge difference; as such, I think we should use PBVH drawing for mesh and multires as well as dyntopo.
I re-implemented functions to convert edit-mode multires grids into and out of global space. Thus, the BMOpFlag BMOP_UNTAN_MULTIRES flag is now respected. This hugely improved multires reprojection when changing topology in edit mode. And unlike when I coded this ten years I now properly ensure that this all happens in the highest subdivision level.
By the way I discovered that interpolating MDisps grids in imperfect tangent spaces is numerically unstable, though I'm not entirely sure why this is the case. I've not eliminated all cases of this happening, that is on the todo list. I also need to code something that detects when individual grid points explode and smooth them out. This can also happen when moving between subdivision levels outside of editmode, so we need something general here.