Removed mutex locking in render thread. 66/27266/12
authorDavid Steele <david.steele@partner.samsung.com>
Tue, 9 Sep 2014 12:28:30 +0000 (13:28 +0100)
committerDavid Steele <david.steele@partner.samsung.com>
Wed, 5 Nov 2014 11:40:15 +0000 (11:40 +0000)
Communication is now through the UpdateRenderSync object, which has a single mutex.

[Problem]
Need to add new interface onto render thread without using messages.
Current mechanism for ReplaceSurface API uses a mutex lock around a
double buffered data store.

Adding new interface using this method with a different mutex around a
different data-store could easily lead to deadlock.

[Solution]
Remove all mutexs from within RenderThread. There are 2 communication
points in RenderThread from UpdateRenderSync: RenderSyncWithUpdate(),
which triggers the start of the render, and RenderFinished(), which
informs that the render has finished.

Utilize RenderSyncWithUpdate to pass requests from UpdateRenderSync
into render thread. Any responses should be written back into these
requests.

Modify UpdateRenderController to talk with UpdateRenderSync rather
than directly to RenderThread.  Event requests will block until the
next render pass, it is UpdateRenderSync's responsibility to ensure
that the update/render threads are awake and available to process.

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

index 35566bf..fdf3509 100644 (file)
@@ -42,6 +42,45 @@ Integration::Log::Filter* gRenderLogFilter = Integration::Log::Filter::New(Debug
 #endif
 }
 
+
+RenderRequest::RenderRequest(RenderRequest::Request type)
+: mRequestType(type)
+{
+}
+
+RenderRequest::Request RenderRequest::GetType()
+{
+  return mRequestType;
+}
+
+ReplaceSurfaceRequest::ReplaceSurfaceRequest()
+: RenderRequest(RenderRequest::REPLACE_SURFACE),
+  mNewSurface( NULL ),
+  mReplaceCompleted(false)
+{
+}
+
+void ReplaceSurfaceRequest::SetSurface(RenderSurface* newSurface)
+{
+  mNewSurface = newSurface;
+}
+
+RenderSurface* ReplaceSurfaceRequest::GetSurface()
+{
+  return mNewSurface;
+}
+
+void ReplaceSurfaceRequest::ReplaceCompleted()
+{
+  mReplaceCompleted = true;
+}
+
+bool ReplaceSurfaceRequest::GetReplaceCompleted()
+{
+  return mReplaceCompleted != 0u;
+}
+
+
 RenderThread::RenderThread( UpdateRenderSynchronization& sync,
                             AdaptorInternalServices& adaptorInterfaces,
                             const EnvironmentOptions& environmentOptions )
@@ -51,13 +90,11 @@ RenderThread::RenderThread( UpdateRenderSynchronization& sync,
   mEglFactory( &adaptorInterfaces.GetEGLFactoryInterface()),
   mEGL( NULL ),
   mThread( NULL ),
-  mSurfaceReplacing( false ),
-  mNewDataAvailable( false ),
-  mSurfaceReplaceCompleted( false ),
-  mEnvironmentOptions( environmentOptions )
+  mEnvironmentOptions( environmentOptions ),
+  mSurfaceReplaced(false)
 {
   // set the initial values before render thread starts
-  mCurrent.surface = adaptorInterfaces.GetRenderSurfaceInterface();
+  mSurface = adaptorInterfaces.GetRenderSurfaceInterface();
 }
 
 RenderThread::~RenderThread()
@@ -76,7 +113,7 @@ void RenderThread::Start()
   // create the render thread, initially we are rendering
   mThread = new boost::thread(boost::bind(&RenderThread::Run, this));
 
-  mCurrent.surface->StartRender();
+  mSurface->StartRender();
 }
 
 void RenderThread::Stop()
@@ -87,7 +124,7 @@ void RenderThread::Stop()
   if( mThread )
   {
     // Tell surface we have stopped rendering
-    mCurrent.surface->StopRender();
+    mSurface->StopRender();
 
     // wait for the thread to finish
     mThread->join();
@@ -97,44 +134,6 @@ void RenderThread::Stop()
   }
 }
 
