maimai_jp's blog

twitterでは書ききれないことなどをこちらに。

えいごのおべんきょう:Unity Gear VR Gameのパフォーマンスを搾り出せの続き

続きが来てたので、対訳形式で翻訳してみるその3です。

Blog — Squeezing Performance out of your Unity Gear VR Game Continued | Oculus

Squeezing Performance out of your Unity Gear VR Game Continued
Unity Gear VR Gameのパフォーマンスを搾り出せの続き


Part 2: Solving Performance Problems
パフォーマンス問題を解決せよ

In my last post I discussed ways to build efficient Gear VR games and the traits of Gear VR devices.
前回の記事ではGearVRゲームの効率的なビルドとGearVRデバイスの性能について検討しました。

In this installment I’ll focus on ways to debug Unity applications that are not sufficiently performant on those devices.
今回はそれらデバイス上で十分な性能が出ていないUnityアプリのデバッグ方法について焦点をあててみます。


Performance Debugging
パフォーマンスデバッグ

Even if you’ve designed your scene well and set reasonable throttling values, you may find that your game does not run at a solid 60 frames per second on the Gear VR device.
シーンを申し分なくデザインしてきて、適切なスロットル設定がされているにも関わらず、GearVR上でゲームが60FPSで動作しない場合。

The next step is to decipher what’s going on using three tools: Unity’s internal profiler log, Unity’s Profiler , and the Oculus Remote Monitor.
次のステップとしては、3つのツールを使って何が起こっているのかを解読することです: Unityの内部プロファイラログ(ビルトインプロファイラ)、Unityのプロファイラ、そしてOculusリモートモニターです。

The very first thing you should do when debugging Unity performance is to turn on the Enable Internal Profiler option in Player Settings.
Unityでのパフォーマンスのデバッグで真っ先にすべきことは、PlayerSettingでEnableInternalPlofilerオプションをオンにすることです。

This will spit a number of important frame rate statistics to the logcat console every few seconds, and should give you a really good idea of where your frame time is going.
これは多くの重要なフレームレートの統計をlogcatコンソールに数秒ごとに吐き出します。そして、フレーム時間がどのようになっているかということから得策を提供するでしょう。

To illustrate the common steps to debugging performance, let’s look at some sample data from a fairly complicated scene in a real game running on a Note 4 Gear VR device:
パフォーマンスのデバッグについて一般的な手順を例に挙げるとするなら、サンプルデータとしてNote4使用のGearVR上で実際のゲームを走らせた中で十分に複雑なシーンから取ったものをご覧ください。

Android Unity internal profiler stats:
cpu-player>    min:  8.8   max: 44.3   avg: 16.3
cpu-ogles-drv> min:  5.1   max:  6.0   avg:  5.6
cpu-present>   min:  0.0   max:  0.3   avg:  0.1
frametime>     min: 14.6   max: 49.8   avg: 22.0
draw-call #>   min: 171    max: 177    avg: 174     | batched:    12
tris #>        min: 153294  max: 153386  avg: 153326   | batched:  2362
verts #>       min: 203346  max: 203530  avg: 203411   | batched:  3096
player-detail> physx:  0.1 animation:  0.1 culling  0.0 skinning:  0.0 
               batching:  0.4 render: 11.6 fixed-update-count: 1 .. 1
mono-scripts>  update:  0.9   fixedUpdate:  0.0 coroutines:  0.0 
mono-memory>   used heap: 3043328 allocated heap: 3796992  
               max number of collections: 0 collection total duration:  0.0

To capture this sample you’ll need to connect to your device over Wi-Fi using ADB over TCPIP and run adb logcat while the device is docked in the headset (for more information, see “Android Debugging” in the Mobile SDK documentation).
このサンプルをキャプチャするために、Wi-Fi環境で"ADB over TCPIP"を使ってデバイスへ接続する必要があります。そしてデバイスがヘッドセットにはめられている間、adb logcatを走らせておく必要があります。(詳しくは、Mobile SDKドキュメントの"Android Debugging"をご覧ください。)

What the sample above tells us is that our average frame time is 22 ms, which is about 45 frames per second?way below our target of 60 frames per second.
上記のサンプルが示しているのはフレームの平均が22msだということで、約45fps――目標となる60fpsを下回るということになります。

We can also see that this scene is heavy on the CPU?16.3 ms of our 22 ms total is spent on the CPU.
また、このシーンがCPU上で非常に重いことも分かります――全22ms中、16.3msがCPUで消費されています。

