Unityパフォーマンス最適化のポイントメモ

【Unity道場16】パフォーマンス最適化のポイント に行ってきました。 その時のメモ ちょいちょい抜けてる所あるのでそこだけ注意

パフォーマンスの良いアプリ

パフォーマンス, 反応
CPU と GPU どっちをチューニングするべき -> 両方
どちらか片方でも時間かかった場合目標値にたどり着かないから

パフォーマンスはプラットフォームによって特性がある その特性に沿ったチューニングが必要

それぞれのCPU、GPU、メモリの参考

種類 CPU GPU MEM
スタンドアローン 潤沢 潤沢 潤沢
ゲーム機 物による 物による 物による
スマフォ 弱い
webGL 弱い 絶望
VR 弱い |

他要因 IOのアクセス メモリバンド

Unityエディタの特性はスタンドアローンに近い Unityエディタはアセットをキャッシュしまくる!

CPU、GPUの確認する方法は ー> とりあえずプロファイラを使う CPUとGPU時間が表示されるよ(単位はms)
より細かいのだと各プラットフォームに依存したプロファイラを利用したほうがよい

GPUについて

重くなる原因 - 重いシェーダ - フィルレート - オーバードロー - ハイポリ - 描画内容が多い - 画面外の描画 - ミップマップ未使用

解像度の高いデバイスでピクセルシェーダーは負荷が高い
pow exp log cos sin tan等の利用を避ける
normalize, dot, inversesqrt等を自前で記述は避ける
浮動小数点の精度を下げる

Unityはアルファ分を計算して最小限のポリゴンにしてフィルレートを下げてる
今まではハイポリでそこまで問題になってなかった、VRは2回描画するという特性で問題になってきた

表示するポリゴンを削減する方法
- LOD - OcculusionCulling

処理はCPUで行っている点に注意

ポリゴンを結合するアイディア (CPU負荷軽減) - オブジェクトを結合し、単一メッシュとして描画 - 欠点:カリングによってポリゴンが削減されない

影の描画負荷を減らす(GPU負荷軽減) - CastShadowsをOFFに - Received ShadowをOFFに

ローディングについて

不要なものはロードしない

アセットバンドルの場合は少し特殊
アセットバンドルに格納した場合、暗黙的に参照しているアセットを含めてしまう
シーンとアセットバンドルで同じアセットを参照した場合、それぞれは別の存在として認識される
アセットバンドルの依存関係の設計で重複ロードを避けるようにする必要がある

メモリが多いならロードしたアセットを破棄しないという方法もある

アセットバンドルの中にFBXを含んだ場合

ほしいのはアニメーション情報だが実際にデシリアライズするのはFBXの中身を行おうとする
FBXを読むのではなく、対象のアセットを参照するアセットを読む
プレハブなど必要なアセットを参照している場合はFBX全体をアセットバンドルに含まれるということはない

シーンで配置をして画面構築か ランタイムでの画面構築か

同じオブジェクトが1万個の並ぶ実態を作る場合
Create(Instantiate) Load(Scene) Clone(実態1つ作って)の中ならCloneが一番はやい
Createの中の処理だとadd componentが重い
Loadでは値の反映が一番重い パラメータの流し込む処理
スクリプタブルオブジェクトを利用することで改善をすることができる(デシリアライズが減る)

Clone処理が実験では一番早かったが、親子関係の構築するところで負荷が高い
GameObject.Instantiate(newGo, parentTransform)というAPIがある(5.4から)
alloc無しで親子関係が構築できる
parentTransform.hierarchyCapacity = 1000
GameObject生成・破棄のコストが安くなる
親の変更コストが上がる
InstantiateParentをした後に親子を変更すると負荷が高い

CPUに付いて

CPUの効率を求めるならCPUに働かせないこと!
けど、一切働かないのは無理 なので、バレないようにサボる

Culling Group - 部屋のレベルで処理のON/OFF - OnBecameVisible/OnBecameInvisible - 毎フレーム処理しない

CPU関連の一番の大きな問題 GameObject

ヒエラルキーの運用
UnityでGameObjectをフォルダ代わりに利用していることが多いが階層は作るのも動かす(座標だけ動かすだけ)のも負荷が高い
5.4でマシになった 5.6で早くなる
5.6になるまではGameObjectの階層には注意が必要

