2011年12月10日

環境依存部分

Linux で動作を確認したらパス周りでうまくいかないところがあった。
File.separatorChar で区切りを分けることはしていたけど、ドライブレター周りの処理が不完全だった。
対処はしたけれど、もう少し処理を見直した方が良さそう。

また、文字表示をしようとした時もフォントの指定に不備があったようで思うように出なかった。
吉里吉里2でデフォルトになっているMS Pゴシックを探して見付からなかったら、Locale.JAPAN で列挙したリストの一番上のものを指定していたけど、うまく出なかった。
Font.MONOSPACED を指定するようにしてもうまくでず。
仕方ないので、フォント候補を列挙してその中のものを探すようにした。
IPA や VL、さざなみを入れたので、Linux だとだいたい見付かると思うが……
Mac OSX はよくわからないので、ヒラギノ角ゴとヒラギノ丸ゴを入れておいた。
日本語を表示できるフォントをうまく指定できる方法があるといいんだけど。
swing のウィンドウタイトルとか日本語表示しているので、その辺りのプロパティから引っ張ってくるのがいいんだろうか。
まあ、選択するフォントリストを明示するのが確実と言えば確実だけど、汎用的な方法があるのならその方がいいし。
ちなみにリスト全て見付からない時は Font.MONOSPACED で指定されたものを使う。
確実なのはIPAフォントを内蔵してしまうことだけど、ちょっと大きいのでその辺りは選択肢として別に用意しておいた方が良さそう。
フォントを含めておいてデフォルトはそれにする等。

まだ実装はしていないけれど、動画周りもネック。
Android は別にいいとしてそれ以外の Java環境でどうするか。
検索してもあまりいいものは見付からず。
Java でデコーダー書くのはさすがに厳しいと思うので、ここはJNIを使ってしまうかもしれない。
作りかけの動画再生エンジンを使って、マルチプラットフォームのデコーダー書くつもり。

Android とその他ではグラフィック周りやWindow周りでいろいろと依存部分があるけど、とりあえず割愛。
そこは分離して作ってはいる。

投稿者 Takenori : 16:52 | トラックバック

2011年12月13日

プラグイン

ソースコードを加えてコンパイルしてしまえば、プラグイン機構はなくてもいいと言えばいいんだけど、あった方が良いことも確か。PC なら、jar ファイルからクラスを読み込んで動的にロード出来るので、その辺りのハンドリングを決めておけば実現することは難しくない。
リフレクションによって、Java のクラスを TJS2 のクラスに登録してしまえるので、クラスの追加は楽に行える。

問題は、Android 。
以前、LGPL の ライブラリを使った時は、動的読み込みにするために Android のサービスによる RPC を使った。
計測はしていないが、RPC なのでオーバーヘッドが気になる。
で、以前少し TJS2 を Delvik バイトコードにして動かすことについて調べていた時に、ClassLoader に Delvik バイトコード食わせてクラスをロード出来るのを思い出し、検索してみたら普通に dex ファイルからクラスを読めるようだ。
以下参考。

Custom Class Loading in Dalvik(翻訳)
アプリケーションパッケージ(.apk)中のクラスを列挙する
アプリケーションパッケージ(.apk)中のクラスを列挙してロードする

これが出来るのなら Android でもプラグインを作ることが出来るな。
吉里吉里Javaとは関係ないけど、サービスを使っていたものはこっちに切り替えようかなと思った。

リフレクションによる呼び出しのオーバーヘッドは、TJS2 の関数呼び出しから見た場合、それほど大きくない。
TJS2 の関数呼び出しとループ処理(TJS2から複数回呼び出しのためのループ)を合わせて計測した場合、リフレクションにしたことによるオーバーヘッドは10%強程度。
周辺部にある程度呼び出しコストがあるためか、リフレクションのオーバーヘッドは意外と少ない。
集中的に呼び出した場合でこれくらいなので、実使用で組み込み関数呼び出しばかりするような状況でなければ、それほど気にすることはないようだ。
ただ、TJS2 の組み込みクラスはリフレクションではなく、メソッドごとにクラスが作られるスタイルで実装したが(オリジナルのC++のTJSと同じような形)。
リフレクションと比べた場合のデメリットは、クラスが増大するためかバイナリサイズは大きくなることと、ちょっと記述が面倒なこと。
オリジナルはマクロで軽減しているが、Java はマクロがないのでちまちま書く必要がある。
無名インナークラスがあるおかげで、C++ で書くのに比べたらかなりマシだけど。
リフレクションなら、簡略化しようとしたら登録関数一発で Java のクラスを TJS2 のクラスに出来るけど。

初期リリースにはプラグイン機能を入れないつもりでいるけど、入れられることがわかったので良かった。

投稿者 Takenori : 18:47 | トラックバック

2011年12月20日

KAG3 のコンパイル時間とバイトコード

オリジナル : KAG3 のスクリプト読み込みに 90ms、KAGMainWindow のコンストラクタに 55ms。
吉里吉里Java : KAG3 のスクリプト読み込みに 335ms、KAGMainWindow のコンストラクタに 633ms。
Java 版遅い。
コンパイル時間は最初計測した時、600ms強だったんだけど、System.gc をコンパイルごとに呼ぶのを止めたら倍くらいに早くなった。
強制的に GC を呼ぶのはよくタイミングを考えた方がいいか。

コンパイルが遅いのはある程度予想していた。
オリジナルは文字列のポインタを直接読んで字句抽出しているところを Java版では文字アクセスクラス経由で実装しているのでそこは少し遅いはず。
Java だと配列にしても範囲チェックが入ってしまうのもある。
ここはもっとダイレクトにアクセスするようにした方がいいかもしれないが、ネイティブに比べて 3.7倍程度で済んでいるので、それほどコンパイル遅いと言うことはない気もする。
後、構文解析器がオリジナルは bison なのに比べて。Java 版は再帰下降法で手書きされている。
昔に読んだ yacc の本では、yacc は手書きよりも少し効率が劣ると書いていたけど、今どうなんだろう?
flex は、手書きよりも効率の良いコードを出力するとその時でも書いてた。
ただ、私が書いたパーサーが早いかどうかはわからないが。

TJS2 のベンチマークでは、使用している PC に比べて Android は80倍くらい遅かったので、それをそのまま当てはめて考えると、コンパイルに 26秒もかかってしまう。
これはちょっと致命的。
ただ、KAG3 のコンパイルは別に実行時にやる必要はないので、バイトコードを入出力できるようにすれば、ここは極めて高速に処理できるようになるはず。

で、KAGMainWindow のコンストラクタ。
何でこんなに遅いのだろうか。
何が遅いのか調べて最適化しないまずい。
Window周りの設定は、Android だとほとんど無視というか、出来ないのでそこがネックになっているのなら、Android はそれほど遅くないことになりそうだけど、調べてみないことには何とも言えないな。
コンパイル時間はバイトコード使うことで大きく短縮できるが、KAGMainWindow のコンストラクタは地道に最適化しないとどうにもならない。

投稿者 Takenori : 18:42 | トラックバック

2011年12月23日

クラスのロードが遅い

KAGMainWindow のコンストラクタが遅い問題でいろいろと速度を計測していると、どうもクラスのロードが遅い様子。
ネイティブのWindow クラス ( JFrame を継承したクラス ) のロード自体に 120ms 程度かかっていたので、JFrame を new してみると最初の1回目だけ遅い。
そこで KAGMainWindow を2回作ってみると、2回目のコンストラクタは 162ms になった ( 2回同じところを通ることで特定部分についてはJIT等の影響があるかもしれない )。
オリジナルの 55ms に比べると3倍程度かかっている。

TJS2 のコンパイル時間と合わせて推測すると、純粋に処理時間としてはネイティブに比べて、Java は3倍程度なのだろうか?
ベンチマークで速度が出るのは、JIT によってネイティブコードになることで、30%程度の速度差で済んでいるのかもしれない。

クラスのロードが遅いとすると、Android では Window は違った傾向を示すと言うか、このタイミングで遅くなることはないはず。
Android は、最初に生成された Window を Activity と関連づけるようにする予定なので、KAGMainWindow のコンストラクタが呼ばれる時点では、既に Activity はロードされている。
だから、ここでクラスロードに時間がかかる可能性は低い。
ただ、クラスロード時間がないと思われる 162ms を基準にしたとしても、この時間の 80 倍時間がかかると 13秒もここでかかってしまう ( Activity 以外のクラスロードはあるのでもっと遅いかもしれない )。
スプラッシュを何個か出せばごまかせなくはないかもしれないけど、これは辛い。
Android の場合、Window 周りの処理やメソッドの多くは無効というか設定できなくなるので、この辺りで少しは高速化されるかもしれないが……
地道に最適化していくしかないか。
そのままでも動くけど遅い、KAG3 にある程度手を入れてプリプロセッサで切り分けて、Android 版は軽量化を図るとかになってしまうかもしれないな。
Android からすると要らない(無効な)処理が、KAG3 には入っているのでそれらを削れるだけでもだいぶ違うはず。
どうにもならなくなったら、Dalvik バイトコードへの AOT コンパイルか。

投稿者 Takenori : 19:11 | トラックバック

2011年12月28日

direct threaded code

VM を素直に実装すると、switch-case で命令を振り分けて対応する処理をするような作りになる。
これを switch-case で振り分けるのではなく、命令(だいたい単なる整数)に処理が書かれた先のアドレスを埋め込み、そこへ直接ジャンプすることで、switch-case をなくす。
さらに、そこから次の命令に飛ぶ時、その命令の処理の末尾で直接次の命令の処理先へジャンプすることで、一度命令を振り分けるところに戻る処理を省ける。
この辺り threaded code や direct threaded code などと言うテクニックのようだ。
説明は、YARV Maniacs 【第 3 回】 命令ディスパッチの高速化を読むとわかりやすい。
後、Context threading と言うのもあるよう。

