Fixed Foveated Rendering (FFR)

Oculus Quest supports dynamic Fixed Foveated Rendering (FFR), where the level of eye texture foveation is automatically adjusted based on GPU utilization.

FFR renders the edges of your eye textures at a lower resolution than the center. The effect lowers the fidelity of the scene in the viewer’s peripheral vision and is nearly imperceptible. This reduces the GPU load due to the reduction in pixel shading requirements. In addition, apps using FFR can increase the resolution of the eye texture, improving the image shown in the headset. Complex fragment shaders also benefit from this form of multi-resolution rendering.

With dynamic FFR, the foveation level is adjusted automatically based on the GPU utilization. Non-dynamic FFR is also available, where the foveation level must be adjusted manually, but dynamic FFR is the recommended way to use the feature most effectively, especially now that content will run on 2 headsets (Quest and Quest2) with different GPU performance baselines.

Note that unlike some other forms of foveation technologies, Oculus Quest’s fixed foveation system is not based on eye tracking. The highest-resolution pixels are “fixed” in the center of the eye texture.

A detailed look at the benefits of using FFR can be found in our Optimizing Oculus Go for Performance blog post.

How to Implement Dynamic Foveation

Dynamic foveation adjustment is the recommended way to use FFR. Dynamic FFR can be enabled with:

vrapi_SetPropertyInt( &Java, VRAPI_DYNAMIC_FOVEATION_ENABLED, true );

Dynamic foveation is disabled by default. When dynamic foveation is enabled, the foveation level will be adjusted automatically with a maximum level set to VRAPI_FOVEATION_LEVEL. To set this, call the following:

vrapi_SetPropertyInt( &Java, VRAPI_FOVEATION_LEVEL, level );

When calling, level can be 0, 1, 2, 3, or 4. Foveation levels 3 (high) and level 4 (high top) are treated as the same level under dynamic foveation. Which of these two levels is chosen coincides with the VRAPI_FOVEATION_LEVEL selection. For example, if VRAPI_FOVEATION_LEVEL is set to level 4, dynamic foveation will vary between levels 0, 1, 2, and 4 exclusively.

Because dynamic foveation uses GPU utilization to increase or decrease foveation level, it’s fairly reactive and the level should increase before a stale frame is created in comparison to a fixed FFR level, but if increased stale frames are noticed in specific levels, dynamic FFR can be enabled and disabled in those areas. Use of dynamic FFR is highly recommended.

If developers with a dynamic FFR app want to visualize what their app looks like to see if their FFR level is too high, they can use the following setprop command:

setprop debug.oculus.foveation.dynamic 0

This command will instantly disable dynamic FFR, and the foveation level will instantly go to VRAPI_FOVEATION_LEVEL without the need to recompile a non-dynamic FFR version of the app.

How to Imprelement Non-Dynamic Fixed Foveated Rendering

First, if you want to check if the device supports foveated rendering. Check the system property VRAPI_SYS_PROP_FOVEATION_AVAILABLE, which will return VRAPI_TRUE if foveated rendering is supported.

Then, to use FFR, call the following to set the degree of foveation:

vrapi_SetPropertyInt( &Java, VRAPI_FOVEATION_LEVEL, level );

When calling, level can be 0, 1, 2, 3, or 4.

Foveation Example Images

In the following images, the resolution in the center white areas is native: every pixel of the texture will be computed independently by the GPU. However, in the red areas, only 1/2 of the pixels will be calculated, 1/4 for the green areas, 1/8 for the blue areas, and 1/16 for the magenta tiles. The missing pixels will be interpolated from the calculated pixels at resolve time, when the GPU stores the result of its computation in general memory.

Low FFR (1)

Medium FFR (2)

High FFR (3)

High Top FFR (4)

You may choose to change the degree of foveation based on the scene elements. Apps or scenes with high pixel shader costs will see the most benefit from using FFR. Apps with very simple shaders may see a net performance loss from the overhead of using FFR. Proper implementation of FFR requires testing and tuning to balance visual quality and GPU performance.

Tips and Best Practices

Many developers simply use the high FFR setting for their entire app as a general solution for performance, which some have found to have a very noticeable impact on visuals. Here are some tips and best practices on better tuning the FFR settings in-game:

  • FFR levels can be changed on a per-frame basis and should be changed according to the content being displayed. In cases where the player may need to look to the sides of the field of view more often, if high frequency content such as text is introduced, or if performance requirements change, the FFR level should be changed to match the situation. Starting a new level, opening/closing menus, and entering new map areas are generally good points to consider changing the FFR setting. However, avoid changing FFR levels frequently within the same scene without another transition as the jump between FFR levels can be fairly noticeable.
    • A simple, effective example would be to turn off FFR on menu screens, where there is performance headroom to spare and a lot of text elements, and then turning it on after loading into the game where the performance is needed.
    • Another example would be to set FFR to medium or low on an outdoor scene, changing to high when entering a darker cave or tunnel level, and then switching back to medium when changing to an outdoor level again.
  • Foveation is more apparent and noticeable in bright/high contrast scenes, and with higher frequency content such as text. In darker/low contrast scenes, the high foveation setting may result in reasonable visual quality, while that may not be the case in a brighter/higher contrast scene. This can play into what foveation level can be used for certain scenes in a game, and should be used in conjunction with the ability to change FFR levels per-frame. Medium should be a suitable FFR level in most cases, but low is a good option if there is performance to spare. High and high top should be reserved for cases where the extra performance is really needed.
  • The system property debug.oculus.foveation.level is a system-wide FFR setting override that can be used to quickly test different FFR settings without changing/reinstalling/restarting the app, with 0 = Off, 1 = Low, 2 = Medium, 3 = High, 4 = High Top (for example, adb shell setprop debug.oculus.foveation.level 2 will set the FFR level to medium).
  • Use the VrApi logcat outputs to determine suitable FFR levels. VrApi generates performance information in logcat every second, including FFR level, GPU rendering times, and average FPS. The sample log FPS=72,Prd=45ms,Tear=0,Early=67,Stale=0,VSnc=1,Lat=1,Fov=3,CPU4/GPU=2/2,1651/414MHz,OC=FF,TA=0/0/0,SP=N/N/N,Mem=1017MHz,Free=1576MB,PSM=0,PLS=0,Temp=27.0C/0.0C,TW=2.25ms,App=5.51ms,GD=0.77ms shows an app running at 72 fps (FPS=72), with the app’s GPU rendering time at 5.51ms (App=5.51ms), and FFR on high (Fov=3). Regardless of visuals, in this case there is more than enough room to turn the FFR level down for a general improvement in the visual quality.