- Home /
Performance issues with mesh processing
In order to create a certain effect, I need to calculate the pose of a set of skinned meshes, since Unity doesn't expose that data in version 3. I was able to get everything working, but I'm suffering from some major performance issues. Since I don't have Unity Pro, I did some ad-hoc timing of functions using Time.realtimeSinceStart and narrowed down my bottleneck to this section of code:
private function CalculatedPosedMesh(skinnedMesh : SkinnedMeshRenderer) : Mesh
{
// Build a new mesh
var baseMesh = skinnedMesh.sharedMesh;
var mesh = new Mesh();
var baseMeshVertices = baseMesh.vertices;
var baseMeshVerticesLength = baseMeshVertices.Length;
var baseMeshBoneWeights = baseMesh.boneWeights;
var baseMeshBindPoses = baseMesh.bindposes;
var newVert : Vector3[] = new Vector3[baseMeshVertices.Length];
var i : int = 0;
for(i = 0; i < baseMeshVerticesLength; i++)
{
//Only the first bone is being factored in right now in order to cut down on calculations.
// Apply bone weights
newVert[i] = GetBoneInfluence(skinnedMesh, baseMesh, baseMeshBoneWeights[i].boneIndex0, 1.0, baseMeshVertices[i]);
// Transform the vertex to be in local space for convenience.
newVert[i] -= transform.position;
}
mesh.vertices = newVert;
mesh.uv = baseMesh.uv;
mesh.triangles = baseMesh.triangles;
mesh.RecalculateBounds();
mesh.RecalculateNormals();
return mesh;
}
private function GetBoneInfluence(skinnedMesh : SkinnedMeshRenderer, baseMesh : Mesh, boneIndex : int, boneWeight : float, vertex : Vector3) : Vector3
{
// Transform the mesh vertice first so that it's local in bone space, and then transform the
// local coordinates to world coordinates using the current bone transform.
var localVertexPosition : Vector3 = baseMesh.bindposes[boneIndex].MultiplyPoint3x4( vertex );
return skinnedMesh.bones[boneIndex].transform.localToWorldMatrix.MultiplyPoint3x4( localVertexPosition ) * boneWeight;
}
This code works just fine, and the effect looks excellent, but this piece of code is causing 0.2-1.0 second hitches each time I call it on a 3k vertex mesh, which is not acceptable. I was able to optimize it down to what it is now by caching a lot of the return values from the mesh class (a lot of functionality is apparently being done behind the scenes by the property accessors), but I've run out of ideas in that regard. If I can bring the calculation cost down consistently to half a second or less, I think I can mask the rest of it with multithreading or coroutines.
Can anyone spot redundant or unnecessary operations here that I might have missed, or another way to approach the problem? Any help would be appreciated!
Answer by Paulius-Liekis · Oct 29, 2012 at 09:35 AM
As it was said in the comments: runtime mesh modification is an expensive operation.
But besides that move these out of the loop:
baseMesh.bindposes[boneIndex]
skinnedMesh.bones[boneIndex]
Don't ever dobaseMesh.bindposes[index] or mesh.vertices[index], etc in the loop. Every time this operations is performed Unity does a copy of C++ data into C# (whole array! and in vertices case it has to convert data from combined vertex stream to Vector3 array). Simply do this before the loop:
Matrix4x4[] bindposes = baseMesh.bindposes[boneIndex];
this way a copy of the array is performed only once - not on every vertex. And then use bindposes[index] in the loop.
IIRC Unity caches result of localToWorldMatrix, but simply creating an array of matrices before the main loop and caching them should help a bit too.
I was able to track down the main point of slowdown: the "mesh.vertices = newVert;" line. The vertex calculations themselves were actually trivial, since I was already caching the problem variables. Fortunately, all I really needed were the vertex positions for my effect, so cutting out that line and just storing the array of vertices ins$$anonymous$$d of a mesh got this whole thing into real-time territory.
Thanks for the insights, guys, I hadn't realized just how much functionality was being hidden behind the accessors.
There should be some documentation or wiki page which lists these horrendous accessor operations. I've been doing similar real-time deformations myself and little bits of info like "Every time this operations is performed Unity does a copy of C++ data into C#" can save days and weeks of debugging!
@daterre, well...this actually applies to almost any array that you get from Unity API in C# - almost all of them perform a data copy from C++ to C# (and vice versa). $$anonymous$$aybe vertices (normals, tangents and other mesh data) is more extreme, because Unity not only converts data, but also (un)packs into streams.
That makes sense...even though I'd expect some kind of property value caching for the duration of an update. Is it theoretically possible to write a C++ plugin in Pro that performs mesh queries/operations and only exposes to script scenario-specific values? Or does the plugin run in a sandbox and marshaling occurs anyway?
No, I don't think you can access Unity C++ data from your C++ plugin. You would still have to get data in your C# script and pass that to your C++ plugin, so it wouldn't help at all. The only time where is makes sense is when you're doing a lot of mesh processing.