I wrote a Thread helper class that can be used to execute a piece of code in Unity's main Thread.
This is the functions blue print:
public static void executeInUpdate(System.Action action)
The complete script is really long and will make this post unnecessarily long. You can see the rest of the script helper class here.
Then I can use unity's API from another Thread
like this:
UnityThread.executeInUpdate(() =>
{
transform.Rotate(new Vector3(0f, 90f, 0f));
});
The problem is that when I use a variable declared outside of that delegate, it would allocate memory. The code above allocates 104 bytes each frame. That's because of the transform
variable that is used inside that closure.
This may seem like nothing now but I do this 60 times per-sec and I have about 6 camera's I need to connect to and display images on the screen. I do not like the amount of garbage that is generated.
Below is an example of how I am downloading images from a camera and uploading it into Unity. I get about 60 frames per-sec. The receiveVideoFrame()
function runs on a separate Thread. It downloads the image, send's it to Unity's main Thread
and Unity uploads the image bytes into Texture2D
. The Texture2D
is then displayed with RawImage
. The allocation happens when closure is captured due to UnityThread.executeInUpdate
.
bool doneUploading = false;
byte[] videoBytes = new byte[25000];
public Texture2D videoDisplay;
void receiveVideoFrame()
{
while (true)
{
//Download Video Frame
downloadVideoFrameFromNetwork(videoBytes);
//Display Video Frame
UnityThread.executeInUpdate(() =>
{
//Upload the videobytes to Texture to display
videoDisplay.LoadImage(videoBytes);
doneUploading = true;
});
//Wait until video is done uploading to Texture/Displayed
while (!doneUploading)
{
Thread.Sleep(1);
}
//Done uploading Texture. Now set to false for the next run
doneUploading = false;
//Repeat again
}
}
How can I use closure without causing memory allocation?
If that's not possible, is there another way to do this?
I can just remove the class I use to execute code in the main Thread and re-write those logic in the main script but that would be messy and long.
After hours of experiment I discovered that if you put the code with the closure in a function, then cache it to an Action
variable once in the Start
or Awake
function, the closure memory allocation will be gone when you invoke it in inside the while loop.
Also, I made the doneUploading
boolean variable a volatile
so that both Threads
can see the changes. I don't think that the lock
keyword is needed for this boolean
variable here.
private volatile bool doneUploading = false;
byte[] videoBytes = new byte[25000];
public Texture2D videoDisplay;
Action chachedUploader;
void Start()
{
chachedUploader = uploadToTexture;
}
void uploadToTexture()
{
UnityThread.executeInUpdate(() =>
{
//Upload the videobytes to Texture to display
videoDisplay.LoadImage(videoBytes);
doneUploading = true;
});
}
//running in another Thread
void receiveVideoFrame()
{
while (true)
{
//Download Video Frame
downloadVideoFrameFromNetwork(videoBytes);
//Display Video Frame (Called on main thread)
UnityThread.executeInUpdate(chachedUploader);
//Wait until video is done uploading to Texture/Displayed
while (!doneUploading)
{
Thread.Sleep(1);
}
//Done uploading Texture. Now set to false for the next run
doneUploading = false;
//Repeat again
}
}
I am open to more improvement if anyone find anything bad. Although, I have solved my problem. Closures does not allocate memory anymore.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With