Open1

Unityのレンダリングとか制御周りメモ

kajitaj63b3kajitaj63b3

https://docs.unity3d.com/Manual/srp-using-scriptable-render-context.html
SRP では、C# スクリプト
を参照して、レンダリング コマンドを設定し、スケジュールします。 その後、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

https://docs.unity3d.com/Manual/GraphicsCommandBuffers.html
CommandBufferは、レンダリング コマンドのリストを保持します。(レンダー ターゲットの設定や指定したメッシュの描画など。)
レンダー パイプラインのさまざまなポイントで、これらのコマンドをスケジュールして実行するように Unity に指示できます。
SRPは内部でこれをやってると思われる。
カスタムのCommandBufferを即座に実行することも、指定したタイミングで実行することも可能。
CommandBufferで実行可能な命令一覧はこれ。
https://docs.unity3d.com/ScriptReference/Rendering.CommandBuffer.html

CommnadBufferをスケジューリングするには、Camera.AddCommandBuffer API と CameraEvent enum を使用し、Light.AddCommandBuffer API と LightEvent enum を使用します。CameraEventとLightEventの実行順序を参照してください。

実例
https://lindenreidblog.com/2018/09/13/using-command-buffers-in-unity-selective-bloom/
https://colourmath.com/2018/tutorials/adventures-in-commandbuffers-an-epic-pt-1/
https://discussions.unity.com/t/using-commandbuffers-for-early-z-test-rejection/646598
https://github.com/keijiro/TextureUpdateExample
https://tips.hecomi.com/search?q=Command+Buffer+

GPU Fence

...
RenderPassなしで特定のタイミングでGPU処理の通過後に処理を挟めたりする。
https://discussions.unity.com/t/graphicsfence-for-metal/776228

GraphicsFenceは、非同期コンピュート(すなわち、ExecuteCommandBufferAsyncおよびそのフレンドを介して実行されるレンダリングCommandBuffer)のためだけのものです。非同期CommandBufferでComputeShader.Dispatch/DispatchIndirectまたはCommandBuffer.DispatchComputeを使用する場合、それはあなたにとって役に立ちません。
非同期のコンピュートディスパッチの順序は保証されており、そうでない場合はUnityにバグがある可能性があります。 もしそうでないなら、Unityにバグがあるかもしれません。

https://discussions.unity.com/t/no-requestasyncreadback-available-with-graphics-executecommandbufferasync/930420/3

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した時に何フレームのラグが出ているかを常時デバッグ出来る機能は欲しい。
https://github.com/keijiro/AsyncCaptureTest
https://dev.to/alpenglow/unity-fast-pixel-reading-part-2-asyncgpureadback-4kgn

https://www.youtube.com/watch?v=B2jiquau_TQ