強火で進め

このブログではプログラム関連の記事を中心に書いてます。こちらで( https://nakamura001outdoor.hatenablog.jp/ ) アウトドア関連の記事も書いてます。

「第7回 GREE Tech Talk」に行って来ました

募集ページ

  • 2015/03/25開催

[ヽ(^◇^*)/ 300名まで増枠!] GREE Tech Talk #07「Unity Performance Casual Talk」- #greetech07 : ATND
https://atnd.org/events/63402

Toggtterまとめ

第7回 GREE Tech Talk #greetech07 - Togetterまとめ
http://togetter.com/li/800364

Live2Dの描画の裏側の話

  • 発表者:阿曽 直貴(株式会社Live2D)
以前のネイティブプラグインで作成したいた時代の話
  • 約3年前(2012年初旬)
  • 実機Android2.2やiPhone3GSPSPくらいでもちゃんと動く

Unityへの移植の検討

  • C#?速いの?
  • ネイティブプラグインで今までの資産が動く
  • ネイティブのOpenGLが遅いはすが無い

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

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

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

// 以下、延々と同様の処理

元に戻す

// 描画をUnityに戻す前にステンシルテストを元の状態に復元して、
if (last_stencilTest) glEnable(last_stencilTest);
else glDisable(last_stencilTest);

...以下略

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

デバッグが大変

  • Unityの管理外なのでプロファイラなどは使えない
  • Unityのバージョンアップで動かなくなる
ネイティブプラグインをやめてC#実装に変更

高速に2Dを描くにはどうすれば良いか調査

Graphics.DrawMeshが使えそう

Unity - スクリプティング API: Graphics.DrawMesh
http://docs.unity3d.com/ja/current/ScriptReference/Graphics.DrawMesh.html

Visual Sudioでコーディングしていると「使用しないで下さい」の警告が

Unity 5の更新情報にもこれは削除されるので「使用しないで下さい」の文字が

代わりに Graphics.DrawMeshNow を使う

Unity - スクリプティング API: Graphics.DrawMeshNow
http://docs.unity3d.com/ja/current/ScriptReference/Graphics.DrawMeshNow.html

Graphics.DrawMeshNow の特徴

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

ドローコールは75になった。パーツの数だけドローコールが発生している様で有った
  ↓
Conbineでまとめる

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

改善具合

Tips

Array.Reverseが遅かった

配列を反転させるためにArray.Reverseを使っていた

Array.Reverse(buf4, 0, 4);
return BitConverter.ToSingle(buf4, 0);

大量に使うとかなり遅いことが分かったので、素直に自分で書いた。

tmp[0] = buf4[3];
tmp[1] = buf4[2];
tmp[2] = buf4[1];
tmp[3] = buf4[0];

return BitConverter.ToSingle(tmp, 0);

データ読み込み速度が20%くらいアップ

開発スピードとパフォーマンスの両立(仮)

  • 発表者:稲森 亮介(グリー株式会社)

カジュアルゲーム

Application.targetFramerateを動的に変える
同時にfarClipの距離を調整して速度改善も図る

雲、Overdraw(同じ位置に重ねて描画される部分)多すぎ

パーティクルでやっていた

Prewarm(※)は重いのでロード時点で長めに流して対応
※表示された時に既にパーティクルが出てる様にしたい場合に使用するもの。使わないとパーティクルが放たれ始める部分がユーザの目に触れる

地形が複雑な時

遠景は大きなスプライト

中景はほどほどのサイズ

ライトは使わずテクスチャにグラデーション

傾ける&揺らす

パーティクル→自前のビルボード

Asset Storeを活用

そのまま使うのでは統一感が無くなる

元の素材は昼のシーンだったがゲームは夜のシーンなので調整

  • Skyboxの変更
  • フォグで夜っぽい色に調整

これだけでは違和感が有る(※)のでライティングを行いたい
※地形が昼の表現のまま

しかし、この素材はベイクされているにも関わらず、元のライトが削除されている
  ↓
ライトマップテクスチャ(OpenExrファイル)自体をPhotoshopで調整

動的にライトマップを解除
  • 洞窟のシーンで明るいシーンと暗いシーンが有る。暗いシーンでは照準のカーソルがライトに成っていてライトが当たってる所だけ見えるという仕様
  • 管理の都合上、洞窟の暗いシーンと明るいシーンは1つにしたかった
Renderer[] = renderers = FindeObjectsOfType<Renderer> ();
foreach (var renderer in renderers) {
	renderer.liehgtmapIndex = -1;
}
  • lightmapIndexはLightmapSettings.lightmapsのインデックスだが、-1はライトマップが割り当てられてない事を示す初期値。これを設定する事でライトマップを動的にOFFに出来る
  • 敵キャラは頂点ライトのみ
  • Direction Lightのみだと流石に安っぽくなってしまうので、Point Lightを複数配置
敵キャラの動き

敵の位置が固定の場合、手付けで制御点を置く事が多い

今回は人数が少ないのでそれは使いたく無かった

手付けでは無く、計算式で実装。移動+回転の組み合わせなど

ドローコール伝説の終わり 〜新たなるパフォーマンスの道を求めて〜

StatsからDrawCallが無くなっている

ドローコールとは
  • 描画のCPU側の処理
  • 厳密に言うと、CPU側のドライバーがGPUに描画内容を渡す処理
  • OpenGLAPIで言うとglDrawElements()とかが該当

CPUでやっている描画関連の処理はDrawCall以外にも色々有る

例えば頂点データ、テクスチャ、マトリックスの処理なども行う


厳密なDrawCallは矢印のCPUからGPUへ処理を渡す処理の部分のみ


この部分は昨今のドライバーの進化(Metal/Vulkanなど)でどんどん高速に

DrawCallは初回は遅いが、2回目以降は高速。小さなウェイトなので多少の数の場合には気にしなくてよいが流石に100回とかになってくると注意が必要

頂点をまとめる処理の方がコストが高くなってきている

Unity 5からStatic Batchingの処理にも変更が行われている

GPU側の描画の問題

Renderがいっぱい(ピクセル描画が重い)

Tilerがいっぱい(頂点量が多すぎ)

ドローコールを減らしても解決しない

Xcode & Instruments でチェック

GPU Profiler

PVRTCなどフォーマットの方がテクスチャのキャッシュにヒットしやすい

Cube mapのようなランダム・アクセスするテクスチャはキャッシュミスがヒドい

モバイルでUnityの新しいシェーダのライティングをするとCube mapの解像度が低いのはパフォーマンスが悪い為

ピクセル描きすぎ(オーバードロー)

Tilerがいっぱい

  • 送信している頂点数が多すぎ

モバイルは半透明に弱い(discardのコストが高い)

  • 2DはUnity 2Dを使う(半透明のピクセルを極限まで減らす)

【具体例】

※透過付きの2ポリゴンより、透過の無いポリゴン数が多いもの方が良い
頂点が多くなっても塗る範囲が減る最適化をする

重い描画と重くない描画の発見

Rendererがいっぱい

  • fragment shaderを簡単にする/テクスチャのキャッシュミス→GPUベンダーのプロファイルでチェック
  • ピクセル描きすぎ(オーバードロー)→フレームデバッガ/Xcode/シーンビューでチェック

Tilerがいっぱい

  • GPUに送信している頂点数が多過ぎる→フレームデバッガ/Xcodeでチェック
結論
  • 描画が重い=ドローコールではない
  • 重い原因に気を配ろう(重いドローコールと重くないドローコールの切り分け)
  • 重い原因を解決しよう
  • 原因の発見にはUnityのProfiler/Frame Debugger/GPUメーカーやベンダーのProfilerを使おう
おまけ

Unity 5でGL Frame Captureを使うには

  • Unity 5.0p2以上を使用
  • Player SettingsでGraphics APIOpenGL ES 2.0に設定

Unity 5でInstrumentsでシンボルを見るには

  • 出力されたXcodeのプロジェクト設定でDebug Information Formatを「DWARF with dSYM File」に設定