-void RenderThread::ReplaceSurface( RenderSurface* surface )
-{
-  DALI_LOG_INFO( gRenderLogFilter, Debug::Verbose, "RenderThread::ReplaceSurface()\n");
-
-  // Make sure it's a new surface. Note! we are reading the current value of render thread here, but reading is ok
-  DALI_ASSERT_ALWAYS( surface != mCurrent.surface && "Trying to replace surface with itself" );
-
-  // lock and set to false
-  {
-    boost::unique_lock<boost::mutex> lock( mSurfaceChangedMutex );
-    mSurfaceReplaceCompleted = false;
-  }
-
-  // lock cache and set update flag at the end of function
-  {
-    SendMessageGuard msg( *this );
-    // set new values to cache
-    mNewValues.replaceSurface = true;
-    mNewValues.surface = surface;
-  }
-
-  // Ensure the current surface releases any locks.
-  mCurrent.surface->StopRender();
-}
-
-void RenderThread::WaitForSurfaceReplaceComplete()
-{
-  DALI_LOG_INFO( gRenderLogFilter, Debug::Verbose, "RenderThread::WaitForSurfaceReplaceComplete()\n");
-
-  boost::unique_lock<boost::mutex> lock( mSurfaceChangedMutex );
-
-  // if already completed no need to wait
-  while( !mSurfaceReplaceCompleted )
-  {
-    mSurfaceChangedNotify.wait( lock ); // Block the main thread here and releases mSurfaceChangedMutex so the render-thread can notify us
-  }
-}
-
 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 // The following methods are all executed inside render thread !!!
@@ -150,9 +149,6 @@ bool RenderThread::Run()
 
   bool running( true );
 
-  // Wait for first update
-  mUpdateRenderSync.RenderSyncWithUpdate();
-
   Dali::Integration::RenderStatus renderStatus;
 
   uint64_t currentTime( 0 );
@@ -160,49 +156,42 @@ bool RenderThread::Run()
   // render loop, we stay inside here when rendering
   while( running )
   {
-    DALI_LOG_INFO( gRenderLogFilter, Debug::Verbose, "RenderThread::Run. 1 - Begin loop\n");
+    // Sync with update thread and get any outstanding requests from UpdateRenderSynchronization
+    DALI_LOG_INFO( gRenderLogFilter, Debug::Verbose, "RenderThread::Run. 1 - RenderSyncWithUpdate()\n");
+    RenderRequest* request = NULL;
+    running = mUpdateRenderSync.RenderSyncWithUpdate( request );
+
+    DALI_LOG_INFO( gRenderLogFilter, Debug::Verbose, "RenderThread::Run. 2 - Process requests\n");
 
     // Consume any pending events
     ConsumeEvents();
 
-    // Check if we've got updates from the main thread
-    CheckForUpdates();
+    // Check if we've got any requests from the main thread (e.g. replace surface)
+    bool requestProcessed = ProcessRequest( request );
 
     // perform any pre-render operations
-    DALI_LOG_INFO( gRenderLogFilter, Debug::Verbose, "RenderThread::Run. 2 - PreRender\n");
-    if(PreRender() == true)
+    DALI_LOG_INFO( gRenderLogFilter, Debug::Verbose, "RenderThread::Run. 3 - PreRender\n");
+    if( running && PreRender() == true)
     {
        // Render
-      DALI_LOG_INFO( gRenderLogFilter, Debug::Verbose, "RenderThread::Run. 3 - Core.Render()\n");
+      DALI_LOG_INFO( gRenderLogFilter, Debug::Verbose, "RenderThread::Run. 4 - Core.Render()\n");
       mCore.Render( renderStatus );
 
       // Notify the update-thread that a render has completed
-      DALI_LOG_INFO( gRenderLogFilter, Debug::Verbose, "RenderThread::Run. 4 - Sync.RenderFinished()\n");
-      mUpdateRenderSync.RenderFinished( renderStatus.NeedsUpdate() );
+      DALI_LOG_INFO( gRenderLogFilter, Debug::Verbose, "RenderThread::Run. 5 - Sync.RenderFinished()\n");
+      mUpdateRenderSync.RenderFinished( renderStatus.NeedsUpdate(), requestProcessed );
 
       uint64_t newTime( mUpdateRenderSync.GetTimeMicroseconds() );
 
       // perform any post-render operations
       if ( renderStatus.HasRendered() )
       {
-        DALI_LOG_INFO( gRenderLogFilter, Debug::Verbose, "RenderThread::Run. 5 - PostRender()\n");
+        DALI_LOG_INFO( gRenderLogFilter, Debug::Verbose, "RenderThread::Run. 6 - PostRender()\n");
         PostRender( static_cast< unsigned int >(newTime - currentTime) );
       }
 
-      if(mSurfaceReplacing)
-      {
-        // Notify main thread that surface was changed so it can release the old one
-        NotifySurfaceChangeCompleted();
-        mSurfaceReplacing = false;
-      }
-
       currentTime = newTime;
     }
-
-    DALI_LOG_INFO( gRenderLogFilter, Debug::Verbose, "RenderThread::Run. 6 - RenderSyncWithUpdate()\n");
-
-    // Wait until another frame has been updated
-    running = mUpdateRenderSync.RenderSyncWithUpdate();
   }
 
   // shut down egl
