Unreal Engine が 5 日で asm.js 化出来たと聞いて 2014/03/25 KLab ALM 余興 LT @muo_jp
Nov 29, 2014
Unreal Engineが5日でasm.js化出来たと聞いて2014/03/25 KLab ALM 余興LT @muo_jp
なかざわ けい @muo_jp
プログラミング19年目 29歳(BASIC→C→PHP→Ruby→Python→C#2.0→C#3.0→C#4.0)
社会人 10年目(20歳~)、経営学修士(IT業界の経営戦略論)
KLab 4年目(Kラボラトリー所属 C#充)
PlaygroundというゲームエンジンのOSSメンテナ
どんな話? !
100,000行のC++コードを JavaScriptに変換する話
前日譚: C++/CLIで PSVita/PSMへ移植 しようとして失敗 (2013年ゴールデンウィーク)
前回までのあらすじ
Webの未来 ~ PNaClとasm.jsでカワルミライ - いま、モバイルWebの先端で起こっていること
http://www.slideshare.net/KeiNakazawa/web-pnaclasmjs-web
2013/11/22前後に調べてた諸々をまとめたもの
前回の結論
遠くない未来に、一定以上のコンテンツがWebへ回帰するはず
現状で見えている、C/C++で書かれたゲームエンジン移植の有力選択肢はPNaClとasm.js
この先は一度やってみたほうが見えてくるはず
Playgroundのアーキテクチャ
http://www.klab.com/jp/press/130926.html
asm.js化する部分
100,000行超のC++コード
まず10時間やってみよう
8時間ほどコードを削り倒す
当然一筋縄ではいかない
(`ェ´)ピャー
abort() at Error at stackTrace (/.../playground/Engine/porting/emscripten/p.js:1032:15) at abort (/.../playground/Engine/porting/emscripten/p.js:289649:25) at nullFunc_viiiiii (/.../playground/Engine/porting/emscripten/p.js:8235:1574) at Array.b978 [as 0] (/playground/Engine/porting/emscripten/p.js:287407:99) at Object._main (/.../playground/Engine/porting/emscripten/p.js:11640:43) at Object.callMain (/.../playground/Engine/porting/emscripten/p.js:289544:30) at doRun (/.../playground/Engine/porting/emscripten/p.js:289597:25) at run (/.../playground/Engine/porting/emscripten/p.js:289612:5) at Object.<anonymous> (/.../playground/Engine/porting/emscripten/p.js:289669:1) at Module._compile (module.js:456:26)
心折れました(3月上旬)
12時間ほどおかわり
足りないライブラリのソースを拾ってきたりプラットフォーム依存部分を直したり
気合で読む 15945 $1 = 0; 15946 $2 = $argc; 15947 $3 = $argv; 15948 $width = 800; 15949 $height = 480; 15950 _emscripten_asm_const(((1168)|0)); 15951 $6 = (__Znwj(32)|0); 15952 $7 = $6; 15953 __THREW__ = 0; 15954 invoke_viiiiii(6,($7|0),((1264)|0),((1272)|0),((1280)|0),((1288)|0),((1296)|0)); 15955 $8 = __THREW__; __THREW__ = 0; 15956 $9 = $8&1; 15957 if (!($9)) { 15958 $pRequest = $7; 15959 $10 = (__ZN12CPFInterface11getInstanceEv()|0); 15960 $pfif = $10; 15961 $11 = $pRequest; 15962 __ZN15CAndroidRequest4initEv($11); 15963 $12 = $pRequest; 15964 __ZN15CAndroidRequest11setHomePathEPKc($12,(1312)); 15965 $vararg_ptr = ($vararg_buffer); 15966 HEAP32[$vararg_ptr>>2] = (1320); 15967 _emscripten_log(1,($vararg_buffer|0)); 15968 $13 = $pfif; 15969 $14 = $pRequest; 15970 $15 = $14; 15971 __ZN12CPFInterface18setPlatformRequestEP16IPlatformRequest($13,$15); 15972 (__ZN18KLBPlatformMetrics11getInstanceEv()|0);
asm.jsを読む
ifのelse側を冗長化して埋め込むケースもある。アセンブリ読んでるとよくあること。
多分MSILを日常的に読んでるC#勢からしたら朝食パンケーキ
みたいなもの
※sourcemapはちょい用途が違うので、やっぱアセンブリを読む
で、出来た結果は…
結果
Luaスクリプトの実行まで成功
glClearColorとか動く
テクスチャ描画はまだ
元版(iOS Simulator)
Chrome 33
Firefox 27
asm.js化できた部分
先は100時間+かかりそうだけど、 できそうな見立ては立った
以下asm.js読書感想文
ここがイケてるemscripten: ファイルシステムの扱い
非常に簡単にファイル群を取り込める
!
!
!
!
ちなみにWebブラウザ上で生成したデータをIndexedDB経由で永続化できる仕組みを提供してる
$ emcc -s ASSERTIONS=2 -s LEGACY_GL_EMULATION=1 --embed-file simple_item/@/install/ -O0 ./*.o -o p.html
16 Module['FS_createPath']('/', 'install', true, true); 17 Module['FS_createDataFile']('/install', 'itemimage.png.imag', [76, 73, 78, 75, 0, 0, 0, 17, 116, 101, 120, 116, 117, 114, 101, 66, 111, 97, 116 18 Module['FS_createDataFile']('/install', 'SimpleItem.lua', [102, 117, 110, 99, 116, 105, 111, 110, 32, 115, 101, 116, 117, 112, 40, 41, 13, 10, 19 Module['FS_createDataFile']('/install', 'start.lua', [102, 117, 110, 99, 116, 105, 111, 110, 32, 115, 101, 116, 117, 112, 40, 41, 13, 10, 101, 20 Module['FS_createDataFile']('/install', 'textureBoat.texb', [84, 69, 88, 66, 0, 1, 146, 43, 0, 18, 84, 116, 101, 120, 116, 117, 114, 101, 66, 1
ここがイケてるemscripten: EM_ASM();インラインアセンブリ的なインラインJavaScript
うわっと思うけど、無いより100倍マシ。良い選択
一応、この中で同スコープのポインタ触れる501 int main(int argc, char* argv[]) 502 { 503 int width = 800; 504 int height = 480; 505 EM_ASM( 506 Browser.createContext(Module.canvas, true, true, {}); 507 Browser.setCanvasSize(800, 480, true); 508 ); 509 510 CAndroidRequest * pRequest = new CAndroidRequest("model", "brand", "board", "version", "Asia/Tokyo"); 511 CPFInterface& pfif = CPFInterface::getInstance();
OpenGL ES 1.1用コールは 冷遇の姿勢
お、おう(PlaygroundはOpenGL ES 1.1+ターゲット)
4280 var glTexEnvi = (typeof(_glTexEnvi) != 'undefined') ? _glTexEnvi : function(){}; 4281 _glTexEnvi = _emscripten_glTexEnvi = function _glTexEnvi(target, pname, param) { 4282 GLImmediate.TexEnvJIT.hook_texEnvi(target, pname, param); 4283 // Don't call old func, since we are the implementor. 4284 //glTexEnvi(target, pname, param); 4285 };
emscripten/1.13.0/src/library_gl.js あたりを読む
地道デバッグ
一部を変更してem++でコンパイル、emccでJSファイルを吐き出してリロードあたりまでは10秒ぐらいでいける。かなりインタラクション速い
ただし: ChromeのJSコンソールで15万行越えのあたりにブレークポイントを止めようとすると30秒ぐらいかかる
リビルド最速を目指して
-O0コンパイル-O0リンク(8.8MiB・速い)
-O0コンパイル-O2リンク(2.7MiB・遅い)
-O2コンパイル-O2リンク(2.8MiB・遅い)
WonderSwan:emscripten keinakazawa$ ls -l p.* -rw-r--r-- 1 keinakazawa staff 101607 Mar 25 18:57 p.html -rw-r--r-- 1 keinakazawa staff 67 Mar 25 18:57 p.html.map -rw-r--r-- 1 keinakazawa staff 8967061 Mar 25 18:57 p.js -rw-r--r-- 1 keinakazawa staff 65 Mar 25 12:11 p.js.map
WonderSwan:emscripten keinakazawa$ ls -l p.* -rw-r--r-- 1 keinakazawa staff 101607 Mar 25 18:59 p.html -rw-r--r-- 1 keinakazawa staff 67 Mar 25 18:57 p.html.map -rw-r--r-- 1 keinakazawa staff 2831435 Mar 25 18:59 p.js -rw-r--r-- 1 keinakazawa staff 65 Mar 25 12:11 p.js.map
WonderSwan:emscripten keinakazawa$ ls -l p.* -rw-r--r-- 1 keinakazawa staff 101607 Mar 25 19:02 p.html -rw-r--r-- 1 keinakazawa staff 67 Mar 25 18:57 p.html.map -rw-r--r-- 1 keinakazawa staff 2941528 Mar 25 19:02 p.js -rw-r--r-- 1 keinakazawa staff 65 Mar 25 12:11 p.js.map
試行錯誤中はO0でビルド、実行成功まで 持っていくのが吉(ビルド時間が3倍ぐらい違う)
24時間付き合った印象
emscripten(asm.js)は、JSとC++で変わってくる部分をうまく吸収しつつ、時に美しく時に泥臭く(=最強)進行してるプロジェクト
残るJSのつらみ: メモリ空間共有できる マルチスレッド機構がないとpodcastで喋ってたら コメント頂いた
!
!
!
!
!
\( 'ω')/ウオオオオオアアアアアァァァーーッッッ!!https://bugzilla.mozilla.org/show_bug.cgi?id=933001
所感今のところは、携帯端末での動作よりもWindows/Mac上での動作に絞ったほうが良い結果になる(SDLと似た立ち位置)Luaのスクリプトを変更して3秒で動作確認できる(小規模なら)
開発中、どんどんリソースを書き換えていくのに良い
データサイズはやっぱり問題。けど、バックグラウンドロードをうまく実装すれば、少なくとも社内でのテスト用途には割といける?
もう少し進むと、従来WebView+…でやってたものの形が逆転する
今後に向けた手持ち課題Makefileをいい感じに整備
asm.js化するプロジェクトによって性質が異なるので、ちゃんと考える
差分ビルドをやりやすいように
emscripten固有部分をなるべく少ない箇所に閉じ込める
実行時にエラーとなりうる箇所をmake testなどで事前に掴めるように
これについては、非ブラウザ実行時(node.js実行時)にOpenGL系のコールを一切発行しないようにするなど、何かしらの手を入れる必要がある