LoginSignup
4
4

More than 5 years have passed since last update.

PMXのスキニングをAndroidで行った場合の実装による速度差のメモ

Last updated at Posted at 2015-05-22

チェックした端末

  • Nexus5
  • Galaxy Nexus
  • Nexus9
  • SHIELD TABLET

評価対象オブジェクト

  • Tda式ミク
    • 25164頂点
    • 1体表示

処理

  • ソフトウェアスキニング〜CPUアクセス可能なメモリへ書き戻し完了までの時間を計測する。
    • 最初の60フレームを切り捨て、その後300フレームの平均値を算出する
    • 上記の方法で計測後、最も速い記録を採用する
  • 上記の後、glBufferSubDataによって頂点のアップロードを行う
  • BDEF1、BDEF2、BDEF4の設定を反映させた頂点スキニングを行う。
  • コンパイルにclangを用いる
  • C++で記述する
  • 必要なメモリは可能な限りalloc済みにする

頂点ブレンディングのみ行う

CPU(単純実装)

  • 頂点をfor文で全て処理
  • 頂点ごとにスキニング処理をif文で分岐
端末 デバッグビルド 最適化ビルド 備考
Nexus5 28.3ms 1.9ms 案外早い
Galaxy Nexus 65.0ms 4.4ms そこそこ速い

CPU(hard-float有効化)

  • hard-floatを有効化して、なるべくハードウェアを利用するように修正
Android.mk
LOCAL_ARM_MODE := arm
LOCAL_ARM_NEON := true
LOCAL_CPPFLAGS += -mfpu=neon
Application.mk
APP_ABI := armeabi-v7a-hard
端末 デバッグビルド 最適化ビルド 備考
Nexus5 25.3ms 1.5ms かなり速くなる
Galaxy Nexus 51.0ms 4.1ms 速度に大差なし

CPU(vec3 -> vec4に変更)

  • 頂点構造体の位置情報にvec3を利用していた
  • そのため、vec4に変更して一時オブジェクトを生成しないようにする
  • メモリ消費量が微増(4byte/vertex)
  • 速度差は誤差レベルでしか発生しなかった
端末 デバッグビルド 最適化ビルド 備考
Nexus5 20.2ms 1.4ms 誤差レベル
Galaxy Nexus 53.0ms 4.5ms 誤差レベル

CPU(glmのインライン化)

  • GLM_FORCE_INLNIEで強制的にinlineにする
  • 性能が上下したけど、誤差レベルな気がする
端末 デバッグビルド 最適化ビルド 備考
Nexus5 - ms 1.2ms 多少早くなった?
Galaxy Nexus - ms 4.8 ms 多少遅くなった?

CPU(全て1頂点として計算し、分岐を削除)

  • ブレンド数の低下はかなり効果がある
  • if文の削除はあまり効果がなかった(恐らく最適化のため)
端末 デバッグビルド 最適化ビルド 備考
Nexus5 - ms 1.0ms 速度向上
Galaxy Nexus - ms 4.5 ms 速度向上

CPU(事前に頂点タイプごとにグルーピングしてif文を回避)

  • if文がコンパイラ最適化によってボトルネックにならないため、ほとんど速度差が生まれない
  • for文内で分岐を行ったほうが、最適化によって良い結果を生むらしい
    • 2.5万回もif文展開してもさしてボトルネックにならないのはスゴイな
端末 デバッグビルド 最適化ビルド 備考
Nexus5 - ms 1.7ms 多少遅くなる
Galaxy Nexus - ms 5.7 ms 多少遅くなる

CPU(複数Threadで分割処理)

  • 適当な数のThreadに処理を分散する
  • Threadの同期処理のほうがコストが高いため(?)か、処理が遅くなる
  • 概ね、6Thread前後が良い結果となった
    • 遅いことに変わりは無し
  • 更に大量のデータを処理したら変わるかも?
端末 デバッグビルド 最適化ビルド 備考
Nexus5 - ms 3.5 ms 遅い
SHIELD TABLET - ms 3.8 ms 遅い

法線計算を加味する

1Threadでif文付きで計算させる

  • MEMO:不要なThreadがあると処理に影響するので止めておく
端末 デバッグビルド 最適化ビルド 備考
Nexus5 - ms 2.8ms(r10d) → 1.9 ms(r10e) → 1.3ms(コード手動最適化) NDKのバージョンアップで高速化
Nexus9 - ms 2.2 ms(r10d) → 1.7ms(r10e) → 0.9ms(コード手動最適化) NDKのバージョンアップで高速化
SHIDLE TABLET - ms 2.9ms(r10d) 多分検証すれば速くなってるハズ
Zenfone2(ML550) - 2.1ms コードをイジった後

複数Threadに分割する

端末 デバッグビルド 最適化ビルド 備考
Nexus5 - ms 5.1 ms(r10d) → 3.3 ms(r10e) 速くはなったが、結構時間がかかる
Nexus9 - ms 2.4 ms(r10d) 速い

RenderScript版

注意点

  • 16byte単位にデータ境界が設定されるため、構造体の定義に注意が必要
  • AllocationからGL用バッファへの書き戻しコストが非常に高くてあまり現実的じゃない
  • 構造体をoutさせるとJava → NDKへのデータ転送が遅いため、I8配列として渡してRS内部で構造体にキャストする方法を選択した
    • 書き戻し時間は減ったが、恐らく並列化の恩恵が少なくなるので実行時間は増える
    • NDKから直接データをIOできる機能が欲しい(r10e時点で無し)

結果

端末 デバッグビルド 最適化ビルド 備考
Nexus5 - ms 4.0 ms 遅い
Nexus9 - ms 4.4 ms 遅い

まとめ

  • 少量オブジェクトを変形させる場合は、CPU/1Threadでやったほうが軽くなりそう
    • Thread制御のオーバーヘッドのほうがでかくなる
    • アルゴリズムを見直すことによる手動のコード最適化も十分に効果的
    • コンパイラばかりに頼らないほうが良い場合もある
  • Makefileのオプションによっては更に高速化が行えると思われる
    • コンパイラの最適化レベルを上げれば多分もうちょい早くなる?
    • 未検証
  • RenderScriptを頂点シェーダー代わりに使用するのは難しい
    • RenderScriptからのメモリの書き戻し時間優先 or 処理速度優先のどちらかを取るしか無い
    • RenderScriptへの書き込みは、多少の工夫で高速に(uncheckedなメソッドで)可能
  • Android NDK r10d→r10eで処理速度が向上した
    • 特定分野の処理だけかもしれないが、最適化されやすくなったのだろうか
4
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
4