Andreas Atteneder

glTF in Unity optimization - 2. Avoid Tangents and Normals Calculation

This is part 2 of a mini-series.

Instant side quest

Originally I wanted to investigate the benefit of parallel jobs in this post. I started by creating test cases and profiling the status quo. That's where I figured that there's higher potential in other optimizations.

Analysis

I created two test scenes

This is the HighRes scene in Unity's profiler. More specifically, the first frame:

"HighRes scene part 1: preparation and job execution"

Most time is spent/lost allocating Unity's data structures (a hint to switch to the new Mesh API, maybe). Let's look at the second part in the following frame:

"HighRes scene part 2: Mesh creation"

Here the Unity Mesh is created and it takes a devastating 1.279 seconds !!! Most of that time (1.06 seconds) is spent calculating tangents. Overall this scene loads in ~1.6 seconds.

For the Sixfold scene the result is similar, except that the six Mesh creations are spread amongst six frames, so at least the frame stall is not as bad.

Optimize

So tangent recalculation is expensive. The best and first way to make code faster is to not execute it, so why do we need tangents in first place? Some materials/shaders rely on correct them. I know they are needed for consistent normal mapping, but I'm not sure if for anything else (maybe anisotropic shading?). For now I assume we only need them for normal mapped materials.

Calculating normals is less expensive than tangents, but while we're at it, let's think about them as well. They are necessary for all shaded materials, but not for unlit ones.

So in glTFast 0.10.2 I changed the importer to only calculate normals/tangents, if the material requires them. Let's have a look at the final frame when loading the previous scene again:

"HighRes scene: optimized mesh creation"

Boom, down a full second. The whole scene now loads in ~700 ms. It's even faster when using unlit materials:

"HighRes scene unlit: optimized mesh creation"

Loads consistently in under 400 ms. Nice achievement for a couple of lines of code.

This was released in glTFast 0.10.2.

Next up

What about all the cases where having normal/tangents is absolutely necessary? The Unity mesh API does not provide us with a threaded version of RecalculateNormals or RecalculateTangents, so the only way around this would be to make one's own C# job that does the normal/tangent calculations on a thread.

I definitely plan to do this, but only after glTFast switched to the new Mesh API (based on NativeArrays) and other improvements, so stay tuned!

Follow me on twitter or subscribe the feed to not miss updates on this topic.

If you liked this read, feel free to

ko-fi

Next: 3 Parallel Jobs

Overview of this mini-series