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();
}
}
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;
}
}
}
Performance Evaluation
FPS Baseline Drop from 57.53fps to
old implementation: 22.89% (44.36fps)
new implementation: -0.83% (58.01fps)
Top comments (3)
you dont actually need to use coroutines.
when requesting a readback, you can provide a delegate when the readback is complete. and i think that is more performant. delegate takes an argument of AsyncGPUReadbackRequest.
like so
you call the readback start on start function once, and then when its completed its going to call oncompletereadback function for you.
this way is a bit more performant.
Thanks for your two posts! Your implementations helped me in reading the textures a lot faster. And actually this second AsyncGPUReadBack does improve the performance a lot (roughly about 30~40%) on my end. And as in Unity 2023 you can use the new Async/awaitable to further simplify this. Again thanks for your work!
Hey Daniel! great work. Some friends at Unity recently told me that method would really help a project i'm working on. Would you be interested in hacking on some stuff with me on some nights and weekends? I can tell you more about the project and we can go from there.