Caching complex meshes
How to use caching to improve performance for static drawings.
Contents
Introduction
In some cases you'll find yourself with a very large and complex rendering that stays the same every frame. If your method for drawing this is kinda slow, you can use caching to speed it up.
There are two ways to do this, using a redraw scope to control the lifetime of a mesh manually, or to use hashing to implicitly cache the mesh.
Using a redraw scope
The first way to cache a mesh is to use a redraw scope. This provides you with a token that will keep the rendered mesh alive (rendering it every single frame) until you dispose of the token.
private RedrawScope redrawScope;
void Start () {
redrawScope = DrawingManager.GetRedrawScope();
using (var builder = DrawingManager.GetBuilder(redrawScope)) {
builder.WireSphere(Vector3.zero, 1.0f, Color.red);
}
}
void OnDestroy () {
redrawScope.Dispose();
}
Using hashing
The second way to cache a mesh is to use hashing. This allows you to hash all the inputs that you use for rendering your complex thing, and only re-run your drawing code if the hash changes.
// Just a nice looking curve (which uses a lot of complex math)
// See https://en.wikipedia.org/wiki/Butterfly_curve_(transcendental)
static float2 ButterflyCurve (float t) {
t *= 12 * math.PI;
var k = math.exp(math.cos(t)) - 2*math.cos(4*t) - math.pow(math.sin(t/12f), 5);
return new float2(k * math.sin(t), k * math.cos(t));
}
// Make the butterfly "flap its wings" two times per second
var scale = Time.time % 0.5f < 0.25f ? new float2(1, 1) : new float2(0.7f, 1);
// Hash all inputs that you use for drawing something complex
var hasher = new DrawingData.Hasher();
// The only thing making the drawing change, in this case, is the scale
hasher.Add(scale);
// Try to draw a previously cached mesh with this hash
if (!DrawingManager.TryDrawHasher(hasher)) {
// If there's no cached mesh, then draw it from scratch
using (var builder = DrawingManager.GetBuilder(hasher)) {
// Draw a complex curve using 10000 lines
var prev = ButterflyCurve(0);
for (float t = 0; t < 1; t += 0.0001f) {
var next = ButterflyCurve(t);
builder.xy.Line(prev*scale, next*scale, Color.white);
prev = next;
}
}
}
Performance
Performance-wise, the two approaches are pretty much identical, but depending on what you are doing, one approach might be more convenient than the other. However, both approaches only make sense for complex drawings that are static or change infrequently. Drawing just a few lines this way is not recommended as the overhead of caching will outweigh the benefits.
You'll get the best performance for camera-independent drawings. Circles, spheres and text are examples of things that depend on the camera position for their resolution and exact position, and therefore require some additional processing each frame. While, for example, lines and solid meshes are camera-independent and can be cached more effectively.
As an example, the A* Pathfinding Project caches the rendering of navmeshes to significantly improve performance, especially for large graphs with hundreds of thousands of nodes.