なるほど、そう言うテクニックがあるのかと思ったけど、Java では書けない。
C言語でも厳しい。
gcc だとラベルのアドレスを拾って、そこへ飛べる拡張があるようで、gcc なら C言語で書けるようだ。

高速化のために調べたものの、吉里吉里Java では使えそうにないことがわかった。
Java や Dalvik バイトコードを出力できるようにすれば大丈夫だけど、その場合、switch-case とかそう言う話ではなく、TJS2 のバイトコードではなくなってしまって、JVM や Dalvik VM で命令部分が処理されることになるんだけど。

その他参考リンク

SquirrelFish Extreme 高速化の話
direct threaded code
direct threadingでフィボナッチ計算
VM の高速化のために Context Threading を学ぶ - Scheme VM を書く

投稿者 Takenori : 21:13 | トラックバック

2012年01月01日

Android での起動時間

Android で KAG3 がある程度動くようになったので、速度を計測した。

Xperia arc (Android 2.3.3) で測った結果

KAG3のコンパイルに 14秒。
KAGMainWindow のコンストラクタに 2秒。
吉里吉里の初期化全体で 19秒。

コンパイルとコンストラクタ以外の部分で 3秒かかっている計算になる。
TJS2 のバイトコード読み込みに対応して、事前コンパイルしておけば、起動は 5秒程度まで縮まるだろうか?
コンパイル時間にはトップレベルスクリプトの実行時間も入っているので、14秒が丸々なくなることはないだろうと思われるが。
2.2 未満の場合もっと時間がかかりはず。

まあ、何にしても 5秒程度であれば許容範囲だろう。
スプラッシュを出せばそれほど気にならない範囲ではある。
Dalvik バイトコードを出力しなくても、TJS2 の事前コンパイルのみでも何とかなりそうなことはわかった。

ただ、開発中は何度も再起動するので、起動時間の短縮は開発中にいろいろとがんばるはず。

投稿者 Takenori : 23:53 | トラックバック

2012年01月03日

OutOfMemory対策

Android で画像を読み込んだり、生成した時によく発生する OutOfMemoryError 。
本当にメモリが足りない時はどうしようもないが、解放されずに残っているオブジェクト等の影響で確保に失敗することがある。
このような状況を避けるためには、単純に以下のようにして1度目は OutOfMemoryError 例外をキャッチして、GC を実行し、再度確保を試みるようにすれば、成功することが多い。
当然、自身で多くのメモリを使用している場合はどうしようもない。

try {
 mImage = Bitmap.createBitmap( w, h, Bitmap.Config.ARGB_8888 );
} catch( OutOfMemoryError e ) {
 java.lang.System.gc();
 // 2回目は try-catch せず、2回目も失敗した時は諦める
 mImage = Bitmap.createBitmap( w, h, Bitmap.Config.ARGB_8888 );
}

後、Bitmap は不要になった時、bitmap.recycle(); して、null を代入しておいた方が良い。
recycle() は、ネイティブオブジェクト(ピクセルデータ) との関連を強制的に断ち切る。
Bitmap オブジェクトが他で参照されていても GC が発生したら、ピクセルデータは解放される。
当然、再使用しようとしたらエラーになる ( isRecycled で使えなくなっているかどうか確認できる )。

吉里吉里Java だと、上記の GC を試してみる前に、System.doCompact 処理を実行して、内部のキャッシュを参照解除 して、GC で解放されるようにする。
これで出来るだけメモリを空けて Bitmap の読み込みや生成が成功するようにする。
このような処理で OutOfMemoryError で落ちる確率は減るが、GC が走ると数百msec程度処理が止まる。
落ちるよりはマシだが、カクつく原因にはなる。

再読込等で生成しやすい画像データは、読み込んだ後 recycle() をあらかじめ実行して置いて、必要時にはisRecycled でまだ使えるかチェックして、使えなくなっている時は再度読み込んで必要な処理を行うことで、より OutOfMemoryError に陥る可能性を減らせるようだが、頻繁に再読み込みが発生すると困るので、このような処理は行っていない。
メモリ的に厳しいようであれば、このような処理も検討する。
実際問題として recycle() した後どの程度解放されてしまうのかによっては、普通に使用しても問題ないかも知れないが、ドキュメントを見る限り次に GC が実行されたら消えてしまうように思える。
2回目もメモリ確保に失敗した時は、ロードしやすいデータは recycle() してしまい、OutOfMemoryError で落ちる確率をより減らすような処理があった方がいいかもしれない。
ただ、その場合は今表示されているデータが解放されてしまい、ロード地獄に陥るかも知れないが。

投稿者 Takenori : 22:05 | トラックバック

InterCodeContext を分離

オリジナルの tTJSInterCodeContext と同様に、吉里吉里Java も InterCodeContext でコード生成と実行時のオブジェクト実体 ( VM ) の両方を兼ねている構造だったが、TJS2 バイトコードを読み書きしやすいようにするためと、見通しを改善するために、InterCodeGenerator と InterCodeObject に分離した ( TJS2の disassmbler も一体化していたんだけど、こっちも分離済み )。
分離に伴い、InterCodeGenerator から InterCodeObject を生成する時に少しのコピーとオブジェクトの置き換えが発生するために、少しコンパイル速度は低下しているはず。
ただ、PC 上では特に速度低下はみられなかった。
全体に占める割合からすると微々たる処理なのかもしれない。
後、ScriptBlock と言うのがあって、これはだいたいtjsファイルと1対1で対応するのだが、これもコンパイル処理と実行時に InterCodeObject を束ねる機能を兼ね備えている。
別に一体になっていてもいいが、見通しと実行時のメモリ使用量の削減 ( 微々たるものだが ) のために分離を考えている。

これらを分離すれば、Compiler + InterCodeGenerator → ScriptBlock + InterCodeObject と言う通常の TJS2 コンパイル動作と、バイトコードローダーから ScriptBlock + InterCodeObject を生成する処理を綺麗に分けられる。
生成元がTJS2コンパイラかTJS2バイトコードローダーかに関わらず、同じように実行時のオブジェクトを生成出来る形になる。
また、InterCodeGenerator は、CodeGenerator インターフェイスを継承するクラスにしようと思っている。
実際のバイトコードを生成するクラスを抽象化することで、TJS2 バイトコードのみでなく、Dalvik バイトコード等を生成しやすい構造にして行く狙い。
実際に Dalvik バイトコード を生成するクラスを作るかどうかはともかくとして。

このような処理によってコンパイル性能は若干低下するかも知れないが、リリース時はバイトコードを直接読む形になるであろう事から、それほど問題はないと考えている。
PC の場合は、あまり速度低下自体見られないようだし問題はないと思う。

投稿者 Takenori : 22:56 | トラックバック

2012年01月13日

バイトコードファイル読み込み

現在以下仕様で実装している。
Script.execStorage Script.evalStorage で指定されたファイルの拡張子が tjb ならバイナリバイトコードファイルとみなして読み込む。
拡張子 tjs の時は、同名で拡張子が tjb のファイルがあるかどうか見て、あったら tjb ファイルをバイナリバイトコードファイルとして読み込む。
上記、条件に合わない時は tjs ファイルをスクリプトとして読み込む。

この仕様について、以下のような提言を受けた。
----
いまどきの実装なら拡張子依存ではなく、マジックコード読んで処理するほうが良い。
a. 内部的なロード処理ではマジックコードでどちらでも読めるようにする
b. ファイル名のマッピングを差し替えれる機構をプラグ的に準備(拡張子を差し替えて優先ロード、とか) はそれぞれ別にする。
勝手に指定してないファイルが読まれる可能性がある仕様は個人的には好ましくない。
----

この部分の仕様は開発をどのように進めるかが仕様決定の主因であり、それを想定して仕様を決めた。
拡張子依存ではなく、マジックコードで~と言うのは確かに一理ある。
一応、バイトコードかどうかの判定は、埋め込まれた FourCC 等によって判別はしているが、一致しなかった場合にスクリプトとみなしてコンパイルを試みる等の動作はしていない。

スクリプトをそのままコンパイルして、コンパイルしたバイナリをデータフォルダに突っ込めば、勝手にそっち読んで動いてくれる。
バイナリとスクリプトを見分けやすい。
バイナリファイル消せば、スクリプト読んで動いてくれる。
これらの動作を元に仕様を決めた。

提案のあった内容は、どのような使い方を想定したものなのだろうか?
拡張子ではなく、マジックコードで判定するのはいいとして、引き渡すファイルの拡張子は tjs にするんだろうか?
だとしたら、スクリプトの tjs ファイルと、バイトコードになった tjs ファイルが混在して混乱の元だと思うけど、そこは気にしないのだろうか?
もしくは、引き渡すファイルの拡張子を元々 tjb 等として、バイトコード前提のスクリプトを別に準備するのだろうか?
バイトコード前提のスクリプトを別に準備するとなると、常にコンパイルしなければならなくなるが……
毎回コンパイルを避けるとなると、TJS で自前で tjb ファイルがあったらそっちを読む等の判定処理を書いて、それを使う前提なのだろうか?
TJS で自前で書とある程度スクリプトに修正を入れることになるが、上述した中ではこの使い方が一番現実的か。

拡張子ではなく中身で判別するべき。
指定していないファイルが読まれるのは好ましくない。
と言う二点によって、提案されたもので実際の用法を考慮に入れたものではない可能性もある。