エディタの時だけ親子構築するなどの対策必要かも

じゃあAnimatorは? -> 回避策がある
FBXのインスペクターでRigタグでOptimize Game Objectを有効に オブジェクトの結合をする
手のボーンなど一部の座標が欲しい場合はExtra Transform to Exposeを利用 ただしリードオンリー
スキニングの処理負荷がガッツリ下がる
Animatorの場合Culling Groupを利用なくてもカリングができる
Culling ModeでCull Completelyに設定

非アクティブオブジェクトの扱い

有効な方法だが注意の必要な項目がある - Collider - Rigidbody2D - Canvas (ジオメトリ情報が破棄され、UIの再構築が走る 負荷が高い) - Animator

この3つはアクティブを切り替えると初期化される

対策
Canvas GameObjectのアクティブをするのではなく、Canvasをdisableにする
Animatorも同様 (初期化おきない)
Collider Rigidboyはどうしようもない
Rigidbody2DはSimulatedのチェックを外す

補足 Animatorのアクティブ切り替えの場合パラメータの流し込みの負荷が高い

UIについて

UIのバッチング UnityのUIは動かさない場合負荷がとても軽い
ただ 1つでも変化するとCanvas以下でオブジェクトの再構築が走る
変化するオブジェクトにCanvasを追加してやる
Canvas単位で再構築されるため

Fill Centerの無効

スクリプトのパフォーマンスの稼ぎ方)

効果の高い順に数字

1. local positionを使用する
2. VectorのOperatorを減らす
3. VectorのMathfを利用しない
4. transformをキャッシュする
5. engine codeの呼び出しを削減
6. Time.deltatimeをキャッシュする

Unity5.6でlocal position使用しない場合の負荷が軽減される

その他 - foreachを利用しない (5.5でコンパイラが変わったので効果がないかも) - ListではなくArray - NonAllocと名のつくAPIを使う - stringの結合を避ける - UpdateやLateUpdateのコールバックを避ける

おさらい - CPUとGPU両方共チューニングする - どの程度かはプラットフォームによって変わるy - CPUはシェーダーやポリゴンの設定を見直す - ローディングは余計なものはなくす - アニメーションhあオプティマイズを利用する - Transformの移動は控えめに - localPositionを使う - Vectorのオペレーションは避ける

Unity5.4くらいからマルチスレッドレンダリングが行えるようになった

Golang 覚書 その2

qiita.com

やっとその2が書けた。。。 主にデータ構造について記載。

TOMLというミニマル言語

設定ファイルをjsonからTOMLに移行

ツールなんかを作るときある程度は自由度持たせるために設定をjsonで書いていたが、最近どうしてもカッコが面倒になってきた。
パラメータが少ない時なんかは問題無いんだけど、データが増えてくるとどうしても書くのが手間。(コメントも書けないし)
そんなことを思っていたらTOMLという物を発見。

# コメント 
Key1 = "Value1"
Array = [1, 2, 3]
[Object]
Key3 = "Value2"
[ObjectInArray]
Array = ["a","b","c"]

キーバリューペアの形式で書いていく形になる。
jsonの様にArrayやObject型などがあるため移行もしやすそう。

コメント

