GC allocations when animating shapes

Avatar
  • updated

Animating using the animator is allocating memory every frame:

Image 832

I noticed this when it was only allocating 80 bytes. However, I'm also just animating 2 objects. Increasing the number of these objects will proportionally increase these allocations (20 objects, 800 bytes, every frame):

Image 833

So I dug a little bit into the code, maybe thinking these are editor-only allocations, but this is what I found (this is disc.cs, but it's present in every shape class):

Image 834

This is allocating a new array every time, seemingly for a single material instance.
To make sure I tried implementing a simple fix:

private protected override Material[] GetMaterials()
{
var array = ArrayPool<Material>.Alloc(1);
array[0] = ShapesMaterialUtils.GetDiscMaterial(type)[BlendMode];
return array;
}

And then making sure to free the array in the calling method...

private protected void UpdateMaterial() {
Material[] targetMats = GetMaterials(); // Here's the call
// ... some other stuff rnd.sharedMaterials = targetMats;
ArrayPool<Material>.Free(targetMats); // Returning the array back to the pool is fine here, since (apparently?) sharedMaterials will copy the array content
}

(This is using the ArrayPool implementation inside the Shapes package)

Now checking the profiler again, only implementing the fix in disc.cs so far (67 animated objects, 0 allocation per frame):

Image 835

So...

  • Can we be sure that these methods will only ever create an array with a single element? I haven't used Shapes much and currently my use case is pretty narrow, so I might not encounter situations where this assumption becomes an issue. However I would like to use it more but I certainly prefer not having unnecessary allocations every frame.
  • Is this known and intended for whatever reason or simply a little oversight?

I would like to know more about this, but for now I'll just implement the ArrayPools for each shape and hope it doesn't break anything.

Edit: Digging a little bit deeper, Polyline appears to be the only shape that can potentially return an array with 2 elements, like so:

private protected override Material[] GetMaterials() {
if( joins.HasJoinMesh() )
return new[] { ShapesMaterialUtils.GetPolylineMat( joins )[BlendMode], ShapesMaterialUtils.GetPolylineJoinsMat( joins )[BlendMode] };
return new[] { ShapesMaterialUtils.GetPolylineMat( joins )[BlendMode] };
}
Reporting a bug? please specify Unity version:
2022.3.30f1
Reporting a bug? please specify Shapes version:
4.3.1
Reporting a bug? please specify Render Pipeline:
URP