再度仕様を考える。
1. 指定されたファイルの中身でファイルの種類を判別し、スクリプトかバイトコードとして読み込む。
2. バイトコードでない場合は、tjb ファイルを探すが、探すかどうかはオプションによって切り替え可能とする。
このような形が妥当か?
ただ、この場合 TJS2 以外のスクリプトを許容するようになった時に破綻するが、その時は拡張子で判定を再び復活させるか、別のメソッドにすることになる。
まあ、そうなることはなかなかなさそうだけど。
後、推奨する使用法は最初に書いた拡張子を使ってくださいと言うことになるかな。

----
追記
開発時はスクリプト、リリース時にバイナリ化すると言う運用なので、ファイル名は同名で問題ないと言うことのようだ。
運用上混在しない形だったのか。
ただ、Android だとコンパイル時間がある程度かかるので、事前コンパイルしておいた方が良いと言うことについては、フォルダを分けで make 等使い毎回コンパイルするようにして回避すると言う案のよう。

投稿者 Takenori : 18:15 | トラックバック

2012年01月24日

サスペンド / リジューム

Android での起動の高速化のためにあれこれ考えているが、起動した直後の状態を直接構築出来れば、それが一番速い。
startup.tjs を実行した直後の状態が起動直後の状態なので、その時の状態を保存して、その状態を復元することが出来れば理屈上は最も速く起動が完了するはず。
ただ、環境によって起動状態を変更する必要が生じた時に対応出来ない。
依存しそうなものは、画面解像度とSDのパス辺りかな。
依存部分だけ保存しない作りにすればいいが、後々維持管理が大変になる可能性が高い。
startup.tjs 実行直後の状態を保存して~と言うのは避けた方が良さそう。

いつでも状態を保存して復元できると言うのは、Android だとあるとかなり助かる機能なのは間違いない。
他のアプリが立ち上がってバックグラウンドに行って、気付いたら死んでることがあるので、オートセーブは必要になるだろうし、スマートフォンの用法を考えるといつでも中断/再開が出来るとありがたい。

理屈上はシリアライズに対応すればサスペンド / リジュームに対応出来る。
シリアライズの問題点として、オブジェクトを保持している時はいいとして、参照しているだけの場合どのようにしてその状態を保存し復元するのかと言うものがあり、これは2パスで保存するしかないと思っていた。
少し調べて見ると、これは 時空を越えるオブジェクト の一番下の方法によって解決出来ることがわかった。
後、問題となるのはレイヤー画像の保存。
読み込んだままの画像はファイル名を覚えておけばいいが、変更が加えられている場合は画像そのものを保存しなければならない。
メッセージレイヤーは確実に画像を保存することになると思われる。
他には、TJS2 で記述された各種オブジェクトのバイトコードの保存と読み込み、全クラスでのシリアライズ処理のサポート。
保存と復元に時間がかかりすぎるとあっても無意味。

サスペンド / リジューム出来るメリットは大きいが、その分実装とテストも大変。
対応するかどうか悩むところ。

投稿者 Takenori : 17:39 | トラックバック

2012年01月28日

Layer.affine 系

吉里吉里2の Layer.affine 系のメソッドは、matrix 指定と頂点指定のどちらかを選べる。
吉里吉里2では、matrix で指定された場合、頂点に変換して、頂点を使って描画する。

吉里吉里Java の場合は、常に matrix を指定して描画する。
そのため頂点指定の場合は、頂点から matrix を割り出す必要がある。
matrix から頂点を求めるのは簡単 (普通の行列演算) だけど、逆は手間がかかる。
最初は幾何学的に求められるかと思ったが、いろいろ検討するも難しい。
と言うことで方程式を解くことに。
吉里吉里2では3つの頂点が与えられるので、2つの連立三元一次方程式を作ることができる。
これは matrix から頂点を求める式そのまま。

連立三元一次方程式1
x0 = (-0.5)*a + (-0.5)*c + tx
x1 = (width-0.5)*a + (-0.5)*c + tx
x2 = (-0.5)*a + (height-0.5)*c + tx

連立三元一次方程式2
y0 = (-0.5)*b + (-0.5)*d + ty
y1 = (width-0.5)*b + (-0.5)*d + ty
y2 = (-0.5)*b + (height-0.5)*d + ty

このうち a, b, c, d, tx, ty が求める値。
それ以外は事前にわかっている。
連立方程式は、ガウス=ザイデル法やガウスの消去法等いくつか数値演算で求める方法がある。
効率を考えてガウス=ザイデル法を用いて上式を解くようにしたら、解が収束せずに求められず、ガウスの消去法を用いることにした。
期待通り matrix が求められている。

ただ、オリジナルとは異なり頂点を与える方が方程式を求める分効率が悪い。
直後の描画処理に比べたらそれほどでもないかも知れないが。

投稿者 Takenori : 17:28 | トラックバック

2012年02月02日

レンダリング済みフォント

レンダリング済みフォントのサポートをどうするか悩んでいたけど対応した。
Java では縦書きフォントが読めない。
Android も調べた範囲では読めない。
後、フォントを共通化する為にもレンダリング済みフォントは有用と言うことで対応した。

FreeType の Java 版があるか探したが見付からず。
自力で FreeType を Java に移植することも考えたが、かなり労力がかかりそう。
FreeType があれば、共通化や縦書きも出来るようになると思うが、対応は大変。
と言うことで、自前でのフォント関係の処理をすることは諦めた。


少メモリ環境下では、ディスク容量やメモリ容量節約のため使用する文字のフォントデータのみ出力したいが、オリジナルのレンダリング済みフォント作成ツールでは指定した文字のフォントデータのみ出力する機能はない。
ツールのソースを読んでもそれっぽい機能はない。
機能追加するのはそれほど難しくなさそう。
作るかどうか悩んでいたら、既にあることを教えてもらった。

吉里吉里レンダリング済みフォント作成ツール改

投稿者 Takenori : 23:35 | トラックバック

2012年03月02日

Android での動画再生

Android で共通にサポートされている動画形式は、このページに書かれている。
MPEG-4系とVP8 ( Android 2.3.3以降 ) となっている。
端末によっては WMV もサポートされているが、全端末でサポートされているわけではない。

H.264 のライセンスは、MPEG LA、H.264の共同ライセンス体系を発表-エンコーダ/デコーダは0.20ドル。無料放送は1万ドル/年 では、「12分以下のものにはライセンス徴収を行なわない。」というのと、「搭載製品の総数が年間10万個/以下の場合は、ライセンス料を支払う必要は無く」という記述があるが、「初期のライセンス期間は2010年の12月31日まで」となっていて、その後どのようになったのかはっきりしない。
無料のインターネット配信の場合は、2015年まで無料で、その後さらに永久的に無料という発表はあったが、その他は不明。
5年ごとにライセンスは見直されるらしいが……
「デコーダ搭載製品」に当たるのかどうかも不明。

VP8 ( WebM ) は、現時点ではライセンス料は発生していないが、Android 2.3.3以降となっているので、それ以前のバージョンでの再生に問題が残る。

Android 版では、初回リリースは OS の機能をそのまま使う予定なので、これらの問題はそのまま発生する。
動画についてのライセンスについてはよく調べて対処してもらう必要がある。
NDK で Theorarm を使って Theroa を再生すると言う手もあるが、NDK を使うとそれはそれで再生できない機種が出る問題がある。

ちなみに、VC-1 ( WMV ) も、ライセンス料が発生しうる。
WMV3 ( WMV9 ) は、グレー。
昔、WMV のライセンスまとめ にまとめている。

投稿者 Takenori : 16:25 | トラックバック

2012年04月05日

View 周りのメモリ使用量削減検討

View 周りでどれくらい変わるのか確認してみた。
Xperia arc 854x480 で確認したところ――
SurfaceView のみの場合、Java ヒープに約2.7MB、ネイティブヒープに約4.2MB と約7MB。
GLSurfaceView でそのまま設定した場合、Java ヒープに約2.7MB、ネイティブヒープに約4.8MB と約7.5MB。
GLSurfaceView で GLSurfaceView.setEGLConfigChooser( 8,8,8,0,0,0 ); と GLSurfaceView.Renderer を実装したクラスを GLSurfaceView に設定する前に指定して、ステンシルやデプスバッファを使わないようにすれば、SurfaceView より少し使用メモリが多い程度でほとんど同じサイズになった。
約0.5MBはデプスバッファだろうか。詳しくは調べていない。
GLSurfaceViewを使えば、バックバッファ等 VRAM に配置されて使用量が減るかとも思ったが、そんな事はなかった。
ImageView (640x480の画像を指定) のみの場合、Java ヒープに約2.7MB、ネイティブヒープに約5.4MB と約8.1MB

SurfaceView を使うのが一番使用メモリ量が少なそうだ。

これらのサイズは画面サイズが異なると当然使用量に変化が出ると考えられる。
実際、EeePad transformer TF101 (1280x800) では、SurfaceView のみの起動で 約9MB使われていた。

検索したところ Xperia Arc の VRAM はメインメモリと共有のようだ。
GLSurfaceView にして、テクスチャに転送しても使用メモリ量が減らなかったり、GLSurfaceView でも差がないのはこの辺りが原因だろうか? 詳しくは調べていないが、使用メモリ量が減らない機種があるのなら、GLSurfaceView に対応してもあまり意味はなさそうだ。
将来的に選択可能になっていても良いとは思うが。


