[Unity] Fast Pixel Reading (Part 2): AsyncGPUReadback

alpenglow

Daniel Chou Rainho

Posted on May 30, 2023

[Unity] Fast Pixel Reading (Part 2): AsyncGPUReadback

From my previous post, I felt a bit disappointed with the results.

Although my approach for reading pixels was significantly faster, in the end, the performance hit on the running fps count was approximately the same.

Hence this second post, in which I build on the previous post to create a solution that leads to a negligible drop from the baseline fps count for a default empty scene, as compared to the optimized approach from my previous post, which leads to a baseline drop of 22.89%, all while using the optimized Compute Shader from my previous post.

AsyncGPUReadback

The following implementation is an adaptation of my code from the previous post, being an implementation of the ReadPixels method using a Compute Shader for optimized performance.

In particular, in my new approach, I use the AsyncGPUReadback method, which allows me to read data back from the GPU in a non-blocking way.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;
using System;

public class PixelReader2 : MonoBehaviour
{
    [SerializeField] private RenderTexture inputTexture;
    private Texture2D texture2D;

    [SerializeField] private ComputeShader ReadFirstPixel;
    private ComputeBuffer outputBuffer;
    private int kernelID;

    private void Start()
    {
        texture2D = new Texture2D(inputTexture.width, inputTexture.height, TextureFormat.RGBA32, false);

        // Create the output buffer
        outputBuffer = new ComputeBuffer(1, sizeof(float) * 4);

        // Get the kernel ID of the ReadFirstPixel function
        kernelID = ReadFirstPixel.FindKernel("ReadFirstPixel");

        // Set the input texture and output buffer to the shader
        ReadFirstPixel.SetTexture(kernelID, "inputTexture", inputTexture);
        ReadFirstPixel.SetBuffer(kernelID, "outputBuffer", outputBuffer);
    }

    private void Update() {
        StartCoroutine(ReadPixel());
    }

    private IEnumerator ReadPixel()
    {
        // Dispatch the compute shader
        ReadFirstPixel.Dispatch(kernelID, 1, 1, 1);

        // Request the data from the GPU to the CPU
        AsyncGPUReadbackRequest request = AsyncGPUReadback.Request(outputBuffer);

        // Wait for the request to complete
        while (!request.done)
        {
            yield return null;
        }

        if (request.hasError)
        {
            Debug.Log("GPU readback error detected.");
        }
        else
        {
            // Extract the color components from the output array
            float[] outputArray = request.GetData<float>().ToArray();
            Color color = new Color(outputArray[0], outputArray[1], outputArray[2], outputArray[3]);
        }
    }

    void OnDestroy()
    {
        // Release the output buffer
        outputBuffer.Release();
    }
}

Enter fullscreen mode Exit fullscreen mode

Test Setup

For evaluating performance I use the following script, which logs the average fps count over the first 30 seconds of running the game.

using UnityEngine;

public class AverageFPSCounter : MonoBehaviour
{
    private float startTime;
    private int frameCount;

    private void Start()
    {
        startTime = Time.time;
        frameCount = 0;
    }

    private void Update()
    {
        frameCount++;

        if (Time.time - startTime >= 30f)
        {
            float averageFPS = frameCount / (Time.time - startTime);
            Debug.Log("Average FPS over 10 seconds: " + averageFPS);

            // Optionally, you can stop the script from updating further.
            enabled = false;
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Performance Evaluation

FPS Baseline Drop from 57.53fps to
old implementation: 22.89% (44.36fps)
new implementation: -0.83% (58.01fps)

💖 💪 🙅 🚩
alpenglow
Daniel Chou Rainho

Posted on May 30, 2023

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related