We’re spending 5 ms in the driver (“cpu-ogles-drv”), which suggests that we’re sending the driver down some internal slow path.
5msはドライバに費やされています(“cpu-ogles-drv”)。これは内部の遅いパスでドライバを送っているということです。

The probable culprit is pretty clear: at 174 draw calls per frame, we’re significantly over our target of 100.
大凡の犯人は明確です:フレーム当たり174ドローコール。はっきり目標の100をオーバーしています。

In addition, we’re pushing more polygons than we would like.
それに加えて、望ましい量より多くのポリゴンを送っています。

This view of the scene doesn’t really tell us what’s happening on the GPU, but it tells us that we can explain our 45 ms frame rate just by looking at the CPU, and that reducing draw calls should be the focus of our attention.
このシーンの表示ではGPUで何が起こっているのか実際には教えてくれません。しかし、CPUを見ることによって45fpsの理由が説明できるようになり、注目しなければならないのはドローコールを減らすことだと示してくれます。

This data also shows that we have regular spikes in the frametime (represented by max frametime of 49.8 ms).
このデータはまた、フレーム時間で定期的なスパイクがあることも示しています。(最大フレーム時間が49.8msということが示しています)

To understand where those are coming from, the next step is to connect the Unity Profiler and look at its output.
それがどこから発生しているのかを理解するために、次のステップはUnityプロファイラに接続し、その出力を見ることです。

Profiler 1
図:プロファイラ1

As expected, the graph shows a regular spike.
予想通り、グラフは定期的なスパイクを示しています。

During non-spike periods, our render time is similar to the values reported above, and there is no other significant contributor to our final frametime.
スパイク以外の区間においては、描画時間は上記にあった値に沿っています。最終的なフレーム時間に対して、その他の主だった要因はありません。

Profiler 2
図:プロファイラ2

The profiler blames the spikes on something called Gfx.WaitForPresent.
プロファイラは、このスパイクは何かが"Gfx.WaitForPresent"を呼んでいるせいだ、と示しています。

Curiously, our actual render time is not significantly increased in the spike frame.
奇妙なことに、実際の描画時間はスパイクのフレームで著しく上がっているわけではありません。

What’s going on here anyway?
では、何が起こっているのでしょう?


Wait for present
描画を待つ

WaitForPresent (and its cousin, Overhead) appears to be some repeated cost that comes along and destroys our frame.
描画待ち(その類縁にオーバーヘッドがある)はフレーム毎に発生し破棄される、リピートコストとして出現します。

In fact, it does not actually represent some mysterious work being performed.
実際は、実行中におかしな挙動として表に出るようなことはありません。

Rather, WaitForPresent records the amount of time that the render pipeline has stalled.
むしろ、描画待ちはレンダーパイプラインが失速した時間の量として記録されます。

One way to think of the render pipeline is to imagine a train station.
レンダーパイプラインについてイメージするには、電車の駅を思い浮かべます。

Trains leave at reliable times?every 16.6 ms.
電車はきっかり16.6ms毎に出発します。

Let’s say the train only holds one person at a time, and there is a queue of folks waiting to catch a ride.
その電車には一人が乗っていて、乗り込むのを待つ人々のキューがあるとします。

As long as each person in the queue can make it to the platform and get on the next train before it leaves, you’ll be able to ship somebody out to their destination every 16 ms.
各々がプラットフォームにいて次の電車が出発する前に乗り込める限り、それぞれの目的地に向かって16ms毎に送りだすことができます。

But if even one guy moves too slowly?maybe he trips on his shoelaces?he’ll not only miss his train, he’ll be sitting there waiting on the platform for another 16 ms for the next train to come.
しかし、たった一人の男がものすごく動きが遅かったとしたら――恐らく靴紐で躓いて――彼が電車を逃すだけではなく、プラットフォームで次の電車が来るまでの16msを待つことになるでしょう。

Even though he might have only been 1 ms late for his train, missing it means that he has to sit there and wait a long time.
たった1ms電車に乗り遅れただけでも、その喪失はその場で長時間座って待つことになることを意味します。

In a graphics pipeline, the train is the point at which the front buffer (currently displayed) and back buffer (containing the next frame to display) swap.
グラフィックパイプラインにおいて、電車はフロントバッファ(現在の表示)とバックバッファ(次に表示されるフレームが入っている)のスワップ(切り替え)を示しています。

