DispatchIndirectの使い方

UnityでGPGPUその1でDispatch命令の使い方は解説したが、次にDispatchIndirectを使おうと思ったがなんかハマったのでメモ。
これはDispatch命令の引数3つ(x,y,z)をCPUから指定するのではなくGPUのバッファに入れておいてそのGPUバッファを指定する命令。
例えば動的にパーティクルの数が変動していて、GPU側ではパーティクル数を記憶しているのにCPU側では記憶してないときに、わざわざGPU→CPUにデータ転送してDispatch命令をしなくても良くなる。

これも例によっていろいろ調べてなんか中国語のサイトに巡り合い解決した。

https://zhuanlan.zhihu.com/p/72747058


Unityでの実装

C#
using System.Collections;
using System.Collections.Generic;
using UnityEngine;


public class Hoge0 : MonoBehaviour
{
    public ComputeShader shader;

    void Start()
    {
        int[] host_A = { 2, 2, 2 };//これがグループサイズx,y,zに対応
        int[] host_B = new int[16];

        ComputeBuffer A = new ComputeBuffer(host_A.Length, sizeof(int), ComputeBufferType.IndirectArguments);
        ComputeBuffer B = new ComputeBuffer(host_B.Length, sizeof(int));

        int k = shader.FindKernel("CSMain");

        A.SetData(host_A);
        B.SetData(host_B);

        //引数をセット
        shader.SetBuffer(k, "B", B);

        // GPUで計算
        //shader.Dispatch(k, 2, 2, 2);と同じ
        shader.DispatchIndirect(k, A, 0);

        // device to host
        B.GetData(host_B);

        Debug.Log("GPU上で計算した結果");
        for (int i = 0; i < host_B.Length; i++)
        {
            Debug.Log(host_B[i]);
        }


        //解放
        A.Release();
        B.Release();
    }
}

CpmputeShader
#pragma kernel CSMain

RWStructuredBuffer<int> B;

[numthreads(1, 1, 1)]
void CSMain(uint3 id : SV_DispatchThreadID)
{
	int idx = id.x + id.y * 2 + id.z * 4;
	B[idx] = idx + 100;
}




2*2*2スレッドが起動しているので答えは100~107の数字が格納されているはず。

aaa


GithubにもUP

これにより、並列数をCPUからでなくGPUから指定することができ、GPUの計算結果によって並列数を動的に変えたい場合にGPU→CPUのブロックをなくすことができる。
注意だが
A = new ComputeBuffer(3, 4, ComputeBufferType.IndirectArguments);

の部分を忘れないように。忘れてもとりあえずプログラム自体は動いてしまうのでバグに気づかない。

ちなみにUnityのリファレンスにもあるようhttps://docs.unity3d.com/ja/2018.4/ScriptReference/ComputeShader.DispatchIndirect.html
第三引数のargsOffsetにあたるところにはbyteで指定しないといけない。だからAがint型でGPUメモリに乗っていればここは4,8,12など4の倍数で指定しないといけない。