I'm using this piece of code to save renders and I'm saving renders in every frame inside Update()
.
Code:
using UnityEngine;
using System.IO;
using System;
//getty code
public class Capture : MonoBehaviour
{
private static int resWidth = 3840;
private static int resHeight = 2160;
private static GameObject camObj = null;
public static string ScreenShotName()
{
return string.Format("{0}/screenshots/{1}.png",
Application.dataPath,
Util.name_);
}
void Start()
{
camObj = gameObject;
Directory.CreateDirectory(Application.dataPath + "/screenshots");
resHeight=GetComponent<Camera>().pixelHeight;
resWidth=GetComponent<Camera>().pixelWidth;
//Util.everyThingHot = true;
}
public static void TakeHiResShot()
{
try
{
RenderTexture rt = new RenderTexture(resWidth, resHeight, 24);
camObj.GetComponent<Camera>().targetTexture = rt;
Texture2D screenShot = new Texture2D(resWidth, resHeight, TextureFormat.RGB24, false);
camObj.GetComponent<Camera>().Render();
RenderTexture.active = rt;
screenShot.ReadPixels(new Rect(0, 0, resWidth, resHeight), 0, 0);
camObj.GetComponent<Camera>().targetTexture = null;
RenderTexture.active = null;
Destroy(rt);
byte[] bytes = screenShot.EncodeToPNG();
string filename = ScreenShotName();
File.WriteAllBytes(filename, bytes);
Debug.Log(string.Format("Took screenshot to: {0}", filename));
bytes = null;
}
catch(Exception e)
{
Debug.Log("Error");
}
}
}
This is giving me 4K images.
But the problem is, it's taking a lot of memory. I have 8 Gigs and it's using 95% of it.
I'm quite sure that it shouldn't take this much memory. I should free up the memory. How can I achieve that. I know .Net has it's own garbage collector, but before it works, it takes so much memory.
So how to do it manually?
I added GC.Collect()
, but no good and it even hangs. Previously it used to take like 2-3 minutes to occupy 95%, now it takes only 30 seconds or less.
I used this code :
RenderTexture rt = new RenderTexture(resWidth, resHeight, 24);
camObj.GetComponent<Camera>().targetTexture = rt;
Texture2D screenShot = new Texture2D(resWidth, resHeight, TextureFormat.RGB24, false);
camObj.GetComponent<Camera>().Render();
RenderTexture.active = rt;
screenShot.ReadPixels(new Rect(0, 0, resWidth, resHeight), 0, 0);
camObj.GetComponent<Camera>().targetTexture = null;
RenderTexture.active = null;
rt = null;
Destroy(rt);
byte[] bytes = screenShot.EncodeToPNG();
string filename = ScreenShotName();
File.WriteAllBytes(filename, bytes);
bytes = null;
screenShot = null;
GC.Collect();
I also added after calling this method:
Capture.TakeHiResShot();
GC.Collect();
After changing :
using UnityEngine;
using System.IO;
using System;
public class Capture : MonoBehaviour
{
private static int resWidth = 3840;
private static int resHeight = 2160;
private static GameObject camObj = null;
private static RenderTexture rt;
private static Texture2D screenShot;
private static byte[] bytes;
public static string ScreenShotName()
{
return string.Format("{0}/screenshots/{1}.png",
Application.dataPath,
Util.name_);
}
void Start()
{
camObj = gameObject;
Directory.CreateDirectory(Application.dataPath + "/screenshots");
rt = new RenderTexture(resWidth, resHeight, 24);
screenShot = new Texture2D(resWidth, resHeight, TextureFormat.RGB24, false);
}
public static void TakeHiResShot()
{
try
{
camObj.GetComponent<Camera>().targetTexture = rt;
camObj.GetComponent<Camera>().Render();
RenderTexture.active = rt;
screenShot.ReadPixels(new Rect(0, 0, resWidth, resHeight), 0, 0);
camObj.GetComponent<Camera>().targetTexture = null;
RenderTexture.active = null;
Destroy(rt);
bytes = screenShot.EncodeToPNG();
string filename = ScreenShotName();
File.WriteAllBytes(filename, bytes);
}
catch(Exception e)
{
Debug.Log(e.Data);
}
}
}
Now its like this :
Profiler :
GC.Collect();
\$\endgroup\$new
. Store your RenderTexture, Texture2D, arrays and everything that is of reference type in class fields and reuse them instead of creating and throwing away. \$\endgroup\$RenderTexture
after the first time you use it, meaning on subsequent frames it'snull
soReadPixels
tries to read from the main frame buffer instead. But the frame buffer is smaller (950 x 498 in this case), and not available to read from because your code is not running in the right part of the rendering loop to access it. Easy fix: don'tDestroy()
stuff you plan to re-use. \$\endgroup\$GC.Collect()
is rather highly not recommended. It is seldom we (programmers) know better than the runtime when to call it. :) \$\endgroup\$