Fix the frame sync issue in COMBINED_UPDATE_RENDER mode 75/134275/4
authorRichard Huang <r.huang@samsung.com>
Thu, 15 Jun 2017 16:16:11 +0000 (17:16 +0100)
committerRichard Huang <r.huang@samsung.com>
Mon, 26 Jun 2017 16:33:30 +0000 (17:33 +0100)
There could be a small gap between the start time of the current frame and the
sleep-until time calculated in the previous frame. In addition, the update or
rendering in the current frame could take too much time so that the calculated
sleep-until time has already passed. In order to make the frames keep syncing
to the default frame duration (i.e. 16 ms) in these cases, we should keep tracking
any extra time taken by the current frame and shorten the duration of the next frame
accordingly.

Change-Id: Ic3dcbfbc5b8aded6514310ee17843839daf12f03

adaptors/base/combined-update-render/combined-update-render-controller.cpp
adaptors/base/combined-update-render/combined-update-render-controller.h

index d9a5b63..79891e1 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -376,8 +376,10 @@ void CombinedUpdateRenderController::UpdateRenderThread()
 
   bool useElapsedTime = true;
   bool updateRequired = true;
+  uint64_t timeToSleepUntil = 0;
+  int extraFramesDropped = 0;
 
-  while( UpdateRenderReady( useElapsedTime, updateRequired ) )
+  while( UpdateRenderReady( useElapsedTime, updateRequired, timeToSleepUntil ) )
   {
     LOG_UPDATE_RENDER_TRACE;
 
@@ -422,8 +424,8 @@ void CombinedUpdateRenderController::UpdateRenderThread()
     if( useElapsedTime )
     {
       // If using the elapsed time, then calculate frameDelta as a multiple of mDefaultFrameDelta
-      // Round up if remainder is more than half the default frame time
-      noOfFramesSinceLastUpdate = ( timeSinceLastFrame + mDefaultHalfFrameNanoseconds) / mDefaultFrameDurationNanoseconds;
+      noOfFramesSinceLastUpdate += extraFramesDropped;
+
       frameDelta = mDefaultFrameDelta * noOfFramesSinceLastUpdate;
     }
     LOG_UPDATE_RENDER( "timeSinceLastFrame(%llu) noOfFramesSinceLastUpdate(%u) frameDelta(%.6f)", timeSinceLastFrame, noOfFramesSinceLastUpdate, frameDelta );
@@ -478,8 +480,40 @@ void CombinedUpdateRenderController::UpdateRenderThread()
     // FRAME TIME
     //////////////////////////////
 
+    extraFramesDropped = 0;
+
+    if (timeToSleepUntil == 0)
+    {
+      // If this is the first frame after the thread is initialized or resumed, we
+      // use the actual time the current frame starts from to calculate the time to
+      // sleep until the next frame.
+      timeToSleepUntil = currentFrameStartTime + mDefaultFrameDurationNanoseconds;
+    }
+    else
+    {
+      // Otherwise, always use the sleep-until time calculated in the last frame to
+      // calculate the time to sleep until the next frame. In this way, if there is
+      // any time gap between the current frame and the next frame, or if update or
+      // rendering in the current frame takes too much time so that the specified
+      // sleep-until time has already passed, it will try to keep the frames syncing
+      // by shortening the duration of the next frame.
+      timeToSleepUntil += mDefaultFrameDurationNanoseconds;
+
+      // Check the current time at the end of the frame
+      uint64_t currentFrameEndTime = 0;
+      TimeService::GetNanoseconds( currentFrameEndTime );
+      while ( currentFrameEndTime > timeToSleepUntil + mDefaultFrameDurationNanoseconds )
+      {
+         // We are more than one frame behind already, so just drop the next frames
+         // until the sleep-until time is later than the current time so that we can
+         // catch up.
+         timeToSleepUntil += mDefaultFrameDurationNanoseconds;
+         extraFramesDropped++;
+      }
+    }
+
     // Sleep until at least the the default frame duration has elapsed. This will return immediately if the specified end-time has already passed.
-    TimeService::SleepUntil( currentFrameStartTime + mDefaultFrameDurationNanoseconds );
+    TimeService::SleepUntil( timeToSleepUntil );
   }
 
   // Inform core of context destruction & shutdown EGL
@@ -492,7 +526,7 @@ void CombinedUpdateRenderController::UpdateRenderThread()
   mEnvironmentOptions.UnInstallLogFunction();
 }
 
-bool CombinedUpdateRenderController::UpdateRenderReady( bool& useElapsedTime, bool updateRequired )
+bool CombinedUpdateRenderController::UpdateRenderReady( bool& useElapsedTime, bool updateRequired, uint64_t& timeToSleepUntil )
 {
   useElapsedTime = true;
 
@@ -507,6 +541,11 @@ bool CombinedUpdateRenderController::UpdateRenderReady( bool& useElapsedTime, bo
     LOG_UPDATE_RENDER( "      mDestroyUpdateRenderThread:  %d", mDestroyUpdateRenderThread );
     LOG_UPDATE_RENDER( "      mNewSurface:                 %d", mNewSurface );
 
+    // Reset the time when the thread is waiting, so the sleep-until time for
+    // the first frame after resuming should be based on the actual start time
+    // of the first frame.
+    timeToSleepUntil = 0;
+
     mUpdateRenderThreadWaitCondition.Wait( updateLock );
 
     if( ! mUseElapsedTimeAfterWait )
index 9179c6b..8cde53e 100644 (file)
@@ -192,12 +192,13 @@ private:
   /**
    * Called by the Update/Render Thread which ensures a wait if required.
    *
-   * @param[out] useElapsedTime  If true when returned, then the actual elapsed time will be used for animation.
-   *                             If false when returned, then there should NOT be any animation progression in the next Update.
-   * @param[in]  updateRequired  Whether another update is required.
+   * @param[out] useElapsedTime    If true when returned, then the actual elapsed time will be used for animation.
+   *                               If false when returned, then there should NOT be any animation progression in the next Update.
+   * @param[in]  updateRequired    Whether another update is required.
+   * @param[out] timeToSleepUntil  The time in nanoseconds to put the thread to sleep until.
    * @return false, if the thread should stop.
    */
-  bool UpdateRenderReady( bool& useElapsedTime, bool updateRequired );
+  bool UpdateRenderReady( bool& useElapsedTime, bool updateRequired, uint64_t& timeToSleepUntil );
 
   /**
    * Checks to see if the surface needs to be replaced.