quest-perf

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Quest performance: the budget, the lie, and the capture

Quest 性能:帧预算、误导性指标与捕获分析

Performance work on Horizon OS fails in one of two ways: optimising without a capture, or reading the capture wrong. This skill fixes the second first, because the most-quoted number on the platform —
GPU%
— is misleading exactly when it matters.
For a whole-product roadmap or stage audit, use
quest-lifecycle
; a single technical task stays with this owner. If absent, identify the current stage, its evidence and the next prerequisite inline.
Read
references/mobile-rendering.md
when choosing the render pipeline or investigating tile, render-pass, geometry, overdraw and asset costs.
Horizon OS 上的性能工作往往败在两种方式之一:未捕获数据就进行优化,或错误解读捕获数据。本 skill 优先解决第二种问题,因为平台上最常被引用的数字——
GPU%
——恰恰在最关键的时候具有误导性。
若需要制定完整的产品路线图或进行阶段审计,请使用
quest-lifecycle
;单个技术任务仍由本 skill 负责。如果该 skill 不存在,请直接在此处说明当前阶段、对应证据和下一项前置条件。
选择渲染管线,或排查 tile、render-pass、geometry、overdraw 和 asset 成本时,请阅读
references/mobile-rendering.md
。

The budget is a deadline, not a target

帧预算是截止时间,而不是目标

Refresh rateTime per frame
72 Hz13.9 ms
90 Hz11.1 ms
120 Hz8.3 ms
CPU, render-thread, GPU and compositor work form a pipeline and can overlap across frames. Compare the critical path and synchronization against the deadline; do not add independent CPU/GPU samples as a universal frame-time formula. Missed submissions may invoke reprojection, with visible artifacts that depend on motion, depth and the selected runtime path.
刷新率每帧时间
72 Hz13.9 ms
90 Hz11.1 ms
120 Hz8.3 ms
CPU、render thread、GPU 和 compositor 工作构成一条 pipeline,并且可以跨帧重叠执行。请将 critical path 和同步情况与截止时间进行比较;不要把相互独立的 CPU/GPU 采样值相加,当作通用的帧时间计算公式。提交超时可能触发 reprojection,并产生可见伪影;这些伪影取决于运动、深度以及所选的 runtime path。

The GPU% lie, and the 50% threshold

GPU% 的误导性,以及 50% 阈值

When an app drops to half rate the GPU finishes early and idles, so utilisation falls while the app is firmly GPU-bound.
text
FPS=36/72  Stale=36  GPU%=0.65  App=18.05ms
  • FPS=36/72
    — half rate: every frame shown twice.
  • App=18.05ms
    — the real per-frame GPU time, against a 13.9 ms budget.
  • GPU%=0.65
    — measured across the doubled 27.8 ms interval, which is why it reads 65% while the app needs a 23% cut, not the 35% "headroom" it implies.
Rule: read
App
in milliseconds against the refresh-rate budget. Only trust
GPU%
near saturation.
To climb back out of half rate, GPU work must fit one refresh interval — i.e. utilisation must fall below ~50% while at half rate.
If
App
is inside budget and
Stale
remains non-zero, inspect CPU submission, synchronization and compositor timing in a trace. That symptom alone does not prove a CPU bottleneck. The worked half-rate example above assumes the stated metric definitions and no intentional SpaceWarp mode; verify both first.
当应用降至一半刷新率时,GPU 会提前完成并闲置,因此即使应用明确处于 GPU-bound 状态,GPU 利用率反而会下降。
text
FPS=36/72  Stale=36  GPU%=0.65  App=18.05ms
  • FPS=36/72
    ——一半刷新率:每一帧显示两次。
  • App=18.05ms
    ——实际的单帧 GPU 时间,对应 13.9 ms 的预算。
  • GPU%=0.65
    ——在翻倍后的 27.8 ms 区间内测得,因此读数为 65%,但应用实际需要削减 23%,而不是它看似所暗示的 35%“余量”。