This usually happens when the previous frame finishes its scanout to the display.
これは一般に前のフレームでの表示のスキャンアウト完了時に発生します。
(※スキャンアウト:Scan chainというICデザインの用語、スキャンのアウトプット。 Scan chain - Wikipedia, the free encyclopedia)

Assuming that the GPU can execute all of the render commands buffered for that frame in a reasonable amount of time, there should be a buffer swap every 16 ms.
GPUがそのフレームでバッファされたレンダーコマンドを妥当な時間内に実行できるという前提において、バッファスワップは16ms毎にあります。

To maintain a 60 frames-per-second refresh rate, the game must finish all of its work for the next frame within 16 ms.
リフレッシュレートを60fpsに維持するためには、ゲームは次のフレームまでの全ての処理を16ms以内に終わらせなければいけません。

When the CPU takes too long to complete a frame, even if it’s only late by 1 ms, the swap period is missed, the scanout of the next frame begins using the previous frame’s data, and the CPU has to sit there and wait for the next swap to roll around.
CPUがフレームを完了させるのに時間が長すぎた場合、仮にそれが1msの遅延であってもスワップの終了に失敗し、次フレームのスキャンアウトが先のフレームのデータを使って開始されます。そして、CPUはそのままで次のスワップが回ってくるまで待つことになります。

To use the parlance of our example above, the train is the swap and the frame commands issued by the CPU are the passengers.
上の例の流れを用いるなら、電車はスワップ、そして発行されるフレームコマンドは乗客ということになります。

WaitForPresent indicates that this sort of pipeline stall has occurred and records the amount of time the CPU idles while waiting on the GPU.
描画待ちはこのパイプラインの動作停止の類が発生し、そしてGPU待ちのためのCPUの無作動時間が記録されることを指し示しています。

Though less common, this can also happen if the CPU finishes its work very quickly and has to wait for the next swap.
あまり一般的ではないものの、これはCPUがかなり早く完了し、次のスワップを待つ必要がある場合にも発生することがあります。

In this particular example, it’s pretty clear that our frame rate is inconsistent enough that we cannot schedule our pipeline reliably.
特徴的な例としては、パイプラインの確実性について計画できないことでフレームレートに一貫性がなくなることは極めて明白です。

The way to fix WaitForPresent is thus to ignore it in the profiler and concentrate on optimizing everything else, which in this case means reducing the number of draw calls we have in the scene.
よって、描画待ちを固定にするにはプロファイラの前述の事例を無視し、他のものの最適化に集中することです。今回の場合であれば、シーンのドローコール数を減らすことです。


Other Profiler Information
その他のプロファイラ情報

The Unity profiler is very useful for digging into all sorts of other runtime information, including memory usage, draw calls, texture allocations, and audio overhead.
Unityプロファイラはあらゆる他のランタイム情報を掘り下げるのに非常に便利です。メモリ使用量、ドローコール、テクスチャアロケーション、そしてオーディオのオーバーヘッドを含みます。

For serious performance debugging, it’s a good idea to turn off Multithreaded Rendering in the Player Preferences.
重要なパフォーマンスデバッグでは、Player Preferencesのマルチスレッドレンダリングをオフするのが得策です。

This will slow the renderer down a lot but also give you a clearer view of where your frame time is going.
これはレンダラをかなり減速させるでしょうが、フレーム時間がどうなっているかをより明らかにするでしょう。

When you’re done with optimizations, remember to turn Multithreaded Rendering back on.
最適化後には、マルチスレッドレンダリングをオンに戻すことを忘れないでください。

In addition to draw call batching, other common areas of performance overhead include overdraw (often caused by large transparent objects or poor occlusion culling), skinning and animation, physics overhead, and garbage collection (usually caused by memory leaks or other repeated allocations).
またドローコールバッチや、その他一般的なパフォーマンスのオーバーヘッドは、オーバードロー(巨大な半透明オブジェクトやオクルージョンカリング不足が主な原因)、スキニング、アニメーション、物理特性オーバーヘッド、そしてガベージコレクション(メモリリークやその他アロケーションの繰り返しが一般的な原因)を含んでいます。

Watch for these as you dig into the performance of your scene.
シーンのパフォーマンスを掘り下げてこれらを見てみてください。

Also remember that displaying the final VR output, which includes warping and TimeWarp overhead, costs about 2 ms every frame.
最終的なVR出力の描画についても忘れないでください。ワープ処理やタイムワープによるオーバーヘッドとして毎フレームに2msのコストを含みます。


