Develop

Use ovrgpuprofiler for GPU Profiling

Updated: Aug 19, 2026
ovrgpuprofiler is a performance monitoring CLI tool for Meta Quest headsets that developers can use to access a range of real-time GPU metrics and perform render stage traces. ovrgpuprofiler is included with the Meta Quest runtime and does not need to be manually installed.

Use ovrgpuprofiler to Retrieve Real-time Metrics

Open a shell via ADB on a connected Meta Quest before using ovrgpuprofiler. If not using a shell, precede all commands in this topic with adb shell <command>.

Get Metrics List

To list all supported real-time metrics and their ID number, enter the following from the command line when a Meta Quest is connected via ADB:
ovrgpuprofiler -m
The beginning of the output for this command looks like the following:
    47 metrics supported:
    1       Clocks / Second
    2       GPU % Bus Busy
    3       % Vertex Fetch Stall
    4       % Texture Fetch Stall
    5       L1 Texture Cache Miss Per Pixel
    6       % Texture L1 Miss
    7       % Texture L2 Miss
    8       % Stalled on System Memory
    9       Pre-clipped Polygons/Second
    10      % Prims Trivially Rejected
    11      % Prims Clipped
Note: The metric count shown in this example output (47) is specific to the device and runtime version used when the output was captured. The actual number of supported metrics varies by device and runtime version.
The ID numbers are positions in the list that ovrgpuprofiler prints, starting at 1. They are not fixed hardware counter identifiers, so the ID for a given metric can differ across devices and runtime versions. List the metrics on the device you are profiling and read the IDs from that output. ovrgpuprofiler rejects an ID outside the printed range with a message naming the valid range.
Alternatively, ovrgpuprofiler -m -v provides the same list with more verbose descriptions for each metric.

Get Metric Data

To retrieve data for a metric, the command takes the following format:
ovrgpuprofiler -r<metric ID number>
For example, to retrieve the metric Texture Fetch Stall (ID number 4), enter ovrgpuprofiler -r4 and the console prints data every second until you press Ctrl-C.

Get Data for Multiple Metrics

You can also request multiple metrics at once by separating ID numbers with commas in a string, such as ovrgpuprofiler -r"4,5,6". The following shows output from ovrgpuprofiler -r"4,5,6":
$ ovrgpuprofiler -r"4,5,6"
% Texture Fetch Stall                      :           2.449
L1 Texture Cache Miss Per Pixel            :           0.124
% Texture L1 Miss                          :          20.338

% Texture Fetch Stall                      :           2.369
L1 Texture Cache Miss Per Pixel            :           0.122
% Texture L1 Miss                          :          20.130

% Texture Fetch Stall                      :           2.580
L1 Texture Cache Miss Per Pixel            :           0.127
% Texture L1 Miss
...
Note: Do not request more than 30 real-time metrics simultaneously.

Use ovrgpuprofiler for Render Stage Tracing

ovrgpuprofiler supports render stage GPU tracing on a tile-per-tile level. Unlike direct-mode GPUs, which execute draw calls sequentially, tile-based renderers batch draw calls for an entire surface. That surface is then split into tiles that are computed sequentially, where each tile executes all the draw calls that touched that tile. ovrgpuprofiler can tell you how much time was spent in each rendering stage for each surface rendered during a trace’s duration.

Prepare for Render Stage Tracing

Tracing on a tile-per-tile level requires the app’s GPU context to be in detailed GPU profiling mode. To set the OS to start subsequent apps in detailed GPU profiling mode, enter the following command:
ovrgpuprofiler -e
If an app is running when the command is entered, it must be restarted for the change to take effect.
To limit detailed profiling to a single app, pass its package name as an argument, as in ovrgpuprofiler -e com.YourCompany.YourApp. Without an argument, the mode applies to every app started afterward.
ovrgpuprofiler -i shows whether detailed GPU profiling mode is currently enabled. Use ovrgpuprofiler -d to disable it.
Note: Detailed GPU profiling incurs an approximately 10% overhead in GPU rendering times. Keep this overhead in mind when reading trace output.

Execute a Trace

