【Unity】Unity Performance Casual Talk 「Live2Dの描画の裏側の話」 レポート


# レポート一覧

* [Live2Dの描画の裏側の話](http://qiita.com/baba_s/items/0de1702e6055a4503167)
* [Unityカジュアルゲーム・ケーススタディ](http://qiita.com/baba_s/items/9289f1eab711f04faec8)
* [ドローコール伝説の終わり](http://qiita.com/baba_s/items/5260807ced7fc3c02ca6)

# GREE Tech Talk #07 : Unity Performance Casual Talk

http://techtalk.labs.gree.jp/07/

## niconico LIVE

http://live.nicovideo.jp/watch/lv214504347

## スライド

http://www.slideshare.net/naokiaso75/live2-d

## パフォーマンスの最適化

# 講演内容

* Live2D for Unity
* ネイティブプラグインによる高速化
* スクリプトから高速にメッシュを描画する

# Live2Dの紹介

## Live2Dって？

* イラストをそのまま動かす技術

## どんなところで使われているの？

* http://www.live2d.com/showcase


## どうやって使うの？

* Live2D SDK for Unity
	* 無料で始められます
	* Live2Dスタッフブログにまとめました
	* http://staff.live2d.com/

# Unityの話に入る前に

* 技術的には3Dと何が違うのか
	* 基本的には3D技術の上に成り立っている
	* ポリゴンの描画
	* 一般に3Dより描画時のメモリ消費は多め
	* 高機能なボーンのようなもの＞デフォーマ

## 3Dとの違い

1. 頂点の形状変形が常にある
2. 半透明合成が重要
3. メッシュでリアルタイムクリッピングが必要
4. テクスチャのクオリティが必要  
適当にPVRTC圧縮すると劣化する

## それらを踏まえて

* 3D技術の延長ではあるけど、3Dからするとマニアックすぎて情報が少ない
* 3D技術で再現できるけど、最適化されていないので極端に遅かったりする
* 具体的にUnity上で試行錯誤した話

# ネイティブプラグインによる高速化

* Unity3.5とかの時代です
* ネイティブプラグインとOpenGLについての話
* 現在とは状況が異なる

## Live2D当時の状況

* 約3年前(2012年初旬)
* Android,iOSのネイティブなライブラリはある(C++とJava)
* パフォーマンスについてもすでに作りこんである
* 実機Android2.2やiPhone3GSやPSPでもちゃんと動く

## Unityへの移植の検討

* C#？速いの？
* ネイティブプラグインで今までの資産が動く！
* ネイティブのOpenGLが遅いはずがない！

# ネイティブプラグイン+OpenGL

## 必要そうな情報を記憶しておく

```cs
// Live2D描画する前に
// ステンシルテストの状態を記憶して、
last_stencilTest = glIsEnabled(GL_STENCIL_TEST);
// デプステストの状態を記憶して、
last_depthTest = glIsEnabled(GL_DEPTH_TEST);

// 以下長々と同様の処理
```

##元に戻す

```cs
if (last_stencilTest) glEnable(last_stencilTest)
else glDisable(last_stencilTest);
``` 


...以下略


状態、バッファ、シェーダプログラム、テクスチャなど
に対して思いつく限り復元する

## 結果

* 動いた！
* 確かに速いように思える
* 見かけのドローコールは1(意味はない)


## でも

* プラットフォームごとに自分で書かないといけない
* デバッグが非常に大変

# デバッグが大変

* Unityの管理外なのでプロファイラなどは使えない
* OpenGLの状態を正しく復元しないといけない
* Unityのバージョンアップで動かなくなる
* 内部処理の影響をうけるのでそもそも情報公開されてない可能性あり

ネイティブプラグインは自己責任


## まとめ

* 当時としてはチャレンジングなプラグインだった
* 高速だけどメンテナンスが大変
* Android機種依存とかは自力対応しないと
* ネイティブによりすぎて、Unityの能力を生かしきれなかった

# スクリプトから高速にメッシュを描画する

ネイティブプラグインをやめてC#で書くことに決まった


## 注意

* Unityでゲームを使ったわけではないので、制作のほうはわかりません
* GameObjectと関係なく、スクリプトからメッシュを描画する方法について
* C#で書き直すために調査

## Graphics.DrawMesh


* メッシュの数が多くても、ゲームオブジェクトの管理が楽になる、ということらしい
* 「使わないでください。」というメッセージが出てきた
* Unity5で削除予定とのことだったので使用せず

## Graphics.DrawMeshNow


* UpdateじゃなくてOnPostRenderで呼ぶ
* MaterialのPassの設定が必要(普通は0)

## 結果

* テクスチャ1枚
* マテリアル一つ
* パールの数だけ
* ドローコールが増えてる

## ドローコールのための基本事項

* とにかく同じ描画設定で描く
* そのために
	* マテリアルは最小数にする
	* テクスチャもまとめて一枚にする

## メッシュは自分でまとめよう

## Mesh.CombineMeshes

```cs
var combine = new CombineInstance( 10 );
for ( int i = 0; i < combine.Length; i++ ){
	combine[ i ].mesh = meshes[ i ];
}
// このメッシュにすべての他のメッシュをまとめる
mesh. Mesh.CombineMeshes( combine );
```

## 結果2

* ドローコール実質1回

## 本当に速くなったのか検証

* ドローコール削減前
	* Live2Dキャラクターを11対表示
	* ドローコール800超
	* 15FPS以下

* ドローコール削減後
	* ドローコール12
	* 30FPS以上

* 正直思った以上に違う
* ただし、純粋な検証ではなく、あくまでLive2D上での比較です

# パフォーマンスのためのTips

## テクスチャアトラス

* テクスチャを一枚にまとめる
* Live2D SDKは次期バージョンで対応予定

## GCの最適化

* 1キャラクター描画するときに毎フレーム約40KBメモリ確保していた
* 40KB*60フレーム = 毎秒2.4MBの消費!
* Mesh.verticiesをやり取りするときにコピーが発生
* 配列はキャッシュしておく

## Array.Reverseが遅かった

* 配列を反転するためにArray.Reverseを使っていた
* 大量に使うとかなり遅いことが分かったので、素直に自分で書いた
* データ読み込み速度が20%くらいアップ
* 「1行で書いたらカッコイイから」ではなくちゃんと使う理由を考えたほうが良い

## プロファイラに頼らない方法

* お手軽に時間を計測できるような簡易クラスをつくておくと便利

```cs
MyDebugTimer.start( "テスト計測" );
for ( int i = 0; i < 10; i++ )
{
	// 重そうな処理
}
MyDebugTimer.end( "テスト計測" );
```

# まとめ

* Live2D for Unityでどんなことをやっているか
* 2Dだけど、レンダリング技術的には3D技術です
* Unityでの開発は苦労したとはいえ、ネイティブ言語での開発よりはだいぶ効率的

# ミドルウェアとして

* Live2Dでは現在5言語、10以上のプラットフォーム
* 専門部隊がいるわけではない
* 効率的にやらないといけない
* 特定のプラットフォームに依存しないこと
* 自力で多数のプラットフォームに対応すること
* 普遍的な技術であるために

# Live2D Cubism 2.1


* クリッピングの対応
* 便利になったPSDインポート
* 編集効率の向上

# Live2D Euclid

* http://www.live2d.com/news/euclid_development