ハッシュ記号(#)から改行までがコメントとなる。

# コメントだよ
Key = Value #こんなのも可能

文字列

文字列の記述は4種類の方法がある。

基本

ダブルクォートで囲むと文字列として扱われる。
クォート、バックスラッシュなどはエスケープをする必要がある。

エスケープシーケンスの短縮形も用意されている。

\b         - backspace       (U+0008)
\t         - tab             (U+0009)
\n         - linefeed        (U+000A)
\f         - form feed       (U+000C)
\r         - carriage return (U+000D)
\"         - quote           (U+0022)
\/         - slash           (U+002F)
\\         - backslash       (U+005C)
\uXXXX     - unicode         (U+XXXX)
\UXXXXXXXX - unicode         (U+XXXXXXXX)

複数行文字列

改行も文字列として扱いたい場合、ダブルクォート3つで囲むことで表現出来る。
文字列の頭にすぐ改行を入れた場合その改行は無視される。

string = """
TOML Test
TOML Test"""

リテラル文字

正規表現のパターンを書くような場合、エスケープをするケースが増えとても面倒なケースが多い。
TOMLはリテラル文字列をサポートしているので、利用することでエスケープを省略して利用することが可能となっている。
リテラル文字を利用する場合シングルクォート囲むことで利用できる。

regex = \`[\d]+\'

複数リテラル文字

リテラル文字ではエスケープを利用することができないため、シングルクォートを利用することができない。
TOMLではリテラル文字の複数行をサポートしているのでそちらを利用する。
シングルクォートを3つで囲むことでリテラル文字として扱われる。

整数

全ての数は整数として扱われる。
正の数は頭にプラスを付けても、つけなくても表現できる。
負の数は頭にマイナスを付けて表現する。
大きな数字を表現する場合、アンダースコアを数字の間に挟むことで区切り表現をすることができる。

1_000
123_456_789

整数の表記を0から始めることはできず、2進数8進数16進数の形式で表現することは出来ない。
精度は64bit(long)

小数

小数部と指数部で表現が可能。
マイナス、プラスやアンダースコアでの区切りは整数と同じルールで利用可能

123.456_790

精度は64bit(double)

ブーリアン

ブーリアンはただのトークン。
小文字のみ利用可能。

日付

日付型はRFC 3339に準じる。(この規格しらないけど)
年-月-日T時:分:秒-UTCオフセットの形式になる。
UTCオフセットを0にする場合"年-月-日T時:分:秒Z"と記述すればよい。
日本のUTCオフセットは09:00なので 2016-02-29T00:00:00-09:00 となる。
タイムゾーンを利用するような場合以外は使わないとは思うが。
ちなみに秒には小数表現が可能。

配列

配列は[]この括弧で囲まれたプリミティブ型の集まり。
空白は無視され、要素はカンマで句切られる。
データ型は共通である必要がある。

array = [1, 2, 3]
matrix = [[1, 2, 3],[4, 5, 6]]
arrayStr = ["aa", "bb", "cc"]

テーブル

キーとバリューのペアからなる集まり。(jsonのオブジェクト型と同じ扱いができる)
テーブルはで囲まれたテーブル名から始まる。
配列表現と同じ括弧を利用するが、区別はしやすい。
配列の場合必ず値として表現するが、テーブルは値である必要はなく [table name] この様に記載する。

テーブルはで開始され、次のテーブルの開始またはファイルの終端までこのテーブルに属する。
使える文字は英数字、アンダースコア、ダッシュのみ(a-zA-Z0-9_-)

[table]
key1 = "value1"

key2 = "value2"

[table2]
key1 = "value1"
key2 = "value2"

と書いた場合jsonでは以下の構造を表現する。

{
  "table": {
    "key1": "value1",
    "key2": "value2"
  },
  "table2": {
    "key1": "value1",
    "key2": "value2"
  }
}

ネストしたテーブルを表現する場合ドットを利用する。

[table]
key1 = "value1"

key2 = "value2"

[table.NestTable]
key1 = "value1"
key2 = "value2"

この場合jsonで以下の構造を表現する。

{
  "table": {
    "key1": "value1",
    "key2": "value2",
    "NestTable": {
      "key1": "value1",
      "key2": "value2"
    }
  }
}

上位のテーブルを記載する必要が無い(値が存在しない)なら省略することも可能。
上位のテーブルを省略しても、後の行で上位のテーブルの内容を書くことはできる。
ただ、キーやテーブルの再定義は出来ないため、各定義は必ず1つである必要がある。

[table]
kay1 = "value1"

[table] # これはNG
kay2 = "value2"

テーブル配列

テーブルの再定義は行えないが、テーブルの配列を定義することはできる。
テーブル名を [ [table name] ] 括弧2重で囲むことで同じ名前のテーブルは配列の要素となる。
テーブルは表記順番で挿入され、キーとバリューのペアを持たないテーブルはからのテーブルとして扱われる。

[[TableArray]]
Key1 = "Value1"
[[TableArray]]
Key2 = "Value2"

これは以下のjsonの構造となる。

{
    "TableArray":[
        { "Key1" : "Value1" },
        { "Key2" : "Value2" }
    ]
}

ネストしたテーブルの配列も定義することができる。
その場合子テーブルにも括弧2重で囲めばよい。

まとめ

自分が使いそうな表記はこのぐらい。
頭でも書いたがjsonから機能の置き換えとしては十分。(むしろこっちのが良く思える)
パーサーも自分が使う言語はほぼあるので困ることはなさそう。

ただ、javascriptを使う時だけはなんとも言えない。
jsonはそのままjavascriptで使うことが出来るのに対して、TOMLは一度パーサーを通さないといけないから悩みどころ。
でも、製品で使う様な機能でない限りTOML採用かな。

参考URL
https://github.com/toml-lang/toml

※まだバージョンは0.4.0と1.0になってない所だけ注意

Go言語 覚書 その1

最近go言語を始めたので、忘れないための覚書をqiitaに投稿。

qiita.com

とりあえず環境構築、制御構造、関数に関して記載

古いバージョンのUnityスクリプトリファレンスのURL

Unity5で開発をしている最中にUnity5.1がリリースされてUnity公式のAPIリファレンスも5.1に対応されたため、過去のAPIが確認できなくなってしまいとても焦った。 いろいろ調べて旧バージョンのUnityAPIを確認する方法がわかったので忘れないように残しておく

Unityの公式ページからスクリプトリファレンスのURLは下記になっている
(要は最新のスクリプトリファレンス)
http://docs.unity3d.com/ScriptReference/index.html

過去バージョンのスクリプトリファレンスを見たい場合はこちら
http://docs.unity3d.com/(version)/Documentation/ScriptReference/index.html

(バージョン)と書いてある所をUnity5なら500にして
http://docs.unity3d.com/500/Documentation/ScriptReference/index.html
Unity4.6ならば
http://docs.unity3d.com/460/Documentation/ScriptReference/index.html

としてアクセスすれば過去バージョンのAPIが確認できるっぽい。
ただ、マイナーバージョンの単位では存在するわけでなく、APIに更新の入るミドルバージョン以上の単位で存在する様に思われる。(ざっくりしか見てないからあってるのかわからないけど)
少なくとも 460, 500, 510は存在することを確認

UnityのPropertyDrawerでReorderableListを利用する

UnityでReorderableListが使えるようになっていたのでPropertyDrawerで指定出来るようにしてみた

ReorderableList

本当なら

public class ReorderableListTest : MonoBehaviour
{
    [ReorderableList]public List<int> data_;
}

こんな形でIntReorderableListみたいなクラスを作らないでメンバー変数に直接Listを持たせてAttributeの指定をすればいい形にしたかったがやり方がわからん。
Listに直接Attributeの指定をしてもサイズが1以上ないとPropertyDrawer側の関数が呼ばれなく、呼ばれたあともListクラスが保持しているメンバー変数がserializedPropertyに投げられるためReorderableListの生成が出来なかった。

そのためLIstを持ったクラスを宣言して、メンバーに持たせることでとりえあず回避。 Inspector拡張を利用すればこんな形にしなくて済みそうだが、ReorderableListのためだけに書くとなるとちょっと面倒。

なんか良い方法ないかなーー

初めてのcmake備忘録

最近vimを利用してC++を書く関係でmakefileを調べたりしてたが、先日cmakeを利用してのプロジェクト構築がmakefile書くよりも楽で便利にとても気に入ったので忘れないように備忘録として書いておこう。

ワーキングディレクトリ(cmakeの実行結果出力先)はどこでもいいが、引数で指定するcmkaeのソースパスにはCMakeLists.txtが配置されている必要がある。

実験で記載していたglfw、glew, boostを利用したプロジェクトのCMakeLists.txtはこちら。(自分の環境がmacなのでそれ以外は考えない!!)

初めてのcmake

CMakeLists.txtの用意ができてしまえばcmakeコマンドを実行して ビルド方法によって各ファイルを出力するだけ。

project_root
.
├── CMakeLists.txt
├── build
└── src
    └── main.

プロジェクトの構成としてはこんな感じになる。

ワーキングディレクトリがproject_rootだとしてそこで

cmake ./

とすることでproject_root直下にmake用の設定ファイルが出力される。

ワーキングディレクトリがproject_root/build にいる場合

cmake ../

因みにmakeではなくてxcodeで出力したい場合

cmake -G Xcode ./

と"-G Xcode"とオプションを指定すれば良い。

一番大事なCMakeLists.txtがほぼコピペで理解が浅いが一先ず使うことはできるので一先ずここまでにして後はちょいちょい調べてわかったことから徐々に記載していこう!!