Oculus Remote Monitor
Oculusリモートモニター

OVRMonitor is a new tool recently released with the Oculus Mobile SDK.
OVRモニターはつい先日リリースされたOculusモバイルSDKに含まれる新しいツールです。

It helps developers understand the way pipelining works and identify pipeline stalls.
これは開発者にとって、パイプライン動作方法の理解やパイプライン動作停止の識別への補助となります。

It can also stream low resolution unwarped video from a Gear VR device wirelessly, which is useful for usability testing.
また、無線でGearVRデバイスからワープなしの低解像度映像がストリーミングできます。これはユーザビリティテストに便利です。

OVRMonitor is currently in development, but this early version can still be used to visualize the graphics pipeline for Gear VR applications.
OVRモニターは目下開発中です。しかし、この初期バージョンでもGearVRアプリケーションのグラフィックパイプラインを可視化するために使用することができます。

Here’s a shot of a tool inspecting a game running the same scene discussed above:
上記で検討したシーンと同様のゲームを走らせ調査するツールを撮ったものがこちらです。

OVRMonitor
図:OVRモニター

The yellow bar represents the vertical sync interrupt that indicates that scan out for a frame has completed.
黄色のバーはフレームのスキャンアウトが完了したことを示す垂直同期割り込みを表しています。

The image at the top of the window is a capture of the rendered frame, and the left side of the image is aligned to the point in the frame where drawing began.
ウィンドウ上部の絵は描画されたフレームのキャプチャです。絵の左端はフレームの描画開始点に揃えられています。

The red bar in the middle of the image shows the TimeWarp thread, and you can see it running parallel to the actual game.
絵の中ほどにある赤のバーは、タイムワープのスレッドです。これは実際のゲームと並列して動作しているものとして見ることができます。

The bottom blue are indicates the load on CPU and GPU, which are constant (i.e., in this case, all four CPUs are running).
下部の青はCPUとGPUの読み込みを示しています。これらは一定です(即ち、この場合は全て4つのCPUが動作しているということ)。

This shot actually shows us one of the WaitForPresent spikes we saw above in the Unity Profiler.
実はこの場面、前述のUnityプロファイラで見た描画待ちのスパイクを表示しています。

The frame in the middle of the timeline began too late to complete by the next vertical blank, and as a result the CPU blocked for a full frame (evidenced by the lack of screen shot in the subsequent frame and the 16.25 ms WarpSwapInternal thread time).
タイムラインの中ほどにあるフレームで次の垂直方向の空白から遅すぎて完了しなかった状態が始まっています。結果、CPUはフルフレームがブロックされました。

OVRMonitor is a good way to get a sense of what is happening in your graphics pipeline from frame to frame.
OVRモニターはフレーム間でグラフィックパイプラインに何が起こっているかを感覚で捉えるのに良い方法です。

It can be used with any Gear VR app built against the latest SDK.
これは最新のSDKでビルドされていればどんなGearVRアプリでも利用可能です。

See the documentation in SdkRoot/Tools/OVRMonitor for details.
詳細は"SdkRoot/Tools/OVRMonitor"にあるドキュメントをご覧ください。

More documentation and features are coming soon.
より詳細なドキュメントや特集記事は近々公開します。


Tips and Tricks
ティップスとトリック

Here are a few performance tricks we’ve used or heard about from other developers.
普段使っていたり他の開発者から聞いた、いくつかのパフォーマンスの秘訣があります。

These are not guaranteed solutions for all VR applications, but they might give you some ideas about potential solutions for your particular scene.
それらは全てのVRアプリに対して保障された解決策ではありませんが、何か特定のシーンに対する解決の糸口となるようなアイディアが得られるかもしれません。


Draw big, opaque meshes last.
大きく不透明なメッシュを最後に描画しましょう。

Try sorting your skybox into the Geometry+1 render queue so that it draws after all the other opaque geometry.
スカイボックスが全ての他の不透明なジオメトリの後に描画されるように、スカイボックスをジオメトリの次のレンダーキューに並べ替えてみてください。

Depending on your scene, this probably causes a lot of the pixels covered by the skybox to be discarded by depth test, thus saving you time.
シーンにもよりますが、スカイボックスが多くのピクセルをカバーしていることで深度テストの際に破棄される要因に大概なっているので、時間が節約できます。