吉里吉里Java では、オフスクリーンバッファを使用している。
SurfaceView は、ダブルバッファリングを使って描画しているので、フリップすると1フレーム前の画像が入っているため差分描画を行う吉里吉里Java(オリジナルと同じ)だとうまく描画出来ない。
Android 側のソースをざっと見たところ SurfaceHolder.lockCanvas( Rect dirty ) で、dirty を指定すると、フロントバッファから dirty 以外の領域をコピーして、Canvas の clipRect に dirty が指定された Canvas が返って来る。
Surface.unlockCanvas でロック解除するとフリップを行わずにロック解除されるようだから、差分描画の1回目に dirty rect を指定しフロントバッファから変更しない部分をコピーして、差分描画し2回目以降は、dirty rect に null 指定して全体を更新する指定してコピーしないようにすることでオフスクリーンバッファを使わないように出来なくはない。
( 実際にこのような挙動になるのかどうかは動作させて確認をしたわけではないので、もしかしたら違う挙動かも知れない )
ただ、吉里吉里Java でゲームのサイズと画面サイズが異なり、拡大縮小が必要となった時差分描画で誤差が出て隙間が出来てしまう可能性がある。
オフスクリーンバッファで画像を保持しておいた方が良い局面もあるので、結局オフスクリーンバッファを使用するのが無難な実装だと考えられる。

SurfaceView でオフスクリーンバッファを使うと言う、元々の実装がやはり良さそうと判断した。
最終描画周りで使用メモリ量を削減できないかいろいろと検討したが、この辺り削るのは難しそうだ。

投稿者 Takenori : 00:57 | トラックバック

2012年04月06日

遅延確保と明示的解放

使用メモリを少しでも節約できるように Android 版にいくつか機能を追加した。

一つは遅延確保で、実際にそのレイヤーが描画される時、もしくは画像が読み込まれた時まで、メモリを確保しない。
オリジナルでは、setImageSize 等されると、その時点でそのサイズ分のメモリが確保されるが、Android ではこの時にそのレイヤーでまだメモリ確保されていないのであれば、この時点ではまだメモリを確保しない。
また、描画時も未確保のレイヤーを確保済みのレイヤーに描画する場合、単一色で塗りつぶす処理を行い、メモリ確保は行わない。
一種のシェイプ、もしくはカラードポリゴンのように扱われる。
ただ、状況によってはメモリ確保されるケースもある。
逆に描画される側の場合は必要なので、その時点でメモリ確保される。
未確保のレイヤーの全体を fill した場合、未確保のままで色を指定できる。
つまり、未確保のレイヤーはサイズと色を持った矩形のように扱われる。

レイヤーに purgeImage というメソッドを追加して、保持している画像のメモリを明示的に解放する処理を追加した。
KAG3 にある freeimage は、レイヤーと画像サイズを 32x32 に縮小するが、ピクセルデータが完全に未使用になるわけではない。
32x32 であれば、4KB 程度なのであまり大きくはないが、なくせるのならなくせた方がいい。
また、purgeImage はピクセルデータは解放するが、画像サイズは維持する。
ただ、吉里吉里2 はCopy-On-Writeで画像を共有しているケースがあるので、共有されている場合はピクセルデータは解放しない(すると他で壊れる)。
と書いたが、purgeImage が呼ばれた時に明示的に共有解除してしまった方が良いかもしれないので検討する。

レイヤーを非表示にした時に、内部で purgeImage をコールして、非表示にしたレイヤーのピクセルデータを破棄してしまうようにしたが、これは好ましくない状況もあると思うので、オプションで切り替えられるようにしようと思っている。


互換性を気にしないのであれば、複数の形状のシェイプや画像のタイリング等を加えれば、使用可能な状況ではメモリ使用量を削減できるが、UI やメッセージレイヤーなど結構限定されるので、無理をしていびつな形で入れるのは避けることに。

投稿者 Takenori : 23:46 | トラックバック

2012年04月11日

吉里吉里Java/KAG3 のメモリ消費量

吉里吉里Java の実際の使用量だが、吉里吉里Javaで KAG3 のバイトコードを読み込んで動作している段階で、Javaヒープは約10MB使われている。

SurfaceView のみで起動するだけでも、ネイティブヒープは約4.2MB必要。

トランジションや Layer.piledCopy を使用した時、そのベースとなったレイヤーと同サイズのキャッシュ領域が内部に作られる。
キャッシュ領域は、一時的なもの( 設定によって毎回削除しないようにする事も可能 )ではあるが、キャッシュ領域が必要となる機能を使う場合、このサイズも考慮に入れておく必要がある。
LayerManager はレイヤー合成に必要な領域を持つ。
この領域は画像演算の分割処理の指定によってサイズは変わるが、現状吉里吉里Javaでは画像演算の分割処理をなしにしているので、プライマリレイヤーと同じサイズ確保される事になる( レイヤー合成は画像演算の分割処理を考慮した作りとなっているが、現状トランジションはこの辺り考慮していないため )。
後、プライマリレイヤー1枚は最低必要となるだろうから、オフスクリーン、キャッシュ、合成領域、プライマリの4枚分最低限必要となる(トランジションと Layer.piledCopy を使わないのであれば、キャッシュ領域は不要)。

800x480 で 32bitカラーで計算すると 800x480x4x4 = 約6MB

ここまで合計すると約20.2MBは必要になる。
アプリケーションヒープが24MBの機種で動作させるのは KAG3 を使わなくても相当きついことがわかってくる。
KAG3 では、さらに最低でもヒストリーレイヤー、プライマリーレイヤーの裏 で約3MBは必要になる。
これで23MB。
そのままでは立ち絵やメッセージを出したらメモリが足りなくなる可能性が濃厚。
追加した purgeImage で普段は必要のない ヒストリーレイヤーと裏にあるレイヤーの分を解放しておけば、この分は必要なくなる(使用時は必要になるので、その時の検討は必要)。

トランジションを使わないようにすれば、キャッシュ領域が作られないので、その分使用量を浮かせられる。
黒一色のレイヤーにα値を変化させながら合成すれば、フェードアウト/インするトランジションが出来るので、キャッシュ領域を使わないトランジションが出来る( base に画像、その上のレイヤーに黒一色でα値を変化させるレイヤーを置く形。遅延確保によって単一色レイヤーをメモリ確保せずに実現できる )。
また、完全に画面が消えた時に、バックで表に画像を読み込むことになるので裏面も不要になる。

メッセージレイヤーは、そのサイズ分と文字描画用のレイヤーがメッセージレイヤーの幅+8 x 高さ+8 分使われる。
また、リンク等のハイライトでメッセージレイヤー幅 x 文字高さが使われる。
このため無駄なメッセージレイヤーは出来るだけ作らないようにし、初期サイズも出来るだけ小さくしておいた方が使用メモリ量は少なくて済む。
メッセージレイヤーにフレーム画像を使わないようにして、purgeImage して単一色で塗ればメッセージレイヤーのベース部分のメモリが浮かせられる。

この辺りの工夫を入れる事で、見栄えは少し劣るが 24MB で動作させることも不可能ではなくなるはず。
3.0 以降は largeHeap を使って PC と遜色ない作りにして、それ未満は少メモリ版とする対応もありかもしれない。
数年で 4.0 が普及する事を見込んで、largeHeap 使う前提で作るという手もあるが。

投稿者 Takenori : 17:13 | トラックバック

2012年04月30日

Android のメニュー

Android 3.0 以降でメニューボタンでの操作が廃止され、ActionBar による実装が推奨されている。
Android では互換性のため、AndroidManifest.xml で targetSdkVersion に Android 3.0 未満を指定すれば、画面下部にメニューボタンが追加される。
タブレット等ハードウェアボタンを全く搭載しないのであれば、他のボタンと同列に並ぶのでいいのだが、そうでない場合メニューボタンだけが表示されてずいぶん見た目が不格好になる。
Android 3.0 以降でメニューボタンなしでも操作出来るようにするためには、メニューなしでも操作出来た方が良い。
また、Android のメニューは、2階層までとなっているので、吉里吉里2のメニュー設定をそのまま持ってこられるようにするには、標準機能では足りない。
以上の理由により、Android 版ではメニューそのものを廃止してしまう事も考えたのだが、移植性も考え独自にメニューを実装する事にした。
ただ、このメニューは非推奨 ( 問題なく機能するようには作っている )。
当初標準のダイアログを使って実装する事を考えたが、チェックやラジオボタンの実装がやっかいなため、描画や操作は自前で実装してある。
メニューは以下のような表示になる。


メニューボタンを押すと表示され、タップで選べる。
バックボタンで階層を戻れ、メニュートップの時はメニューを閉じる。
表示中メニューボタンを押すと閉じる。
メニューで下位の項目がある場合は「>」が末尾に表示される。

画面に対して項目が大きいのは指で押す事を考慮しているため。
これより小さいと少し押しづらい。
高さが 50dip 以上になるようになっている (解像度に合わせたサイズになる) 。
高さを 50dip で割った数が縦方向の項目数となる。
割った時の余りは各項目に分散される (上の方の項目が 1px大きくなる可能性がある) 。
幅は、その階層で表示されるメニューの文字の幅によって計算される。
文字数が少ないと、次の画像のように横の項目数が増える。


横方向の項目数は、その階層のメニューの最大幅で画面幅を割った数で計算される。
1未満になる時は、1になる。
1になった時で、文字が幅を超える時はそのメニュー文字列はそのメニューに入るように文字が削られ末尾に「…」が付与される。
1画面内にメニューが入りきらない時はメニュー末尾に「▽次へ」が挿入され、そのメニューを押下する事ではみ出てメニュー項目を表示するメニュー表示に切り替わる。
2ページ以降では、最初に「△前へ」が表示され、前のメニュー項目に戻れるようになっている。
セパレータは非表示で、その項目自体ないものとして扱われる。
チェックがある場合は以下のように表示される。


ラジオボタンは次のように表示される。


文字、背景、線の色はリソースの colors.xml で指定可能になっている。
文字、背景は通常、押下、無効の3種類定義してある。

メニューボタンを搭載しない場合、画面下部にメニューを表示しないように AndroidManifest.xml の targetSdkVersion で API Level 11 以上を指定したときのことを考慮し、Window.showMenu() を追加する。
これによってメニューボタンを押してメニュー表示した時と同じ動作をする。
API Level 11 以上を指定し、メニューを使用する場合は、画面内にこのメソッドを呼び出すボタン等を配置しておかないとメニューでのみ実現している機能があるのなら、その機能を呼び出す事が出来なくなってしまうので注意する必要がある。