@@ -218,16 +207,16 @@ void RenderThread::InitializeEgl()
 {
   mEGL = mEglFactory->Create();
 
-  DALI_ASSERT_ALWAYS( mCurrent.surface && "NULL surface" );
+  DALI_ASSERT_ALWAYS( mSurface && "NULL surface" );
 
   // initialize egl & OpenGL
-  mCurrent.surface->InitializeEgl( *mEGL );
+  mSurface->InitializeEgl( *mEGL );
 
   // create the OpenGL context
   mEGL->CreateContext();
 
   // create the OpenGL surface
-  mCurrent.surface->CreateEglSurface( *mEGL );
+  mSurface->CreateEglSurface( *mEGL );
 
   // Make it current
   mEGL->MakeContextCurrent();
@@ -243,39 +232,39 @@ void RenderThread::InitializeEgl()
 void RenderThread::ConsumeEvents()
 {
   // tell surface to consume any events to avoid memory leaks
-  mCurrent.surface->ConsumeEvents();
+  mSurface->ConsumeEvents();
 }
 
-void RenderThread::CheckForUpdates()
+bool RenderThread::ProcessRequest( RenderRequest* request )
 {
-  // atomic check to see if we've got updates, resets the flag int
-  if( __sync_fetch_and_and( &mNewDataAvailable, 0 ) )
+  bool processedRequest = false;
+
+  if( request != NULL )
   {
-    // scope for lock
-    // NOTE! This block is the only place in render side where mNewValues can be used inside render thread !!!
+    switch(request->GetType())
     {
-      // need to lock to access new values
-      boost::unique_lock< boost::mutex > lock( mThreadDataLock );
-
-      // check if the surface needs replacing
-      if( mNewValues.replaceSurface )
+      case RenderRequest::REPLACE_SURFACE:
       {
-        mNewValues.replaceSurface = false; // reset the flag
         // change the surface
-        ChangeSurface( mNewValues.surface );
-        mNewValues.surface = NULL;
+        ReplaceSurfaceRequest* replaceSurfaceRequest = static_cast<ReplaceSurfaceRequest*>(request);
+        ReplaceSurface( replaceSurfaceRequest->GetSurface() );
+        replaceSurfaceRequest->ReplaceCompleted();
+        processedRequest = true;
+        break;
       }
     }
   }
+  return processedRequest;
 }
 
-void RenderThread::ChangeSurface( RenderSurface* newSurface )
+void RenderThread::ReplaceSurface( RenderSurface* newSurface )
 {
   // This is designed for replacing pixmap surfaces, but should work for window as well
   // we need to delete the egl surface and renderable (pixmap / window)
   // Then create a new pixmap/window and new egl surface
   // If the new surface has a different display connection, then the context will be lost
-  DALI_ASSERT_ALWAYS( newSurface && "NULL surface" )
+  DALI_ASSERT_ALWAYS( newSurface && "NULL surface" );
+
   bool contextLost = newSurface->ReplaceEGLSurface( *mEGL );
 
   if( contextLost )
@@ -288,24 +277,13 @@ void RenderThread::ChangeSurface( RenderSurface* newSurface )
   // if both new and old surface are using the same display, and the display
   // connection was created by Dali, then transfer
   // display owner ship to the new surface.
-  mCurrent.surface->TransferDisplayOwner( *newSurface );
+  mSurface->TransferDisplayOwner( *newSurface );
 
   // use the new surface from now on
-  mCurrent.surface = newSurface;
-
-  // after rendering, NotifySurfaceChangeCompleted will be called
-  mSurfaceReplacing = true;
+  mSurface = newSurface;
+  mSurfaceReplaced = true;
 }
 
-void RenderThread::NotifySurfaceChangeCompleted()
-{
-  {
-    boost::unique_lock< boost::mutex > lock( mSurfaceChangedMutex );
-    mSurfaceReplaceCompleted = true;
-  }
-  // notify main thread
-  mSurfaceChangedNotify.notify_all();
-}
 
 void RenderThread::ShutdownEgl()
 {
@@ -313,7 +291,7 @@ void RenderThread::ShutdownEgl()
   mCore.ContextToBeDestroyed();
 
   // give a chance to destroy the OpenGL surface that created externally
-  mCurrent.surface->DestroyEglSurface( *mEGL );
+  mSurface->DestroyEglSurface( *mEGL );
 
   // delete the GL context / egl surface
   mEGL->TerminateGles();
@@ -321,7 +299,7 @@ void RenderThread::ShutdownEgl()
 
 bool RenderThread::PreRender()
 {
-  bool success = mCurrent.surface->PreRender( *mEGL, mGLES );
+  bool success = mSurface->PreRender( *mEGL, mGLES );
   if( success )
   {
     mGLES.PreRender();
@@ -335,9 +313,11 @@ void RenderThread::PostRender( unsigned int timeDelta )
   mGLES.PostRender(timeDelta);
 
   // Inform the surface that rendering this frame has finished.
-  mCurrent.surface->PostRender( *mEGL, mGLES, timeDelta, mSurfaceReplacing );
+  mSurface->PostRender( *mEGL, mGLES, timeDelta, mSurfaceReplaced );
+  mSurfaceReplaced = false;
 }
 
+
 } // namespace Adaptor
 
 } // namespace Internal
index ff1a4a6..55bea30 100644 (file)
@@ -46,12 +46,73 @@ class UpdateRenderSynchronization;
 class EglFactoryInterface;
 class EnvironmentOptions;
 
+
+class RenderRequest
+{
+public:
+  enum Request
+  {
+    REPLACE_SURFACE, // Request to replace surface
+  };
+
+  /**
+   * Constructor.
+   * @param[in] type The type of the request
+   */
+  RenderRequest( Request type );
+
+  /**
+   * @return the type of the request
+   */
+  Request GetType();
+
+private:
+  Request mRequestType;
+};
+
+class ReplaceSurfaceRequest : public RenderRequest
+{
+public:
+
+  /**
+   * Constructor
+   */
+  ReplaceSurfaceRequest();
+
+  /**
+   * Set the new surface
+   * @param[in] newSurface The new surface to use
+   */
+  void SetSurface(RenderSurface* newSurface);
+
+  /**
+   * @return the new surface
+   */
+  RenderSurface* GetSurface();
+
+  /**
+   * Called when the request has been completed to set the result.
+   */
+  void ReplaceCompleted();
+
+  /**
+   * @return true if the replace has completed.
+   */
+  bool GetReplaceCompleted();
+
+private:
+  RenderSurface* mNewSurface;     ///< The new surface to use.
+  unsigned int mReplaceCompleted; ///< Set to true when the replace has completed.
+};
+
+
 /**
  * The render-thread is responsible for calling Core::Render() after each update.
  */
 class RenderThread
 {
 public:
+
   /**
    * Create the render-thread; this will not do anything until Start() is called.
    * @param[in] sync update-render synchronization object
@@ -81,19 +142,6 @@ public:
   void Stop();
 
   /**
-   * Replaces the rendering surface. This method returns immediately
-   * You can call WaitForSurfaceReplaceComplete to block until the
-   * replace is completed in render thread. Note, you need to make sure
-   * that render thread is actually running!!!
-   */
-  void ReplaceSurface( RenderSurface* surface );
-
-  /**
-   * Blocks until surface replace has been completed
-   */
-  void WaitForSurfaceReplaceComplete();
-
-  /**
    * Offscreen was posted to onscreen
    */
   void RenderSync();
@@ -120,23 +168,19 @@ private: // Render thread side helpers
   void ConsumeEvents();
 
   /**
-   * Check if main thread posted updates
+   * Check if main thread made any requests, e.g. ReplaceSurface
    * Called from render thread
+   * @return true if a request was processed, false otherwise.
    */
-  void CheckForUpdates();
+  bool ProcessRequest(RenderRequest* request);
 
   /**
-   * Changes the rendering surface
+   * Replaces the rendering surface
    * Used for replacing pixmaps due to resizing
    * Called from render thread
    * @param newSurface to use
    */
-  void ChangeSurface( RenderSurface* newSurface );
-
-  /**
-   * Notify the main thread that surface has really been changed
-   */
-  void NotifySurfaceChangeCompleted();
+  void ReplaceSurface( RenderSurface* newSurface );
 
   /**
    * Shuts down EGL.
@@ -160,73 +204,16 @@ private: // Render thread side helpers
 
 private: // Data
 
-  UpdateRenderSynchronization&        mUpdateRenderSync; ///< Used to synchronize the update & render threads
-  Dali::Integration::Core&            mCore;             ///< Dali core reference
-  Integration::GlAbstraction&         mGLES;             ///< GL abstraction reference
-  EglFactoryInterface*                mEglFactory;       ///< Factory class to create EGL implementation
-  EglInterface*                       mEGL;              ///< Interface to EGL implementation
-
-  boost::thread*                      mThread;           ///< render thread
-  bool                                mUsingPixmap;      ///< whether we're using a pixmap or a window
-  bool                                mSurfaceReplacing; ///< whether the surface is replacing. If true, need to notify surface changing after rendering
-
-  /**
-   * Structure to hold values that are set by main thread and read in render thread
-   * There are two copies of this data to avoid locking and prevent concurrent access
-   */
-  struct RenderData
-  {
-    /**
-     * Default constructor to reset values
-     */
-    RenderData()
-    : replaceSurface( false ),
-      surface( NULL )
-    {
-    }
-
-    volatile int                replaceSurface; ///< whether the surface needs replacing
-    RenderSurface*              surface;        ///< Current surface
-  };
-
-  RenderData                mCurrent;             ///< Current values, must not be used from main thread
-  RenderData                mNewValues;           ///< New values, sent from main thread to render thread
-  boost::mutex              mThreadDataLock;      ///< mutex to lock values while reading them into render thread
-  volatile int              mNewDataAvailable;    ///< atomic flag to notify the render thread that there's new data
-
-  /**
-   * Helper class for sending message to render thread
-   */
-  class SendMessageGuard
-  {
-  public: // API
-    /**
-     * Constructor, sets the lock
-     */
-    SendMessageGuard( RenderThread& parent )
-    : mLock( parent.mThreadDataLock ), mFlag( &parent.mNewDataAvailable )
-    { // Nothing to do, unique lock will lock automatically and unlock when destructed
-    }
-    /**
-     * Destructor, releases lock and sets flag
-     */
-    ~SendMessageGuard()
-    {
-      // set the flag to tell render thread there are new values, ignoring the return value here
-      (void)__sync_or_and_fetch( mFlag, 1 );
-    }
-
-  private: // Data
-    boost::unique_lock< boost::mutex > mLock;
-    volatile int* mFlag;
-  };
-
-  // sync for waiting for surface change
-  boost::mutex              mSurfaceChangedMutex;  ///< mutex to lock during surface replacing
-  boost::condition_variable mSurfaceChangedNotify; ///< condition to notify main thread that surface has been changed
-  bool                      mSurfaceReplaceCompleted;///< true, while render thread is running and needs to wait for pixmap syncs
-  const EnvironmentOptions& mEnvironmentOptions;     ///< Environment options
-
+  UpdateRenderSynchronization&  mUpdateRenderSync;       ///< Used to synchronize the update & render threads
+  Dali::Integration::Core&      mCore;                   ///< Dali core reference
+  Integration::GlAbstraction&   mGLES;                   ///< GL abstraction reference
+  EglFactoryInterface*          mEglFactory;             ///< Factory class to create EGL implementation
+  EglInterface*                 mEGL;                    ///< Interface to EGL implementation
+  boost::thread*                mThread;                 ///< render thread
+  bool                          mUsingPixmap;            ///< whether we're using a pixmap or a window
+  RenderSurface*                mSurface;                ///< Current surface
+  const EnvironmentOptions&     mEnvironmentOptions;     ///< Environment options
+  bool                          mSurfaceReplaced;        ///< True when new surface has been initialzed.
 };
 
 } // namespace Adaptor
