Lock render thread when surface is lost, wait for new surface. 16/32516/8
authorDavid Steele <david.steele@partner.samsung.com>
Thu, 18 Dec 2014 16:21:22 +0000 (16:21 +0000)
committerDavid Steele <david.steele@partner.samsung.com>
Wed, 11 Feb 2015 13:16:26 +0000 (05:16 -0800)
If PreRender returns false, e.g. when there is no surface (for whatever
reason), then lock the render thread to prevent any GL calls from
occurring. Wait for a NewSurface request from event thread.

Change-Id: Ib64efbc1dfb3c8c5beab84ea76b6be97bfd9dcab
Signed-off-by: David Steele <david.steele@partner.samsung.com>
adaptors/base/render-thread.cpp
adaptors/base/update-render-controller.cpp
adaptors/base/update-render-controller.h
adaptors/base/update-render-synchronization.cpp
adaptors/base/update-render-synchronization.h
adaptors/common/render-surface-impl.h

index e5f93b2..e477191 100644 (file)
@@ -166,12 +166,28 @@ bool RenderThread::Run()
     // Consume any pending events
     ConsumeEvents();
 
-    // Check if we've got any requests from the main thread (e.g. replace surface)
-    bool requestProcessed = ProcessRequest( request );
+    bool processRequests = true;
+    bool requestProcessed = false;
+    while( processRequests && running)
+    {
+      // Check if we've got any requests from the main thread (e.g. replace surface)
+      requestProcessed = ProcessRequest( request );
+
+      // perform any pre-render operations
+      DALI_LOG_INFO( gRenderLogFilter, Debug::Verbose, "RenderThread::Run. 3 - PreRender\n");
+      bool preRendered = PreRender(); // Returns false if no surface onto which to render
+      if( preRendered )
+      {
+        processRequests = false;
+      }
+      else
+      {
+        // Block until new surface... - cleared by ReplaceSurface code in UpdateRenderController
+        running = mUpdateRenderSync.RenderSyncWithRequest(request);
+      }
+    }
 
-    // perform any pre-render operations
-    DALI_LOG_INFO( gRenderLogFilter, Debug::Verbose, "RenderThread::Run. 3 - PreRender\n");
-    if( running && PreRender() == true)
+    if( running )
     {
        // Render
       DALI_LOG_INFO( gRenderLogFilter, Debug::Verbose, "RenderThread::Run. 4 - Core.Render()\n");
index b46ab47..ae773c5 100644 (file)
@@ -125,6 +125,18 @@ void UpdateRenderController::ReplaceSurface( RenderSurface* newSurface )
   mUpdateRenderSync->ReplaceSurface( newSurface );
 }
 
+void UpdateRenderController::NewSurface( RenderSurface* newSurface )
+{
+  // This API shouldn't be used when there is a current surface, but check anyway.
+  RenderSurface* currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
+  if( currentSurface )
+  {
+    currentSurface->StopRender();
+  }
+
+  mUpdateRenderSync->NewSurface( newSurface );
+}
+
 void UpdateRenderController::SetRenderRefreshRate(unsigned int numberOfVSyncsPerRender )
 {
   mNumberOfVSyncsPerRender = numberOfVSyncsPerRender;
index 2c9cd6f..64839ff 100644 (file)
@@ -96,6 +96,13 @@ public:
   void ReplaceSurface( RenderSurface* surface );
 
   /**
+   * Provides a new surface. Should be used if the old surface has been lost
+   * for any reason.
+   * @param surface new surface
+   */
+  void NewSurface( RenderSurface* surface );
+
+  /**
    * @copydoc Dali::Adaptor::SetRenderRefreshRate()
    */
   void SetRenderRefreshRate( unsigned int numberOfVSyncsPerRender );
index 7861f6a..a86df45 100644 (file)
@@ -87,6 +87,7 @@ void UpdateRenderSynchronization::Stop()
   mRenderFinishedCondition.notify_one();
   mVSyncSleepCondition.notify_one();
   mVSyncReceivedCondition.notify_one();
+  mRenderRequestSleepCondition.notify_one();
 
   mFrameTime.Suspend();
 }
@@ -151,7 +152,7 @@ bool UpdateRenderSynchronization::ReplaceSurface( RenderSurface* newSurface )
     mReplaceSurfaceRequest.SetSurface(newSurface);
     mReplaceSurfaceRequested = true;
 
-    mRequestFinishedCondition.wait(lock); // wait unlocks the mutex on entry, and locks again on exit.
+    mRenderRequestFinishedCondition.wait(lock); // wait unlocks the mutex on entry, and locks again on exit.
 
     mReplaceSurfaceRequested = false;
     result = mReplaceSurfaceRequest.GetReplaceCompleted();
@@ -160,6 +161,32 @@ bool UpdateRenderSynchronization::ReplaceSurface( RenderSurface* newSurface )
   return result;
 }
 
