Thanks for the really kind words and being so receptive! I’ll explain my use case and some caveats, and then I’ll go over both potential library improvements and compute shaders.
In the ECS World, memory bandwidth is critical. It is often better to parallelize things by memory region rather than the work itself. What that means is instead of each mesh having a few parallel jobs, there would be a single parallel job where each worker thread does a whole mesh at a time. That may sound like a data management nightmare when just using jobs, but fortunately ECS has tools for this. The input vertices and output normals and tangents would be a DynamicBuffer, while the adjacency caches and source mesh indices, normals, tangents, and UVs would all be in a blob assets. These things don’t have typical safety concerns. I would always prefer using the cached approach, and constructing the caches at bake time if I were to integrate this. I do realize that the caching isn’t as general purpose, but it covers skinning, blend shapes, and various soft bodies which are the main things I want to do. One other thing is that in ECS, deformations rely on the shader graph output block nodes, and shader graph only accepts a float3 in the vertex tangent block node. I haven’t figured out the implications of that yet, but it is something to consider. EDIT: Shader Graph blends the graph-assigned tangent’s xyz with the raw input mesh tangent’s w.
So for the library, I might suggest you separate the math operations from the data storage. Right now the critical pieces are in jobs, with normals and tangents separated. But I can save myself having to load indices and vertices twice if I did both the triangle normal and triangle tangent calculations next to each other. But there may also be times I don’t need the tangents at all. Having static calculation methods where I can pass a bunch of references to the loaded triangle vertices would allow me to make the variants with much less code duplication. Another trick is to interleave data that is always accessed together, especially if it is random access. For example, Tan1 and Tan2 elements are always accessed in pairs. If you interleaved them into a single array by putting them into a struct, loading them random access will only require looking up one cache line instead of two. Similar could be done for positions and UVs (positions are always accessed randomly so they don’t need to be compacted). Though I personally wouldn’t take advantage of these memory optimizations as I’d likely fully rely on the static methods directly. My third suggestion is to maybe either make your names more verbose or add comments to elaborate on the semantics of the adjacency mapping, as well as better document and improve the API for computing caches with multiple meshes sharing vertices. Lastly, Temp allocator is very good in a job, because it is a rewinding allocator that is rewound at the end of the job. The only way to beat it is to make an allocator that can rewind multiple times during a job, such as for each iteration in a loop. I haven’t figured out a general-purpose way to do that either.
Now for compute, I noticed some of your calculations use double precision. You’ll need to fix that as compute shaders don’t normally support double-precision. I’d suggest trying renormalization after skinning. Normal and tangent skinning is a crude approximation, and you may get better results doing the calculation on the fully deformed mesh. I planned to puth the renormalization after skinning in the pipeline. The last challenge for compute will be to eliminate temporary buffers as much as possible, potentially by leveraging groupshared memory and meshlets instead. Temporary buffers are surprisingly expensive on the GPU at scale. I will probably try to solve this myself after I get a CPU-side version working. There’s still a use case for CPU-side where I want to perform softbody deformations with world objects after skinning.
Feel free to consider, ask about, or ignore any of my suggestions! 