index 4d0ef66..b46ab47 100644 (file)
@@ -38,7 +38,8 @@ namespace Adaptor
 
 UpdateRenderController::UpdateRenderController( AdaptorInternalServices& adaptorInterfaces,
                                                 const EnvironmentOptions& environmentOptions )
-: mUpdateThread( NULL ),
+: mAdaptorInterfaces( adaptorInterfaces ),
+  mUpdateThread( NULL ),
   mRenderThread( NULL ),
   mVSyncNotifier( NULL ),
   mUpdateRenderSync( NULL ),
@@ -112,18 +113,16 @@ void UpdateRenderController::RequestUpdateOnce()
   mUpdateRenderSync->UpdateWhilePaused();
 }
 
-void UpdateRenderController::ReplaceSurface( RenderSurface* surface )
+void UpdateRenderController::ReplaceSurface( RenderSurface* newSurface )
 {
-  // tell render thread to start the replace
-  mRenderThread->ReplaceSurface(surface);
+  // tell render thread to start the replace. This call will block until the replace
+  // has completed.
+  RenderSurface* currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
 
-  // Ensure that a frame gets processed and render thread runs at least once
-  // Note: ReplaceSurface may be called while threads are paused so call
-  //       RequestUpdateOnce to ensure we do an update/render pass even if paused
-  RequestUpdateOnce();
+  // Ensure the current surface releases any locks to prevent deadlock.
+  currentSurface->StopRender();
 
-  // block here until replace complete
-  mRenderThread->WaitForSurfaceReplaceComplete();
+  mUpdateRenderSync->ReplaceSurface( newSurface );
 }
 
 void UpdateRenderController::SetRenderRefreshRate(unsigned int numberOfVSyncsPerRender )