Ground planes and other static, opaque objects that touch a lot of pixels and are likely to be mostly occluded by other objects are candidates for this optimization.
多くのピクセルに触れて、他のオブジェクトによって塞がれそうな、地面やその他のstaticで不透明なオブジェクトは、この最適化の候補となります。


Dynamically change your CPU / GPU throttling settings.
活発にCPU/GPUスロットル設定を変更しましょう。

You can change your throttling settings at any time.
スロットル設定はいつでも変更可能です。

If you are able to run most of your game at a low setting but have one or two particularly challenging scenes, consider cranking the CPU or the GPU up just during those scenes.
ゲームの大半が低設定で動作させることができるものの、1~2度特別にシーンを変更することがある場合、CPU/GPUをそれらのシーンの間だけ上げることを考えてみてください。(※訳注、challenging→changing?)

You can also drop the speed of one or both processors in order to save battery life and reduce heat during scenes that are known to be simple.
また、こういったシンプルな方法でバッテリーをセーブしたり発熱を抑えたりするために、片方あるいは両方のプロセッサの速度を落とすこともできます。

For example, why not set GPU to 0 during a scene load?
例えば、シーンのロード中はGPUを0にしてはどうでしょう?


Update render targets infrequently.
レンダーターゲットのアップデート頻度を落としましょう。

If you have secondary render textures that you are drawing the scene to, try drawing them at a lower frequency than the main scene.
シーンを描画するための第二のレンダーテクスチャがある場合、メインシーンの描画より低い頻度にしてみてください。

For example, a stereoscopically rendered mirror might only refresh its reflection for only one eye each frame.
たとえば、立体的に描画される鏡は、いずれのフレームでも片方の目による反射を再描画するだけでいいかもしれません。

This effectively lowers the frame rate of the mirror to 30 frames per second, but because there is new data every frame in one eye or the other, it looks okay for subtle movements.
これは30fpsまで効果的にフレームレートを下げます。しかし、毎フレームどちらか一方の目が新しいデータを更新するので、微妙な動きは問題なくなります。


Lower the target eye resolution.
ターゲットの目の解像度を下げましょう。

By trading a bit of visual quality you can often improve your fill-bound game performance significantly by slightly lowering the size of the render target for each eye.
視界それぞれのレンダーターゲットの画像サイズを若干下げることによって、多少のビジュアルクオリティと引き換えに、大概の場合はフィルバウンド(塗りつぶし結合された?)ゲームのパフォーマンスを大幅に向上させることができます。

OVRManager.virtualTextureScale is a value between 0.0 and 1.0 that controls the size of the render output.
"OVRManager.virtualTextureScale"は0.0から1.0の値で、レンダーの出力サイズを調整できます。

Dropping resolution slightly when running on older devices is often an easy way to support slower hardware.
古いデバイスで動作させる際に解像度を若干下げる方法は、遅いハードをサポートする簡単な方法です。


Compress your textures.
テクスチャを圧縮しましょう。

All Gear VR devices support the ASTC compression format, which can significantly reduce the size of your textures.
全てのGearVRデバイスはASTC圧縮フォーマットをサポートしていて、テクスチャサイズを大幅に減らすことができます。

Note that as of this writing, Unity 4.6 expects ASTC compression to go hand-in-hand with OpenGL ES 3.0.
この記事を書いた時点での注意として、Unity4.6ではASTC圧縮がOpenGL/ES3.0の対応と紐づいていることを想定していることです。

If you use ASTC under GLES 2.0, your textures will be decompressed on load, which will probably lengthen your application’s start-up time significantly.
もしGL/ES2.0以下でASTCを使用する場合、テクスチャはロード時に解凍され、高確率でアプリの開始時間を大幅に延ばすことになるでしょう。

ETC1 is a lower-quality but universally supported alternative for GLES 2.0 users.
ETC1は品質が劣るもののGL/ES2.0ユーザ向けの広くサポートされている代替手段です。


Use texture atlases.
テクスチャアトラスを使用してください。

As described above, large textures that can be shared across a number of meshes will batch efficiently.
前述の通り、複数のメッシュにわたって共用できる大きなテクスチャは、効率のいいバッチができます。

Avoid lots of small individual textures.
多数の小さな個別テクスチャを避けてください。

For more information on optimizing your Unity mobile VR development, see “Best Practices: Mobile” in our Unity Integration guide.
UnityでのモバイルVR開発における最適化に関するより詳しい情報は、Unity Integration guideの"Best Practices: Mobile"をご覧ください。