**规则:读取
App
的毫秒值,并将其与刷新率对应的帧预算比较。只有在 GPU 接近饱和时,才应信任
GPU%
。要从一半刷新率恢复到完整刷新率,GPU 工作必须能在一个刷新间隔内完成——也就是说,在一半刷新率下,利用率必须降至 低于约 50%。
如果
App
处于预算内,但
Stale
仍非零,应在 trace 中检查 CPU 提交、同步和 compositor 时序。该症状本身不能证明存在 CPU 瓶颈。上述一半刷新率示例假设采用文中所述的指标定义,并且未有意启用 SpaceWarp 模式;请先验证这两点。

Select a lever from the measured bottleneck

根据实测瓶颈选择优化手段

Read
references/rendering-playbook.md
when choosing multiview, render scale, foveation, layers, assets, shaders or reprojection. It links each experiment to its prerequisites, visual failure cases and target-device evidence.
  1. Dynamic Resolution — tune render scale through the engine/SDK's supported control path; verify actual behavior rather than assuming the OS enables it.
  2. Foveation — distinguish fixed from eye-tracked; test supported levels and quality. A simple shader can cost more with FFR than without it.
  3. Refresh rate — request a rate the representative workload sustains; observe actual runtime and thermal behavior rather than promising an automatic rate change.
  4. Application SpaceWarp — a supported depth/motion-vector integration with artifact and latency tests, not an automatic rescue or guaranteed percentage gain.
选择 multiview、render scale、foveation、layers、assets、shaders 或 reprojection 时,请阅读
references/rendering-playbook.md
。它将每项实验与前置条件、视觉失效场景和目标设备证据关联起来。
  1. Dynamic Resolution——通过 engine/SDK 支持的 control path 调整 render scale;应验证实际行为,而不是假定 OS 会启用它。
  2. Foveation——区分 fixed 与 eye-tracked;测试受支持的级别和质量。简单的 shader 在启用 FFR 时可能比未启用时成本更高。
  3. 刷新率——请求 representative workload 能够持续维持的刷新率;观察实际 runtime 和 thermal 表现,而不是承诺自动切换刷新率。
  4. Application SpaceWarp——对受支持的 depth/motion-vector 集成进行 artifact 和 latency 测试;它不是自动补救措施,也不保证固定百分比的性能提升。

Capture chain — what each tool answers

捕获链路——每个工具能回答什么问题

QuestionToolHow
Am I missing frames at all, and wherelogcat VrApi stats (
FPS
,
Stale
,
App
,
GPU%
)
metavr log
/
adb logcat
Live numbers inside the headsetOVR Metrics Tool (
ovrmetric
APK)
metavr tools install ovrmetric
, then enable its overlay
CPU vs GPU, thread timeline, stallsPerfetto
metavr
MCP
start_perfetto_capture
→
stop_perfetto_capture
→
analyze_trace
, or the CLI equivalents
Which C/C++ functions burn the CPUsimpleperfAndroid NDK's simpleperf against the running package
Which draw call, shader or render stage burns the GPURenderDoc Meta fork
metavr tools install renderdoc
; draw-call trace, render-stage trace, Vulkan shader stats
Meta ships task skills for two of these —
hz-perfetto-debug
and
hz-simpleperf-debug
, plus
hz-vr-debug
for general on-device debugging. When they are installed, this skill decides what to measure and they drive the tool.
references/capture-playbook.md
holds the exact sequences, including what to capture for a report that another agent can act on.
问题工具使用方式
是否确实存在丢帧,以及丢在哪里logcat VrApi stats(
FPS
、
Stale
、
App
、
GPU%
)
metavr log
/
adb logcat
头显内的实时数据OVR Metrics Tool(
ovrmetric
APK)
metavr tools install ovrmetric
,然后启用其 overlay
CPU 与 GPU 对比、线程时间线、停顿Perfetto
metavr
MCP
start_perfetto_capture
→
stop_perfetto_capture
→
analyze_trace
,或使用等效 CLI 命令
哪些 C/C++ 函数消耗 CPUsimpleperf针对运行中的 package 使用 Android NDK 的 simpleperf
哪个 draw call、shader 或 render stage 消耗 GPURenderDoc Meta fork
metavr tools install renderdoc
;draw-call trace、render-stage trace、Vulkan shader stats
Meta 为其中两种工具提供 task skill——
hz-perfetto-debug
和
hz-simpleperf-debug
——此外还有
hz-vr-debug
,用于常规的设备端调试。安装这些 skill 后,本 skill 负责决定测量什么,再由它们驱动具体工具。
references/capture-playbook.md
包含精确的操作顺序,包括为了生成另一名 agent 可据此执行的报告应捕获哪些内容。