なお、矢印キーでの項目選択やEnterでの選択は現状実装されていない。

投稿者 Takenori : 18:17 | トラックバック

2012年05月16日

Android 版のα1 リリース

吉里吉里Java for Android
Android で実装可能な機能があらかた実装できたのでα版へ。
α版とは言っても、今までのリリースとは大差ない。
α版からさらにいくつかの最適化やバグフィックスを行ったらβ版にする予定。
β版から実際にアプリに組み込んで apk に出来るようにリリースする。

動画関係はかなり制約が多い。
レイヤーモード実装前程度の機能になっている。
詳細はプロジェクト Wiki の方にまとめている。
完全なレイヤーモードは実現が困難だが、動画の前にレイヤーを表示する事は可能なので、レイヤーモードにすると動画の前にレイヤー群が表示されるようにしている。
レイヤーモードの主な使い方は動画にメッセージレイヤーを重ねて動かすことだと思うので、そのような用途は何とか出来るはず。

サウンドもいろいろ制約があるが、ループチューナーを使用していないのであれば、制約は比較的少ない。
ただ、サンプルデータ取得が出来ないので、口パクとかする時は現状困る。
事前にデータを生成して音声の再生と同期出来るような形のものを実装しないといけない。

最適化は主に TJS2 のコンパイルの高速化と KAG パーサーの高速化、KAG バイナリ形式のサポートを行う予定。
TJS2 のバイトコードだけでなく、KAG3 もバイナリにして読み込みを早くする。
他にも細々としたところで最適化してもう少し起動を速くしたい。

投稿者 Takenori : 16:54 | トラックバック

2012年05月23日

KAG3 スクリプト文法まとめ

パーサーを書き直すにあたりまとめておく。
以下の認識で作るが、もしかしたら間違っているかも。
作っている途中に間違いに気付いたら修正する。

----

行頭のTABによるインデントはすべて無視され、ないものとして扱われる。
行頭に特定の文字がある場合に、先に TAB が入っていても、それは無視される。

行頭に以下の文字がある場合
';' : この行はコメントとして扱われ無視される。
'@' : この行はコマンド行として扱われる。この行には一つのタグのみを書くことができる。コマンド行では終端文字は改行かEOFとなる。
'*' : この行はラベルとして扱われる。ラベルは、'|'まで、もしくは'|'がない場合は行末までラベル名。'|'以降が見出しとなる。

上述のもの以外の場合、タグもしくは通常の文字列として扱われる。
ここでのタグとは'['と']'で囲まれた範囲を挿す。
同じく通常の文字列とはタグ以外の文字列を挿す。
ここでのタグは、'['によって開始されるが、"[["と中かっこが2つ連続する場合は、通常文字の'['であるとみなされ、タグ開始文字とは認識せず、タグにはならない(いわゆるエスケープ)。
ここでのタグは、']'を終端文字とする。
通常の文字列では TAB は無視される。

タグ名は、行頭の'@'もしくは、エスケープされていない'['で始まる文字列で、開始文字('@' or '[')とそこに続くスペース(' 'かタブ)を除いた文字から開始され、改行かスペース、終端文字以外が連続した文字列で表される。
タグ名は大文字と小文字は区別されず、すべて小文字として扱われる。
タグ名の後、スペースを除いても終端文字に達していない時、以降の文字列は属性として処理される。
タグ名後のスペースを除き、改行かスペース、終端文字、'='以外の連続した文字列で表されるものが属性名。
属性名後のスペースを除き、'='がある場合で、'='以降のスペースを除き、改行かスペース、終端文字以外の連続した文字列で表されるものが属性値。
もし、属性値が「"」か「'」で開始されていた場合は、次に「"」か「'」(最初の文字と同一のもの)が出現するまでが属性値となる
属性名と属性値は終端文字までの間繰り返し出現する可能性がある。
属性値中の'`'があった場合、直後の文字を問わずに属性値の文字列に含める。('`'がエスケープ文字となる)ただし、改行コードとEOFはエスケープできずエラーとなる。

属性名が来るところに'*'があった場合、マクロ引数の属性が展開されるが、本パーサーでは属性名が'*'であると解釈する。

iscript と endscript で囲まれた部分は TJS2 スクリプトとしてそのまま出力する。
iscript タグは、[iscript] か [iscript]\ 、 @iscript のみが記述された行である必要がある。
endscript タグは、[endscript] か [endscript]\ 、 @endscript のみが記述された行である必要がある。

以上のようにパーサーを作るが、マクロでのデフォルト値やTJS2式の指定などは意味解釈側での役割として、パーサーでは気にせず処理する。

投稿者 Takenori : 23:20 | トラックバック

2012年06月24日

メモリ管理とinvalidateスレッドの問題

オリジナルの吉里吉里2は、リファレンスカウント方式によってメモリ管理を行っているが、吉里吉里Java では Java の GC に頼ったメモリ管理を行っている。
Java の GC に頼るとリファレンスカウント方式の循環参照の回避などメリットがあるが、いくつかの問題とオリジナルと異なる挙動が発生すると言う問題がある。
オリジナルはリファレンスカウントなので、カウントが消えた瞬間に解放されるが、Java の GC の場合はJVM の GC タイミング依存になり、オリジナルとは異なったタイミングでオブジェクトが消える。
TJS2 で参照が消えるのに任せた組み方をしていると、このタイミングの問題が顕在化する事がある。
顕在化するのはレイヤークラスくらいではないかと思っているが、他のクラスでも起こりうる。
レイヤーの場合、ネイティブインスタンスの解放タイミングが異なるためにしばらく親子関係が解消されず、レイヤー階層に参照されなくなったレイヤーが残る事になる。
不要になったときに適切に invalidate していれば、このタイミング依存の問題に遭遇する事はない。
TJS2 の仕様上は、オブジェクトがいつ解放されるかは不定となっているので、仕様は満たせているが、使い方によっては異なる挙動をする事があるので少し注意した方がいいかもしれない。
まあ、レイヤーのようなある程度重いクラスを何度も生成して放置すると言う使い方をする事自体稀だ思うが。

Java の GC はと言うか、finalize メソッドは Finalizer スレッドでコールされる。
Finalizer スレッドは、TJS2 VM とは異なるスレッドでかつ TJS2/吉里吉里2 がマルチスレッドで動作する事を考慮されていないため、Finalizer スレッドで TJS2 オブジェクトの invalidate 処理をすると問題が発生する。
TJS2/吉里吉里2 をリエントラント可能な処理に書き換えるのはコストに合わない。
オブジェクトは GC によって破棄される時だけ別スレッドで、他の処理はシングルスレッドで動作しているため、その時のためにロック処理するのは無駄な負荷が増えると考えられる。
シングルスレッドで動作させるためには、finalize メソッドで invalidate 処理をキューにいれ、TJS2 VM スレッドでそのキューに入った invalidate 処理を実行することになる。
リファレンスカウント方式だとカウントがなくなった時点、つまり TJS2 の処理の途中に解放処理が挟まる事になるが、invalidate 処理をキューにいれて処理をする場合、TJS2 のメソッド呼び出しがすべて返って来るまで処理できない。
つまり、1回のメソッド呼び出しでクラス生成を行ってそのクラスの参照が切れる処理を何度もするとメモリがどんどん消費されてメモリ不足に陥る。
ただ、ゲームのような用途でそのような処理を書く事はないと思われるので、問題は顕在化しないと考えられるが、バッチ処理を書いた場合ははまる可能性が高い。
吉里吉里2 でバッチ処理を書く場合は、現時点だと PC が対象だと思うので、メモリには余裕があるし、ネイティブで動作するオリジナルを使った方がいいため問題にはならいなと思うが。

メモリ管理方法の違いによって発生する問題で認識しているものはこの2つ。

投稿者 Takenori : 23:57 | トラックバック

2012年06月25日

セーブフォルダとライセンス表示

セーブデータのフォルダのベースパスは"外部保存域のルート/Android/data/<package_name>/files/"になる。
外部保存域のルートは、getExternalStorageDirectory()が返すパスで多くの環境では SDカードになっているはず。
このパスは SDカードにデータを置く場合の推奨パスで、Android2.2 以降ならアプリアンインストール時に自動削除される。
自動削除が好ましくない場合は別のパスを指定する事がデフォルトはここ。

オリジナルの吉里吉里2はCtrl + F12でライセンス表示するが、Android の場合キーボードはない方が多いのでショートカットで表示するわけにもいかず、ストアから apk がダウンロードされるのでドキュメント同梱も出来ないと言う事でどうやって表示するかいろいろと悩んでいたけど、上記に記述しているSDカードにデータを置く場合の推奨パスの位置にライセンスのドキュメントを出力する形にすることにした。
初回起動時に表示する、組み込む人にどこかに入れてもらう等も考えたが、手間等を考慮すると SD のデータフォルダにドキュメントを出力するのがライセンスを満たし、見たい人は見られる比較的良い方法だと思う。
この方法を思いつくまでにすごい悩んだんだけど。

セーブデータ等が SD に置かれる関係から、起動時に SD カードが読み書き可能な形でマウントされているかどうかチェックして、されていない場合はトースト出して終了するようにした。

投稿者 Takenori : 01:53 | トラックバック

データの格納場所

オリジナルの吉里吉里2の場合実行ファイルと同じフォルダに data.xp3 を置いて、そこからデータを読み込むのが一般的だと思うが、Android ではこの方法は使えない。
Android でこのようなデータを扱うには、以下のような方法がある。

