hz-perfetto-debug

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Perfetto Debug Skill

Perfetto调试技能

When to Use

适用场景

Use this skill when investigating VR performance issues on Meta Quest devices:
  • Frame drops, jank, or stuttering
  • CPU or GPU bottlenecks
  • Render pass overhead and GPU utilization
  • Thermal throttling and clock frequency changes
  • Frame timing variance and missed vsync deadlines
  • Thread contention and synchronization issues
  • High draw call counts or overdraw
当在Meta Quest设备上排查VR性能问题时使用本技能:
  • 掉帧、卡顿或画面不流畅
  • CPU或GPU瓶颈
  • 渲染过程开销与GPU利用率
  • 热节流与时钟频率变化
  • 帧时序差异与未达到垂直同步 deadline
  • 线程竞争与同步问题
  • 过高的绘制调用次数或过度绘制

VR Frame Time Targets

VR帧时间目标

These are the hard deadlines for each refresh rate. If a frame exceeds its target, the compositor must reproject or the user sees a stale frame.
Refresh RateFrame Time BudgetNotes
120 Hz8.3 msSupported on Quest 2, Quest 3, Quest 3S
90 Hz11.1 msSupported on Quest 2, Quest Pro, Quest 3, Quest 3S
72 Hz13.9 msDefault on all Quest devices
60 Hz16.7 msMedia apps only (Quest 2); interactive apps must use 72 Hz+
Missing a frame deadline by even 1 ms causes a stale frame (reprojection). Stale frames above 10% of total frames indicate a serious performance problem.
以下是各刷新率的硬性时限。如果帧时间超过目标,合成器必须重新投影,用户会看到画面卡顿。
刷新率帧时间预算说明
120 Hz8.3 ms支持Quest 2、Quest 3、Quest 3S
90 Hz11.1 ms支持Quest 2、Quest Pro、Quest 3、Quest 3S
72 Hz13.9 ms所有Quest设备的默认设置
60 Hz16.7 ms仅媒体应用支持(Quest 2);交互应用必须使用72 Hz及以上
即使帧时间仅超过时限1毫秒也会导致画面卡顿(重新投影)。卡顿帧占总帧数的比例超过10%表明存在严重性能问题。

metavr Setup

metavr 设置

Perfetto tracing is powered by the metavr CLI. Invoke via
npx
— no install required:
bash
npx -y metavr --version
Examples below use the bare
metavr
command for brevity; if it is not installed globally, replace
metavr
with
npx -y metavr
. Connect your Quest via USB with developer mode enabled before capturing traces.
Perfetto追踪由metavr CLI提供支持。通过
npx
调用——无需安装:
bash
npx -y metavr --version
以下示例为简洁起见使用
metavr
命令;若未全局安装,请将
metavr
替换为
npx -y metavr
。在捕获追踪前,请通过USB连接Quest设备并启用开发者模式。

Quick Start Workflow

快速开始流程

1. Capture a Trace

1. 捕获追踪

bash
undefined
bash
undefined

Capture a 5-second trace from the currently running VR app

从当前运行的VR应用捕获5秒的追踪数据

metavr perf capture
metavr perf capture

Specify duration and target app

指定时长和目标应用

metavr perf capture --duration 10000 --app com.example.myapp
metavr perf capture --duration 10000 --app com.example.myapp

Enable GPU render stage tracing for detailed pass analysis

启用GPU渲染阶段追踪以进行详细过程分析

metavr perf capture --gpu-render-stage
metavr perf capture --gpu-render-stage

Enable XR runtime metrics

启用XR运行时指标

metavr perf capture --xr-runtime
metavr perf capture --xr-runtime

Custom output name

自定义输出名称

metavr perf capture -o my-session-name

The capture auto-detects the foreground VR app if `--app` is not specified. CPU scheduling and GPU metrics tracing are enabled by default. The trace is pulled to your local machine automatically.
metavr perf capture -o my-session-name

若未指定`--app`,捕获会自动检测前台VR应用。默认启用CPU调度和GPU指标追踪。追踪数据会自动拉取到本地机器。

2. List Available Traces

2. 列出可用的追踪数据

bash
metavr perf traces
Returns
.pftrace
files sorted by modification time (newest first). Searches standard directories including
~/Documents
,
~/Downloads
, and the current working directory.
bash
metavr perf traces
返回按修改时间排序(最新在前)的
.pftrace
文件。搜索标准目录包括
~/Documents
、
~/Downloads
和当前工作目录。

3. Load a Trace

3. 加载追踪数据