Method, so a second capture means something

方法:让第二次捕获真正有意义

  1. Reproduce on a release build on a real headset. A simulator run (
    xrsim
    ,
    spatialsim
    ) checks supported simulated behavior, not headset performance.
  2. Note refresh rate and the resulting budget before looking at anything else.
  3. Capture with the app in the worst scene, not the menu.
  4. Change one thing; re-capture; compare
    App
    ms, not impressions.
  5. Record the pair (before/after
    App
    ,
    Stale
    , scene, build id) in the project's own notes — a performance claim with no capture is an opinion.
  1. 使用 真实头显上的 release build 复现问题。模拟器运行(
    xrsim
    、
    spatialsim
    )只能验证受支持的模拟行为,不能反映头显性能。
  2. 查看任何其他内容之前,先记录刷新率及其对应的预算。
  3. 在应用处于最差场景时捕获,不要在菜单界面捕获。
  4. 每次只更改一项;重新捕获;比较
    App
    的毫秒值,而不是凭印象判断。
  5. 将前后对比数据(
    App
    、
    Stale
    、场景、build id)记录到项目自己的笔记中——没有捕获数据的性能结论只是主观看法。

Gotchas that cost a debugging round

容易导致调试返工的陷阱

  • A thermally throttled headset invalidates the comparison. Long sessions drift; let it cool or state the temperature state with the number.
  • Dev-mode overlays and logging cost frames. Measure with them off, then turn them on only to read the numbers you cannot get otherwise.
  • Counter semantics depend on capture/runtime mode. Inspect timestamps and reprojection state before interpreting a stale-frame count as dropped work or pipeline latency.
  • Draw calls are a CPU cost and fill is a GPU cost. Cutting draw calls on a fill-bound frame moves nothing, which is why the capture comes first.
  • WebXR has its own counters — draw-call metrics in the browser docs; see
    quest-webxr
    before applying native advice to a web build.
Frame budgets, the 65%/18.05 ms worked example and the recovery mechanisms were read from
documentation/native/android/os-missed-frames.md
on 2026-09-20; re-verify with
metavr docs search "missed frames"
.
  • 发生热降频的头显会使对比结果失效。 长时间运行期间会发生温度漂移;应让头显冷却,或在记录数据时注明温度状态及具体数值。
  • 开发者模式的 overlay 和日志记录会消耗帧时间。 测量时应将其关闭,只在需要读取其他方式无法获得的数字时再开启。
  • 计数器的语义取决于 capture/runtime 模式。 在把 stale-frame 计数解读为工作未完成或 pipeline latency 之前,应检查时间戳和 reprojection 状态。
  • Draw call 属于 CPU 成本,fill 属于 GPU 成本。 在 fill-bound 帧上减少 draw call 不会带来任何改善,这正是必须先进行 capture 的原因。
  • WebXR 拥有自己的计数器——浏览器文档中的 draw-call 指标;在将 native 建议应用于 Web 构建之前,请先查看
    quest-webxr
    。
帧预算、65%/18.05 ms 示例及恢复机制依据
documentation/native/android/os-missed-frames.md
(2026-09-20)整理;请使用
metavr docs search "missed frames"
重新验证。

Triggers

触发词

More phrases that route here: "profile the headset" / "профилировать шлем", "app is hot" / "шлем греется", "judder" / "джаддер", "GPU bound" / "упёрлись в GPU", "perfetto trace" / "трейс perfetto", "simpleperf", "RenderDoc", "OVR Metrics", "stale frames", "SpaceWarp", "foveated rendering" / "фовеальный рендеринг", "72 vs 90 Hz".
更多会路由到这里的短语:“分析头显”/“профилировать шлем”、“应用过热”/“шлем греется”、“卡顿”/“джаддер”、“受 GPU 限制”/“упёрлись в GPU”、“Perfetto trace”/“трейс perfetto”、“simpleperf”、“RenderDoc”、“OVR Metrics”、“stale frames”、“SpaceWarp”、“foveated rendering”/“фовеальный рендеринг”、“72 vs 90 Hz”。