I have a Unity app where the engine keeps freezing the main thread when ever i load a large image using LoadImage.
So i decide to try to decode the image on a separate thread from the main thread. Im using a C# jpeg decoder from here
https://gist.github.com/jandk/3889823
I wrote a test code to test it out. The test code spins a cube on the main thread. While the cube is spinning, i create a separate thread where in that the JPG image is downloaded from a website and decoded. After decoding, the main thread takes those decoded bits and applies them to the cube using LoadRawImageData
The idea of using a spinning cube, is so that any engine freeze caused by the image processing will easily be seen because the cube will stop spinning for a moment.
Here is the code
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Threading;
using System.Net;
using UnityEngine.UI;
using System.IO;
using Unity.Collections;
using UnityEngine.Networking;
using Jpeg;
public class Spin : MonoBehaviour
{
class ThreadedImageLoader : MonoBehaviour
{
public Thread thread = null;
public byte[] pixels = null;
public string url;
public bool done = false;
public GameObject g;
public void Download(string url,GameObject g)
{
this.url = url;
this.g = g;
Debug.Log("Downloading from url=" + url);
thread = new Thread(() => DoThread());
thread.Start();
}
public void DoThread()
{
Debug.Log("downloading pic on a thread");
HttpWebRequest lxRequest = (HttpWebRequest)WebRequest.Create(url);
string lsResponse = string.Empty;
using (HttpWebResponse lxResponse = (HttpWebResponse)lxRequest.GetResponse()) {
using (BinaryReader reader = new BinaryReader(lxResponse.GetResponseStream())) {
pixels = reader.ReadBytes(1 * 1024 * 1024 * 10);
}
}
Debug.Log("Done downloading, creating decoder...");
JpegDecoder jdec = new JpegDecoder(pixels);
Debug.Log("decoder created, decompressing...");
byte[] img = jdec.DecodeScan();
Debug.Log("Done decompressing");
done = true;
}
}
List<ThreadedImageLoader> loaders = new List<ThreadedImageLoader>();
public float delta = 1f;
void Start()
{
string url = "https://www.gstatic.com/webp/gallery/4.jpg";
ThreadedImageLoader td = gameObject.AddComponent<ThreadedImageLoader>();
loaders.Add(td);
td.Download(url,gameObject);
}
void Update()
{
float d = delta * Time.deltaTime;
transform.Rotate(new Vector3(d, d, d));
if (loaders.Count > 0) {
ThreadedImageLoader td = (ThreadedImageLoader)loaders[0];
if (td.done) {
Debug.Log("Update:pixels lenth:" + td.pixels.Length);
if (td.pixels.Length > 0) {
Texture2D t = new Texture2D(1, 1);
t.LoadRawTextureData(td.pixels);
t.Apply();
gameObject.GetComponent<Renderer>().material.mainTexture = t;
}
loaders.RemoveAt(0);
Destroy(td);
// keep downloading the image again
string url = "https://www.gstatic.com/webp/gallery/4.jpg";
ThreadedImageLoader tdd = gameObject.AddComponent<ThreadedImageLoader>();
loaders.Add(tdd);
tdd.Download(url,gameObject);
}
}
}
}
As the code runs, the cube starts spinning, the thread gets created, the image is downloaded from the link, but when the jpeg decoder gets created, it crashes inside the decoder with an error reading the image.
The jpeg decoder im using is at that link above. Seems very simple, just one file, but it stops decoding with error while reading the downloaded data.
I know the image data downloaded is correct because if I use LoadImage(pixels) on the same data it the image appears perfectly. Of course i cant use Loadimage in the real code because it freezes the main thread because it decodes the image on the main thread (which causes the freeze).
Does anyone know a better c# jpeg decoder that i can use in this case? one that can run in a thread and doesnt have a lot of dependencies increasing the bloat? I just need it to be able to decode a jpeg within a thread in Unity.
I tried several of the nuget packages, but cant get them to install properly in Unity. The one at that link was the only one file decoder i could find that was easy to use, but of course it crashes!
Thanks for any help.