bash
metavr perf load <trace-file>
Loads and processes the trace for analysis. Accepts a hex session ID, filename (with or without
.pftrace
extension), or a full/relative path.
bash
metavr perf load <trace-file>
加载并处理追踪数据以进行分析。接受十六进制会话ID、文件名(带或不带
.pftrace
扩展名)或完整/相对路径。

4. Get Performance Overview

4. 获取性能概览

bash
metavr perf context
Returns a structured performance analysis including:
  • CPU and GPU frame timing statistics
  • Thread breakdown with utilization percentages
  • GPU counter summaries (if available)
  • Detected bottlenecks and recommendations
bash
metavr perf context
返回结构化的性能分析结果,包括:
  • CPU和GPU帧时序统计
  • 线程利用率百分比细分
  • GPU计数器汇总(若可用)
  • 检测到的瓶颈与建议

5. Run SQL Queries

5. 运行SQL查询

bash
metavr perf query <session-id> "SELECT ts, dur, name FROM slice WHERE name LIKE '%PlayerLoop%' LIMIT 20"
Executes arbitrary SQL against the loaded Perfetto trace database. All Perfetto tables are available:
slice
,
thread_track
,
thread
,
process
,
counter
,
counter_track
,
args
,
sched_slice
, and more.
bash
metavr perf query <session-id> "SELECT ts, dur, name FROM slice WHERE name LIKE '%PlayerLoop%' LIMIT 20"
对加载的Perfetto追踪数据库执行任意SQL查询。所有Perfetto表均可用:
slice
、
thread_track
、
thread
、
process
、
counter
、
counter_track
、
args
、
sched_slice
等。

6. Analyze Thread States

6. 分析线程状态

bash
metavr perf thread-state <session-id> <utid>
bash
metavr perf thread-state <session-id> <utid>

With time range

指定时间范围

metavr perf thread-state <session-id> <utid> --start-ts 1000000 --end-ts 5000000000

Returns a thread state breakdown showing how much time the thread spent running, sleeping, blocked, or waiting for CPU. Useful for identifying whether a thread is CPU-bound, I/O-bound, or starved.
metavr perf thread-state <session-id> <utid> --start-ts 1000000 --end-ts 5000000000

返回线程状态细分,显示线程在运行、休眠、阻塞或等待CPU上花费的时间。有助于确定线程是CPU密集型、I/O密集型还是资源匮乏。

7. Get GPU Metrics

7. 获取GPU指标

bash
metavr perf gpu-counters <session-id> --start-ts 100,200,300 --end-ts 150,250,350
Returns GPU metric counters (mean, standard deviation, quantiles) for GPU frame ranges. Requires at least 20 frames for statistical accuracy. Metrics include texture fetch rates, shader ALU capacity, vertex processing, and fragment shading statistics.
bash
metavr perf gpu-counters <session-id> --start-ts 100,200,300 --end-ts 150,250,350
返回GPU帧范围的GPU指标计数器(平均值、标准差、分位数)。需要至少20帧才能保证统计准确性。指标包括纹理获取率、着色器ALU容量、顶点处理和片段着色统计。

Detailed Analysis Workflow

详细分析流程

Follow these steps in order for a thorough performance investigation.
按以下步骤进行全面的性能排查。

Step 1: Validate Trace Quality

步骤1:验证追踪数据质量

Before analyzing, confirm the trace is usable:
  • Duration: At least 2 seconds of data (ideally 3-5 seconds)
  • Slice count: Should have thousands of slices for a meaningful trace
  • Process presence: The target app process must be present
sql
SELECT
  (MAX(ts) - MIN(ts)) / 1e9 AS duration_seconds,
  COUNT(*) AS total_slices
FROM slice
If the trace has fewer than 1000 slices or is under 1 second, it may not contain enough data for meaningful analysis. Capture a new trace with
metavr perf capture
.
在分析前,确认追踪数据可用:
  • 时长:至少2秒数据(理想为3-5秒)
  • 切片数量:应有数千个切片才能进行有意义的分析
  • 进程存在性:必须存在目标应用进程
sql
SELECT
  (MAX(ts) - MIN(ts)) / 1e9 AS duration_seconds,
  COUNT(*) AS total_slices
FROM slice
如果追踪数据的切片数量少于1000个或时长不足1秒,可能没有足够数据进行有意义的分析。使用
metavr perf capture
重新捕获。

Step 2: Identify Target Process

步骤2:识别目标进程

Find the application process (not system services):
sql
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
For known apps, filter directly by package name.
找到应用进程(而非系统服务):
sql
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
对于已知应用,直接按包名过滤。

Step 3: Identify Game Engine

步骤3:识别游戏引擎

