Fixed Foveated Rendering (FFR) (Deprecated)
Updated: Oct 18, 2024
Mobile SDK Deprecation
As of August 31, 2022, Mobile SDK and the VrApi library are no longer supported. Future updates will be delivered through OpenXR extensions and our OpenXR Mobile SDK, not through any new updates to Meta Mobile or PC APIs. - New apps must use OpenXR unless a waiver is granted.
- New apps will not have access to Meta Native Mobile APIs, but existing apps can continue using them.
- No assistance will be provided for creating new apps with Meta Native APIs. You will find recommendations for migrating existing apps to OpenXR in the developer guides.
- Only critical security, privacy, or safety issues in Meta Native APIs will be addressed.
- Any testing of Meta Native Mobile will be restricted to automated QA tests, only to ensure core features remain functional.
This guide covers how to implement Fixed Foveated Rendering (FFR) in your Native VRAPI application. To learn how FFR works, and how to debug it, go here. 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 Implement 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.
To improve visual quality of foveated rendering, we recommend apps to enable subsampled layout. This will enable bilinear upsampling in low-res areas, resulting in a smoother image and eliminating any pixelated artifacts in your peripheral vision (see example image below). Check the
GL and
Vulkan specifications for more information on subsampled layout.

To enable subsampled layout, simply include the VRAPI_SWAPCHAIN_CREATE_SUBSAMPLED_BIT
create flags when creating the color texture swapchain:
ovrSwapChainCreateInfo swapChainCreateInfo = {
.Format = colorFormat,
.Width = width,
.Height = height,
.Levels = 1,
.FaceCount = 1,
.ArraySize = 2,
.BufferCount = 3,
.CreateFlags = VRAPI_SWAPCHAIN_CREATE_SUBSAMPLED_BIT, // make sure the swapchain is created with subsampled bit!
.UsageFlags = VRAPI_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT,
};
colorTextureSwapChain = vrapi_CreateTextureSwapChain4(&swapChainCreateInfo);