3

I would like to read an image file asynchronously and Im trying to achieve that using async/await from C#. However, I still notice a significant lag with this implementation. Using the profiler I'm pretty confident it is it the FileStream.ReadAsync() method that takes ages to complete, rather than doing it asynchronously and stops the game from updating.

I notice more lag this way than just using File.ReadAllBytes which is odd. I can't use that method that still causes some lag and I would like to not have a stop in frame rate.

Here's some code.

// Static class for reading the image.
class AsyncImageReader
{
    public static async Task<byte[]> ReadImageAsync(string filePath)
        {
            byte[] imageBytes;
            didFinishReading = false;
            using (FileStream fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read, 4096, true))
            {
                imageBytes = new byte[fileStream.Length];
                await fileStream.ReadAsync(imageBytes, 0, (int) fileStream.Length);

            }

            didFinishReading = true;

            return imageBytes;
        }
 }

    // Calling function that is called by the OnCapturedPhotoToDisk event. When this is called the game stops updating completely rather than 
    public void AddImage(string filePath)
    {
        Task<byte[]> readImageTask = AsyncImageReader.ReadImageAsync(filePath);
        byte [] imageBytes = await readImageTask;
        // other things that use the bytes....
    }
user14492
  • 2,116
  • 3
  • 25
  • 44

1 Answers1

0

You don't need Task to load image asynchronously. You can use Unity's UnityWebRequest and DownloadHandlerTexture API to load. The UnityWebRequestTexture.GetTexture simplifies this since it setups up DownloadHandlerTexture for you automatically. This is done in another thread under the hood but you use coroutine to control it.

public RawImage image;

void Start()
{
    StartCoroutine(LoadTexture());
}

IEnumerator LoadTexture()
{
    string filePath = "";//.....
    UnityWebRequest uwr = UnityWebRequestTexture.GetTexture(filePath);
    yield return uwr.SendWebRequest();

    if (uwr.isNetworkError || uwr.isHttpError)
    {
        Debug.Log(uwr.error);
    }
    else
    {
        Texture texture = ((DownloadHandlerTexture)uwr.downloadHandler).texture;
        image.texture = texture;
    }
}

You can also load the file in another Thread with the Thread API. Even better, use the ThreadPool API. Any of them should work. Once you load the image byte array, to load the byte array into Texture2D, you must do it in the main Thread since you can't use Unity's API in the main Thread.

To use Unity's API from another Thread when you are done loading the image bytes, you would need a wrapper to do so. The example below is done with C# ThreadPool and my UnityThread API that simplifies using Unity's API from another thread. You can grab UnityThread from here.

public RawImage image;

void Awake()
{
    //Enable Callback on the main Thread
    UnityThread.initUnityThread();
}

void ReadImageAsync()
{
    string filePath = "";//.....

    //Use ThreadPool to avoid freezing
    ThreadPool.QueueUserWorkItem(delegate
    {
        bool success = false;

        byte[] imageBytes;
        FileStream fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read, 4096, true);

        try
        {
            int length = (int)fileStream.Length;
            imageBytes = new byte[length];
            int count;
            int sum = 0;

            // read until Read method returns 0
            while ((count = fileStream.Read(imageBytes, sum, length - sum)) > 0)
                sum += count;

            success = true;
        }
        finally
        {
            fileStream.Close();
        }

        //Create Texture2D from the imageBytes in the main Thread if file was read successfully
        if (success)
        {
            UnityThread.executeInUpdate(() =>
            {
                Texture2D tex = new Texture2D(2, 2);
                tex.LoadImage(imageBytes);
            });
        }
    });
}
Programmer
  • 121,791
  • 22
  • 236
  • 328