【Unity】FrameTimingManagerを使ってCPUとGPUの処理時間をそれぞれ計測する

UnityのFrameTimingManagerを使ってCPUとGPUの処理時間をそれぞれ計測する方法をまとめました。

Unity2019.2.5

FrameTimingManager

CEDEC2019の「Unity2018/2019における最適化事情」にて、FrameTimingManagerというものを知りました。

cedil.cesa.or.jp

FrameTimingManagerは、Dynamic Resolutionというパフォーマンスを見て動的に解像度を変更する機能の一部として使われているクラスですが、
APIが公開されているためこれを使ってCPUとGPUの処理時間を計測できます。

使い方はDynamic Resolutionのドキュメントのソースコードを見るのが一番手っ取り早いです。

docs.unity3d.com

いままでUnityではGPUの処理時間を計測しづらかったため、この機能は非常に有用そうです。
ただしOpenGL ESには非対応となっており、モバイルでは一部のGPUでのみ使用できるようです。
MetalのiPhoneでは動作していることが確認できました。

使い方

それではこれを実際に使ってみます。
まず準備として、Player SettingsのOther SettingsからEnable Frame Timing Statsを有効にしておく必要があります。

f:id:halya_11:20190919193340p:plain

そして以下のようなコードでフレーム情報を取得します。

using UnityEngine;

/// <summary>
/// 1フレームあたりの処理時間を計測するクラス
/// MetalとVulkanに対応、OpenGLESは非対応
/// </summary>
public class FrameTime : MonoBehaviour
{
    private static FrameTime _instance;
    public static FrameTime Instance
    {
        get
        {
            if (_instance == null)
            {
                _instance = new GameObject(nameof(FrameTime)).AddComponent<FrameTime>();
            }
            return _instance;
        }
    }

    /// <summary>
    /// CPUの1フレーム当たりの処理時間
    /// </summary>
    public float CpuFrameTime { get; private set; }
    /// <summary>
    /// GPUの1フレーム当たりの処理時間
    /// </summary>
    public float GpuFrameTime { get; private set; }

    private FrameTiming[] _frameTimings = new FrameTiming[1];

    private void Update()
    {
        // フレーム情報をキャプチャする
        FrameTimingManager.CaptureFrameTimings();

        // 必要なフレーム数分の情報を取得する
        // 戻り値は実際に取得できたフレーム情報の数
        var numFrames = FrameTimingManager.GetLatestTimings((uint)_frameTimings.Length, _frameTimings);
        if (numFrames == 0) // 2020.02.16修正しました
        {
            // 1フレームの情報も得られていない場合はスキップ
            return;
        }

        // CPUの処理時間、CPUの処理時間を格納
        CpuFrameTime = (float)(_frameTimings[0].cpuFrameTime * 1000);
        GpuFrameTime = (float)(_frameTimings[0].gpuFrameTime * 1000);
    }
}

大体こんな感じでしょうか。
CaptureFrameTimings()でフレーム情報をキャプチャして、
GetLatestTimings()で指定したフレーム数だけフレーム情報を取得します。

使うときはこんな感じで。

using UnityEngine;

public class FrameTimeUI : MonoBehaviour
{
    private double _cpuFrameTime;
    private double _gpuFrameTime;

    private void LateUpdate()
    {
        _cpuFrameTime = FrameTime.Instance.CpuFrameTime;
        _gpuFrameTime = FrameTime.Instance.GpuFrameTime;
    }

    private void OnGUI()
    {
        GUI.Label(new Rect(8, 8, 300, 100), $"CPU : {_cpuFrameTime}");
        GUI.Label(new Rect(8, 108, 300, 100), $"GPU : {_gpuFrameTime}");
    }
}

なおOpenGL ESなどグラフィックスAPIが対応していない場合は0が返却されます。

参考

docs.unity3d.com

docs.unity3d.com