1. APK Expansion Files を使う
Google Play ストアで配信するのであれば、APK Expansion Filesを使うのが持っとも手軽だと思われる。
これは、アプリ本体とは別にリソースを 2GB + 2GB 使えると言うもので、リソースファイルは何でもいい。
つまり、xp3 ファイルを別リソースとして上げてやればそれが使える。
このリソースは自動で SD にダウンロードされる ( されない場合もあるのでその時は自分でダウンロードして ) ので、後はそのファイルを読み込んで実行すればよい。

2. 別サーバーに置いて自前でダウンロード
リソースが合計で 50MB を超えるような場合 Google Play ストアが使えるのであれば、APK Expansion Files を使うのが良いと思うが、そうでない場合は別サーバーを用意してそこからダウンロードして SD に格納し、そのファイルを読み込んで実行する。

3. Assets フォルダに置く
apk ファイルの上限が 50MB なので、コアとリソースを合わせた時に 50MB 以内に収まるのであれば、apk 内にリソースを入れてしまえる。
この場合、assets フォルダか res フォルダ以下のどこかに置ける。
ファイル名や種類の縛りがない assets フォルダが扱いやすいが、2.3 未満では 1MB と言うファイルサイズ制限がある端末がある ( 多くの端末である様子 ) 。
2.3 以上ではサイズ制限がなくなっている ( とは言ってもapkファイルサイズ制限がある ) ので、気にしなくても良くなった。
ただ、assets フォルダのアクセスは遅い。
SD にコピーしてから読み込んだ方が速い。

4. res/raw に置く
res 以下は種類に応じて drawable 等にデータを置ける。
この中で raw は任意のファイルを置ける。
ただ、ファイル名に制限が付くのとフォルダを作れない制限がある。
res 以下のファイルは、R.drawable.xxx 等の ID が割り当てられるので、ファイル名にはJava のメンバ変数名と同じ制約があるはず ( さらに小文字制限がある )。
これらリソースに対してスクリプトからアクセスする方法は実装していないので、速度等の使い勝手については知らない。
また、ファイルに対して柔軟なアクセスが可能かどうか ( File Descriptor が取得可能か ) も未調査 ( 検索した範囲ではなさそう ) 。
ファイルサイズ制限も未調査 ( assetsと同じ制限がありそうではある ) 。

APK Expansion Files を使うのが様々な制限を回避して扱いやすいが、Google Play ストアのみになる。
容量が少ない場合、asserts に入れてしまうのが良いと思うが、2.3 未満にも対応するのならサイズ制限が発生する。
別サーバーに置くのも扱いやすいが、別サーバーを自分で準備する必要があるが、50MB超えていて Google Play ストアに置けないとなると、この方法をとるしかない。
50MB 超える場合、テキストリソースを asserts に置いて、他のデータを別サーバーに置くというのが良いのではないかと思っている。

現在のところ Assets フォルダから読み込むのと SD から読み込む機能のみ実装されているだけなので、ダウンロードやSDへのコピー等組み合わせた指定が可能な機能を組み込んでいく。

投稿者 Takenori : 14:14 | トラックバック

2012年07月23日

Android で apk を作れる物をリリース

データを組み込んでビルドして apk 化出来る SDK をリリースした。
羽々斬ページからリンクをたどって Android 版からダウンロードできる。
吉里吉里Java と呼んでいた物は「羽々斬」と言う名前にした。
マルチプラットフォーム吉里吉里2 → 幅吉里吉里2 → 幅吉里 → 羽々斬、みたいな。
互換システムなので、別名にした方が良いだろうという事で、吉里吉里の名前は外して独自名にした。
まだデータのダウンロード機能など必要と思われる機能がないのでα版。
ドキュメント等所々整合性がとれていないところは順次整理していく。

apk 版の更新はまた後でする。

投稿者 Takenori : 01:16 | トラックバック

アプリケーションヒープ外のメモリ確保

Android 3.0 未満だとどうしてもメモリ容量がネック。
アプリケーションヒープ外にメモリ確保する方法は 2つはあった。
1. MemoryFile を使うとシェアードメモリが確保されて、このメモリはアプリケーションヒープ外。
2. OpenGL ES のテクスチャメモリ。ネイティブヒープは増えるが、これはアプリケーションヒープ外に確保されているようで、上限を超えても確保し続けられる。

MemoryFile へのアクセス速度はBitmap.copyPixelsToBuffer/copyPixelsFromBuffer の倍程度なので、そこそこ速い。
画像をストレージから再読み込みするのと比べると22倍くらい速いのでキャッシュとしての速度は十分ある。
ローメモリー状態になったら自動的に消されるオプションもあるので、キャッシュなどの用途に使いやすい。
Bitmap.copyPixelsToBuffer/copyPixelsFromBuffer よりも高速なので、全部 Java で記述し CPU で描画するのなら、MemoryFile で画像を保持するのはありだが、全部この方法だとかなり遅くなってしまう。

ネイティブヒープが増えていたので View 周りのメモリ使用量削減検討 の時は気付かなかったけど、テクスチャはアプリケーションヒープ外に確保されている様子。
テクスチャメモリで上限超えられるのなら、ほとんど画像関係でメモリ食っているので OpenGL ES2.0 で全部書いてしまえば、メモリの問題は大きく改善する。
ただ、かなりの種類がある合成ルールをシェーダースクリプトに書き換えたり、そこそこ数があるトランジションを書き換えたり、ブラーやガンマ補正等、新たに作らなければならない処理が多い。
後、Android 2.2 以降の制約が付く。

投稿者 Takenori : 23:33 | トラックバック

OpenGL ES 2.0 での実現性

OpenGL ES 2.0 でレイヤー関係の処理を書き換えればメモリ容量の問題は大きく改善するが、吉里吉里2 のレイヤー処理を OpenGL ES 2.0 で実現できるかどうか。
少し実験してみたがレンダーテクスチャとフラグメントシェーダーで実現できそうではある。
レイヤーの copyRect 処理の destination をレンダーテクスチャの対象に設定し、板ポリを描画すると言う方法で実現できるはず。
処理が CPU から GPU に移る事で高速化されるかというと、それはわからない。
ステートを切り替えまくって描画して速度が出るのかどうか……
アプリケーションならレンダーテクスチャを頻繁に切り替えて描画すると言うのは、最近ならありそうだけどゲームでそのような処理をするのはないのではないかと思う。
そもそもスプライトとして一気に全部描画してしまえばいいだけだから。
もう1つはテキスト描画が多いので、キャッシュするにしても文字種が多いのでテクスチャメモリへの転送が頻繁に発生するため、この部分の速度が気になる。
KAG3 では、基本的に1文字ずつ描画していくため、まとめて複数文字転送がしづらい。
描画処理は、内部でコマンドを溜めて、画面に描画する時やファイル書き出し等テクスチャの情報が必要になる時まで遅延させ、必要になった段階でコマンドをすべて処理する形にすれば、まとめて処理できるから、そのようにすれば複数文字を一気に転送出来なくはない。
まあ、テクスチャへのキャッシュを考えると複数文字を一気に転送はやりづらいんだけど。
なんにしても速度面に関してはやってみない事には現実的な速度で動くかはわからないな。
この程度の処理ならいけそうとは思うが。

新規に作らないといけない処理は多いがやってみる価値はありそう。
ダウンロード機能などもっと先に必要な機能が実装できたら検討してみる。

投稿者 Takenori : 23:53 | トラックバック

2012年08月08日

Android 用の SDK α2をリリース

不具合があった場合などで過去のバージョンが必要になることもあるかと思いダウンロードページ以外に、SourceForge のページに過去ジョンも含め置いていくことにした。
開発時はエンジンアプリ版を使うことになると思う。
今回のバージョンで Web からのデータのダウンロードと assets フォルダ内からSDへのコピーを指定できる機能が追加されたので容量制限の問題や assets のアクセス速度の問題が解消されたはず。
容量制限の問題は Google Play ストアに置く場合は、APK expansion files の方が便利だろうけど、その対応はまだ。

投稿者 Takenori : 23:51 | トラックバック

2012年08月09日

転送ツールとインストーラー

開発時はエンジンアプリで動作を確認すると思うけど、Android 端末にデータを転送するのが面倒。
毎回、マウント/アンマウントして SDにファイルコピーするのは面倒だし、DDMS の File Explorer で転送も面倒。
adb コマンドで bat 組んでもいいけど、ボタン押したら差分で更新して欲しい。
そのようなツールを作るのはどうするか調べたら意外と簡単にできそうで、Androidのadbのメモ(6) 直接adb serverと通信するrubyスクリプトを見るとどうやればいいかわかる。
Java で試してみたらこの方法で問題なく動作したので、後は UI 等を作り込んでいけば転送ツールは出来る。

PC 版に Android 版を添付したり、PC でダウンロードして Android でインストールすることを考えると、PC で動作するインストーラーがあると便利。
ダウンロード販売サイトで Android 版を見ると面倒な手順が書いてあって、ちょっとこれは辛いと感じる。
だから、インストーラーも作りたいんだけど、出来れば adb を添付せずに何とかしたいところ。
adb のソースコードは、system/core/adb にあるので、そこのソースを見て adb がやっていることを同じようにすれば出来るはず。

投稿者 Takenori : 23:21 | トラックバック

2012年08月11日

ゲームパッド対応