Look for engine-specific markers:
EngineKey Markers
Unity
PlayerLoop
,
UnityMain
,
PhaseSync
,
PostLateUpdate.FinishRendering
Unreal
UGameEngine::Tick
,
FEngineLoop::Tick
,
RHI Thread
Native OpenXR
xrWaitFrame
,
xrBeginFrame
,
xrEndFrame
without engine markers
查找引擎特定标记:
引擎关键标记
Unity
PlayerLoop
,
UnityMain
,
PhaseSync
,
PostLateUpdate.FinishRendering
Unreal
UGameEngine::Tick
,
FEngineLoop::Tick
,
RHI Thread
Native OpenXR
xrWaitFrame
,
xrBeginFrame
,
xrEndFrame
(无引擎标记)

Step 4: Find Key Threads

步骤4:识别关键线程

Identify the threads that matter for VR rendering:
sql
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
Critical threads to locate:
ThreadPurpose
Main thread (UnityMain / GameThread)Game logic, physics, scripts
Render thread (UnityGfx / RenderThread)Draw call submission
GPU completion (GPU completion / RHI Thread)GPU fence waiting
Worker threads (Job.Worker / TaskGraph)Parallel workloads
Once you have a thread's
utid
, use
metavr perf thread-state <session-id> <utid>
to get a quick breakdown of its running/sleeping/blocked time.
找到对VR渲染至关重要的线程:
sql
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 fence
工作线程(Job.Worker / TaskGraph)并行工作负载
获取线程的
utid
后,使用
metavr perf thread-state <session-id> <utid>
快速查看其运行/休眠/阻塞时间的细分。

Step 5: Detect Frame Boundaries

步骤5:检测帧边界

Find frame start/end markers to segment per-frame analysis:
  • Unity:
    PlayerLoop
    slices on the main thread define frame boundaries
  • Unreal:
    FEngineLoop::Tick
    slices on the game thread
  • OpenXR:
    xrWaitFrame
    to
    xrEndFrame
    sequences
找到帧开始/结束标记以进行逐帧分析:
  • Unity:主线程上的
    PlayerLoop
    切片定义帧边界
  • Unreal:游戏线程上的
    FEngineLoop::Tick
    切片
  • OpenXR:
    xrWaitFrame
    到
    xrEndFrame
    的序列

Step 6: Analyze Expensive Functions

步骤6:分析耗时函数

Find what consumes the most time per frame:
sql
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
找出每帧中消耗时间最多的函数:
sql
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

Step 7: Check High-Frequency Calls

步骤7:检查高频调用

Functions called excessively per frame can indicate batching issues:
sql
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
每帧调用次数过多的函数可能表明批处理问题:
sql
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

Step 8: Analyze GPU Render Passes

步骤8:分析GPU渲染过程

See the GPU analysis reference for detailed render pass breakdown, surface analysis, and GPU counter interpretation.
查看GPU分析参考文档以获取详细的渲染过程细分、表面分析和GPU计数器解读。

Key Perfetto Concepts

Perfetto核心概念

ConceptDescription
SliceA timed span of execution (function call, frame, render pass). Has
ts
(start),
dur
(duration),
name
, and
track_id
.
TrackA timeline lane. Thread tracks hold slices for a specific thread. Counter tracks hold metric values over time.
Thread (utid)Unique thread ID within the trace. Use
utid
(not
tid
) for joins —
tid
can be reused.
Process (upid)Unique process ID within the trace. Use
upid
(not
pid
) for joins.
TimestampsAll timestamps are in nanoseconds. Divide by 1e6 for milliseconds, 1e9 for seconds.
CounterA time-series metric (GPU utilization, clock frequency, temperature). Stored in the
counter
table.
ArgsKey-value metadata attached to slices. Accessed via the
args
table joined on
arg_set_id
.
概念描述
Slice一段有时间范围的执行过程(函数调用、帧、渲染过程)。包含
ts
(开始时间)、
dur
(时长)、
name
和
track_id
。
Track时间线通道。线程通道存储特定线程的切片。计数器通道存储随时间变化的指标值。
Thread (utid)追踪数据内的唯一线程ID。连接时使用
utid
(而非
tid
)——
tid
可能被重复使用。
Process (upid)追踪数据内的唯一进程ID。连接时使用
upid
(而非
pid
)。
时间戳所有时间戳均为纳秒。除以1e6转换为毫秒,除以1e9转换为秒。
Counter时间序列指标(GPU利用率、时钟频率、温度)。存储在
counter
表中。
Args附加到切片的键值元数据。通过
args
表与
arg_set_id
连接访问。

Performance Targets

性能目标