To execute a 100 ms trace on the currently running app, enter the following:
ovrgpuprofiler -t
Trace length can be specified in seconds by including a number with the -t argument. For example, ovrgpuprofiler -t1.2 would run a trace for 1.2 seconds. A duration that cannot be parsed falls back to the 100 ms default and prints a warning.
Add -c to keep polling the trace until you press Ctrl-C, which reports results in batches instead of holding a single long capture in memory. Add -l to run the trace in low-overhead mode, which prints one line per surface and omits the per-stage and per-bin breakdown in exchange for a more accurate measurement.
The output of the trace is printed to the console, listing the surfaces rendered during the trace along with render stage information.

Read a Trace

Lines from the trace output look like the following:
Process com.YourCompany.YourApp has 24 surface executions
Surface 1    | 1216x1344 | color 32bit, depth 24bit, stencil 0 bit, MSAA 4, Mode: 1 (HwBinning) | 60  128x224 bins ( 60  rendered) | 5.08 ms | 130 stages : Binning : 0.623ms Render : 1.877ms StoreColor : 0.309ms Blit : 0.002ms Preempt : 1.286ms
This shows that Surface 1 has a resolution of 1216x1344, 32-bit color, 24-bit depth, and uses MSAA 4. The surface was broken down into 60 tiles/bins with a size of 128x224, 60 of which were rendered, and it took 5.08 ms to render in total. There were 130 render stage executions in the process, and the remaining fields show how much time was spent in each render stage. Not every render stage appears for each surface.
The Mode field reports how the GPU rendered the surface. ovrgpuprofiler prints the mode number followed by a short name:
  • 0 (Direct) - Direct rendering without binning.
  • 1 (HwBinning) - Hardware visibility binning.
  • 2 (SwBinning) - Software binning.
  • 3 (HwDirect) - Hardware visibility direct.
A mode the tool does not have a name for prints as unknown render mode <number>.
A compute-only submission has no surface geometry to report, so it prints on a shorter line that carries the index and the elapsed time:
Compute 4    |                                                                                                     | 1.42 ms

See Per-Stage and Per-Bin Detail

Add -v to expand each surface into the bins and stages that make it up:
ovrgpuprofiler -t1 -v
Surface 1    | 1216x1344 | color 32bit, depth 24bit, stencil 0 bit, MSAA 4, Mode: 1 (HwBinning) | 60  128x224 bins ( 60  rendered) | 5.08 ms | 130 stages : Binning : 0.623ms Render : 1.877ms StoreColor : 0.309ms
    Bin 0   | topLeft 0   x0    | 1x1 logical bins | Fov 8/8
        Stage 0   : Binning            0.623 ms
        Stage 1   : Render             0.031 ms
        Stage 2   : StoreColor         0.005 ms
    Bin 1   | topLeft 128 x0    | 1x1 logical bins | Fov 8/8
        Stage 3   : Render             0.029 ms
        Stage 4   : StoreColor         0.005 ms
Each Bin line gives the bin’s top-left offset in the surface, how many logical bins it covers, and its foveation scale factors. A multiview bin that carries a separate factor per view prints both as Fov L<x>/<y> R<x>/<y>.

Multiview Surface Output by Device

