ssheleg/xr-dev

quest-perf

Use when a Meta Quest app drops frames, judders, overheats or must be profiled: frame budget per refresh rate, Dynamic Resolution, foveation, SpaceWarp, OVR Metrics, Perfetto or RenderDoc captures.

ソースを見る

使い方

  1. インストールコマンドをコピーし、自分の環境へ導入する前にソースを確認します。
  2. 必要なツール、認証情報、対応環境を原文で確認します。
  3. 入力と成果物を指定し、出力を資料と照合します。
npx skills add https://github.com/ssheleg/xr-dev --skill quest-perf

開始前の確認

  • 導入にはモデル契約、API枠、ファイルへのアクセス許可は含まれません。
  • スター数や導入数は活動の指標であり、独立した品質・安全性試験ではありません。
リポジトリの原文

見出し、例、コード、表、リンク、参照画像を含む原文を表示しています。

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

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.

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.

The GPU% lie, and the 50% threshold

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.

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.

  1. Foveation — distinguish fixed from eye-tracked; test supported levels and

quality. A simple shader can cost more with FFR than without it.

  1. Refresh rate — request a rate the representative workload sustains; observe

actual runtime and thermal behavior rather than promising an automatic rate change.

  1. Application SpaceWarp — a supported depth/motion-vector integration with

artifact and latency tests, not an automatic rescue or guaranteed percentage gain.

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, stallsPerfettometavr 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 forkmetavr 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.

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.

  1. Note refresh rate and the resulting budget before looking at anything else.
  2. Capture with the app in the worst scene, not the menu.
  3. Change one thing; re-capture; compare App ms, not impressions.
  4. 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.

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"`.

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".

同じリポジトリから

関連する Skills

すべての Skills
ssheleg
コミュニティ

quest-native

Use when building or reviewing a native Meta Quest app in C or C++ against OpenXR: manifest and Gradle, loader, frame loop, swapchains, Vulkan, passthrough, required SDK levels. NOT for Unity, Unreal, Spatial SDK (quest-spatial), WebXR (quest-webxr), profiling (quest-perf) or Store submission (quest-store). Opt out: no xr, без xr. Triggers - "OpenXR app" / "приложение на OpenXR", "native Quest app" / "нативное приложение для Quest", "xrCreateInstance".

導入数
3
GitHub Stars
1
更新日
10月24日
ssheleg
コミュニティ

quest-tooling

Use when setting up or driving the Meta Quest toolchain on a development machine: the metavr CLI and MCP server, Meta's agent skills, connecting a headset, managed tools, XR Simulator, no headset at all. NOT for app code (quest-native), reading a capture (quest-perf) or submitting a build (quest-store). Opt out: no xr, без xr. Triggers - "metavr", "connect the headset" / "подключить шлем", "adb к шлему", "XR Simulator".

導入数
2
GitHub Stars
1
更新日
10月24日
ssheleg
コミュニティ

quest-webxr

Use when the XR experience runs in the Meta Quest Browser: WebXR with IWSDK, three.js or A-Frame, PWA packaging with Bubblewrap, Horizon Billing and remote debugging. NOT for native OpenXR (quest-native), an APK submission (quest-store) or three.js depth off the headset (web3d-dev). Opt out: no xr, без xr. Triggers - "WebXR", "immersive-vr session" / "иммерсивная сессия", "three.js on Quest" / "three.js на Quest", "PWA для Quest", "IWSDK".

導入数
2
GitHub Stars
1
更新日
10月24日