I have a script which is loading a photo from path
into a texture (photoThumbT2D
), then rotating it:
byte[] byteArray = File.ReadAllBytes(path);
UnityEngine.Object.Destroy(photoThumbT2D);
photoThumbT2D = new Texture2D(0, 0);
photoThumbT2D.LoadImage(byteArray);
RotateImage(photoThumbT2D, -90);
Where the RotateImage function is adapted from here as:
public static void RotateImage(Texture2D originTexture, float angle) {
int width = originTexture.width;
int height = originTexture.height;
float halfHeight = height * 0.5f;
float halfWidth = width * 0.5f;
Color32[] originPixels = originTexture.GetPixels32();
Color32[] rotatedPixels = originTexture.GetPixels32();
int oldX;
int oldY;
float phi = Mathf.Deg2Rad * angle;
float cosPhi = Mathf.Cos(phi);
float sinPhi = Mathf.Sin(phi);
for (int newY = 0; newY < height; newY++) {
for (int newX = 0; newX < width; newX++) {
rotatedPixels[newY * width + newX] = new Color32(0, 0, 0, 0);
int newXNormToCenter = newX - width / 2;
int newYNormToCenter = newY - height / 2;
oldX = (int)(cosPhi * newXNormToCenter + sinPhi * newYNormToCenter + halfWidth);
oldY = (int)(-sinPhi * newXNormToCenter + cosPhi * newYNormToCenter + halfHeight);
bool InsideImageBounds = (oldX > -1) && (oldX < width) && (oldY > -1) && (oldY < height);
if (InsideImageBounds) {
rotatedPixels[newY * width + newX] = originPixels[oldY * width + oldX];
}
}
}
originTexture.Reinitialize(width, height);
originTexture.SetPixels32(rotatedPixels);
originTexture.Apply();
}
However, this application of RotateImage
is leading to what I believe is a heap fragmentation problem where the more times it runs the more memory usage piles up in the profiler. This is discussed here.
In my case, it manifests in Android only (no problem in Editor) as a steady increase in GC Used Memory (Tracked Memory, In Use) until the app crashes:
In the Memory Profiler I get a new chunk of Managed Heap created for every time it runs until everything crashes (here there about 13 identical blue chunks created):
Apparently the only solution is to use NativeArray<Color32> nativeArray = texture2D.GetRawTextureData<Color32>();
and work with that, but since the NativeArray is a direct representation of the Texture data, I'm not sure if it's possible mathematically to rotate it without making a new copy to work from. And if you make a new array copy of the actual color data, you will get the same heap problems as with GetPixels32 and SetPixels32.
Any thoughts or solutions?
Addendum
Unfortunately, DMGregory, I learned from your method but I am still having trouble with your solution.
MEMORY ACCUMULATION
I have tested, and even just the code:
photoThumbT2D = Resources.Load<Texture2D>("bigphoto");
photoThumbnailVE.style.backgroundImage = photoThumbT2D;
buttonClickedEvent += delegate {
Debug.Log("RUN CLICK");
var texels = photoThumbT2D.GetRawTextureData<Color32>();
var texelsCopy = System.Buffers.ArrayPool<Color32>.Shared.Rent(texels.Length);
//Unity.Collections.NativeArray<Color32>.Copy(texels, texelsCopy, texels.Length);
System.Buffers.ArrayPool<Color32>.Shared.Return(texelsCopy);
};
Continues to trigger memory accumulation in Unity for Android in the same manner as the OP code. Thus in even this manner the Rent and Return functions are leading to memory accumulation.
Can you think of any reason this would not be happening? I have Incremental GC collection turned on in Android. I can't make any sense of it. Might it be because it's being run as a delegate or the parent object running it was created by a coroutine (which should be long gone)?
I have tested further and this can be solved instead by using a static List to store the duplicate data in but I would love to know why any other method is causing this problem.
Texture2D.Reinitialize
instead? It allows you to do the same thing with the same texture. This also has the advantage that any references to the texture remain valid. \$\endgroup\$