想像の100倍複雑でした。
結論
- 事象:以下条件において、シェーダーコンパイルが失敗する
- Mali GPU搭載Android端末
- Open GLES (Vulkanでない)
- 「Graphics」設定 > 「BatchRendererGroup Variants」がKeep All
- 原因1:Mali GPU端末はOpen GLESの場合、SSBO(Compute Buffer)に非対応
- SystemInfo.SystemInfo.maxComputeBufferInputsVertexの値が0なら非対応
- Snapdragon端末 (Adreno GPU)は対応
- Vulkanの場合は対応 (なので上記事象は発生しない)
- 原因2:「Graphics」設定 > 「BatchRendererGroup Variants」がKeep Allの場合、Compute Shaderを使用するシェーダーバリアントが生成される
- GPU resident drawerなど、BatchRendererGroupを間接的に使用する機能を使う場合、特に注意(知らない間にオンにしていることも)
- Compute Bufferを使用していないと思っていても、この設定により知らない間にCompute bufferが使用される
- 対策:以下のいずれか
- Open GLESではなくVulkanを使う (ただし古いVulkanドライバはクラッシュ率が高い)
- 「BatchRendererGroup Variants」 をStrip Allにする
発端:VulkanとOpenGLESを端末によって切り替えたい
Android版リリース後、Android12以前の端末(Vulkan1.1)でWarmupAllShaders()を呼ぶとクラッシュする問題が発生しました。
この時はWarmupAllShadersを呼び出さないようにすることで解決したかのように思えました。
…が、一部機種では依然としてクラッシュすることが判明。Playコンソールでの分析でもクラッシュ率は下がったものの、1%で安定してしまいました。
やはりVulkanは不安定なようで、Androidのバージョンに応じてVulkanとOpenGLESを切り替えるで書いた通り、機種によってOpenGLESに切り替える方法も実装することにしました。
切り替え自体は簡単ですが、一つ大きな問題がありました。
なぜかOpenGLESで表示すると、環境光が全く適用されなくなるのです。
環境光が適用されないとどうなるか。
こうなります。

酷いですね。影が完全に破綻しています。
シェーダーのコンパイルに失敗している
Logcatを見た所、Simple litシェーダーのコンパイルに失敗している様子。

Shader Strage Blocksが許容個数(0)を超えている(4)とのことですが、全く心当たりがありません。
そもそもShader Strage Blocksとは一体…?
調べていると、似たようなバグ報告を発見。
Error GLSL link error: The number of vertex shader storage blocks – Unity Engine – Unity Discussions
上記記事を要約すると、
- Mali GPU搭載端末、かつOpen GLESで発生
- Vulkanでは発生しない
- URP、Built-in両方で発生
他にも、Multithreaded renderingやParticleとの関連性も指摘されていましたが、おそらく関係ない様子。(Particleは使用していないのと、Multithreaded renderingはオンオフ試しましたが改善なし)
Mali GPU搭載端末のみ問題が発生
手持ちの4台で試した所、以下の結果となりました。
- Alldocube iPlay 50 mini pro (Mali GPU):発生
- google Pixel 6a (Mali GPU):発生
- Huawei P30 lite (Mali GPU):発生
- Samsung Galaxy S24 (Adreno GPU):問題なし
確かにMali GPU端末で発生するようです。
では、Logcatに出ていたエラーメッセージはどういう意味でしょうか。
GLSL link error: The number of vertex shader storage blocks (4) is greater than the maximum number allowed (0).
調べた所、以下のバグ報告に気になる一節を発見。
This error means that the specific shader is not supported by the device.
The GPU (Mali?) does not support SSBO (like StructuredBuffer in HLSL) in vertex shader.このエラーは、特定のシェーダーがデバイスでサポートされていないことを意味します。
GPU (Mali?) は、頂点シェーダーで SSBO (HLSL の StructuredBuffer など) をサポートしていません。
どうもMali GPU(かつOpen GLES)はSimple Stroage Blocks Object (SSBO)なる機能をサポートしていない様子。
シェーダーの知識は無いため、ここではSSBOの説明は割愛します。
なお、端末がSSBO対応かはSystemInfo.maxComputeBufferInputsVertexで取得できます。
値が0の場合は非対応。先ほどの4端末で試した所、
- Alldocube iPlay 50 mini pro (Mali GPU):0
- google Pixel 6a (Mali GPU):0
- Huawei P30 lite (Mali GPU):0
- Samsung Galaxy S24 (Adreno GPU):4
やはりAdreno GPUはOpen GLESでも対応のようです。
なぜSimple litシェーダーでSSBOが使われている?
つまり、「Simple litシェーダーはSSBOを使っているからMali GPUでは使用不可能」ということ…?
いや絶対におかしい。
URPはモバイル用のレンダーパイプラインであり、その中でもSimple litは性能の低い端末向けのシェーダーです。そんなシェーダーがAndroid端末の大半を占めるMali GPUで使用できないわけがありません。
途方にくれていると、BatchRendererGroupという機能に以下の記述がありました。
BatchRendererGroup のサンプル:低予算デバイスでも高フレームレートを実現
これは、DirectX、Vulkan、Metal、その他さまざまなゲームコンソールなど、大部分のプラットフォーム上で動作しますが、GLES 上では動作しません。問題となるのは、大部分の GLES 3.0 デバイスが頂点ステージ中に SSBO にアクセスできない(つまり、GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS 値が 0 である)ことです。
BatchRendererGroupの詳細は割愛しますが、どうやらSSBOを使用した機能のようです。そしてOpen GLESでは使用できない様子。
しかし、こんな機能を有効にした覚えはありません。
原因はGPU resident drawer(を有効化したときの設定変更)
が、どうやらこの機能、GPU resident drawerを使用する際に間接的に使っているようです。
Unity – Manual: Enable the GPU Resident Drawer in URP
The GPU Resident Drawer automatically uses the BatchRendererGroup API to draw GameObjects with GPU instancing
GPU resident drawerは一度使用した覚えがあり、結局オフにしていました。
ところがオフにする際、1番の設定を元に戻すのを忘れていたことに気が付きました。
- Go to Project Settings > Graphics, then in the Shader Stripping section set BatchRendererGroup Variants to Keep All.
- Go to the active URP Asset and enable SRP Batcher.
- Double-click the renderer in the Renderer List to open the Universal Renderer, then set Rendering Path to Forward+.
- Set GPU Resident Drawer to Instanced Drawing.
そう、BatchRendererGroup用に生成された、SSBOを使用するShaderVariantがずっと生き続けていたのです。

なぜ今まで気づかなかったのか。理由は以下2つ。
- VulkanはSSBO対応
- Adreno GPU搭載端末はSSBO対応
メインの検証用端末はGalaxy S24、つまりAdreno GPU端末。そしてずっとVulkanを使っていたため、Mali GPU端末で動作検証した際も、問題は生じませんでした。
そしてKeep AllからStrip Allに修正し、確認すると…

治りました。めでたしめでたし。
安易な設定変更は避けましょう。
コメント