index aaf5f30..2c9cd6f 100644 (file)
@@ -108,6 +108,8 @@ private:
   // Undefined assignment operator.
   UpdateRenderController& operator=( const UpdateRenderController& );
 
+  AdaptorInternalServices&     mAdaptorInterfaces;
+
   UpdateThread*                mUpdateThread;     ///< The update-thread owned by UpdateRenderController
   RenderThread*                mRenderThread;     ///< The render-thread owned by UpdateRenderController
   VSyncNotifier*               mVSyncNotifier;    ///< The vsync-thread owned by UpdateRenderController
index 2cae3de..f532509 100644 (file)
@@ -54,7 +54,9 @@ UpdateRenderSynchronization::UpdateRenderSynchronization( AdaptorInternalService
   mSyncSeconds( 0u ),
   mSyncMicroseconds( 0u ),
   mFrameTime( adaptorInterfaces.GetPlatformAbstractionInterface() ),
-  mPerformanceInterface( adaptorInterfaces.GetPerformanceInterface() )
+  mPerformanceInterface( adaptorInterfaces.GetPerformanceInterface() ),
+  mReplaceSurfaceRequest(),
+  mReplaceSurfaceRequested( false )
 {
 }
 
@@ -136,6 +138,27 @@ void UpdateRenderSynchronization::UpdateWhilePaused()
   mPausedCondition.notify_one();
 }
 
