本記事は Unity Advent Calendar 2023 4日目の記事です。
3日目:CanvasMaterialでUI Shaderをシンプルに @up-hash さん << ココ >> 5日目:unsafeでアンマネージドな構造体でポインタアクセスとポリモーフィズムのテスト @fukaken5050 さん
TL;DR
- ComputeShader/ComputeBuffer を使う時はUnityのサンプルを読もう
- ComputeShader/ComputeBuffer 周りはググってもChatGPTも答えてくれないことが多いので、覚悟を持って挑もう
- C#のここの行がエラーとは示さないのでエラー文から原因を推測する力が必要です
エラーの経緯
業務て点群のデータ(.ply, .e57等) をUnityで読めるプログラムを制作している際、Particleとして点群を描画するため Graphics.DrawMeshInstancedIndirect関数 を使っていました。
Mac (Editor / MacOS App) では問題なく表示できたのですが、Windows 環境 (Editor/App) では Draw indirect argument buffer too small. Must be at least 20 bytes (5ints).
という謎のエラーが表示され、点群が描画できないという事象にぶつかりました。
もちろんググっても英語のページすらヒットしない状況。
ChatGPT に聞いても
といった具合に当たり障りのない、ちょっと気を利かせた翻訳程度くらいの情報しか得られません。
エラーの原因
Graphics.DrawMeshInstancedIndirect
API では引数に Mesh情報
, Material情報
, 'ComputeBuffer` 等が要求されます。
この ComputeBuffer
ですが、GPGPUに日常的に触れている方いがいはほとんど馴染みのないClassだと思います。
ここのComputeBufferの設定パラメータミスによって今回のエラーが発生していることがわかりました。
ComputeBuffer
ComputeBuffer は名前の通りComputeShader で読み書きするようのデータ置き場のことです。
ComputeBufferはコンストラクタで初期化するのですが、そのときの引数として以下の情報を与える必要があります。
パラメータ名 | 説明 |
---|---|
count | Bufferに格納する要素数。 1: スカラー, 1次元配列, 2: 2次元配列 ... |
stride | Bufferに格納される要素1つあたりのサイズ[byte] ただし2048 未満の4の倍数、つまりは int/uint/float =4byte * 配列長さ の値を入れればOK |
type | [Buffer の種別定義](https://docs.unity3d.com/ScriptReference/ComputeBufferType.html DrawMeshInstancedIndirect を使う場合は ComputeBufferType.IndirectArguments 一択 |
usage | 省略可能. Bufferの利用用途定義 |
ここのstrideは基本的に事前に作成した int[]/uint[]/float[] のLength * sizeof(int)/sizeof(uint)/sizeof(float) って感じでやれば問題ないです。
Draw indirect argument buffer too small. Must be at least XX bytes
エラー文の字のごとく、Bufferサイズが小さいというエラーです。
今回は Must be at least 20 bytes (int5s)
とあり、int[5]以上のBufferがないといけないとのことでした。
void Initialize()
{
...
var args = new uint[4]
{
particleMesh.GetIndexCount(0),
(uint) particles.Length,
particleMesh.GetIndexStart(0),
particleMesh.GetBaseVertex(0),
};
var argBuffer = new ComputeBuffer(
1,
sizeof(uint) * args.Length,
ComputeBufferType.IndirectArguments
);
argBuffer.SetData(args);
...
}
みたいな形で必要なパラメータをargs変数に定義してそれを渡していたのですが、どうやら動かすプラットフォームによって必要な最小バッファサイズが異なるようです。
MacOSXであれば問題なく描画できたのですが、Windowsだと描画に失敗しました。
ちなみに Graphics.DrawMeshInstancedIndirect
のサンプルを見てみると
private int cachedInstanceCount = -1;
private int cachedSubMeshIndex = -1;
private ComputeBuffer positionBuffer;
private ComputeBuffer argsBuffer;
private uint[] args = new uint[5] { 0, 0, 0, 0, 0 };
のように利用しなくても、 uint[5]
で定義しているので、基本的にはサンプルに則って作っておくのがよさそうです。
まとめ
- ComputeShader/ComputeBuffer を使う時はUnity公式のサンプルをしっかり観察しよう
- ComputeShader/ComputeBuffer 周りはググってもChatGPTも答えてくれないことが多いので、覚悟を持って挑もう
- C#のここの行がエラーとは示さないのでエラー文から原因を推測する力が必要です
- 今回だと描画ができない→Shader系
- DrawIndirectの文字がある→ShaderコードではなくAPIを呼んでいる箇所
- buffer 5ints とある→過去に動いていたコードと比較してint配列に差分がないか確認
- あたりだった