ゲームパッドを買ったので Nexus 7 につないで確認してみたら、全く反応しなかった。
吉里吉里のキーコード VK_PAD1 ~ VK_PAD10 に対して、Android のキーコード KeyEvent.KEYCODE_BUTTON_1 ~ KeyEvent.KEYCODE_BUTTON_10 を割り当てていたが、送られてきていたのは KeyEvent.KEYCODE_BUTTON_A ~ の方だった。
KeyEvent.KEYCODE_BUTTON_1 は Key code constant: Generic Game Pad Button #1. となっているんだけど、これは何で送られてくるんだろうか?
何にしても KeyEvent.KEYCODE_BUTTON_A ~ の方で反応するようなので、そちらに変更し確認してみると問題なく動作した。
ただ、十字キーを押すとフリーズしたようになる。
デバッガで追うとインプットイベントがひたすら発行されている様子。
PC 版の方では問題なく動いている。
調べるとどうも VK_PADLEFT と VK_LEFT、VK_PADRIGHT と VK_RIGHT…… と同じキーコードを割り当てていたのが原因の様子。
Android の場合キーボードの十字キーもゲームパッドの十字キーも同じ KeyEvent.KEYCODE_DPAD_LEFT 等を送ってくるので、同じものにしていたけど問題だったようだ。
吉里吉里のパッドのキーコードは独自のキーコードを割り当てて、キーの送信元デバイスを見て、キーボードならキーボードのキーコードのまま、ゲームパッド等ならゲームパッドのキーコードに変換する形にした。
デバイスの判定は KeyEvent.getDevice().getSource() で行える。

ついでにアナログ入力について調べてみた。
アナログ入力は、Activity.onGenericMotionEvent で送られてくる MotionEvent を見て判断すればいいようだ。
Android 3.1 と GamePad のイベントの詳細 (2)に詳しく書かれていた。
Android 3.1 Platform のゲームパッドに付いての説明も参考になる。
アナログ入力は今のところ使わないがメモしておく。

投稿者 Takenori : 22:34 | トラックバック

2012年08月12日

xp3 の暗黙 Intent 対応

アプリ版で xp3 ファイルのURLを含むインテントが投げられた時、http ならダウンロードしてから実行、file ならそのファイルを実行するようにした。
これでツイッターなどで xp3 のリンクをつぶやいて、そのリンクを踏んで羽々斬を起動させることができるように。
ただ、xp3 ファイルが大きいとダウンロードに時間がかかるので、手軽さがあまりないんだけど。

最初ブラウザに反応しなかったが、カテゴリーに BROWSABLE が必要だった様子。
具体的には以下のようなインテントフィルターで起動するようにした。
これで URI に xp3 を含むインテントに反応して起動する。

<intent-filter>
  <action android:name="android.intent.action.VIEW" />
  <category android:name="android.intent.category.DEFAULT" />
  <category android:name="android.intent.category.BROWSABLE" />
  <data android:scheme="http" android:host="*" android:pathPattern=".*\\.xp3" />
  <data android:scheme="file" android:host="*" android:pathPattern=".*\\.xp3" />
</intent-filter>

投稿者 Takenori : 22:33 | トラックバック

2012年08月23日

吉里吉里Java に組み込むプラグイン

当たり前だけど、吉里吉里Java はオリジナルのプラグインは使えない。
対応しようとしたら、プラグインを Java に書き換えて吉里吉里Java に組み込む必要がある。
標準の krmovie.dll とwuvorbis.dll よく使われるであろう extrans.dll は組み込んである ( 動画は環境依存が大きいので機能的には絞られている )。

他のプラグインはどの程度使われているかわからないので、アンケートを作った。
吉里吉里2で使用しているプラグインは何ですか?
もし良かったら回答してください。
twitter連動サービスなので、twitterやってないと回答できないかもしれない ( 連動させずに試していないのでわからない ) 。

結果ページはここ

今見たところだとある程度偏りがありそう。

投稿者 Takenori : 13:52 | トラックバック

2012年10月23日

TJS2 バイトコードファイルフォーマット

基本構造

 0123備考
0'T''J''S''2'シグネチャ
1'1''0''0'\0バージョン
2file size 
3'D''A''T''A' 
4data area sizeForuCC含む
5data... 
6'O''B''J''S' 
7objects area sizeForuCC含む
8top level object index-1の時はnull
9number of objects 
10objects... 


data area - 最初に個数があり、その後配列がある形

countbyte...4バイトアライメント
countshort...4バイトアライメント
countint... 
countlong...8バイト
countdouble...IEEE 754 浮動小数点の「ダブルフォーマット (double format)」ビットレイアウト
countstring...length + char array 4バイトアライメント
countoctet...length + byte array 4バイトアライメント

object area

 0123備考
0'T''J''S''2'  
1object area size整数直値 
2parent indexobject index 
3name文字列(data index) 
4context type整数直値クラスや関数等の種類
5max variable count整数直値固定変数の数
6variable reserve count整数直値固定変数予備 - context type によって決まるので不要だが
7max frame count整数直値レジスタ数
8func decl arg count整数直値定義時の関数の引数の数
9func decl unnamed arg array base整数直値無名配列引数より前に定義されている引数の数
10func decl collapse base整数直値名前付き配列引数より前定義されている引数の数
11prop setterobject indexプロパティセッターオブジェクト、context type が property の時に持つ
12prop getterobject indexプロパティゲッターオブジェクト、context type が property の時に持つ
13super class getterobject indexスーパークラスを取得するためのオブジェクト
15source pos arrayコード位置配列とソース位置配列デバッグ用、現状省略される
16code arrayshort の配列バイトコード実体
17data arraytype と index のペアの配列定数配列 ( Variant 型 )
18super class getter pointer arrayint の配列スーパークラスゲッターのバイトコードオフセット配列
19property object array名前とオブジェクトのペアの配列関数等のメンバー登録するオブジェクト配列

この説明だけではわからないと思うが、一応ここに記しておく。

投稿者 Takenori : 03:44 | トラックバック

2012年12月09日

Android化で困る部分の考察

吉里吉里2をAndroidに移植するにあたって困った部分などについての考察。

・OS固有のGUI周り
メニューやウィンドウスタイル等のGUI周りはプラットフォーム差が大きいし、スクリプトである程度まで再現も出来るので、OS固有の機能を取り入れるのは回避した方が良さげ。
また、(今までの) PC だとマルチウィンドウは問題なく扱えていたが、スマートフォンは全画面で画面切り換えが前提だし、Windows8 の Modern UI などもそう。
Android の画面切り換え ( Activity切り換え ) は、バックに行った Activity のメモリ解放リスクもあるから、OS の画面切り換えを積極的に使うのは避けたいところ。
羽々斬では Activity 切り換えはしない形になっている。
この辺りは普通のスクリプトで実現できるので、OS固有の機能をあえて使う必要性は薄い。
ただ、これらはゲーム用として見た場合なので、ツールなどの用途の場合使い勝手が落ちることに繋がる。
吉里吉里2/KAG3 の場合、KAG3 が所々ダイアログを別ウィンドウで出しているので厳しかったが、ゲームシステム側はそのような固有機能を使わない実装になっているのなら、固有機能があって障害になることは少ないとも考えられる(より多くのプラットフォームで動かしたいのはゲームであると考えられるため)。

・入力デバイス
PC はマウス、スマートフォンはタッチが前提にあるので、その入力方法の違いによって不便が生じる。
今後は PC でもタッチ操作が増えるだろうから、タッチ優先のデザインの方が良いと考えられる。
PC では、ダウン、(ムーブ)、クリック、アップと言うイベント順(クリックは位置間違っているかも)だけど、Android は、ダウン、(ムーブ)、アップでクリックがない。
クリックは、OS 側の判断で入っていると考えられるので、環境差の発生を考えると、システムのイベントは無視してしまった方が良いと思われる。
また、マウスとタッチで大きく違うのは現在位置の有無。要はマウスオーバー系の操作がタッチではないこと。
マウスオーバーでツールチップなどの簡易ヘルプ表示の様な操作系がタッチでは出来ない。
ゲームUIでもこれを前提に作られていると、タッチの時分かりづらくなることが考えられる。
イベントとしてあっても良いと思うが、ゲームUI等のデザインではよく考えた方がいいというか、なくして代替案を考えた方が良いのではないかと考えている。
タッチデバイスの時は完全に別 UI とするのなら気にすることではないが。

・マルチメディア系
音声や動画などはスマートフォンはまだ貧弱。
Android で音声をサンプル単位で何か処理するのは現時点では辛い。
効果音等で無圧縮の音声ファイルを使うのであれば可能だが、Ogg Vorbis 等を基準とするのなら制約が多い。
ただ、スマートフォンの CPU パワーはかなりの勢いで伸びているし、音声周りの API も整備されていっているので、近い内に解決する問題と思える。
そのため、しばらくはスマートフォンで制約があるだろうが、それに合わせて仕様を絞る必要は感じない。
動画も同じようにスマートフォンは貧弱。
ただ、動画は音声に比べるとかなりの CPU パワーが必要なので、PC のパワーに合わせた仕様にするのは悩む部分。
まあ、仕様絞って何かメリットあるかというと特に感じないので、スマートフォン版は制約が多いと言うことでも問題ないかもしれない。

・プラグイン
もっともやっかい。
環境ごとに作らなければならないし、言語が違うとなると作り直さなければならないので大変。
スクリプトがネイティブに近い速度で動作するのならプラグインを使う場面は減少するので、そちら方向への注力は必要と思われる。環境固有の機能を使いたいなどの場合は、別々に作る必要が生じるのでどうしようもない。

・OggVorbis コードブック共有化
Android では、データの開始位置と長さを指定して再生するので、コードブックが共有化されていると困る。
圧縮率向上のためにあると考えられるが、切り捨ててしまってもいいのではないかと考えられるところ。

・リジューム
PC 版では中断/再開はセーブ/ロードを基準にしても問題ないが、スマートフォンなどの場合はリジューム出来ないと辛い。
ただ、エンジンがサポートしていなくても、スクリプトで細かくオートセーブするなどし、リスタート時に自動読込みするかどうか確認する等すればある程度回避可能。
音声や動画の途中再開等厳しい部分もあるので、中断可能な部分を判断しやすいスクリプト側で対処をする方が好ましいかもしれない。


