どの処理でフレームが止まっているか調べる
スクリーンショットやRenderTextureの解析で一瞬止まる場合、ReadPixelsのGPU待ちだけでなく、画像変換やファイル保存にも負荷があるか確認します。この記事はUnity 2022.3 LTSのAPIを基準にした設計・調査手順です。特定端末での改善率を示すものではありません。
ReadPixelsはGPUの完了を待つ
Texture2D.ReadPixelsは、現在のレンダーターゲットの画素をGPUからCPU側のTexture2Dへ読み戻します。この呼び出しはGPUの先行処理が完了するのを待つため、メインスレッドの停止要因になります。Texture2Dを使い回して割り当てを減らしても、この同期の待機は別に残ります。
Profilerでは「描画」「読み戻し」「画像エンコード」「ファイル書き込み」を分け、保存なし・エンコードなしの比較も取ります。毎フレームの読み戻しが必要なのか、保存ボタンを押したときだけでよいのかも先に決めます。
CPUへ渡す必要があるかで経路を選ぶ
- 画像を別のMaterialやUIへ表示するだけなら、CPUへ戻さずGPU内で使う経路を検討します。コピーにはGraphics.CopyTextureやGraphics.Blitなどがありますが、形式や用途に応じた互換条件を確認します。
- CPUでの画像解析やファイル出力が必要で、数フレームの遅延を許容できるならAsyncGPUReadbackが候補です。要求した同じフレームに結果が届く設計にはできません。
- 対応端末ではSystemInfo.supportsAsyncGPUReadbackを確認します。非対応時の機能停止・取得頻度の低い同期経路など、用途に合う代替を用意します。
非同期読み戻しで守ること
- 読み戻す描画が終わる適切なタイミングでAsyncGPUReadback.Requestを発行します。元Textureの範囲と、読み戻し形式の対応を確認します。2022.3ではSystemInfo.IsFormatSupportedとFormatUsage.ReadPixelsで形式の対応を照会できます。
- 同時実行数に上限を設けます。例えば要求中が1件なら次の取得を見送る設計にし、古い結果の破棄や取得間隔も決めます。
- 完了コールバック、またはdoneの確認後にhasErrorを確認し、成功した要求だけGetDataで読みます。成功結果を次のフレームまで保持できると考えず、完了時に処理します。
- 元のRenderTextureは要求完了まで有効に保ちます。GetTemporaryで確保したものを、要求直後にReleaseTemporaryしてはいけません。シーン終了時も未完了の要求を考慮します。
- 後のフレームや保存キューへ渡すデータは、自分が管理するバッファへコピーします。GetDataの返すNativeArrayは要求に属する短命な参照なので、保存し続けたり、自分で確保した配列と同じ扱いでDisposeしたりしないでください。
Requestの直後にWaitForCompletionやWaitAllRequestsで待つと、その場で完了を待つ経路になります。通常の毎フレーム処理では待機せず、後から結果を受け取る流れにします。非同期でもGPU転送の帯域、コピー先のメモリ、完了時のCPU処理は必要です。
PNG変換と保存にも上限を設ける
AsyncGPUReadbackはPNG圧縮やファイル書き込みまで自動で非同期にするAPIではありません。読み戻しだけ改善しても、完了処理で大きな画像を一度に変換すれば停止が残ることがあります。保存側のキューにも上限を設け、取得解像度・領域・頻度を下げた比較を取ります。
ワーカースレッドへ処理を移す場合は、使うエンコードAPIのスレッド対応とデータの所有権を確認します。UnityのTextureや他のUnity APIを、無条件にTask.Run内で操作する手順にはしないでください。
対象Playerで確認する
- 同じ解像度と取得頻度で、フレーム時間の突出とメモリ使用量を比較する。
- 連打や連続保存でも要求数・保存キュー・一時RTが増え続けないか確認する。
- 向き・色・アルファ・HDR変換を保存画像で確認する。MSAA使用時は読み戻し用の単一サンプルRTへ解決する経路も確認する。
- 非対応形式・取得エラー・シーン切替・終了時の後始末を確認する。
Editorでエラーが出ないことは、対象Graphics APIや端末での動作・速度の証明にはなりません。改善の判断には、実際のPlayerでの計測と出力画像の確認を使います。
参考資料
- Texture2D.ReadPixels: synchronous GPU readback
- AsyncGPUReadback: behavior and latency
- AsyncGPUReadback.Request: formats and source lifetime
- AsyncGPUReadbackRequest: result lifetime
- AsyncGPUReadbackRequest.GetData
- SystemInfo.supportsAsyncGPUReadback
- RenderTexture.ResolveAntiAliasedSurface: MSAA resolve
- Unity C# reference source 2022.3: GetData buffer ownership