先日とある方からの報告により発覚したのでメモ。
WebGL で浮動小数点数テクスチャ (float, half-float) を使うときに注意しておかないと、特定のデバイスで「本当はサポートされているのに浮動小数点数テクスチャが使えない」状況に陥ります。
前提
WebGL ではデフォルトで浮動小数点数テクスチャをサポートしておらず、拡張機能を要求してから使用する必要があります。拡張機能は半精度浮動小数点数、単精度浮動小数点数で分かれており、それぞれ別々に要求する必要があります。さらに、線形補間を用いたい場合は、線形補間用の拡張機能も加えて要求する必要があります。倍精度の浮動小数点数テクスチャは今のところ存在しません。
要求すべき拡張機能は次の通りです。
OES_texture_half_float
: 半精度浮動小数点数テクスチャを使用する場合OES_texture_half_float_linear
: 半精度浮動小数点数テクスチャの線形補間を使用する場合OES_texture_float
: 単精度浮動小数点数テクスチャを使用する場合OES_texture_float_linear
: 単精度浮動小数点数テクスチャの線形補間を使用する場合
スマホにおけるサポート状況
MDN を見る限り、Android + Chrome では、よほど古くない限りこれらの拡張はすべて使うことができます。
iOS + Safari の場合は、半精度の方は問題なく使える一方、単精度の方は iOS15 から使えるようになりました。
iOS + Safari における OES_texture_float
の対応状況を見ると、一見 iOS8 から使えるように見えますが、実は OES_texture_float
に内包される機能である「単精度浮動小数点数テクスチャへの書き込み」が iOS15 までサポートされていませんでした。つまりシェーダの書き込み先に使えず、GPGPU 勢にとってはサポートが存在しないに等しい状況でした。
WebGL 2.0 の到来
次世代の WebGL である WebGL 2.0 の到来により、浮動小数点数テクスチャは WebGL 2.0 によってデフォルトでサポートされるようになり、上記のような拡張機能の要求が不要になりました。
Android + Chrome では WebGL 2.0 をかなり前からサポートしており、iOS + Safari では iOS15 からようやくサポートされるようになった形です。
奇妙な対応状況
上記のように Android では古くから WebGL 2.0 が使えるのですが、iOS では依然として使えないデバイスが執筆時点で無視できないほど存在しており、安全策として WebGL 1.0 で半精度浮動小数点数テクスチャを使う方針で色々を作っていました。
しかし、Android 端末で「なんか画面が真っ黒になるんだけど……」という報告を受け[1]、調べてみると OES_texture_half_float
が使えないことが原因であることが判明しました。
あの悪名高い iOS ですら半精度のテクスチャは昔からサポートしているのに、それをサポートしていない Android 端末はどれだけ昔のものなんだ……と思っていたのですが、実は割と最近の端末であることが判明します。
いろんな環境での実機端末でテストができる BrowserStack で試してみたところ、多くの Samsung Galaxy 系列のスマホで OES_texture_half_float
が使えないことが明らかになりました。これはまずい。というか、デバイスのサポート状況として大問題なのでは……?
深まる謎
続いてさらに驚きの事実が発覚します。OES_texture_half_float
が使えない Samsung Galaxy 系列のスマホですが、なんと普通に WebGL 2.0 をサポートしています。
まとめるとこんな感じです。
- WebGL 1.0: OK
- WebGL 1.0 +
OES_texture_half_float
: NG - WebGL 2.0(
OES_texture_half_float
を内包): OK
多分 WebGL 2.0 に早々に対応してしまったので、WebGL 1.0 の拡張機能にまで手が回らなかったとかそんなところだと思います。マジで勘弁してほしい。
解決策
WebGL 2.0 が使える環境では優先して WebGL 2.0 を使うようにしよう。
ただし、注意点としてテクスチャのフォーマット周りで WebGL 2.0 用の対応を入れないと動かなくなります。具体的には、WebGL 1.0 では internalformat
と format
の値は常に同じにしておかなければならなかったのですが、WebGL 2.0 ではそうとも限らずややこしくなります。
- 整数テクスチャの場合:
internalformat = RGBA
,format = RGBA
,type = UNSIGNED_BYTE
のままで OK - 半精度浮動小数点数テクスチャの場合:
internalformat = RGBA16F
,format = RGBA
,type = HALF_FLOAT
とする - 単精度浮動小数点数テクスチャの場合:
internalformat = RGBA32F
,format = RGBA
,type = FLOAT
とする
完全な表はここの下の方にあります。
その他の基本的な機能は WebGL 1.0 時代のコードのままでも動いているように見えますが、何か見落としがあるかもしれないので移行の際は十分注意してください。
ぼちぼち iOS15 の方もシェア率が iOS14 を抜いてきているようなので、いよいよ全面的に WebGL 2.0 を使うようにしても問題ない時代が到来しようとしています。この時を待ちわびていた……ッ!
- Thanks to @MOUNTAINSOFMEAT ↩︎