Daniel Chou Rainho
Posted on May 30, 2023
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)
Posted on May 30, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.