On Meta Quest, ovrgpuprofiler outputs one surface line per slice for multiview apps. This means that there is one surface for each eye. You must add the render times of two eye surfaces for the total frame time.
On Meta Quest 2, however, ovrgpuprofiler outputs one surface line for both views of the surface, due to how the Adreno 650v3 GPU processes multiview commands (Hardware Multiview). On Meta Quest 2, bins of multiview surfaces are shared between both views. Therefore, the following output:
135 96x176 bins
should be interpreted as:
135 96x176x2 bins
Meta Quest 3 and Meta Quest 3S both use the Adreno 740v3 GPU (Meta Quest 3 at 690 MHz, Meta Quest 3S at 492 MHz). Whether ovrgpuprofiler outputs one surface line per eye (as on Meta Quest) or one surface line for both views (as on Meta Quest 2 with Hardware Multiview) has not been verified for these devices. Refer to the release notes for your runtime version or contact developer support for confirmation.
Render stages that appear include the following:
  • Binning - The Meta Quest’s GPU uses a tiled architecture, meaning that all draw calls for a frame are executed in two stages. The first stage is the binning phase, where triangle vertex positions for all draw calls are calculated and assigned to bins that correspond to a partition of the drawing surface.
  • Render - This is the second stage of the draw call that began with binning. One chunk of this represents the total cost of all vertex and fragment operations for one bin. A simplified version of the vertex shaders is executed during binning to find a triangle’s position. The full version of the vertex shaders is re-executed to compute the interpolants used by the fragment shader during this stage.
  • LoadColor - Loads the color from slow memory into fast memory. This can happen when starting to render into a surface without clearing it.
  • StoreColor - After an entire bin of pixel and fragment operations are done executing, the calculated color value is copied from fast memory (dedicated for the bin’s rendering operations) to slow memory.
  • LoadDepthStencil - Loads the depth and stencil buffer from slow memory into fast memory.
  • StoreDepthStencil - Copies the depth and stencil buffer from fast memory back to slow memory.
  • Blit - Copying between slow memory regions. This can happen for various operations, such as mipmap generation and when clearing a surface without rendering anything.
  • Preempt - The compositor is an OS-level service that executes at regular intervals to present the image submitted by the application to the screen. To deliver the image at the proper cadence, the GPU preempts the application’s workload so the compositor can complete its work on time.
  • Load and Store - Generic buffer load and store operations.
  • Dispatch and GLAsyncCompute - Compute shader dispatch, and OpenGL asynchronous compute.
  • VKQueue, VKRenderClear, and VKLoadInput - Vulkan queue submission, render pass clear, and input attachment load.
  • Workload - A generic GPU workload execution stage.
  • RayTracingBuild, RayTracingUpdate, and RayTracingCopy - Building, updating, and copying a ray tracing acceleration structure.
A stage the tool does not have a name for prints as Unknown.

Use ovrgpuprofiler for Per-Render-Stage Metrics

Beyond the per-stage timings above, ovrgpuprofiler can sample GPU metrics for each render stage in a trace. This attributes counter values to a specific binning, render, or store stage of a specific surface, rather than to the frame as a whole.
Per-render-stage metrics use their own metric catalog, separate from both the real-time and the per-draw catalogs.

List Render-Stage Trace Metrics

Combine -m with -t to list the trace metrics supported on the connected device:
ovrgpuprofiler -m -t
Pass -v for verbose descriptions. As with the other catalogs, the IDs are positions in this list and vary by device and runtime version.

Capture a Trace with Per-Stage Metrics

Pass the metric IDs to -s (--renderstage-metrics) and combine it with -t:
ovrgpuprofiler -t3 -s "1,2,3"
-s requires -t. Running -s on its own reports an error and exits with a non-zero status, as does an empty or fully invalid ID list. Detailed GPU profiling mode must be enabled first, the same as for any render stage trace.
The trace reports how many of the requested metrics the GPU counter budget accepted, and names the ones it dropped:
Tracing for 3.0 seconds...
Captured 2 metrics, 1 rejected by SoC counter budget
  rejected: % Texture L2 Miss
Process com.YourCompany.YourApp has 24 surface executions
Surface 1    | 1216x1344 | color 32bit, depth 24bit, stencil 0 bit, MSAA 4, Mode: 1 (HwBinning) | 60  128x224 bins ( 60  rendered) | 5.08 ms | 130 stages : Binning : 0.623ms Render : 1.877ms StoreColor : 0.309ms
    Bin 0   | topLeft 0   x0    | 1x1 logical bins | Fov 8/8
        Stage 0   : Binning            0.623 ms
            Clocks                                     :       58912.000
            % Shaders Busy                             :          39.907
        Stage 1   : Render             0.031 ms
            Clocks                                     :        3204.000
            % Shaders Busy                             :          61.442
Passing -s expands the per-stage breakdown on its own, so -v is not required to see the stage lines. A metric the counter allocator rejected is absent from the per-stage values.
Note: The SoC allocates a fixed number of hardware counters per pass, so a long list of metric IDs can exceed the budget and lose metrics to the rejected list. Run several traces with smaller subsets when you need more metrics than one pass holds.
Combining -s with -x is not supported: per-draw mode takes precedence, -s is ignored, and the tool prints a warning.

Use ovrgpuprofiler for Per-Draw Metrics

In addition to per-surface render stage tracing, ovrgpuprofiler can sample GPU metrics on a per-draw-call basis within each surface. This is useful for identifying which draw calls dominate a frame’s GPU cost or for comparing the relative cost of individual draws.
Per-draw metrics use a separate metric catalog from real-time metrics: the available IDs and their descriptions are not the same.

