UnityRef is currently in early development. Some features may be incomplete and/or not functioning.

UNITYREF

Your Pit Stop For Solving ANYTHING in Unity

optimization

[URP] Fix Frame Spikes During Large RenderTexture PNG Saves

Solution

optimizationgraphicsiomemory managementasset management

Unity 2018.2 - Unity 6.3.x

Published Mon, May 4

Issue

 Saving high-resolution RenderTexture data via ReadPixels and EncodeToPNG causes significant frame spikes because these operations must occur on the main thread, blocking the rendering loop. Standard asynchronous tasks cannot be used for ReadPixels as it requires the GPU context available only on the primary thread.

Quick-Fix

Main-thread stalls occur during AsyncGPUReadback-less texture extraction. Use AsyncGPUReadback and threaded encoding to maintain smooth performance.

Expand Analysis

Performance bottlenecks when saving large textures are commonly attributed to the synchronous nature of legacy transfer methods. To maintain high frame rates, the data extraction must be decoupled from the rendering cycle.

  1. Use AsyncGPUReadback.Request to retrieve your texture data from the GPU without stalling the main thread.
  2. In the callback, convert the returned NativeArray to a byte array using ImageConversion.EncodeNativeArrayToPNG.
  3. Execute the file write operation using File.WriteAllBytesAsync or within a background thread to prevent I/O blocking.
  4. Profile with the Profiler to ensure the AsyncGPUReadback request is not being forced to complete early by immediate access calls.

Additional Tips:

  • Use TextureFormat.RGBA32 for the best compatibility with AsyncGPUReadback requests.
  • Consider using NativeArray<byte> to avoid unnecessary garbage collection allocations during the data transfer process.
  • For extreme performance requirements, save raw bytes instead of encoding to PNG if the file size is manageable and the alpha channel is not required.

Copy


using UnityEngine;
using UnityEngine.Rendering;
using System.IO;
using Unity.Collections;

public class TexturePersistenceManager : MonoBehaviour
{
    public void SaveRenderTextureAsync(RenderTexture sourceTexture, string destinationPath)
    {
        AsyncGPUReadback.Request(sourceTexture, 0, (AsyncGPUReadbackRequest request) => {
            if (request.hasError)
            {
                Debug.LogError("GPU Readback failed.");
                return;
            }

            NativeArray<byte> data = request.GetData<byte>();
            byte[] encodedBytes = ImageConversion.EncodeNativeArrayToPNG(
                data, 
                sourceTexture.graphicsFormat, 
                (uint)sourceTexture.width, 
                (uint)sourceTexture.height
            );

            File.WriteAllBytesAsync(destinationPath, encodedBytes);
        });
    }
}

Related Posts Haven't quite found a solution to your problem? We think these posts might help you.

Content inspired by a Unity discussion post.