MetricTargetWarningCritical
Frame time (90 Hz)< 11.1 ms> 11.1 ms> 16.7 ms
Stale frame rate< 5%> 10%> 25%
Main thread utilization< 80% of budget> 80%> 95%
GPU utilization< 85% of budget> 85%> 95%
Frame variance (std dev)< 1 ms> 2 ms> 4 ms
Draw calls per frame< 100> 200> 500
指标目标警告严重
帧时间(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

Engine-Specific Notes

引擎特定说明

Unity

Unity

  • PhaseSync: VR vsync alignment mechanism. Appears as idle time at the start of
    PlayerLoop
    . This is normal and intentional — do NOT flag as wasted time.
  • Single-pass multiview: Both eyes rendered in one pass. If you see two render passes per frame, the app may be using multi-pass rendering (less efficient).
  • Dynamic batching: Watch for high
    SetPass
    call counts, which indicate materials are not being batched.
  • IL2CPP vs Mono: IL2CPP builds have different function naming in traces. Look for mangled C++ names instead of C# method names.
  • PhaseSync:VR垂直同步对齐机制。在
    PlayerLoop
    开始时表现为空闲时间。这是正常且有意的——不要将其标记为浪费时间。
  • 单通道多视图:一次渲染过程同时渲染双眼。如果每帧看到两个渲染过程,应用可能使用了多通道渲染(效率较低)。
  • 动态批处理:注意高
    SetPass
    调用次数,这表明材质未被批处理。
  • IL2CPP vs Mono:IL2CPP构建在追踪数据中的函数命名不同。查找混淆的C++名称而非C#方法名。

Unreal Engine

Unreal Engine

  • RHI Thread: Unreal uses a separate RHI (Render Hardware Interface) thread for GPU command submission. Check this thread for driver overhead.
  • Forward vs Deferred: Forward rendering is preferred on Quest. Deferred rendering has significantly higher GPU cost.
  • Blueprint Tick: Heavy Blueprint usage shows up as
    UObject::ProcessEvent
    . High counts indicate Blueprints should be converted to C++.
  • Nativized Blueprints: Show up with
    __StaticExec
    suffix in trace names.
  • RHI Thread:Unreal使用单独的RHI(渲染硬件接口)线程提交GPU命令。检查此线程的驱动开销。
  • 前向渲染 vs 延迟渲染:Quest设备优先使用前向渲染。延迟渲染的GPU成本显著更高。
  • Blueprint Tick:大量使用Blueprint会表现为
    UObject::ProcessEvent
    。高调用次数表明应将Blueprint转换为C++。
  • 原生Blueprint:在追踪名称中带有
    __StaticExec
    后缀。

Common Pitfalls

常见误区

  • Do NOT report PhaseSync or xrWaitFrame idle time as a performance problem — these are intentional frame pacing mechanisms.
  • GPU render pass names like
    surface#0
    are not descriptive — correlate them with the resolution and MSAA level to identify what they render.
  • Thread names can be truncated in traces.
    UnityMain
    may appear as
    UnityMai
    or similar.
  • Always use
    utid
    (not
    tid
    ) when joining thread-related tables in SQL queries.
  • Timestamps are nanoseconds. A common mistake is treating them as microseconds.
  • Counter values are instantaneous samples, not averages over a period.
  • 不要将PhaseSync或xrWaitFrame的空闲时间报告为性能问题——这些是有意的帧 pacing 机制。
  • 像
    surface#0
    这样的GPU渲染过程名称不具有描述性——将其与分辨率和MSAA级别关联以确定其渲染内容。
  • 线程名称在追踪数据中可能被截断。
    UnityMain
    可能显示为
    UnityMai
    或类似名称。
  • 在SQL查询中连接线程相关表时,始终使用
    utid
    (而非
    tid
    )。
  • 时间戳为纳秒。常见错误是将其视为微秒。
  • 计数器值是瞬时采样,而非一段时间内的平均值。

References

参考资料

For detailed guides on specific topics, see:
  • Capturing Traces — How to capture Perfetto traces on Quest
  • Analyzing Traces — Step-by-step trace analysis with SQL queries
  • GPU Analysis — Render pass analysis, GPU counters, and metrics
  • Frame Timing — VR frame markers, pacing, and compositor timing
  • SQL Queries — Ready-to-use SQL queries for common analysis tasks
有关特定主题的详细指南,请参阅:
  • Capturing Traces — 如何在Quest设备上捕获Perfetto追踪数据
  • Analyzing Traces — 使用SQL查询进行追踪分析的分步指南
  • GPU Analysis — 渲染过程分析、GPU计数器和指标
  • Frame Timing — VR帧标记、pacing和合成器时序
  • SQL Queries — 用于常见分析任务的即用型SQL查询