From 9aa97141a0e50120a4714a9e8c5450940deca1ee Mon Sep 17 00:00:00 2001 From: David Steele Date: Thu, 18 Dec 2014 16:21:22 +0000 Subject: [PATCH] Lock render thread when surface is lost, wait for new surface. 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 --- adaptors/base/render-thread.cpp | 26 +++++++++++--- adaptors/base/update-render-controller.cpp | 12 +++++++ adaptors/base/update-render-controller.h | 7 ++++ adaptors/base/update-render-synchronization.cpp | 47 +++++++++++++++++++++++-- adaptors/base/update-render-synchronization.h | 22 ++++++++++-- adaptors/common/render-surface-impl.h | 4 ++- 6 files changed, 108 insertions(+), 10 deletions(-) diff --git a/adaptors/base/render-thread.cpp b/adaptors/base/render-thread.cpp index e5f93b2..e477191 100644 --- a/adaptors/base/render-thread.cpp +++ b/adaptors/base/render-thread.cpp @@ -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"); diff --git a/adaptors/base/update-render-controller.cpp b/adaptors/base/update-render-controller.cpp index b46ab47..ae773c5 100644 --- a/adaptors/base/update-render-controller.cpp +++ b/adaptors/base/update-render-controller.cpp @@ -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; diff --git a/adaptors/base/update-render-controller.h b/adaptors/base/update-render-controller.h index 2c9cd6f..64839ff 100644 --- a/adaptors/base/update-render-controller.h +++ b/adaptors/base/update-render-controller.h @@ -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 ); diff --git a/adaptors/base/update-render-synchronization.cpp b/adaptors/base/update-render-synchronization.cpp index 7861f6a..a86df45 100644 --- a/adaptors/base/update-render-synchronization.cpp +++ b/adaptors/base/update-render-synchronization.cpp @@ -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 ); diff --git a/adaptors/base/update-render-synchronization.h b/adaptors/base/update-render-synchronization.h index 4302052..68d4297 100644 --- a/adaptors/base/update-render-synchronization.h +++ b/adaptors/base/update-render-synchronization.h @@ -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 diff --git a/adaptors/common/render-surface-impl.h b/adaptors/common/render-surface-impl.h index 921e56d..5fa8c02 100644 --- a/adaptors/common/render-surface-impl.h +++ b/adaptors/common/render-surface-impl.h @@ -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; -- 2.7.4