+bool UpdateRenderSynchronization::NewSurface( RenderSurface* newSurface )
+{
+  bool result=false;
+
+  UpdateRequested();
+  UpdateWhilePaused();
+  {
+    boost::unique_lock< boost::mutex > lock( mMutex );
+
+    mReplaceSurfaceRequest.SetSurface(newSurface);
+    mReplaceSurfaceRequested = true;
+
+    // Unlock the render thread sleeping on requests
+    mRenderRequestSleepCondition.notify_one();
+
+    // Lock event thread until request has been processed
+    mRenderRequestFinishedCondition.wait(lock);// wait unlocks the mutex on entry, and locks again on exit.
+
+    mReplaceSurfaceRequested = false;
+    result = mReplaceSurfaceRequest.GetReplaceCompleted();
+  }
+
+  return result;
+}
+
+
 void UpdateRenderSynchronization::UpdateReadyToRun()
 {
   bool wokenFromPause( false );
@@ -278,6 +305,22 @@ bool UpdateRenderSynchronization::UpdateTryToSleep()
   return mRunning;
 }
 
+bool UpdateRenderSynchronization::RenderSyncWithRequest(RenderRequest*& requestPtr)
+{
+  boost::unique_lock< boost::mutex > lock( mMutex );
+
+  // Wait for a replace surface request
+  mRenderRequestSleepCondition.wait(lock);
+
+  // write any new requests
+  if( mReplaceSurfaceRequested )
+  {
+    requestPtr = &mReplaceSurfaceRequest;
+  }
+  mReplaceSurfaceRequested = false;
+  return mRunning;
+}
+
 bool UpdateRenderSynchronization::RenderSyncWithUpdate(RenderRequest*& requestPtr)
 {
   boost::unique_lock< boost::mutex > lock( mMutex );
@@ -325,7 +368,7 @@ void UpdateRenderSynchronization::RenderFinished( bool updateRequired, bool requ
   if( requestProcessed )
   {
     // Notify the event thread that a request has completed
-    mRequestFinishedCondition.notify_one();
+    mRenderRequestFinishedCondition.notify_one();
   }
 
   AddPerformanceMarker( PerformanceInterface::RENDER_END );
index 4302052..68d4297 100644 (file)
@@ -111,12 +111,22 @@ public:
   void UpdateWhilePaused();
 
   /**
-   * Inform the render thread that there is a new surface.
+   * Inform the render thread that there is a new surface, and that
+   * it should replace the current surface.
    * @param[in] newSurface The new surface for rendering.
    */
   bool ReplaceSurface( RenderSurface* newSurface );
 
   /**
+   * Inform the render thread that there is a new surface. Should be used
+   * after SurfaceLost() has been used to inform RenderThread that the surface
+   * has gone.
+   *
+   * @param[in] newSurface The new surface for rendering.
+   */
+  bool NewSurface( RenderSurface* newSurface );
+
+  /**
    * Called by Update thread before it runs the update. This is the point where we can pause
    */
   void UpdateReadyToRun();
@@ -145,6 +155,13 @@ public:
   bool UpdateTryToSleep();
 
   /**
+   * Block the render thread whilst waiting for requests e.g. providing a new
+   * surface.
+   * @param[in] request Pointer to set if there are any requests
+   */
+  bool RenderSyncWithRequest(RenderRequest*& request );
+
+  /**
    * Called by the render-thread to wait for a buffer to read from and then render.
    * @pre Called by render thread only.
    * @param[in] request Pointer to set if there are any requests
@@ -256,7 +273,8 @@ private:
   boost::condition_variable mVSyncReceivedCondition;  ///< The render thread waits on this condition
   boost::condition_variable mVSyncSleepCondition;     ///< The vsync thread waits for this condition
   boost::condition_variable mPausedCondition;         ///< The controller waits for this condition while paused
-  boost::condition_variable mRequestFinishedCondition;///< The controller waits for this condition
+  boost::condition_variable mRenderRequestSleepCondition;   ///< The render thread waits for this condition
+  boost::condition_variable mRenderRequestFinishedCondition;///< The controller waits for this condition
 
   FrameTime mFrameTime;                               ///< Frame timer predicts next vsync time
   TriggerEventInterface& mNotificationTrigger;        ///< Reference to notification event trigger
index 921e56d..5fa8c02 100644 (file)
@@ -128,9 +128,11 @@ public: // API
 
   /**
    * Invoked by render thread before Core::Render
+   * If the operation fails, then Core::Render should not be called until there is
+   * a surface to render onto.
    * @param[in] egl The Egl interface
    * @param[in] glAbstraction OpenGLES abstraction interface
-   * @return True if the operation is successful
+   * @return True if the operation is successful, False if the operation failed
    */
   virtual bool PreRender( EglInterface& egl, Integration::GlAbstraction& glAbstraction ) = 0;