Чтение файла изображения асинхронно - PullRequest
0 голосов
/ 16 сентября 2018

Я хотел бы прочитать файл изображения асинхронно, и я пытаюсь добиться этого, используя async/await из C #. Тем не менее, я все еще замечаю значительное отставание в этой реализации Используя профилировщик, я уверен, что это метод FileStream.ReadAsync(), который длится целую вечность, а не асинхронно и останавливает обновление игры.

Я замечаю больше лагов, чем просто использование File.ReadAllBytes, что странно. Я не могу использовать этот метод, который все еще вызывает некоторое отставание, и я бы не хотел останавливаться на частоте кадров.

Вот код.

// 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....
    }

1 Ответ

0 голосов
/ 17 сентября 2018

Вам не нужно Task для асинхронной загрузки изображения.Вы можете использовать Unity UnityWebRequest и DownloadHandlerTexture API для загрузки.UnityWebRequestTexture.GetTexture упрощает это, поскольку автоматически настраивает DownloadHandlerTexture.Это сделано в другом потоке под капотом, но вы используете сопрограмму для управления им.

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;
    }
}

Вы также можете загрузить файл в другом потоке с помощью Thread API.Еще лучше использовать API ThreadPool.Любой из них должен работать.После загрузки байтового массива изображения для загрузки байтового массива в Texture2D вы должны сделать это в основном потоке, поскольку вы не можете использовать API Unity в основном потоке.

Чтобы использовать API Unity из другого потока, когда вы закончите загрузку байтов изображения, вам понадобится оболочка для этого.Пример ниже сделан с C # ThreadPool и моим UnityThread API, который упрощает использование API Unity из другого потока.Вы можете получить UnityThread отсюда.

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);
            });
        }
    });
}
...