Enable Phase Sync for Native Mobile Apps

Phase Sync is a frame timing management technique used to manage latency adaptively. It is available for native Oculus Quest and Oculus Quest 2 apps as an option in the latest Mobile SDK release.

More About Phase Sync

Phase Sync provides Oculus Quest and Quest 2 apps an alternative to the traditional fixed-latency mode to manage frame timing. Fixed-latency mode means frames are composited as early as possible to avoid the missing the current frame and needing to reuse a stale frame. Stale frames can negatively impact the user experience.

In contrast to fixed-latency, Phase Sync handles frame timing adaptively according to the app’s workload. The goal of Phase Sync is to have a frame finish rendering just before the compositor needs the completed frame. This reduces rendering latency without missing frames.

The adaptive frame timing offered by Phase Sync should be enabled for apps that target both Quest and Quest 2. Note that Quest 2 has more CPU and GPU resources than Quest and can render frames too early, increasing latency, and Phase Sync helps reduce this latency.

The following image shows the difference between a fixed latency and when Phase Sync is enabled for a typical multi-threaded VR app.

Chart comparing Phase Sync to Fixed Latency

Note the following when Phase Sync is enabled:

  • There is no additional performance overhead.
  • If the app’s workload fluctuates violently or spikes frequently, Phase Sync may cause more stale frames than when Phase Sync is not enabled.
  • Late-Latching and Phase Sync typically complement each other.
  • If extra latency mode and Phase Sync are both enabled, extra latency mode will be ignored.

Ensure the Frame Loop is Correct

To enable Phase Sync, call vrapi_WaitFrame and vrapi_BeginFrame each frame.

  • vrapi_WaitFrame marks a new frame’s start and automatically adjusts the frame start time to reduce pose prediction latency. In doing so, vrapi_WaitFrame may block the application’s calling thread.
    • For multi-threaded applications, vrapi_WaitFrame must be called from the main (simulation) thread.
    • For single-threaded applications, vrapi_WaitFrame may be called from the render thread before calling vrapi_BeginFrame.
  • vrapi_BeginFrame is called prior to the start of frame rendering and marks a new render frame’s start. Applications must guarantee that vrapi_BeginFrame is only called after a corresponding call to vrapi_WaitFrame has completed. vrapi_BeginFrame should be called from the same thread as vrapi_SubmitFrame2.
  • Correspondingly, vrapi_SubmitFrame2 should be called from the same thread as vrapi_BeginFrame (render thread) as it indicates the render frame’s completion.

The following image shows the required frame API ordering for multi-threaded apps:

Frame API order for multi-threaded apps

The following image shows the required frame API ordering for single-threaded apps:

Frame API order for multi-threaded apps

Note: It is the app developer’s responsibility to do external synchronization and make sure these APIs are being called from the proper thread and with the correct order. Otherwise, the behavior is undefined.

Enable Phase Sync

After the correct frame API ordering is in place, Phase Sync can be successfully enabled. To do so, the app needs to set the VrApi mode flag VRAPI_MODE_FLAG_PHASE_SYNC to enable Phase Sync explicitly, as in this example:

ovrModeParms parms = vrapi_DefaultModeParms(&app->Java);
// Enable Phase Sync
parms.Flags |= VRAPI_MODE_FLAG_PHASE_SYNC;
app->Ovr = vrapi_EnterVrMode(&parms);

Testing Phase Sync

After enabling Phase Sync in your app, you can verify that it is active and see how much latency it has saved by examining the logcat logs.

adb logcat -s VrApi

Logcat output when Phase Sync is active

  • If Phase Sync is not active, the Lat value is Lat=0 or Lat=1, indicating extra latency mode.
  • If Phase Sync is active, the Lat value is Lat=-1, indicating the latency is managed dynamically.

The Prd value indicates the render latency as measured by the runtime. To calculate how much latency Phase Sync has saved, compare the Prd values between when Phase Sync is active and not active. For example, if the Prd with Phase Sync is 35ms and the Prd without is 45ms, you saved 10ms of latency with Phase Sync.

To more easily compare performance with and without Phase Sync, you can turn it on and off with an adb shell setprop. You must restart the app after changing the setprop for the change to have effect.

  • Turn off: adb shell setprop debug.oculus.phaseSync 0
  • Turn on: adb shell setprop debug.oculus.phaseSync 1

Troubleshooting

If you don’t see Lat=-1 after enabling Phase Sync, it is most likely you didn’t call vrapi_WaitFrame, vrapi_BeginFrame, and vrapi_SubmitFrame in the proper sequence. For example, if vrapi_WaitFrame was missed in a certain frame, the runtime will turn off Phase Sync automatically in this app session, and logcat will print something like the following:

VrApi : Phase sync mode isn't allowed on app [your package name]  since WaitFrame / BeginFrame APIs weren't invoked explicitly

Further Learning