entry-header-eye-catch.html
entry-title-container.html

entry-header-author-info.html
Article by

6年を経てFloat16ArrayをStage 3にしてもらった

こんにちは。福岡オフィスエンジニアの @petamoriken です。趣味でFloat16Arrayのponyfill1を公開しECMAScriptに入れてもらうように活動していたところ、喜ばしいことに2023年5月のTC39会議にてStage 3となりました。折角なのでその経緯を書いていこうと思います。

github.com

半精度浮動小数点数とは

コンピュータで実数を扱うときに主に使われるのが浮動小数点数です。通常IEEE 754-2019で定義される基本形式の倍精度浮動小数点(binary64)と単精度浮動小数点数(binary32)が使われます。例えば一般的なCの処理系だとdoubleがbinary64でfloatがbinary32ですね。JavaScriptのnumberはbinary64です。

さて基本形式ではないですが半精度浮動小数点数(binary16)が定義されています。仮数部が10ビットと少なく十進数換算でおよそ3桁程度しか扱えませんが、少ないメモリ量でGPUに転送し高速に演算することができます。またモダンなCPUでは変換命令が搭載されています。

Float16Arrayの提案とponyfill

遡ること2014年11月。TypedArrayがThe Khronos GroupのWebGLの仕様からES2015のドラフトに移す作業がなされているさなか、ES DiscussでユーザーからFloat16Arrayの提案がなされました2

esdiscuss.org

WebGLではもともとテクスチャや頂点データ、レンダーバッファにbinary16が扱えます。これはグラフィックス文脈でbinary16がよく使われており、2004年時点ですでにOpenGL拡張で扱えるようになっていた3背景があります。

一方でECMAScriptではbinary16が扱えず、WebGLにUint16Arrayとして与えなければなりません。つまりnumberからbinary16のバイト列への変換を自前で用意する必要がありました。

しばらく議論が止まってしまっていましたが、たまたま自分がWebGLとES2015を勉強中にこのトピックを見つけ、Proxyを使ってFloat16Arrayのponyfillを作ることを思いつきました。Proxyはオブジェクトの操作をトラップすることができる機能です。例えばプロパティアクセスの[[Get]]をトラップしてみると以下のようになります。

gist.github.com

Proxyを使ってUint16Array[[Get]][[Set]]をトラップし、その都度変換することでFloat16Arrayのponyfillを作ることができます。ただいざ実装してみるといくつか難しいところがありました。

  • ECMAScriptはnumberに対してビット演算を使用した場合32ビット整数に丸める仕様となっているため、変換には工夫する必要があった4
  • TypedArrayのメソッドはJavaScriptからアクセスできない内部スロットを多用する仕様となっており、これはProxyではトラップできないためほとんどのメソッドを再実装する必要があった。
  • 当時JavaScriptCore (Safari)でTypedArrayの整数インデックスに対してのディスクリプタがwritable: falseになっていたり、ChakraCore (Microsoft Edge)でWeakMapのキーにProxyが使えなかったりするバグがありワークアラウンドを実装する必要があった。

これらをなんとか解決しponyfillを公開したところ反響があり、Leo Balterさんに2017年5月のTC39会議に取り上げてもらいStage 1となりました。 qiita.com

6年の沈黙、そして

Stage 1になったのは良かったものの残念ながらネイティブに実装する必要性が認められずなかなか進みませんでした。

転機が訪れたのは2022年11月。W3Cのthe Color on the Web Community Groupよりハイダイナミックレンジ(広輝度)、広色域の色をCanvasの2DコンテキストとImageDataで扱うため、8ビット符号なし整数として格納している仕様を拡張してbinary16として格納出来るようにする提案がなされました。

Display-P3色空間のサポート

今までWeb上で扱える色空間はsRGBに限られていました。Appleによって新たに定義されたDisplay-P3色空間を扱えるように推し進められ、今では全てのモダンブラウザでCSS Color Module Level 4のcolor函数を使うことが出来るようになっています5

webkit.org

仕様の中でこのcolor函数を使う場合に最低でも10ビットの精度を要求しており、16ビット以上の浮動小数点数を使って格納することを推奨しています6。また取りうる値も[0, 1]でクランプされず、領域外の色も扱うことができます。

浮動小数点数で色を扱う提案

CSSに倣ってWebGLやCanvasの2DコンテキストにもDisplay-P3色空間を扱う仕様が盛り込まれています7。しかし浮動小数点数で色情報を格納する仕様がまだないためそれぞれで提案されています。

ここではCanvasの2Dコンテキストの方を紹介します。なお将来仕様が変更される可能性があることに注意してください。 github.com

Canvasの2Dコンテキストを作る際のオプションにcolorTypeが追加されます。取りうる値としては"unorm8""float16"です。

gist.github.com

さてCanvasの2DコンテキストではImageDataを使って各ピクセルの色に直接アクセスすることが出来ます。colorType: "unorm8"の場合はUint8ClampedArrayを使いますが、colorType: "float16"の場合はどうすればいいでしょう。残念ながら提案ではFloat16Arrayがないためやむを得ずFloat32Arrayを用いるとされています。

gist.github.com

そこでStage 1のまま停滞しているFloat16Arrayを紹介し、これを進めていかないかと提案してみました。 github.com

好意的に受け止めてもらい、WHATWGなどで取り上げてくれました。その結果Kevin GibbonsさんにTC39の提案の方を進めてもらうこととなり、2023年3月にStage 2に、5月にStage 3になりました🎉

終わりに

このFloat16ArrayはWebGPUやWeb Neural Network APIでも好意的に受け入れられているようです8。ECMAScriptに入ったら色々なWebの仕様で使われることになるのかなと思います。

さてピクシブ株式会社ではECMAScriptやWebの仕様を追いかけるのが大好きなフロントエンドエンジニアを募集しています。フロントエンド互助会の取り組みによって部署を横断して情報共有しており、とても楽しい環境だと思います!

hrmos.co hrmos.co


  1. polyfillに対してグローバルやプロトタイプをモンキーパッチせずにモジュールとして提供するものをponyfillと言います。
  2. このES Discussはすでに運用を停止しています。現在はユーザーから新しい仕様を提案する際にはES Discourseが用いられています。
  3. GL_ARB_half_float_pixelを参照。
  4. 今はES2020 BigIntを使うことで32ビットよりも大きい値に対してビット演算できます。
  5. もちろんハードウェアが対応していなければ表示することが出来ません。判定にはcolor-gamutメディアクエリを使います。
  6. https://www.w3.org/TR/css-color-4/#serializing-color-function-valueを参照。
  7. Add color space support for CanvasRenderingContext2D, ImageData, and ImageBitmapAdd drawingBufferColorSpace/unpackColorSpace attributes to WebGLRenderingContextBaseを参照。
  8. ありがたいことにWebGPU Conformance Test Suiteで自分のponyfillが採用されています。
20191219014119
petamoriken
2017年7月から福岡オフィスでアルバイトをしている。元々フロントエンドエンジニアだったがiOSアプリやサーバーサイドの開発も携わっている。 午後のおやつと同じくらいECMAScriptやDOM APIの仕様を追うのが好き。百合男子。腐男子。