C++ で開発していた OpenGL/OpenGL ES の Project を Emscripten でビルドしてみました。
Native Code 向けのプログラムがそのままブラウザ内で動いています。
・Windows Firefox (29.0.1)
・Android Firefox (29.0.1)
Chrome では 7 fps 程度なので asm.js (Firefox) の効果は絶大でした。
Android でも十分な速度で動いています。
追記 2014/05/24: その後 Chrome でも 60fps 以上出るようになりました。詳しくはこちら
Windows 8.1 Firefox 29 60fps 以上 (Core i7-3615QM) FireOS 3.0 Firefox 29 60fps 以上 (Kindle Fire HDX7 MSM8974) Android 4.4 Firefox 29 34fps 前後 (Nexus 7 2013 APQ8064) Windows 8.1 Chrome 34 7fps 前後 (Core i7-3615QM) FireOS 3.0 Silk 3fps 前後 (Kindle Fire HDX7 MSM8974) Android 4.4 Chrome 34 3fps 前後 (Nexus 7 2013 APQ8064) ・fps の値が大きい方が高速 ・FireOS 3.0 = Android 4.2.2 相当
もともと OpenGL ES 2.0 / EGL に対応していたこともあり、
修正箇所はごく僅かで済んでいます。
使えなかった API は pthread だけで、
追加したのは Mouse/Touch/Keyboard などの Event 周りです。
Network (socket系) とサウンドは未着手。
ソースコード数は 600 file, 26万行ほど。
Native を想定して書いたコードが、拍子抜けするほどあっさりと
ブラウザ内で動いています。
以前から何度か NativeClient (NaCl) への対応化にも取り組んでいたのですが、
あまり本質的でない部分で躓いていました。
同期型 FileIO や Thread 周りといった API 制限だけでなく、
gcc (4.4) が古くて C++11 のコードが通らなかったためです。
その後確認したところ、ARM や pnacl では比較的新しいコンパイラに
置き換わってるようです。
今回使用した Emscripten では何も問題はなく Native 向け API もそのままです。
詳しい説明は次回。
● Emscripten
Android では Java, iOS では Objective-C が使われているように、
プラットフォームによってアプリケーション記述言語はまちまちです。
ただ多くの場合 C/C++ も併用できることが多く、
C/C++ 言語 + OpenGL ES 2.0 の組み合わせは、
移植性の高い共通言語&API としての役割も担っています。
主にゲームで。
Web Browser も OpenGL ES 2.0 (WebGL) に対応しており、
NativeClient (NaCl) や Emscripten といった C/C++ のコードを
走らせる仕組みも登場しています。
C/C++ が使えるようになったことで、同じソースコードのまま
さらに多くの環境にアプリケーションを移植できるようになりました。
NaCl はコンパイルしたバイナリをブラウザ内で走らせますが、
Emscripten は仮想マシンとして JavaScript を利用しています。
ポインタを駆使した C Native なコードが JavaScript に変換されると聞いても
少々奇妙な印象を受けるかもしれません。
しかしながら JavaScript の性能向上は著しく、モバイル含めて
十分な速度で動いているこれらの結果にはやはり驚きを隠せません。
● 速度比較
1. 960×640 63万Tri/191万Vertices, 一部 Bone Animation あり
CPU GPU OS Browser fps ------------------------------------------------------------------------ Ivy Core i7-3615QM Intel HD 4000 Win8.1 x64 Win+OpenGL 100fps 前後 Ivy Core i7-3615QM Intel HD 4000 Win8.1 x64 Firefox 60fps 以上*1 Ivy Core i7-3615QM Intel HD 4000 Win8.1 x64 Chrome 7fps 前後 BayTrail-D J1900 Intel HD Ubuntu14.04 X11+OpenGL 8fps 前後 BayTrail-D J1900 Intel HD Ubuntu14.04 Firefox 5fps 前後 BayTrail-D J1900 Intel HD Ubuntu14.04 Chrome 動作せず Kabini Athlon5350 GeForce GTX650 Ubuntu14.04 X11+OpenGL 880fps 前後 Kabini Athlon5350 GeForce GTX650 Ubuntu14.04 Firefox 30fps 前後 Kabini Athlon5350 GeForce GTX650 Ubuntu14.04 Chrome 4fps 前後 APQ8064 Krait 1.5GHz Adreno 320 Android4.4 NDK+ES3.0 50fps 前後 APQ8064 Krait 1.5GHz Adreno 320 Android4.4 Firefox 34fps 前後 APQ8064 Krait 1.5GHz Adreno 320 Android4.4 Chrome 3fps 前後 Exynos5D Cortex-A15 Mali-T604 Android4.4 NDK+ES3.0 60fps 前後 Exynos5D Cortex-A15 Mali-T604 Android4.4 Firefox 8fps 前後 Exynos5D Cortex-A15 Mali-T604 Android4.4 Chrome 動作せず MSM8974 Krait 400 Adreno 330 FireOS 3.0 Firefox 60fps 以上*1 MSM8974 Krait 400 Adreno 330 FireOS 3.0 Silk fps 前後 ・fps の値が大きい方が高速 ・*1 : VSyncの上限 ・Firefox 29, Chrome 34 ・FireOS 3.0 = Android 4.2.2 相当 ・APQ8064 = Nexus 7(2013), Exynos5D = Nexus 10, MSM8974 = Kindle Fire HDX7
2. 960×640 63万Tri/191万Vertices
CPU GPU OS Browser fps ------------------------------------------------------------------------ Ivy Core i7-3615QM Intel HD 4000 Win8.1 x64 Win+OpenGL 110fps 前後 Ivy Core i7-3615QM Intel HD 4000 Win8.1 x64 Firefox 60fps 以上*1 Ivy Core i7-3615QM Intel HD 4000 Win8.1 x64 Chrome 10fps 前後 BayTrail-D J1900 Intel HD Ubuntu14.04 X11+OpenGL 8fps 前後 BayTrail-D J1900 Intel HD Ubuntu14.04 Firefox 5fps 前後 BayTrail-D J1900 Intel HD Ubuntu14.04 Chrome 動作せず Kabini Athlon 5350 GeForce GTX650 Ubuntu14.04 X11+OpenGL 900fps 前後 Kabini Athlon 5350 GeForce GTX650 Ubuntu14.04 Firefox 30fps 前後 Kabini Athlon 5350 GeForce GTX650 Ubuntu14.04 Chrome 6fps 前後 APQ8064 Krait 1.5GHz Adreno 320 Android4.4 NDK+ES3.0 50fps 前後 APQ8064 Krait 1.5GHz Adreno 320 Android4.4 Firefox 30fps 前後 APQ8064 Krait 1.5GHz Adreno 320 Android4.4 Chrome 5fps 前後 Exynos5D Cortex-A15 Mali-T604 Android4.4 NDK+ES3.0 60fps 以上*1 Exynos5D Cortex-A15 Mali-T604 Android4.4 Firefox 8fps 前後 Exynos5D Cortex-A15 Mali-T604 Android4.4 Chrome 動作せず MSM8974 Krait 400 Adreno 330 FireOS 3.0 Firefox 60fps 以上*1 MSM8974 Krait 400 Adreno 330 FireOS 3.0 Silk 5fps 前後 ・fps の値が大きい方が高速
3. 960×640 6万Tri/19万Vertices
CPU GPU OS Browser fps ------------------------------------------------------------------------ Ivy Core i7-3615QM Intel HD 4000 Win8.1 x64 Win+OpenGL 520fps 前後 Ivy Core i7-3615QM Intel HD 4000 Win8.1 x64 Firefox 60fps 以上*1 Ivy Core i7-3615QM Intel HD 4000 Win8.1 x64 Chrome 15fps 前後 BayTrail-D J1900 Intel HD Ubuntu14.04 X11+OpenGL 60fps 以上*1 BayTrail-D J1900 Intel HD Ubuntu14.04 Firefox 5fps 前後 BayTrail-D J1900 Intel HD Ubuntu14.04 Chrome 動作せず Kabini Athlon 5350 GeForce GTX650 Ubuntu14.04 X11+OpenGL 1020fps 前後 Kabini Athlon 5350 GeForce GTX650 Ubuntu14.04 Firefox 38fps 前後 Kabini Athlon 5350 GeForce GTX650 Ubuntu14.04 Chrome 9fps 前後 APQ8064 Krait 1.5GHz Adreno 320 Android4.4 NDK+ES3.0 60fps 以上*1 APQ8064 Krait 1.5GHz Adreno 320 Android4.4 Firefox 60fps 以上*1 APQ8064 Krait 1.5GHz Adreno 320 Android4.4 Chrome 8fps 前後 Exynos5D Cortex-A15 Mali-T604 Android4.4 NDK+ES3.0 60fps 以上*1 Exynos5D Cortex-A15 Mali-T604 Android4.4 Firefox 9fps 前後 Exynos5D Cortex-A15 Mali-T604 Android4.4 Chrome 動作せず MSM8974 Krait 400 Adreno 330 FireOS 3.0 Firefox 60fps 以上*1 MSM8974 Krait 400 Adreno 330 FireOS 3.0 Silk 8fps 前後 ・fps の値が大きい方が高速
描画負荷を変えても速度が大きく変わらないものは CPU (JavaScript) 側の
限界と思われます。
Chrome, BayTrail-D, Kabini, Exynos 5D(Nexus10) が相当します。
また Native との差が少ないものは GPU 側も上限に近いことを意味しています。
Animation ありの場合骨の計算分 JS の負担が増えます。(頂点blendは GPU)
GPU に余裕があるのに 1. と 2. で大きく差が生じる場合は、
JavaScript の演算能力にも余裕がないことを示しています。
BayTrail-D は描画と CPU (JS) 両方共限界に達しているようです。
Native (X11+OpenGL) の 1./2. のテストでは、画面拡大時に速度が上がり、
オブジェクトが画面に全体が収まる場合は Firefox に近い速度まで下がります。
おそらく頂点性能が足りていないと思われます。
また同時に BayTraild の Firefox では拡大しても速度が上がらず一定なので、
JavaScript の動作速度も制限となっていることがわかります。
倍精度浮動小数点演算が苦手なことも影響しているかもしれません。
Kabini は外付けの GeForce がオーバースペック過ぎて CPU が追いついていません。
CPU (JS) 自体は BayTrail よりはかなり速いようです。
Android の Native (NDK+ES) は Fullscreen 動作となり、1920×1200, 2560×1600 で
レンダリングしているため他と条件が異なっています。
JavaScript の動作は Cortex-A15 よりも Krait/Krait 400 の方が高速でした。
Krait (APQ8064) は 3. のテストで制限がかかっていないため、
JavaScript 自体は余裕があるものの GPU で落ちているようです。
GPU の演算能力に余裕がある Krait 400 (MSM8974) はどのテストでも 60fps
超えています。
Desktop の Kabini よりも Krait/Krait 400 の方が JavaScript の
動作速度が速いこともわかります。
Cortex-A15 の速度が全然出ていませんが、VFP Benchmark の結果でも
倍精度演算は Krait の方が良い結果を出しています。
ただそれ以上に大きく差が開いているので、他にも何らかの要因があるのでは
ないかと思われます。
同じ Cortex-A15 のTegra Note 7 (Tegra4) でも試したかったのですが
RAM 容量が足りず動きませんでした。
今のところ Android + Firefox の組み合わせは RAM 2GB でぎりぎりです。
まだビルドを通したばかりでプロジェクトが巨大なせいもあります。
● まとめ
・Emscripten で C/C++ と OpenGL ES 2.0 のコードがブラウザ上でそのまま走る
・Firefox なら速度も速い (asm.js のおかげ)
・Android でも Firefox + Krait は高速動作 (Cortex-A15 が遅い原因は不明)
追記 (2014/05/20) : Tegra4 (Cortex-A15) では高速に動作することを確認しました
追記 (2014/05/24) : Chrome で速度が遅かった原因がわかりました。修正により 60fps 以上出ています。
26万行が簡単な修正で動くとは驚異的ですね!そして想像以上にFirefoxとChromeに差があり驚きです。
移植は 2日かかっていないので本当に簡単でした。
Firefox が速いのは asm.js のおかげです。
JavaScript の構文の範囲で型を特定できる情報を埋め込み、
JIT の最適化に利用するそうです。
本文中肝心なところで名前が間違っていたので修正しました
(asm.js が asj.js になっていた)
26万行が2日とはすごいですね。
Chromeがasm.jsを直接対応する訳ではないけど、asm.jsなコードもベンチマークに入れたみたいなニュースは昔見ました。
良く出来ています。
言語の変換だけでなくライブラリの再現度も高くて
Emscripten はすごいです。
Chrome でも今後効果が出るならさらに期待できそうですね。
もしかしたら、今速度が出ていないのは
何らかの設定を見逃してるだけかもしれません。
たしかに。それだけライブラリも用意しているっていうのもすごいですね。