Unityのレンダリングとか制御周りメモ
を参照して、レンダリング コマンドを設定し、スケジュールします。 その後、Unityの低レベルグラフィックスアーキテクチャにそれらを実行するように指示し、グラフィックスAPIに命令を送信します。
これを行う主な方法は、ScriptableRenderContextにAPIコールを行うことですが、CommandBuffersをすぐに実行することもできます。
ScriptableRenderContext
SRPでは、ScriptableRenderContextクラスがC#レンダーパイプラインコードとUnityの低レベルグラフィックスコードの間のインターフェイスとして機能します。 SRPレンダリングは遅延実行を使用して動作します。ScriptableRenderContextを使用してレンダリングコマンドのリストを作成し、Unityにそれらを実行するように指示します。 Unityの低レベルグラフィックスアーキテクチャは、グラフィックスAPIに命令を送信します。
レンダリングコマンドをスケジュールするには、次のようにします:
CommandBuffers を ScriptableRenderContext.ExecuteCommandBuffer を使用して ScriptableRenderContext に渡します。
ScriptableRenderContext.CullやScriptableRenderContext.DrawRenderersのように、Scriptable Render Contextに直接APIコールを行う。
スケジュールしたコマンドをUnityに実行させるには、ScriptableRenderContext.Submitを呼び出します。 Unity は ScriptableRenderContext 上のすべてのレンダリングコマンドを同じようにスケジュールし、Submit() を呼び出すまで実行しません。
using UnityEngine;
using UnityEngine.Rendering;
public class ExampleRenderPipeline : RenderPipeline
{
public ExampleRenderPipeline() {
}
protected override void Render(ScriptableRenderContext context, Camera[] cameras) {
// 現在のレンダーターゲットをクリアするコマンドを作成してスケジュール
var cmd = new CommandBuffer();
cmd.ClearRenderTarget(true, true, Color.red);
context.ExecuteCommandBuffer(cmd);
cmd.Release();
// スケジュールされたコマンドをGraphicsAPIに実行させるようにScriptableRenderContextに指示
context.Submit();
}
}
CommandBuffer
Graphics.ExecuteCommandBuffer を呼び出すことで、ScriptableRenderContext を使用せずに CommandBuffer を即座に実行できます。 この API の呼び出しは、レンダー パイプラインの外部で行われます。
CommandBuffersを使用してスケジュールできるコマンドの詳細については、CommandBuffers API documentationを参照してください。
CommandBuffer
レンダー パイプラインのさまざまなポイントで、これらのコマンドをスケジュールして実行するように Unity に指示できます。
SRPは内部でこれをやってると思われる。
カスタムのCommandBufferを即座に実行することも、指定したタイミングで実行することも可能。
CommandBufferで実行可能な命令一覧はこれ。
CommnadBufferをスケジューリングするには、Camera.AddCommandBuffer API と CameraEvent enum を使用し、Light.AddCommandBuffer API と LightEvent enum を使用します。CameraEventとLightEventの実行順序を参照してください。
実例
GPU Fence
...
RenderPassなしで特定のタイミングでGPU処理の通過後に処理を挟めたりする。
GraphicsFenceは、非同期コンピュート(すなわち、ExecuteCommandBufferAsyncおよびそのフレンドを介して実行されるレンダリングCommandBuffer)のためだけのものです。非同期CommandBufferでComputeShader.Dispatch/DispatchIndirectまたはCommandBuffer.DispatchComputeを使用する場合、それはあなたにとって役に立ちません。
非同期のコンピュートディスパッチの順序は保証されており、そうでない場合はUnityにバグがある可能性があります。 もしそうでないなら、Unityにバグがあるかもしれません。
command.SetExecutionFlags()
でAsyncComputeフラグをたてる。
Graphics.ExecuteCommandBufferAsync
で非同期にCommandBufferを実行する。
CommandBuffer.RequestAsyncReadbackIntoNativeArray
でコールバックで処理する。
か
CommandBuffer.CreateAsyncGraphicsFence
で非同期フェンスを作る。
下記みたいな感じで別スレッドでポーリングする。もしくはUpdateやコルーチンでポーリングする。
new Func<Task>(async () =>
{
SynchronizationContext.SetSynchronizationContext(Ucs);
await Task.Yield();
bool isAsync = SystemInfo.supportsAsyncCompute;
Assert.IsTrue(SystemInfo.supportsComputeShaders);
CommandBuffer command = new();
command.SetExecutionFlags(isAsync ? CommandBufferExecutionFlags.AsyncCompute : CommandBufferExecutionFlags.None);
[...]
command.DispatchCompute([...]);
if (isAsync)
{
Assert.IsTrue(SystemInfo.supportsGraphicsFence);
GraphicsFence fence = command.CreateAsyncGraphicsFence();
Graphics.ExecuteCommandBufferAsync(command, ComputeQueueType.Background);
while (!fence.passed)
{
await Task.Yield();
}
}
else
{
Graphics.ExecuteCommandBuffer(command);
}
[...].GetData([...]);
command.Dispose();
[...]
})().Wait();
AsyncGPUReadback
.GetDataはブロッキング処理で同期的。
AsyncGPUReadBackした時に何フレームのラグが出ているかを常時デバッグ出来る機能は欲しい。