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があるかとかがないので…使い勝手が微妙でした。。。
なのでこんなの作ってみました
まとめ
Experimentalな機能ですが、とても良い機能だと思います。
これまで収集することが困難であったPSOの情報+Variantの情報が手軽に取れるようになっています。