+bool UpdateRenderSynchronization::ReplaceSurface( RenderSurface* newSurface )
+{
+  bool result=false;
+
+  UpdateRequested();
+  UpdateWhilePaused();
+  {
+    boost::unique_lock< boost::mutex > lock( mMutex );
+
+    mReplaceSurfaceRequest.SetSurface(newSurface);
+    mReplaceSurfaceRequested = true;
+
+    mRequestFinishedCondition.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 );
@@ -248,7 +271,35 @@ bool UpdateRenderSynchronization::UpdateTryToSleep()
   return mRunning;
 }
 
-void UpdateRenderSynchronization::RenderFinished( bool updateRequired )
+bool UpdateRenderSynchronization::RenderSyncWithUpdate(RenderRequest*& requestPtr)
+{
+  boost::unique_lock< boost::mutex > lock( mMutex );
+
+  // Wait for update to produce a buffer, or for the mRunning state to change
+  while ( mRunning && ( 0u == mUpdateReadyCount ) )
+  {
+    // Wait will atomically add the thread to the set of threads waiting on
+    // the condition variable mUpdateFinishedCondition and unlock the mutex.
+    mUpdateFinishedCondition.wait( lock );
+  }
+
+  if( mRunning )
+  {
+    AddPerformanceMarker( PerformanceMarker::RENDER_START );
+  }
+
+  // write any new requests
+  if( mReplaceSurfaceRequested )
+  {
+    requestPtr = &mReplaceSurfaceRequest;
+  }
+  mReplaceSurfaceRequested = false;
+
+  // Flag is used to during UpdateThread::Stop() to exit the update/render loops
+  return mRunning;
+}
+
+void UpdateRenderSynchronization::RenderFinished( bool updateRequired, bool requestProcessed )
 {
   {
     boost::unique_lock< boost::mutex > lock( mMutex );
@@ -264,27 +315,13 @@ void UpdateRenderSynchronization::RenderFinished( bool updateRequired )
   // Notify the update-thread that a render has completed
   mRenderFinishedCondition.notify_one();
 
-  AddPerformanceMarker( PerformanceMarker::RENDER_END );
-}
-
-bool UpdateRenderSynchronization::RenderSyncWithUpdate()
-{
-  boost::unique_lock< boost::mutex > lock( mMutex );
-
-  // Wait for update to produce a buffer, or for the mRunning state to change
-  while ( mRunning && ( 0u == mUpdateReadyCount ) )
+  if( requestProcessed )
   {
-    // Wait will atomically add the thread to the set of threads waiting on
-    // the condition variable mUpdateFinishedCondition and unlock the mutex.
-    mUpdateFinishedCondition.wait( lock );
+    // Notify the event thread that a request has completed
+    mRequestFinishedCondition.notify_one();
   }
 
-  if( mRunning )
-  {
-    AddPerformanceMarker( PerformanceMarker::RENDER_START );
-  }
-  // Flag is used to during UpdateThread::Stop() to exit the update/render loops
-  return mRunning;
+  AddPerformanceMarker( PerformanceMarker::RENDER_END );
 }
 
 void UpdateRenderSynchronization::WaitSync()
index dc2d37d..5751c78 100644 (file)
@@ -21,6 +21,7 @@
 // INTERNAL INCLUDES
 #include <base/interfaces/performance-interface.h>
 #include <base/frame-time.h>
+#include <base/render-thread.h>
 
 // EXTERNAL INCLUDES
 #include <stdint.h>
@@ -45,6 +46,7 @@ namespace Adaptor
 {
 
 class AdaptorInternalServices;
+class RenderSurface;
 
 /**
  * This object is used to synchronize the update, render and vsync threads.
@@ -108,6 +110,12 @@ public:
   void UpdateWhilePaused();
 
   /**
+   * Inform the render thread that there is a new surface.
+   * @param[in] newSurface The new surface for rendering.
+   */
+  bool ReplaceSurface( RenderSurface* newSurface );
+
+  /**
    * Called by Update thread before it runs the update. This is the point where we can pause
    */
   void UpdateReadyToRun();
@@ -135,19 +143,21 @@ public:
   bool UpdateTryToSleep();
 
   /**
-   * Called by the render thread after it renders a frame.
-   * Used to notify the update-thread that a frame has been rendered.
+   * Called by the render-thread to wait for a buffer to read from and then render.
    * @pre Called by render thread only.
-   * @param updateRequired Whether a further update is required.
+   * @param[in] request Pointer to set if there are any requests
+   * @return True if rendering should continue, false if the render-thread should quit.
    */
-  void RenderFinished( bool updateRequired );
+  bool RenderSyncWithUpdate( RenderRequest*& request);
 
   /**
-   * Called by the render-thread to wait for a buffer to read from and then render.
+   * Called by the render thread after it renders a frame.
+   * Used to notify the update-thread that a frame has been rendered.
    * @pre Called by render thread only.
-   * @return True if rendering should continue, false if the render-thread should quit.
+   * @param[in] updateRequired Whether a further update is required.
+   * @param[in] requestProcessed True if a render request was processed this frame
    */
-  bool RenderSyncWithUpdate();
+  void RenderFinished( bool updateRequired, bool requestProcessed );
 
   /**
    * Called by the render/update threads to wait for a Synchronization
@@ -244,10 +254,14 @@ 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
 
   FrameTime mFrameTime;                               ///< Frame timer predicts next vsync time
   PerformanceInterface* mPerformanceInterface;        ///< The performance logging interface
 
+  ReplaceSurfaceRequest mReplaceSurfaceRequest; ///< Holder for a replace surface request
+  bool mReplaceSurfaceRequested; ///< True if there is a new replace surface request
+
 }; // class UpdateRenderSynchronization
 
 } // namespace Adaptor