关于 hz-perfetto-debug
用于诊断 Meta VR 设备性能问题的 Skill,通过 metavr 捕获并分析 Perfetto 跟踪。它指导开发者检查帧时间、CPU/GPU、线程和渲染瓶颈。
主要功能
- 01捕获并加载 Perfetto 跟踪
- 02查看 CPU 和 GPU 帧时间
- 03检查线程状态与利用率
- 04用 SQL 查询跟踪数据
- 05分析 GPU 计数器与渲染阶段
适用场景
- 01排查卡顿或错过帧期限
- 02定位 CPU 或 GPU 瓶颈
- 03检查线程争用与同步问题
- 04分析渲染通道或绘制调用开销
MCP 相关
这是一个 Agent Skill,并非 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。开始捕获跟踪前,请通过 USB 连接 Meta VR 设备并启用开发者模式。
快速入门工作流
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>
加载并处理跟踪以供分析。接受十六进制会话 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。可使用所有 Perfetto 表:slice、thread_track、thread、process、counter、counter_track、args、sched_slice 等。
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 核心概念
| 概念 | 说明 |
|---|---|
| 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 联接后访问。 |
性能目标
| 指标 | 目标 | 警告 | 严重 |
|---|---|---|---|
| 帧时间(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开始处的空闲时间。这是正常且有意为之的——请勿将其标记为浪费时间。 - 单通道多视图:在一个通道中渲染双眼画面。如果每帧出现两个渲染通道,应用可能正在使用多通道渲染(效率较低)。
- 动态批处理:注意
SetPass调用次数是否过高,这表明材质未被批处理。 - IL2CPP 与 Mono:IL2CPP 构建在跟踪记录中使用不同的函数命名。请查找名称修饰后的 C++ 名称,而非 C# 方法名。
Unreal Engine
- RHI 线程:Unreal 使用单独的 RHI(Render Hardware Interface)线程提交 GPU 命令。请检查此线程的驱动开销。
- Forward 与 Deferred:Meta VR 上更推荐 Forward 渲染。Deferred 渲染的 GPU 开销明显更高。
- Blueprint Tick:大量使用 Blueprint 会显示为
UObject::ProcessEvent。计数较高表明应将 Blueprint 转换为 C++。 - Nativized Blueprints:在跟踪名称中会显示带有
__StaticExec后缀的条目。
常见陷阱
- 不要将 PhaseSync 或 xrWaitFrame 的空闲时间报告为性能问题——这些是有意设计的帧节奏机制。
surface#0等 GPU 渲染通道名称缺乏描述性——请结合分辨率和 MSAA 级别判断其渲染内容。- 跟踪中的线程名称可能被截断。
UnityMain可能显示为UnityMai或类似形式。 - 在 SQL 查询中联接与线程相关的表时,始终使用
utid(而非tid)。 - 时间戳的单位是纳秒。常见错误是将其当作微秒处理。
- 计数器值是瞬时采样值,而非某个时间段内的平均值。
参考资料
有关特定主题的详细指南,请参阅: