Quick answer: Use the callback overload AsyncGPUReadback.Request(rt, callback) or poll request.done in Update. Never WaitForCompletion in a hot path. Readback once per several frames, not every frame.

You readback a render texture for a screen-color sampler. WaitForCompletion blocks the main thread for ~16ms each frame; FPS halves.

The Symptom

Frame time spikes correlate with readback calls. Profiler shows blocking time inside AsyncGPUReadback.WaitForCompletion.

The Fix

private AsyncGPUReadbackRequest? _req;

void RequestSample()
{
    _req = AsyncGPUReadback.Request(myRT, 0, request =>
    {
        if (request.hasError) return;
        var data = request.GetData<byte>();
        // process on main thread; safe to touch UnityEngine
        UpdateUI(data);
    });
}

Callback fires on the main thread when the readback completes (1-3 frames later). No blocking.

Polling Alternative

void Update()
{
    if (_req.HasValue && _req.Value.done)
    {
        var data = _req.Value.GetData<byte>();
        UpdateUI(data);
        _req = null;
    }
}

Poll done; consume when ready. Useful if you need explicit control over when the data is processed.

Frequency Budget

Don’t fire a readback every frame; fire every ~10 frames or on event:

if (Time.frameCount % 10 == 0) RequestSample();

Verifying

Profiler shows AsyncGPUReadback in worker threads, not main. Frame time stable. WaitForCompletion does not appear.

“Callback or poll. Never wait. Frame stays smooth.”

Related Issues

For Niagara CPU/GPU readback, see Niagara readback. For async unobserved, see async exceptions.

Async stays async. No stalls.