Prepare for Per-Draw Tracing

Per-draw tracing requires the app’s GPU context to be in detailed GPU profiling mode. Follow the same setup as for render stage tracing by entering ovrgpuprofiler -e and restarting the target app.

List Per-Draw Metrics

To list the per-draw metrics supported on the connected device, enter:
ovrgpuprofiler -x -m
The output lists each metric ID along with its name. Pass -v for more verbose descriptions. As with real-time metrics, the available set varies by device and runtime version.

Capture a Per-Draw Trace

To capture a per-draw trace, combine the -x (draw call) flag with -t (trace), passing a comma-separated list of per-draw metric IDs to record:
ovrgpuprofiler -t3 -x="1,2,3,4,12,15,16,17,18,22,27,28,29,38,49,50"
In this example, -t3 runs a 3-second trace (the default trace length is 0.1 seconds) and the -x argument selects 16 per-draw metric IDs to sample. A per-draw trace with no metric IDs reports that there is nothing to track and stops.
Note: The number of metrics that can be requested in a single trace pass is limited by the GPU hardware. Requesting more metrics than the device supports per pass can cause some metric values to be missing from the output. If you need more metrics than fit in a single pass, run multiple traces with different metric subsets.

Read a Per-Draw Trace

The output groups results by process and command buffer. For each draw call, the trace prints a header line with the frame number, draw number, and an optional draw label, followed by the LRZ (low-resolution Z) state and the value of each requested metric:
Tracing for 3.0 seconds...
Process com.YourApp has 30 commandbuffers
    Frame 1   : Draw 1   Label 0xffffffff
        LRZ State: TestEnabled, WriteEnabled <0x03>
        Clocks                                     :       58912.000
        % Vertex Fetch Stall                       :           0.061
        % Shaders Busy                             :          39.907
        Fragments Shaded                           :           3.203
    Frame 1   : Draw 2   Label 0xffffffff
        LRZ State: TestEnabled, WriteEnabled <0x03>
        Clocks                                     :       44008.000
        % Vertex Fetch Stall                       :           0.000
        ...
The LRZ state prints as TestEnabled, TestEnabled, WriteEnabled, or Disabled. A driver that does not report the state prints Unknown(old driver?).
Note: Per-draw timings are best used as a relative comparison between draws within the same trace. Per-draw measurement introduces pipeline stalls that affect absolute timings.
When you are finished capturing per-draw traces, disable detailed profiling mode with ovrgpuprofiler -d.

Command-Line Argument Reference

The following command-line arguments are available for ovrgpuprofiler:
ArgumentDescription
-h/--help
Prints the list of supported arguments and exits.
-r/--realtime
Prints the value of the real-time metrics every second. Accepts an optional comma-separated list of metrics IDs to track.
-m/--metrics
Prints the list of available real-time metrics IDs, their name, and their description. When combined with -x, lists the per-draw metric catalog instead; when combined with -t, lists the render-stage trace metric catalog.
-v/--verbose
Adds more detailed information to most other commands. With -t, expands each surface into its per-bin and per-stage breakdown.
-e/--enable-detailed
Enables detailed profiling mode on the GPU driver; required for render stage tracing. Only applies to applications started after this mode is started. Accepts an optional application name to attach to that application only.
-d/--disable-detailed
Disables detailed profiling mode on the GPU driver.
-i/--is-detailed
Queries if the GPU driver is in detailed profiling mode.
-t/--trace
Executes a render stage trace, with an optional trace length as argument in seconds (default 0.1 s).
-c/--continuous
If you specify this along with -t/--trace, the results of the render stage trace are polled periodically to reduce memory pressure.
-l/--low-overhead
If you specify this along with -t/--trace, the render stage trace is performed in low-overhead mode, which omits many details in exchange for a more accurate measurement.
-s/--renderstage-metrics
Captures per-render-stage metric values during a trace. Takes a required comma-separated list of render-stage metric IDs, for example -s "1,2,3" or --renderstage-metrics="1,2,3". Must be combined with -t; ignored when -x is also present.
-x/--drawcall
Switches the tool into per-draw mode. Combine with -m to list per-draw metrics, or with -t and an "=" comma-separated list of metric IDs to record a per-draw trace (for example, -x="1,2,3").

See Also