If you’ve been trying to improve your app’s performance, you may have heard the recommendation to use
OVR Metrics Tool, or the VrApi logs in logcat. FPS is the most obvious stat in both, which is very useful to tell if your app is running at full framerate or not, but there are a lot of other numbers where it’s maybe not as clear as to what they mean, or how you can take advantage of them. Here is a guide to the stats within the OVR Metrics Tool, what you can maybe ignore, and what you definitely shouldn’t.
Screenshot of OVR Metrics Tool with all stats enabled:
Example VrApi Logcat output:
FPS=72,Prd=46ms,Tear=0,Early=72,Stale=0,VSnc=1,Lat=1,Fov=0,CPU4/GPU=2/4,1651/670MHz,OC=FF,TA=0/E0/0,SP=N/F/N,Mem=1804MHz,Free=1642MB,PSM=0,PLS=0,Temp=31.0C/0.0C,TW=3.40ms,App=0.69ms,GD=0.97ms,CPU&GPU=3.86ms,LCnt=3,GPU%=0.37,CPU%=0.08(W0.10)
//
OVR Metrics Tool Stats w/ Corresponding VrApi Log Value
The IMPORTANT StuffInstead of going by listed order or alphabetical order, I’m going to go by order of importance (or at least what I consider important). This means you’ll probably have to work backwards if you’re trying to figure out what a specific stat means, sorry (you can always Ctrl+F).
Stale Frames (per second) OVR Metrics Tool: STALE
VrApi Logs: Stale
You may be surprised that I didn’t put FPS at number 1. However, Stale frames is actually a more useful metric when evaluating the user’s quality of experience. What is a stale frame? Because of the way VR works, the screen display refresh rate isn’t directly tied to the app’s rendering of frames. Instead, there is an intermediate step called a compositor, AKA TimeWarp, that takes the last app-rendered frame, calculates orientation correction to account for user head movement, and presents it on the physical display. Therefore, TimeWarp expects that the last app-rendered frame was presented at a specific time. If that frame isn’t actually ready yet, it has to use the frame before that, which at this point is usually quite out of date, or ‘stale’. So how is this different than FPS? If the app misses a frame, stale frames goes up one, and frame rate goes down one, so it seems like they are perfect inverses of each other. This actually isn’t the case! Because the CPU and GPU are working in parallel, it’s possible that rendering a frame takes longer than a single frame’s time length in total, but neither the CPU or GPU takes longer than a single frame individually. Therefore an app can run at 72fps, but have 72 stale frames per second. This isn’t actually that bad, the latency between rendering and display time is higher, but the release tempo of frames is steady. The problem comes in when more than zero, but less than 72 frames are stale. At this point some frames are displayed twice in a row, some frames are skipped, and the user will have a bad time. To avoid this, we use something called Extra Latency Mode (which is on by default if you are using Unity or Unreal Engine). This tells TimeWarp to always wait an extra frame, and not to consider frames stale unless they aren’t ready after the second frame. If the app does render quickly, the frame will be considered ‘Early’, but everything will look smooth. For further reading on how stale frames work, I’d recommend reading one of my previous blog posts on
Understanding Gameplay Latency for Oculus Quest, Oculus Go and Gear VR. App GPU TimeOVR Metrics Tool: APP T (in μs, 1/1000th ms)
VrApi Logs: App (in ms)
This is probably one of the most useful numbers to pay attention to when optimizing your application. App GPU Time gives you the amount of time your application spends rendering a single frame. If it’s more than a single frame length (i.e., 13.88ms for 72fps) you are GPU bound. If it’s less than that you’re probably CPU bound. It’s also incredibly useful to watch over time, as you make changes to shaders, add more textures, change meshes etc., this will tell you how much headroom you have on your GPU, and if you add debug logic to turn on and off specific objects, you can actually tell you how performance intensive those objects are. This is the closest you can get to doing real profiling without busting out something like
RenderDoc or
Snapdragon Profiler.
CPU and GPU UtilizationOVR Metrics Tool: CPU U and GPU U
VrApi Logs: GPU% and CPU%
CPU and GPU Utilization are useful for figuring out if your app is CPU or GPU bound, but there are a few caveats. GPU Utilization is actually the more useful metric, since the GPU is functionally single cored (this isn’t how a GPU works, but for our metaphor, let's go with that). Both the app and TimeWarp submit work to the GPU, the GPU performs the work, and a single utilization number can be calculated based on the total amount of work performed in a given time window. If this hits 100% you are GPU bound. You might actually start to have trouble if it gets over 90% just due to scheduling. CPU Utilization is actually way less useful. Because Mobile CPU’s are multicore, we chose utilization to represent the worst performing core (VrApi logs show both average and worst). However, with most apps being multithreaded, and the scheduler assigning threads to which ever core is available, even if your main thread is running very slowly, it might not be represented in the CPU Utilization metric, since that thread will end up running on different cores. When debugging, you could set the thread affinity to bind your main and render thread to a specific core, which makes this number more useful. However, this actually makes the whole system less efficient, since the scheduler is actually pretty good at keeping your throughput high. Therefore it’s useful to keep an eye on, but you can’t rely on it to determine where your load is. You’ll need to use a profiler like
systrace or one built in to your engine to figure out where your bottleneck is on the CPU side.
CPU and GPU LevelOVR Metrics Tool: CPU L and GPU L
VrApi Logs: CPU[X]/GPU (X is the current main core)
On Gear VR, these numbers need to be manually set by your application. However, with the introduction of Oculus Go, we added automatic dynamic clocking, which will increase these numbers if the app isn’t hitting framerate at the requested levels. If your app isn’t making framerate, it’s good to review the CPU and GPU level as it provides signal if you are CPU or GPU bound, along with the approximate load on each. For example, if you notice that CPU level is 4, and GPU level is 2, you need to optimize your CPU usage. If both are at 4, then you’ll need to look at some of the other metrics that I listed above. You should also keep in mind that the utilization numbers are going to be relative to the correlated level number. For example, 90% utilization at GPU Level 2 is actually better performance than 60% at GPU level 4.
Average Frames Per SecondOVR Metrics Tool: FPS
VrApi Logs: FPS
Frames per second probably doesn’t need a whole lot of explanation. If it’s matching the display refresh rate, you’re probably good (see
Stale Frames for why this is
probably). If it’s less than the display refresh rate, you have some work to do.
Available MemoryOVR Metrics Tool: A MEM
VrApi Logs: Free
The reported available memory comes from the Android OS. On Android, memory is managed in a somewhat opaque way, which makes this number less than actionable. For instance, if your app goes to the background, the newly foregrounded apps can pull in a lot of memory, so even if you have a couple hundred MB available, your app may still get terminated. It is however a good way to monitor if memory is being allocated faster than expected, or if you aren’t releasing memory that you thought you were.
The ‘Good to Know’ MetricsThe next set of stats are going to be less useful to monitor. They are mainly useful to verify that what you’ve set in your app is what you expect, and the system isn’t doing anything you don’t expect. These are also useful for troubleshooting problems in apps that you didn’t write yourself (which is a large part of my job).
Foveation Level OVR Metrics Tool: FOV
VrApi Logs: Fov
Foveation level refers to the Fixed Foveated Rendering intensity of the app. 0 is off, 1 is minimal, 2 is medium, 3 is high, 4 (only available on Quest) is high top, where the lower half of the screen will be clearer than the top (useful for apps that display hands). This number will have direct GPU performance implications, and increasingly noticeable visible artifacts on the edge of the screen as you change the level. The important thing is to pick the most visually acceptable level for the performance increase you need.
Eye Buffer Width/HeightOVR Metrics Tool: EBW and EBH
This is the resolution of the texture to which your app is rendering. The default for Oculus Go/Gear VR is 1024x1024, for Quest it’s 1216x1344. This is useful to verify that you’ve changed this to what you expect, and to confirm your aspect ratio is correct for your field of view. Resolution has a direct impact on GPU rendering time, more pixels means more time in fragment shaders.
TimeWarp TimeOVR Metrics Tool: TW T
VrApi Logs: TW
This is how long TimeWarp takes to render. This time directly correlates to the number of layers you are using and their complexity (Equirect and Cylinder layers are more expensive on the GPU than Quad and Projection layers). Most apps don’t need to worry about this, however this can be relevant for video apps, as TimeWarp taking too long can lead to screen tearing.
Early Frames
OVR Metrics Tool: EARLY
VrApi Logs: Early
As mentioned earlier during the overview on Stale frames, when using Extra Latency Mode, it’s possible to deliver frames before they are needed. A few early frames can be ignored. If you’re persistently running at high early frames, make sure your CPU/GPU level aren’t set higher than you need. If your early frames have matching FPS, I would recommend turning off Extra Latency Mode. You could also take advantage of your headroom by increasing the resolution or increasing shader complexity.
TearsOVR Metrics Tool: TEARS
VrApi Logs: Tear
This reports when TimeWarp takes too long and experiences a screen tear. This was more common on early Gear VR devices. With the Oculus Go and Quest this should basically never happen unless your application is using too many layers (this might be the case if you are using quad or cylinder layers to display UI elements, or using multiple overlapping equirect video layers).
Used MemoryOVR Metrics Tool: U MEM
It seems like used memory would be really useful, however, this number reports PSS (Proportional Set Size) Memory. Because apps can share memory in Android, this adds up all the unique memory used by the app, and a fractional part of shared memory based on how many apps are sharing it. For instance, if two apps use a library that takes 20mb, it would add 10mb each to the PSS of the individual apps.
The Wikipedia Page on PSS does a good job of explaining how this logic works. This metric does have value to keep track of how much relative memory your app is allocating, but the actual number reported is pretty useless for tracking your true memory footprint.
Extra Latency Mode OVR Metrics Tool: LAT
VrApi Logs: LAT
Check out
Stale Frames for an explanation of Extra Latency Mode. LAT is how you check what it’s set to. This will almost always be 1, which is why it’s this far down the list despite how important this number is.
Swap IntervalOVR Metrics Tool: SWAP
VrApi Logs: VSync
Swap interval tells the app how many frames to skip before rendering the next frame. This will almost always be 1, as entering 2 can cause the app to render at half rate, which can be uncomfortable. Battery saver mode on Gear VR turned on Swap Interval 2, however we never enable it on Oculus Quest because it is uncomfortable with 6DOF movement. You might use Swap Interval for debug purposes or when recording extremely high resolution on device, however you should never have this enabled within a published app as it’s easily noticeable just by turning your head.
Prediction TimeOVR Metrics Tool: PRED
VrApi Logs: Prd
Prediction time is the absolute time between when the app queries the pose before rendering, and the time the frame is displayed on the screen. This should almost always be a fixed number between 40 and 50 ms, depending on your engine and display refresh rate. It’s only really useful to know if it shows a number much higher than expected. It also doesn’t tell you the whole story about latency, since this is the pose used to render, and both Unity and Unreal will use a different pose to update gameplay logic than to render.
Display Refresh RateOVR Metrics Tool: DRR
On Oculus Go and Oculus Quest, the display refresh rate can be changed between 60 and 72. This metric simply tells you which is currently set. This is useful to know in case you’re seeing FPS at 60 when the display refresh is 72. It means you have a lot of optimizing to do.
Battery LevelOVR Metrics Tool: BAT L
Not useful for profiling, but if you want to monitor your battery level in app, here you go.
Only (Slightly) Useful on Gear VR, or Safe to IgnoreThese are some metrics that are really only useful or reported on Gear VR. On Oculus Go and Quest, since the hardware is fixed, and temperature shouldn’t be a concern, you can ignore these. There are also a number of stats reported that may not be really useful to anyone outside of my team.
Sensor TemperatureOVR Metrics Tool: TEMP
Temperature could be a concern on early Gear VR devices. The low power phones could heat up quickly when their clocks were running at the speed required for VR, potentially leading to the dreaded ‘device overheated’ screen. As phones have improved, this has become less of an issue, but if you’re still targeting S6 and S7, it’s good to monitor.
Power LevelOVR Metrics Tool: POW L
This metric tells you if the device is in battery saver mode, the three reported levels are NORMAL = 0, SAVE = 1, DANGER = 2. As the device heats up, the power level will automatically change from NORMAL to SAVE, and eventually to DANGER. Once DANGER is reached, an overheat dialog will be displayed. For your application, you can query the current power level, so it’s recommended to change your app’s behavior when it hits SAVE to reduce rendering cost. See our
documentation on Power Management for more info on the subject.
CPU/GPU/MEM FrequencyOVR Metrics Tool: CPU F, GPU F, and MEM F
This is the clock speed of the CPU/GPU/Memory, which will change when CPU level or GPU level is changed. This isn’t terribly useful to monitor, since the raw number doesn’t really give you a performance delta between devices with different SoC’s anyway.
Battery TemperatureOVR Metrics Tool: B TEM
The current temperature of the battery. More important to know on Gear VR.
Battery/Power Current, Power VoltageOVR Metrics Tool: BAT C, POW C, and POW V
The current coming from the battery in milliamps, and the voltage in volts. Theoretically you could monitor these to figure out how much you’re draining the battery (Amps x Volts = Watts), but you’re better off optimizing against CPU/GPU levels and utilization.
Remote/Controller TemperatureOVR Metrics Tool: LC TM and RC TM (formerly R TEM)
The temperature of the controllers.
Maximum Rotational SpeedOVR Metrics Tool: M ROT
The fastest speed the headset rotated.
VrApi Log Stats
The VrApi Logs contain much of the same stats that appear in OVR Metrics Tool. However, there are a few that are unique to this line. The usefulness of these extra metrics is going to be dependent on your specific needs.
First, I’m going to break down what the whole log line means:
FPS={FPS},Prd={Prediction}ms,Tear={Tears},Early={Early},Stale={Stale},VSnc={Swap Interval},Lat={Extra Latency Mode},Fov={Foveation Level},CPU{Measured CPU Core}/GPU={CPU Level}/{GPU Level},{CPU Frequency}/{GPU Frequency}MHz,OC={Online Core Mask},TA={TimeWarp Thread Affinity}/{Main Thread Affinity}/{Render Thread Affinity},SP={Timewarp Scheduling Priority}/{Main Thread Scheduling Priority}/{Render Thread Scheduling Priority},Mem={Memory Frequency}MHz,Free={Available Memory}MB,PSM={Power Save Mode},PLS={Power Level},Temp={Battery Temperature}C/{Sensor Temperature}C,TW={TimeWarp GPU Time}ms,App={App GPU Time}ms,GD={Guardian GPU Time}ms,CPU&GPU={CPU & GPU Time}ms,LCnt={Layer Count},GPU%={GPU Utilization},CPU%={Average CPU Utilization}(W{Worst Core CPU Utilization})
Unique VrApi MetricsThese Metrics are sorted by the order they appear in the VrApi log line. I’ve noted what each can be used for in their description, and given them my personal usefulness rating out of 5 (You may find them more or less useful depending on your needs).
Measured CPU CorePersonal Usefulness Rating: 1/5
This number simply refers to the core that is being measured when reporting the CPU frequency. For most CPU architectures, there are both big and little cores, this core will be a big core unless CPU Level is set to 0 (except on Quest, which will always report the big core).
Online Core MaskPersonal Usefulness Rating: 0/5
Certain older Gear VR devices would power down cores when the CPU Level was set lower. Modern CPU’s no longer do this, and can reduce energy usage on cores without taking them offline, so this isn’t really useful.
TimeWarp/Main/Render Thread AffinityPersonal Usefulness Rating: 3/5
This represents the thread affinities of the various threads. Useful for verifying your threads are running on big cores, but these days it’s better to avoid manually setting affinity. On Quest, TimeWarp will report 0.
TimeWarp/Main/Render Thread Scheduling PriorityPersonal Usefulness Rating: 4/5
This is a character that refers to the priority of the threads. ‘F’ = SCHED_FIFO which is the highest priority, ‘N’ = SCHED_NORMAL. TimeWarp should always report as ‘F’, except on Quest where this will show as ‘N’, otherwise you can experience tearing. Main and Render will be set to F as well if you pass them into vrapi_SetPerfThreads (UE4 does this automatically).
Power Save ModePersonal Usefulness Rating: 0/5
Power Save Mode is a binary value that simply refers to whether Power Level is equal to SAVE, and therefore provides no additional value over Power Level.
Guardian GPU TimePersonal Usefulness Rating: 3/5
Similar to App GPU Time and TimeWarp GPU Time, we measure and report Guardian GPU Time. This will of course only have a value on Quest. There isn’t anything that you can do about this number, so it’s not terribly useful, other than to know that some time is being used by guardian.
CPU & GPU TimePersonal Usefulness Rating: 5/5
This is the total time your application took to render a frame. This is currently only available when using Unity or Unreal Engine, and measures from when your Render or RHI thread begins processing the frame until the GPU completes rendering. Subtracting App GPU Time from this can give you an approximate clock time for your Render thread, which can be useful if this is your bottleneck.
Layer CountPersonal Usefulness Rating: 3/5
The number of layers TimeWarp is rendering per frame. This includes system layers such as Guardian. This is good to keep track of, as there will be a direct correlation between this number and TimeWarp GPU Time. Keeping this number to a minimum will help avoid screen tearing.
Digging Deeper
While
OVR Metrics Tool and VrApi logs provide easy access to some of the key metrics needed to identify that you have a performance problem, it's often just the first step towards deeper profiling using tools like
RenderDoc and
SnapDragon Profiler. The major game engines also include excellent profilers for debugging CPU bottlenecks.
- Trevor Dasch