hz-perfetto-debug について
metavrでPerfettoトレースを取得・分析し、Meta VRデバイスの性能問題を調べるスキルです。開発者向けに、フレーム時間、CPU/GPU、スレッド、レンダリングのボトルネック調査を案内します。
主な機能
- 01Perfettoトレースの取得と読み込み
- 02CPU・GPUフレーム時間の確認
- 03スレッド状態と使用率の調査
- 04SQLによるトレースデータの検索
- 05GPUカウンターと描画ステージの分析
活用シーン
- 01スタッターやフレーム期限超過の調査
- 02CPUまたはGPUのボトルネック特定
- 03スレッド競合と同期問題の確認
- 04描画パスやドローコールの負荷分析
MCP 対応
エージェントスキルであり、MCPサーバーやクライアントではありません。READMEにMCP接続の説明はありません。
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ meta-quest/ agentic-tools --skill hz-perfetto-debugインストール後、Claude Code で/hz-perfetto-debugを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/analyzing-traces.mdcapturing-traces.mdframe-timing.mdgpu-analysis.mdsql-queries.md詳細
Perfetto デバッグスキル
使用するタイミング
Meta VR デバイスで VR パフォーマンスの問題を調査するときに、このスキルを使用します。
- フレーム落ち、カクつき、またはスタッター
- CPU または GPU のボトルネック
- レンダーパスのオーバーヘッドと GPU 使用率
- サーマルスロットリングとクロック周波数の変化
- フレームタイミングのばらつきと vsync の期限超過
- スレッド競合と同期の問題
- ドローコール数やオーバードローの多さ
VR のフレーム時間目標
各リフレッシュレートの厳格な期限は次のとおりです。フレームが目標を超えると、コンポジターによる再投影が必要になるか、ユーザーには古いフレームが表示されます。
| リフレッシュレート | フレーム時間の予算 | 備考 |
|---|---|---|
| 120 Hz | 8.3 ms | Quest 2、Quest 3、Quest 3S でサポート |
| 90 Hz | 11.1 ms | Quest 2、Quest Pro、Quest 3、Quest 3S でサポート |
| 72 Hz | 13.9 ms | すべての Quest デバイスでのデフォルト |
| 60 Hz | 16.7 ms | メディアアプリのみ(Quest 2)。インタラクティブアプリは 72 Hz 以上を使用する必要があります |
フレーム期限をわずか 1 ms でも超過すると、古いフレーム(再投影)が発生します。全フレームの 10%を超える古いフレームは、深刻なパフォーマンス問題を示しています。
metavr のセットアップ
Perfetto のトレース機能は metavr CLI を利用します。スタンドアロンバイナリを PATH にインストールするか(metavr-cli スキルを参照)、インストールせずに npx 経由で実行します。
metavr --version
以下の例では metavr コマンドをそのまま使用しています。npm 配布版を使用する場合は、先頭に npx -y を付けてください。トレースをキャプチャする前に、開発者モードを有効にした Meta VR デバイスを USB で接続してください。
クイックスタートのワークフロー
1. トレースをキャプチャする
# Capture a 5-second trace from the currently running VR app
metavr perf capture
# Specify duration and target app
metavr perf capture --duration 10000 --app com.example.myapp
# Enable GPU render stage tracing for detailed pass analysis
metavr perf capture --gpu-render-stage
# Enable XR runtime metrics
metavr perf capture --xr-runtime
# Custom output name
metavr perf capture -o my-session-name
--app が指定されていない場合、キャプチャ時にフォアグラウンドの VR アプリが自動検出されます。CPU スケジューリングと GPU メトリクスのトレースは、デフォルトで有効になっています。トレースは自動的にローカルマシンに取得されます。
2. 利用可能なトレースを一覧表示する
metavr perf traces
変更日時の新しい順に並べた .pftrace ファイルを返します。~/Documents、~/Downloads、現在の作業ディレクトリなど、標準のディレクトリを検索します。
3. トレースを読み込む
metavr perf load <trace-file>
分析用にトレースを読み込み、処理します。16 進数のセッション ID、ファイル名(.pftrace 拡張子の有無を問わない)、またはフルパス/相対パスを指定できます。
4. パフォーマンスの概要を取得する
metavr perf context
次の内容を含む、構造化されたパフォーマンス分析を返します。
- CPU と GPU のフレームタイミング統計
- スレッドごとの使用率(割合)を含む内訳
- GPU カウンターの概要(利用可能な場合)
- 検出されたボトルネックと推奨事項
5. SQL クエリを実行する
metavr perf query <session-id> "SELECT ts, dur, name FROM slice WHERE name LIKE '%PlayerLoop%' LIMIT 20"
読み込んだ Perfetto トレースデータベースに対して、任意の SQL を実行します。slice、thread_track、thread、process、counter、counter_track、args、sched_slice など、すべての Perfetto テーブルを利用できます。
6. スレッド状態を分析する
metavr perf thread-state <session-id> <utid>
# With time range
metavr perf thread-state <session-id> <utid> --start-ts 1000000 --end-ts 5000000000
スレッドが実行中、スリープ中、ブロック中、または CPU 待ちの状態に費やした時間を示す、スレッド状態の内訳を返します。スレッドが CPU バウンドか、I/O バウンドか、またはリソース不足で実行できない状態かを特定するのに役立ちます。
7. GPU メトリクスを取得する
metavr perf gpu-counters <session-id> --start-ts 100,200,300 --end-ts 150,250,350
GPU フレーム範囲の GPU メトリクスカウンター(平均、標準偏差、分位数)を返します。統計的な精度を確保するには、少なくとも 20 フレームが必要です。メトリクスには、テクスチャフェッチレート、シェーダー ALU の処理能力、頂点処理、フラグメントシェーディングの統計が含まれます。
詳細な分析ワークフロー
パフォーマンスを徹底的に調査するには、次の手順を順番に実行します。
ステップ 1:トレースの品質を検証する
分析の前に、トレースが使用可能であることを確認します。
- 長さ:データが 2 秒以上あること(理想的には 3〜5 秒)
- スライス数:有意義なトレースには数千のスライスが必要です
- プロセスの存在:対象アプリのプロセスが存在すること
SELECT
(MAX(ts) - MIN(ts)) / 1e9 AS duration_seconds,
COUNT(*) AS total_slices
FROM slice
トレースのスライス数が 1000 未満、または長さが 1 秒未満の場合、有意義な分析に十分なデータが含まれていない可能性があります。metavr perf capture で新しいトレースをキャプチャしてください。
ステップ 2:対象プロセスを特定する
アプリケーションのプロセス(システムサービスではないもの)を見つけます。
SELECT upid, pid, name
FROM process
WHERE name NOT LIKE 'com.oculus%'
AND name NOT LIKE '/system%'
AND name NOT LIKE 'com.android%'
AND name IS NOT NULL
ORDER BY pid
既知のアプリの場合は、パッケージ名で直接絞り込みます。
ステップ 3:ゲームエンジンを特定する
エンジン固有のマーカーを確認します。
| エンジン | 主なマーカー |
|---|---|
| Unity | PlayerLoop、UnityMain、PhaseSync、PostLateUpdate.FinishRendering |
| Unreal | UGameEngine::Tick、FEngineLoop::Tick、RHI Thread |
| Native OpenXR | エンジンのマーカーを伴わない xrWaitFrame、xrBeginFrame、xrEndFrame |
ステップ 4:重要なスレッドを特定する
VR レンダリングに関係するスレッドを特定します。
SELECT t.utid, t.tid, t.name, p.name AS process_name
FROM thread t
JOIN process p USING(upid)
WHERE p.name = '<target-process>'
ORDER BY t.name
特定すべき重要なスレッド:
| スレッド | 目的 |
|---|---|
| メインスレッド(UnityMain / GameThread) | ゲームロジック、物理演算、スクリプト |
| レンダースレッド(UnityGfx / RenderThread) | ドローコールの送信 |
| GPU 完了(GPU completion / RHI Thread) | GPU フェンスの待機 |
| ワーカースレッド(Job.Worker / TaskGraph) | 並列ワークロード |
スレッドの utid がわかったら、metavr perf thread-state <session-id> <utid> を使って、実行中/スリープ中/ブロック中の時間をすばやく確認します。
ステップ 5:フレーム境界を検出する
フレーム単位の分析を行うために、フレームの開始/終了マーカーを見つけます。
- Unity:メインスレッド上の
PlayerLoopスライスがフレーム境界を示します - Unreal:ゲームスレッド上の
FEngineLoop::Tickスライス - OpenXR:
xrWaitFrameからxrEndFrameまでのシーケンス
ステップ 6:負荷の高い関数を分析する
フレームごとに最も多くの時間を消費する処理を見つけます。
SELECT name, COUNT(*) AS call_count, SUM(dur)/1e6 AS total_ms, AVG(dur)/1e6 AS avg_ms
FROM slice
WHERE track_id IN (
SELECT id FROM thread_track WHERE utid = <main_thread_utid>
)
GROUP BY name
ORDER BY total_ms DESC
LIMIT 20
ステップ 7:高頻度の呼び出しを確認する
フレームごとに過剰に呼び出される関数は、バッチ処理の問題を示している可能性があります。
SELECT name, COUNT(*) AS calls
FROM slice
WHERE track_id IN (
SELECT id FROM thread_track WHERE utid = <utid>
)
AND dur < 100000
GROUP BY name
HAVING calls > 1000
ORDER BY calls DESC
ステップ 8:GPU のレンダーパスを分析する
詳細なレンダーパスの内訳、サーフェス分析、GPU カウンターの解釈については、GPU 分析リファレンスを参照してください。
主要な Perfetto の概念
| 概念 | 説明 |
|---|---|
| スライス | 実行時間を持つ区間(関数呼び出し、フレーム、レンダーパス)です。ts(開始時刻)、dur(継続時間)、name、track_id を持ちます。 |
| トラック | タイムライン上のレーンです。スレッドトラックには特定のスレッドのスライスが格納されます。カウンタートラックには時間経過に伴うメトリクス値が格納されます。 |
| スレッド(utid) | トレース内で一意のスレッド ID です。結合には tid ではなく utid を使います。tid は再利用されることがあるためです。 |
| プロセス(upid) | トレース内で一意のプロセス ID です。結合には pid ではなく upid を使います。 |
| タイムスタンプ | すべてのタイムスタンプはナノ秒単位です。ミリ秒にするには 1e6 で、秒にするには 1e9 で割ります。 |
| カウンター | 時系列のメトリクス(GPU 使用率、クロック周波数、温度)です。counter テーブルに格納されます。 |
| 引数 | スライスに付加されたキーと値のメタデータです。arg_set_id をキーに args テーブルと結合して取得します。 |
パフォーマンス目標
| メトリクス | 目標 | 警告 | 重大 |
|---|---|---|---|
| フレーム時間(90 Hz) | < 11.1 ms | > 11.1 ms | > 16.7 ms |
| 古いフレームの割合 | < 5% | > 10% | > 25% |
| メインスレッドの使用率 | 予算の < 80% | > 80% | > 95% |
| GPU 使用率 | 予算の < 85% | > 85% | > 95% |
| フレームのばらつき(標準偏差) | < 1 ms | > 2 ms | > 4 ms |
| フレームあたりのドローコール数 | < 100 | > 200 | > 500 |
エンジン固有の注意事項
Unity
- PhaseSync:VR の垂直同期を調整する仕組みです。
PlayerLoopの開始時にアイドル時間として表示されます。これは正常かつ意図的な動作であり、無駄な時間としてフラグを付けないでください。 - シングルパス・マルチビュー:両目を 1 回のパスでレンダリングします。1 フレームあたり 2 つのレンダーパスが見られる場合、アプリがマルチパスレンダリングを使用している可能性があります(効率が低下します)。
- 動的バッチ処理:
SetPassの呼び出し回数が多い場合は、マテリアルがバッチ処理されていないことを示すため、確認してください。 - IL2CPP と Mono:IL2CPP ビルドでは、トレース内の関数名が異なります。C# のメソッド名ではなく、マングルされた C++ 名を探してください。
Unreal Engine
- RHI スレッド:Unreal は、GPU コマンドを送信するために専用の RHI(Render Hardware Interface)スレッドを使用します。ドライバーのオーバーヘッドを調べるには、このスレッドを確認してください。
- フォワードレンダリングとディファードレンダリング:Meta VR ではフォワードレンダリングが推奨されます。ディファードレンダリングは GPU コストが大幅に高くなります。
- Blueprint の Tick:Blueprint を多用すると、
UObject::ProcessEventに現れます。件数が多い場合は、Blueprint を C++ に変換する必要があります。 - ネイティブ化された Blueprint:トレース名に
__StaticExecという接尾辞が付いて現れます。
よくある落とし穴
- PhaseSync や xrWaitFrame のアイドル時間をパフォーマンスの問題として報告しないでください。これらは意図的なフレームペーシングの仕組みです。
surface#0のような GPU レンダーパス名は内容を示すものではありません。レンダリング対象を特定するには、解像度と MSAA レベルを照合してください。- トレースではスレッド名が途中で切り詰められる場合があります。
UnityMainがUnityMaiなどと表示されることがあります。 - SQL クエリでスレッド関連のテーブルを結合する際は、必ず
tidではなくutidを使用してください。 - タイムスタンプの単位はナノ秒です。マイクロ秒として扱ってしまうのはよくある間違いです。
- カウンター値は瞬時のサンプル値であり、一定期間の平均値ではありません。
参考資料
各トピックの詳細なガイドについては、以下を参照してください。