Was doing a garbage check on project and noticed FrustumPlanes.FromCamera(Camera, NativeList) produces garbage every call. After closer inspection it uses the non cached version of
GeometryUtility.CalculateFrustumPlanes which creates a Plane[6] array every call.
Easy enough to work around by replicating the behaviour.
private readonly UnityEngine.Plane[] planes = new UnityEngine.Plane[6];
private NativeArray<float4> GetCullingPlanes(Camera camera, Allocator allocator)
{
// Ideally would use FrustumPlanes.FromCamera(Camera.main, cullingPlanes);
// But this produces garbage as it creates a Plane[6] array every frame
// Instead replicate the behaviour while caching the array.
var cullingPlanes = new NativeArray<float4>(6, allocator);
GeometryUtility.CalculateFrustumPlanes(camera, this.planes);
for (var i = 0; i < 6; ++i)
{
cullingPlanes[i] = new float4(
this.planes[i].normal.x,
this.planes[i].normal.y,
this.planes[i].normal.z,
this.planes[i].distance);
}
return cullingPlanes;
}
However I would not expect garbage to be produced when calling a method taking a NativeArray.
3 Likes
Where do you use that? I suppose it is on a custom RenderMeshSystem.
I also use that but in my version there is no FrustumPlanes.FromCamera and instead of FrustumPlanes.FromCamera(Camera, NativeList) it uses GeometryUtility.CalculateFrustumPlanes(Camera) that generates no garbage.
Here is my FrustumPlanes struct:
struct FrustumPlanes {
public float4 Left;
public float4 Right;
public float4 Down;
public float4 Up;
public float4 Near;
public float4 Far;
public enum InsideResult {
Out,
In,
Partial
};
public FrustumPlanes(Camera camera) {
Plane[] sourcePlanes = GeometryUtility.CalculateFrustumPlanes(camera);
Left = new float4(sourcePlanes[0].normal.x, sourcePlanes[0].normal.y, sourcePlanes[0].normal.z, sourcePlanes[0].distance);
Right = new float4(sourcePlanes[1].normal.x, sourcePlanes[1].normal.y, sourcePlanes[1].normal.z, sourcePlanes[1].distance);
Down = new float4(sourcePlanes[2].normal.x, sourcePlanes[2].normal.y, sourcePlanes[2].normal.z, sourcePlanes[2].distance);
Up = new float4(sourcePlanes[3].normal.x, sourcePlanes[3].normal.y, sourcePlanes[3].normal.z, sourcePlanes[3].distance);
Near = new float4(sourcePlanes[4].normal.x, sourcePlanes[4].normal.y, sourcePlanes[4].normal.z, sourcePlanes[4].distance);
Far = new float4(sourcePlanes[5].normal.x, sourcePlanes[5].normal.y, sourcePlanes[5].normal.z, sourcePlanes[5].distance);
}
public InsideResult Inside(WorldMeshRenderBounds bounds) {
var center = new float4(bounds.Center.x, bounds.Center.y, bounds.Center.z, 1.0f);
var leftDistance = math.dot(Left, center);
var rightDistance = math.dot(Right, center);
var downDistance = math.dot(Down, center);
var upDistance = math.dot(Up, center);
var nearDistance = math.dot(Near, center);
var farDistance = math.dot(Far, center);
var leftOut = leftDistance < -bounds.Radius;
var rightOut = rightDistance < -bounds.Radius;
var downOut = downDistance < -bounds.Radius;
var upOut = upDistance < -bounds.Radius;
var nearOut = nearDistance < -bounds.Radius;
var farOut = farDistance < -bounds.Radius;
var anyOut = leftOut || rightOut || downOut || upOut || nearOut || farOut;
var leftIn = leftDistance > bounds.Radius;
var rightIn = rightDistance > bounds.Radius;
var downIn = downDistance > bounds.Radius;
var upIn = upDistance > bounds.Radius;
var nearIn = nearDistance > bounds.Radius;
var farIn = farDistance > bounds.Radius;
var allIn = leftIn && rightIn && downIn && upIn && nearIn && farIn;
if (anyOut)
return InsideResult.Out;
if (allIn)
return InsideResult.In;
return InsideResult.Partial;
}
}
@GilCat It’s from FrustumPlanes.cs in the HybridRenderer package. Technically in your own version, Plane[ ] sourcePlanes
still gets GC’ed. 
Speaking of the HybridRenderer… @tertle Do you know how the new BatchRendererGroup works? Can you help me take a look?: RenderMeshSystemV2 has substantial amount of memory leak
1 Like
Well it should creates a static array internally once, so after there is no garbage.
But yeah i’m the one with an older version. I’m still waiting for the new RenderSystem to support per instance overrides