見出し画像

GraphicsStateCollectionについて

Unity6よりGraphicsStateCollectionという機能がExperimental(実験的)として追加されました。
ShaderVariant周りに関する機能となっており、Vulkan/Metal/DirectX12などでは大きな恩恵を得られるかもしれない機能になっています。

本記事ではGraphicsStateCollectionについて簡単に説明していきたいと思います。

なぜこの機能が必要だったか?

Shaderは実際に描画をする際、事前にWarmupを行わないと、利用時にスパイクが発生することがあります。
Shaderの中でも、Variant(キーワードの組み合わせ)によって、実際に利用されるGPU Programが異なり、それごとに事前のWarmupが必要となっているからです。
そのため、ShaderVariantCollectionというのがあります。

しかし、Metal/Vulkan/DirectX12といったGraphicsAPIではVariantによる事前ウォームアップだけでは不十分でした。
これらのGraphicsAPIでは、PSO( パイプライン状態オブジェクト ) を考慮したウォームアップが必要となるためです。
PSOでは頂点レイアウトやレンダリングターゲットの設定などがあり、実際にそれらの設定がどうなっている時に、ShaderのどのVariantで描画されるかを把握する必要がありました。

ShaderのVariantの洗い出しだけではなく、PSOも考慮することになってしまうため、Metal/Vulkan/DirectX12といった環境下ではShaderの事前Warmupはとても困難でした。

実際に動かした情報を収集して、それを元にWarmupできたらどんなに良いことか………。

そんなことを叶えてくれるのが今回のGraphicsStateCollectionです。

補足:もし、PSOが異なるという理由でShaderWarmupが走る場合は、「CreateGPUPipelineImp」という項目として現れる事となります。
https://discussions.unity.com/t/graphicsstatecollection-tracing-and-warmup-in-unity-6/951031

使い方

GraphicsStateCollectionでは、事前にVariant及びPSOを収集する機能と、ランタイム上でWarmupする機能の大きく2つに分けられます。

事前に収集(Trace)する

まずは実機上などでGraphicsStateCollectionをnew して、BeginTrace・EndTraceします。これだけでデータ収集が行われます。
そして、GraphicsStateCollectionは実機上にそのまま保存する事もできますし、Editor上で保存させることも可能です。

サンプルコードとしては下記のようなものになります。

using UnityEngine;
using UnityEngine.Experimental.Rendering;
using System.IO;

public class EndTraceExample : MonoBehaviour
{
    public GraphicsStateCollection graphicsStateCollection;

    void Start()
    {
        // GraphicsStateCollectionオブジェクトを作り、Traceを開始します
        graphicsStateCollection = new GraphicsStateCollection();
        graphicsStateCollection.BeginTrace();
    }

    void OnDestroy()
    {
        // Traceを終了します
        graphicsStateCollection.EndTrace();

        // 拡張子は「graphicsstate」に
        string fileName = "collection.graphicsstate";
        string localPath = Path.Combine( Application.persistentDataPath, fileName);
        // ローカルに保存します
        graphicsStateCollection.SaveToFile( localPath );
        // Profiler等でEditorと繋いでいる場合、Editorにデータを送信して保存します
        if (PlayerConnection.instance.isConnected)
        {
            graphicsStateCollection.SendToEditor(fileName );
        }
    }
}

ビルドしたランタイムで収集したほうが良い理由として、Unityのビルド時に頂点データで利用していない属性がStripされるといったことが起こり得るためです。
またGraphicsStateCollectionは、どのプラットフォーム、どのGraphicsAPI上で実行されたか?という情報も保持していますので、そのへんも見失うことなく出来るので良いです。

またこのTraceですが、VulkanやMetal、DirectX12でなくても情報を収集してくれているようです。DirectX11やAndroid上のOpenGLES上でも「どのShaderのどのVariantで描画されたか?」といったことを記録してくれていました。ただGraphicsのState自体は記録されていませんでした。
(Experimentalなので今後変わる可能性もありえますが…)

ランタイム上でWarmupする

Warmupも、とても簡単です。先程の方法で作られた「.graphicsstate」ファイルはUnityのアセットとして認識されます。
それをWamUp呼び出しするだけです。WarmupはJobとして発行することが出来るので、他の処理をしつつWarmupをさせるといったことが可能になります。

サンプルコードは以下のようになります。ここでは即時完了待ちをしています。

using UnityEngine;
using UnityEngine.Experimental.Rendering;
using Unity.Jobs;

public class WarmUpSynchronousExample : MonoBehaviour
{
    // Inspector上で設定するよう
    [SerializedField]
    private GraphicsStateCollection graphicsStateCollection;

    void Start()
    {
        // Warmupを要求します
        JobHandle handle = graphicsStateCollection.WarmUp();
        // 完了まで待ちます
        handle.Complete();
    }
}

なお、このWarmup処理はMetalやVulkan、DirectX12でしか効果がありませんでした。OpenGLESやDirectX11でWarmup処理を呼び出しましたが効果がありませんでした。これらのプラットフォームではShaderVariantCollectionのWarmupをする必要があるようです。
(Experimentalなので今後変わる可能性もありえますが…)

その他

ビルド時の扱い

AssetBundleビルド時にShaderVariantCollectionアセットをShaderと同じAssetBundleにいれることで、過剰なVariantストリップを抑止できるのですが…
GraphicsStateCollectionアセットでもそういうことが行われてそうか検証しました。しかし、だめそうです…
(Experimentalなので今後変わる可能性もありえますが…)

Viewer作ってみたよ

GraphicsStateCollectionのInspectorには、どんなShaderがあるかとかがないので…使い勝手が微妙でした。。。

GraphicsStateCollectionのInspector

なのでこんなの作ってみました

Viewerはこんな感じで見れます。

まとめ

Experimentalな機能ですが、とても良い機能だと思います。
これまで収集することが困難であったPSOの情報+Variantの情報が手軽に取れるようになっています。

関連記事


この記事が気に入ったらサポートをしてみませんか?