すぐに思い出されるのはこれくらい。
また思い出したり、気付いたことがあれば書く。

投稿者 Takenori : 18:04 | トラックバック

2013年01月11日

プラグインアンケート集計

吉里吉里Java に組み込むプラグインで実施したアンケートを集計しておきます。
票数の多い順に並べ替えて0の項目は削除しています。

wuvorbis.dll12
extrans.dll10
csvParser.dll9
KAGParserEx.dll7
layerExImage.dll7
layerExBTOA.dll4
layerExDraw.dll4
layerExSave.dll4
scriptsEx.dll4
windowEx.dll4
fstat.dll3
layerExRaster.dll3
flashPlayer.dll2
json.dll2
lineParser.dll2
saveStruct.dll2
win32dialog.dll2
Other2
addFont.dll1
adjustMonitor.dll1
clipboardEx.dll1
dirlist.dll1
expat.dll1
fftgraph.dll1
getSample.dll1
javascript.dll1
layerExAgg.dll1
layerExAreaAverage.dll1
layerExAVI.dll1
layerExCairo.dll1
layerExGdiPlus.dll1
layerExLongExposure.dll1
layerExMovie.dll1
layerExPerspective.dll1
perspective.dll1
shellExecute.dll1
shrinkCopy.dll1
stdio.dll1
systemEx.dll1
videoEncoder.dll1

投稿者 Takenori : 01:19 | トラックバック

2013年02月19日

羽々斬 支援版

少しでも開発費を捻出できればいいと思って登録した。
羽々斬 支援版

エンジンアプリとして公開しているものと同じもの。
ただ、Google Play に登録しているものは Android 4.0 以降用に設定している。
Android 4.0 以降の方がメモリに余裕があってやりやすいから。

羽々斬は SDK を公開してゲームを作って公開することが出来るところまで作っているけど、細かい部分で作り込みたいところはいくつか残っている。
開発が停滞しているのは開発費の問題が大きいので、なんとか出来ないか?とは常々思っている。

高速化に関しては TJS2 バイトコードを Dalvik バイトコードにするくらいしか有効そうなものは残ってない。
そう考えると軽量版 KAG を作るのが効果的かもしれない。

羽々斬登録前に Nexus7 で久しぶりに動かしたけど、普通に動く。
普通に作れるところまでは作っていたから当然といえば当然だけど、改めてキモいと思った。

Google Play に 2000円で登録したけど、ぱっと見かなり高額に見える。
PC 用ソフトなどだと特にそうは思わないんだけど、Android 用で登録されているとそう感じてしまう不思議。
このように感じてしまうのは結構問題のように思うんだけど、どうしたものか……
高額アプリをバシバシ登録すれば払拭されるかな。
専門性の高いもの ( 数出ないもの ) は必然的に高くなるはずだし。

投稿者 Takenori : 17:43 | トラックバック

2013年02月20日

羽々斬 Libraries を考える

羽々斬がある程度動いていた時から考えていたんだけど、羽々斬から TJS2 VM をとっぱらって、吉里吉里2のクラスライブラリが Java からコール出来るような羽々斬 Libraries を作ることを考えている。
ずっと作るかどうか悩んでいたけど、作ろうと思う。

Android 4.0 以降の機種であれば、現状の羽々斬+KAG3 などでも問題ないんだけど、それ以前となると KAG3 に手を入れるなどして軽量化しないと厳しい部分がある。
厳しいのは、主にメモリ周りなので TJS2 なくしても KAG3 そのままだと厳しいんだけど、KAG3 部分を Java で作り替えるとなるとメモリ周りを意識して作るし、初期化周りの速度も改善できる ( 当然スマートフォンを意識した作りにする ) 。
ただ、そうなると全然互換エンジンじゃないってことになるけど、TJS2 バイトコードを Dalvik バイトコードに変換可能にするのなら、TJS2 VM はなくなり、ラッパー層が入ることになるので、トランスコードするのなら互換性は出来る。
Java で書けるようになった後に、最初に TJS2 で書くかというと疑問だけど。
自分は型付きの言語の方が作りやすいし。
だから、TJS2 バイトコード から Dalvik バイトコードへ変換するのではなく、Java を TJS2 に変換する方を作る方が良いのではないかとも思う。
まあ、トランスコードの部分はどうするかは置いておいて、吉里吉里2 が持つ程度のクラスライブラリがあると何かと便利。

手が空いたら実験してみる。

投稿者 Takenori : 23:06 | トラックバック

2013年02月21日

羽々斬は 4.0 以降用へ

Android 4.0 以降であれば、比較的まともに動くので 4.0以降に限定してしまうかどうかは結構悩んでいたところなんだけど、羽々斬 Libs を作るのであれば限定してしまい、いくつかの制限を取り払ってしまった方がいいと考えている。
羽々斬がバックグラウンドに行ったときに終了すると言うのは、主にメモリの制約に起因しているので、この辺り4.0に限定するのなら解消できる。
羽々斬 Libs は互換性はあまり意識せず、新規にシステム作ることを目的に作るから、互換性が欲しいということであれば、羽々斬になるはず。
羽々斬 Libs は、羽々斬2とか言う名前にしたらすっきりするんじゃないかと思うくらい。

現時点だと、最近リリースしたアプリで割合を見ると4.0以降は半分くらい。
まだ4.0以降に限定してしまうのはちょっと心もとないが、来年辺りにはそれほど気にならなくなっている気もする。
数年すれば羽々斬も普通に実用的になりそうだから、状況を見つつ考えていく必要がある。
羽々斬 Libs で専用に作ったシステムの方が常にアドバンテージがあるのは間違いないけど、互換システムと新規システムはだいたいそんなものだろう。

投稿者 Takenori : 16:01 | トラックバック

2015年12月20日

羽々斬をGitHubに移動

羽々斬に関して問い合わせがあったので、ついでに SourceForge から GitHub へ移動した。
場所はここ
今のところソースコードを単純に移動しただけ。

元々 Eclipse で開発していたけど、ビルドシステムを Gradle にすればマルチプロジェクトで Android 用と Desktop 用でばらばらになっていたソースコードをうまく共存できるんじゃないか?と少し触ってみた。

羽々斬は、jp.kirikiri.tjs2 と jp.kirikiri.tvp2 の 共通部分と jp.kirikiri.tvp2env の環境依存部分で出来ていて、tvp2 と tvp2env は相互に参照している。
最初はマルチプロジェクトで共通部分を core、環境依存部分をそれぞれ android/desktop として作ろうとしたけど、相互に参照しているせいでうまくいかなかった。
あれこれ試したところ、単純にソースセットの指定に両方渡してやれば Android Studio でツリーにソース表示されるし、ビルドも通った。
sourceSets { main { java.srcDirs = ['src/main/java', '../core/src'] } }

Android 側はこれでいいんだけど、問い合わせしてくれた人は Mac でプレイできるようにするために羽々斬使えそうと言うことだったので、上の対応じゃなくて、Eclipse でうまくやるのが必要。


羽々斬リリース当時 Android の非力さ等で色々と微妙だった部分は、現在の環境だとだいたい解消されているし、いくつか今の状況に合わせて修正すれば、結構実用的かもしれない。
RoboVM も正式バージョンがリリースされているので、羽々斬→RoboVM→ネイティブ(iOS/MacOSX/Linux) を吐くと言うのも、下まわり何とかすれば出来そうな状況になってるはず。
RoboVM は OSS 版とサブスクリプションモデルの有料版と言う形になっていた。

羽々斬開発当時は Pure Java で作ろうとしていたけど、今から手を入れるのなら、いくつかは C/C++ で作ってしまった方がいいと思える。
確かあの頃は C/C++ で作れるかどうかは何とも言えない時期だった。
画像処理周りの SIMD 使うところなどは、C/C++ で書いて環境ごとに so/dll 準備するのが速くなるし妥当だろう。
音周りも環境依存大きいからネイティブで書いてしまっていいかもしれない。

もっとも悩むところは、吉里吉里Zに手を入れて Android 版を作るのか、羽々斬を頑張ってマルチプラットフォームを推進する方がいいのか、どちらが工数少なくて、将来も楽そうなのかと言うところだろうか。
Android 版をネイティブで書くと言っても Android はやっぱり Java 主体で、C++ のみだと辛いところがある。
何か所か Java の呼び出しが必要になってくるから、Java 主体の羽々斬に C++ 足して行くのか、C++ 主体の吉里吉里Zに Java と環境依存コード足して行くのか。

Android 5.0 以降での ART が想像以上に強力で、Java で書かれた TJS2 のコンパイラ部分とか普通に速くてネイティブで書く必要ない感じ。
画像処理などの SIMD が必要なところは意味ありそうだけど、その辺り RenderScript で頑張ろうと思えばできる。
RenderScript で書くと楽だけど、CPU のみなら SIMD で頑張った方が速いだろうと思う。
ただ、RenderScript はマルチスレッドでやってくれるから、マルチコアだとわからない。
ネイティブでうまくマルチスレッド化すればいいんだろうけど。

将来の修正なども考慮して出来るだけ共通にしたいと言うのなら、吉里吉里Z~ってのがいいんだろうけど、初期の対応 OS で考えるのなら Windows のみの Z に比べて、それ以外を網羅する羽々斬も共通部分多いわけで……
RoboVM は、Java のバイトコードからネイティブ吐いてくれるけど、Windows 向けがないのが残念。
元々は iOS/Android のソースをJavaで共通化するみたいな感じだから仕方ないのかもしれない。
内部的には、LLVM IR を途中で吐いていて、いくつかサポート用の関数等を加えてリンクする感じだから、そのサポート用の関数周りを準備してやれば Windows 用バイナリも吐けるように出来そうな感じではある。

投稿者 Takenori : 18:11

 
Total : Today : Yesterday :