RenderThread::RenderThread( ThreadSynchronization& sync,
AdaptorInternalServices& adaptorInterfaces,
const EnvironmentOptions& environmentOptions )
-: mThreadSync( sync ),
+: mThreadSynchronization( sync ),
mCore( adaptorInterfaces.GetCore() ),
mGLES( adaptorInterfaces.GetGlesInterface() ),
mEglFactory( &adaptorInterfaces.GetEGLFactoryInterface()),
bool RenderThread::Run()
{
- // install a function for logging
+ DALI_LOG_INFO( gRenderLogFilter, Debug::Verbose, "RenderThread::Run\n");
+
+ // Install a function for logging
mEnvironmentOptions.InstallLogFunction();
InitializeEgl();
- bool running( true );
-
Dali::Integration::RenderStatus renderStatus;
+ RenderRequest* request = NULL;
- uint64_t currentTime( 0 );
-
- // render loop, we stay inside here when rendering
- while( running )
+ // Render loop, we stay inside here when rendering
+ while( mThreadSynchronization.RenderReady( request ) )
{
- // Sync with update thread and get any outstanding requests from ThreadSynchronization
- DALI_LOG_INFO( gRenderLogFilter, Debug::Verbose, "RenderThread::Run. 1 - RenderSyncWithUpdate()\n");
- RenderRequest* request = NULL;
- running = mThreadSync.RenderSyncWithUpdate( request );
-
- DALI_LOG_INFO( gRenderLogFilter, Debug::Verbose, "RenderThread::Run. 2 - Process requests\n");
+ DALI_LOG_INFO( gRenderLogFilter, Debug::Verbose, "RenderThread::Run. 1 - RenderReady\n");
// Consume any pending events to avoid memory leaks
+ DALI_LOG_INFO( gRenderLogFilter, Debug::Verbose, "RenderThread::Run. 2 - ConsumeEvents\n");
mDisplayConnection->ConsumeEvents();
- bool processRequests = true;
- bool requestProcessed = false;
- while( processRequests && running)
+ // Check if we've got a request from the main thread (e.g. replace surface)
+ if( request )
{
- // 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 ThreadController
- running = mThreadSync.RenderSyncWithRequest(request);
- }
+ // Process the request, we should NOT render when we have a request
+ DALI_LOG_INFO( gRenderLogFilter, Debug::Verbose, "RenderThread::Run. 3 - Process requests\n");
+ ProcessRequest( request );
}
-
- if( running )
+ else
{
- // Render
- 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. 5 - Sync.RenderFinished()\n");
- mThreadSync.RenderFinished( renderStatus.NeedsUpdate(), requestProcessed );
-
- uint64_t newTime( mThreadSync.GetTimeMicroseconds() );
-
- // perform any post-render operations
- if ( renderStatus.HasRendered() )
+ // No request to process so we render
+ if( PreRender() ) // Returns false if no surface onto which to render
{
- DALI_LOG_INFO( gRenderLogFilter, Debug::Verbose, "RenderThread::Run. 6 - PostRender()\n");
- PostRender( static_cast< unsigned int >(newTime - currentTime) );
+ // Render
+ DALI_LOG_INFO( gRenderLogFilter, Debug::Verbose, "RenderThread::Run. 3 - Core.Render()\n");
+
+ mThreadSynchronization.AddPerformanceMarker( PerformanceInterface::RENDER_START );
+ mCore.Render( renderStatus );
+ mThreadSynchronization.AddPerformanceMarker( PerformanceInterface::RENDER_END );
+
+ // Perform any post-render operations
+ if ( renderStatus.HasRendered() )
+ {
+ DALI_LOG_INFO( gRenderLogFilter, Debug::Verbose, "RenderThread::Run. 4 - PostRender()\n");
+ PostRender();
+ }
}
-
- currentTime = newTime;
}
+
+ request = NULL; // Clear the request if it was set, no need to release memory
}
- // shut down egl
+ // Shut down EGL
ShutdownEgl();
- // install a function for logging
+ // Uninstall the logging function
mEnvironmentOptions.UnInstallLogFunction();
return true;
}
-bool RenderThread::ProcessRequest( RenderRequest* request )
+void RenderThread::ProcessRequest( RenderRequest* request )
{
- bool processedRequest = false;
-
if( request != NULL )
{
switch(request->GetType())
ReplaceSurfaceRequest* replaceSurfaceRequest = static_cast<ReplaceSurfaceRequest*>(request);
ReplaceSurface( replaceSurfaceRequest->GetSurface() );
replaceSurfaceRequest->ReplaceCompleted();
- processedRequest = true;
+ mThreadSynchronization.RenderInformSurfaceReplaced();
break;
}
}
}
- return processedRequest;
}
void RenderThread::ReplaceSurface( RenderSurface* newSurface )
mDisplayConnection->InitializeEgl(*mEGL);
- bool contextLost = newSurface->ReplaceEGLSurface(*mEGL);
- if( contextLost )
- {
- DALI_LOG_WARNING("Context lost\n");
- mCore.ContextDestroyed();
- mCore.ContextCreated();
- }
+ newSurface->ReplaceEGLSurface(*mEGL);
// use the new surface from now on
mSurface = newSurface;
return success;
}
-void RenderThread::PostRender( unsigned int timeDelta )
+void RenderThread::PostRender()
{
// Inform the gl implementation that rendering has finished before informing the surface
- mGLES.PostRender(timeDelta);
+ mGLES.PostRender();
if( mSurface )
{
// Inform the surface that rendering this frame has finished.
- mSurface->PostRender( *mEGL, mGLES, mDisplayConnection, timeDelta, mSurfaceReplaced );
+ mSurface->PostRender( *mEGL, mGLES, mDisplayConnection, mSurfaceReplaced );
}
mSurfaceReplaced = false;
}
/**
* Check if main thread made any requests, e.g. ReplaceSurface
* Called from render thread
- * @return true if a request was processed, false otherwise.
*/
- bool ProcessRequest(RenderRequest* request);
+ void ProcessRequest( RenderRequest* request );
/**
* Replaces the rendering surface
/**
* Called after core has rendered the scene
* Called from render thread
- * @param[in] timeDelta Time since PostRender was last called in microseconds
*/
- void PostRender( unsigned int timeDelta );
+ void PostRender();
/**
* Helper for the thread calling the entry function.
private: // Data
- ThreadSynchronization& mThreadSync; ///< Used to synchronize the all threads
+ ThreadSynchronization& mThreadSynchronization; ///< Used to synchronize the all threads
Dali::Integration::Core& mCore; ///< Dali core reference
Integration::GlAbstraction& mGLES; ///< GL abstraction reference
EglFactoryInterface* mEglFactory; ///< Factory class to create EGL implementation
delete mThreadSync;
}
-void ThreadController::Start()
+void ThreadController::Initialize()
{
// Notify the synchronization object before starting the threads
- mThreadSync->Start();
+ mThreadSync->Initialise();
+ // We want to the threads to be set up before they start
mUpdateThread->Start();
mRenderThread->Start();
mVSyncNotifier->Start();
}
-void ThreadController::Pause()
+void ThreadController::Start()
{
- mThreadSync->Pause();
-
- // if update thread is napping, wake it up to get it to pause in correct place
- mThreadSync->UpdateRequested();
+ mThreadSync->Start();
}
-void ThreadController::ResumeFrameTime()
+void ThreadController::Pause()
{
- mThreadSync->ResumeFrameTime();
+ mThreadSync->Pause();
}
void ThreadController::Resume()
void ThreadController::RequestUpdate()
{
- mThreadSync->UpdateRequested();
+ mThreadSync->UpdateRequest();
}
void ThreadController::RequestUpdateOnce()
{
- // we may be sleeping
- mThreadSync->UpdateRequested();
// if we are paused, need to allow one update
- mThreadSync->UpdateWhilePaused();
+ mThreadSync->UpdateOnce();
}
void ThreadController::ReplaceSurface( RenderSurface* newSurface )
mThreadSync->ReplaceSurface( newSurface );
}
-void ThreadController::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();
- }
-
- mThreadSync->NewSurface( newSurface );
-}
-
void ThreadController::SetRenderRefreshRate(unsigned int numberOfVSyncsPerRender )
{
mNumberOfVSyncsPerRender = numberOfVSyncsPerRender;
mThreadSync->SetRenderRefreshRate(numberOfVSyncsPerRender);
}
-
} // namespace Adaptor
} // namespace Internal
~ThreadController();
/**
+ * Initializes the thread controller
+ */
+ void Initialize();
+
+ /**
* @copydoc Dali::Adaptor::Start()
*/
void Start();
void Stop();
/**
- * Ensure the frame time values are reset before the next call to Core::Update()
- * following a Resume application state change.
- */
- void ResumeFrameTime();
-
- /**
* Called by the adaptor when core requires another update
*/
void RequestUpdate();
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 );
--- /dev/null
+#ifndef __DALI_INTERNAL_THREAD_SYNCHRONIZATION_DEBUG_H__
+#define __DALI_INTERNAL_THREAD_SYNCHRONIZATION_DEBUG_H__
+
+/*
+ * Copyright (c) 2015 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// EXTERNAL INCLUDES
+#include <dali/integration-api/debug.h>
+
+namespace Dali
+{
+
+namespace Internal
+{
+
+namespace Adaptor
+{
+
+namespace
+{
+#ifdef DEBUG_ENABLED
+
+#define ENABLE_LOG_IN_COLOR 1
+#define ENABLE_VSYNC_COUNTER_LOGGING 1
+#define ENABLE_UPDATE_COUNTER_LOGGING 1
+#define ENABLE_VSYNC_THREAD_LOGGING 1
+#define ENABLE_UPDATE_THREAD_LOGGING 1
+#define ENABLE_RENDER_THREAD_LOGGING 1
+#define ENABLE_EVENT_LOGGING 1
+
+Debug::Filter* gLogFilter = Debug::Filter::New( Debug::NoLogging, true, "LOG_THREAD_SYNC" );
+
+#define LOG_THREAD_SYNC(level, color, format, args...) \
+ if( gLogFilter && gLogFilter->IsEnabledFor( level ) ) { Dali::Integration::Log::LogMessage( Dali::Integration::Log::DebugInfo, "%s" format "%s\n", color, ## args, COLOR_CLEAR ); }
+
+#define LOG_THREAD_SYNC_TRACE(color) \
+ Dali::Integration::Log::TraceObj debugTraceObj( ( gLogFilter && gLogFilter->IsEnabledFor( Debug::Concise ) ) ? gLogFilter : NULL, "%s%s%s", color, __FUNCTION__, COLOR_CLEAR )
+
+#define LOG_THREAD_SYNC_TRACE_FMT(color, format, args...) \
+ Dali::Integration::Log::TraceObj debugTraceObj( ( gLogFilter && gLogFilter->IsEnabledFor( Debug::Concise ) ) ? gLogFilter : NULL, "%s%s: " format "%s", color, __FUNCTION__, ## args, COLOR_CLEAR )
+
+#else // DEBUG_ENABLED
+
+#define LOG_THREAD_SYNC(color, format, args...)
+#define LOG_THREAD_SYNC_TRACE(color)
+#define LOG_THREAD_SYNC_TRACE_FMT(color, format, args...)
+
+#endif // DEBUG_ENABLED
+
+#ifdef ENABLE_LOG_IN_COLOR
+#define COLOR_RED "\033[31m"
+#define COLOR_YELLOW "\033[33m"
+#define COLOR_BLUE "\033[34m"
+#define COLOR_LIGHT_RED "\033[91m"
+#define COLOR_LIGHT_YELLOW "\033[93m"
+#define COLOR_LIGHT_BLUE "\033[94m"
+#define COLOR_WHITE "\033[97m"
+#define COLOR_CLEAR "\033[0m"
+#else
+#define COLOR_RED
+#define COLOR_YELLOW
+#define COLOR_BLUE
+#define COLOR_LIGHT_RED
+#define COLOR_LIGHT_YELLOW
+#define COLOR_LIGHT_BLUE
+#define COLOR_WHITE
+#define COLOR_CLEAR
+#endif
+
+#ifdef ENABLE_VSYNC_COUNTER_LOGGING
+#define LOG_VSYNC_COUNTER_VSYNC(format, args...) LOG_THREAD_SYNC(Debug::Verbose, COLOR_LIGHT_RED, "%s: " format, __FUNCTION__, ## args)
+#define LOG_VSYNC_COUNTER_UPDATE(format, args...) LOG_THREAD_SYNC(Debug::Verbose, COLOR_LIGHT_YELLOW, "%s: " format, __FUNCTION__, ## args)
+#else
+#define LOG_VSYNC_COUNTER_VSYNC(format, args...)
+#define LOG_VSYNC_COUNTER_UPDATE(format, args...)
+#endif
+
+#ifdef ENABLE_UPDATE_COUNTER_LOGGING
+#define LOG_UPDATE_COUNTER_UPDATE(format, args...) LOG_THREAD_SYNC(Debug::Verbose, COLOR_YELLOW, "%s: " format, __FUNCTION__, ## args)
+#define LOG_UPDATE_COUNTER_RENDER(format, args...) LOG_THREAD_SYNC(Debug::Verbose, COLOR_LIGHT_BLUE, "%s: " format, __FUNCTION__, ## args)
+#else
+#define LOG_UPDATE_COUNTER_UPDATE(format, args...)
+#define LOG_UPDATE_COUNTER_RENDER(format, args...)
+#endif
+
+#ifdef ENABLE_VSYNC_THREAD_LOGGING
+#define LOG_VSYNC(format, args...) LOG_THREAD_SYNC(Debug::General, COLOR_RED, "%s: " format, __FUNCTION__, ## args)
+#define LOG_VSYNC_TRACE LOG_THREAD_SYNC_TRACE(COLOR_RED)
+#define LOG_VSYNC_TRACE_FMT(format, args...) LOG_THREAD_SYNC_TRACE_FMT(COLOR_RED, format, ## args)
+#else
+#define LOG_VSYNC(format, args...)
+#define LOG_VSYNC_TRACE
+#define LOG_VSYNC_TRACE_FMT(format, args...)
+#endif
+
+#ifdef ENABLE_UPDATE_THREAD_LOGGING
+#define LOG_UPDATE(format, args...) LOG_THREAD_SYNC(Debug::General, COLOR_YELLOW, "%s: " format, __FUNCTION__, ## args)
+#define LOG_UPDATE_TRACE LOG_THREAD_SYNC_TRACE(COLOR_YELLOW)
+#define LOG_UPDATE_TRACE_FMT(format, args...) LOG_THREAD_SYNC_TRACE_FMT(COLOR_YELLOW, format, ## args)
+#else
+#define LOG_UPDATE(format, args...)
+#define LOG_UPDATE_TRACE
+#define LOG_UPDATE_TRACE_FMT(format, args...)
+#endif
+
+#ifdef ENABLE_RENDER_THREAD_LOGGING
+#define LOG_RENDER(format, args...) LOG_THREAD_SYNC(Debug::General, COLOR_BLUE, "%s: " format, __FUNCTION__, ## args)
+#define LOG_RENDER_TRACE LOG_THREAD_SYNC_TRACE(COLOR_BLUE)
+#define LOG_RENDER_TRACE_FMT(format, args...) LOG_THREAD_SYNC_TRACE_FMT(COLOR_BLUE, format, ## args)
+#else
+#define LOG_RENDER(format, args...)
+#define LOG_RENDER_TRACE
+#define LOG_RENDER_TRACE_FMT(format, args...)
+#endif
+
+#ifdef ENABLE_EVENT_LOGGING
+#define LOG_EVENT(format, args...) LOG_THREAD_SYNC(Debug::Concise, COLOR_WHITE, "%s: " format, __FUNCTION__, ## args)
+#define LOG_EVENT_TRACE LOG_THREAD_SYNC_TRACE(COLOR_WHITE)
+#define LOG_EVENT_TRACE_FMT(format, args...) LOG_THREAD_SYNC_TRACE_FMT(COLOR_WHITE, format, ## args)
+#else
+#define LOG_EVENT(format, args...)
+#define LOG_EVENT_TRACE
+#define LOG_EVENT_TRACE_FMT(format, args...)
+#endif
+} // unnamed namespace
+
+} // namespace Adaptor
+
+} // namespace Internal
+
+} // namespace Dali
+
+#endif // __DALI_INTERNAL_THREAD_SYNCHRONIZATION_DEBUG_H__
// CLASS HEADER
#include "thread-synchronization.h"
-// EXTERNAL INCLUDES
-#include <dali/integration-api/debug.h>
-
// INTERNAL INCLUDES
#include <base/interfaces/adaptor-internal-services.h>
+#include <base/thread-synchronization-debug.h>
namespace Dali
{
namespace
{
const unsigned int TIME_PER_FRAME_IN_MICROSECONDS = 16667;
-const unsigned int MICROSECONDS_PER_SECOND( 1000000 );
-const unsigned int INPUT_EVENT_UPDATE_PERIOD( MICROSECONDS_PER_SECOND / 90 ); // period between ecore x event updates
-
+const int TOTAL_THREAD_COUNT = 3;
+const unsigned int TRUE = 1u;
+const unsigned int FALSE = 0u;
} // unnamed namespace
-ThreadSynchronization::ThreadSynchronization( AdaptorInternalServices& adaptorInterfaces,
- unsigned int numberOfVSyncsPerRender)
-: mMaximumUpdateCount( adaptorInterfaces.GetCore().GetMaximumUpdateCount()),
- mNumberOfVSyncsPerRender( numberOfVSyncsPerRender ),
- mUpdateReadyCount( 0u ),
- mRunning( false ),
- mUpdateRequired( false ),
- mPaused( false ),
- mUpdateRequested( false ),
- mAllowUpdateWhilePaused( false ),
- mVSyncSleep( false ),
- mSyncFrameNumber( 0u ),
- mSyncSeconds( 0u ),
- mSyncMicroseconds( 0u ),
- mFrameTime( adaptorInterfaces.GetPlatformAbstractionInterface() ),
+ThreadSynchronization::ThreadSynchronization( AdaptorInternalServices& adaptorInterfaces, unsigned int numberOfVSyncsPerRender)
+: mFrameTime( adaptorInterfaces.GetPlatformAbstractionInterface() ),
mNotificationTrigger( adaptorInterfaces.GetProcessCoreEventsTrigger() ),
mPerformanceInterface( adaptorInterfaces.GetPerformanceInterface() ),
mReplaceSurfaceRequest(),
- mReplaceSurfaceRequested( false )
+ mUpdateThreadWaitCondition(),
+ mRenderThreadWaitCondition(),
+ mVSyncThreadWaitCondition(),
+ mEventThreadWaitCondition(),
+ mMaximumUpdateCount( adaptorInterfaces.GetCore().GetMaximumUpdateCount()),
+ mNumberOfVSyncsPerRender( numberOfVSyncsPerRender ),
+ mTryToSleepCount( 0u ),
+ mState( State::STOPPED ),
+ mVSyncAheadOfUpdate( 0u ),
+ mUpdateAheadOfRender( 0u ),
+ mNumberOfThreadsStarted( 0u ),
+ mUpdateThreadResuming( FALSE ),
+ mVSyncThreadRunning( FALSE ),
+ mVSyncThreadStop( FALSE ),
+ mRenderThreadStop( FALSE ),
+ mRenderThreadReplacingSurface( FALSE ),
+ mEventThreadSurfaceReplaced( FALSE ),
+ mVSyncThreadInitialised( FALSE ),
+ mRenderThreadInitialised( FALSE ),
+ mRenderThreadSurfaceReplaced( FALSE )
{
}
{
}
-void ThreadSynchronization::Start()
-{
- mFrameTime.SetMinimumFrameTimeInterval( mNumberOfVSyncsPerRender * TIME_PER_FRAME_IN_MICROSECONDS );
- mRunning = true;
-}
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// EVENT THREAD
+///////////////////////////////////////////////////////////////////////////////////////////////////
-void ThreadSynchronization::Stop()
+void ThreadSynchronization::Initialise()
{
- mRunning = false;
-
- // Wake if sleeping
- UpdateRequested();
-
- // we may be paused so need to resume
- Resume();
+ LOG_EVENT_TRACE;
- // Notify all condition variables, so if threads are waiting
- // they can break out, and check the running status.
- mUpdateFinishedCondition.notify_one();
- mRenderFinishedCondition.notify_one();
- mVSyncSleepCondition.notify_one();
- mVSyncReceivedCondition.notify_one();
- mRenderRequestSleepCondition.notify_one();
-
- mFrameTime.Suspend();
+ ConditionalWait::ScopedLock lock( mUpdateThreadWaitCondition );
+ if( mState == State::STOPPED )
+ {
+ LOG_EVENT( "INITIALISING" );
+ mState = State::INITIALISING;
+ }
}
-void ThreadSynchronization::Pause()
+void ThreadSynchronization::Start()
{
- mPaused = true;
-
- AddPerformanceMarker( PerformanceInterface::PAUSED );
- mFrameTime.Suspend();
-}
+ LOG_EVENT_TRACE;
-void ThreadSynchronization::ResumeFrameTime()
-{
- mFrameTime.Resume();
-}
+ bool start = false;
+ {
+ ConditionalWait::ScopedLock lock( mUpdateThreadWaitCondition );
+ if( mState == State::INITIALISING )
+ {
+ start = true;
+ }
+ }
-void ThreadSynchronization::Resume()
-{
- mPaused = false;
- mVSyncSleep = false;
+ // Not atomic, but does not matter here as we just want to ensure we only start from State::INITIALISING
+ if( start )
+ {
+ LOG_EVENT( "STARTING" );
+ mFrameTime.SetMinimumFrameTimeInterval( mNumberOfVSyncsPerRender * TIME_PER_FRAME_IN_MICROSECONDS );
- mPausedCondition.notify_one();
- mVSyncSleepCondition.notify_one();
+ {
+ ConditionalWait::ScopedLock lock( mEventThreadWaitCondition );
+ while( mNumberOfThreadsStarted < TOTAL_THREAD_COUNT )
+ {
+ mEventThreadWaitCondition.Wait( lock );
+ }
+ }
- AddPerformanceMarker( PerformanceInterface::RESUME);
+ {
+ ConditionalWait::ScopedLock lock( mUpdateThreadWaitCondition );
+ mState = State::RUNNING;
+ }
+ mUpdateThreadWaitCondition.Notify();
+ }
}
-void ThreadSynchronization::UpdateRequested()
+void ThreadSynchronization::Stop()
{
- mUpdateRequested = true;
+ LOG_EVENT_TRACE;
- // Wake update thread if sleeping
- mUpdateSleepCondition.notify_one();
-}
+ bool stop = false;
+ {
+ ConditionalWait::ScopedLock lock( mUpdateThreadWaitCondition );
+ if( mState != State::STOPPED )
+ {
+ stop = true;
+ mState = State::STOPPED;
+ }
+ }
-void ThreadSynchronization::UpdateWhilePaused()
-{
+ // Not atomic, but does not matter here as we just want to ensure we do not stop more than once
+ if( stop )
{
- boost::unique_lock< boost::mutex > lock( mMutex );
+ LOG_EVENT( "STOPPING" );
- mAllowUpdateWhilePaused = true;
- }
+ // Notify update-thread so that it continues and sets up the other threads to stop as well
+ mUpdateThreadWaitCondition.Notify();
- // wake vsync if sleeping
- mVSyncSleepCondition.notify_one();
- // Wake update if sleeping
- mUpdateSleepCondition.notify_one();
- // stay paused but notify the pause condition
- mPausedCondition.notify_one();
+ mFrameTime.Suspend();
+ }
}
-bool ThreadSynchronization::ReplaceSurface( RenderSurface* newSurface )
+void ThreadSynchronization::Pause()
{
- bool result=false;
+ LOG_EVENT_TRACE;
- UpdateRequested();
- UpdateWhilePaused();
+ bool addPerformanceMarker = false;
{
- boost::unique_lock< boost::mutex > lock( mMutex );
+ // Only pause if we're RUNNING or SLEEPING
+ ConditionalWait::ScopedLock lock( mUpdateThreadWaitCondition );
+ if( ( mState == State::RUNNING ) ||
+ ( mState == State::SLEEPING ) )
+ {
+ LOG_EVENT( "PAUSING" );
- mReplaceSurfaceRequest.SetSurface(newSurface);
- mReplaceSurfaceRequested = true;
+ mState = State::PAUSED;
- mRenderRequestFinishedCondition.wait(lock); // wait unlocks the mutex on entry, and locks again on exit.
+ mUpdateThreadResuming = FALSE;
- mReplaceSurfaceRequested = false;
- result = mReplaceSurfaceRequest.GetReplaceCompleted();
+ mFrameTime.Suspend();
+
+ addPerformanceMarker = true;
+ }
}
- return result;
+ if( addPerformanceMarker )
+ {
+ // Can lock so we do not want to have a lock when calling this to avoid deadlocks
+ AddPerformanceMarker( PerformanceInterface::PAUSED );
+ }
}
-bool ThreadSynchronization::NewSurface( RenderSurface* newSurface )
+void ThreadSynchronization::Resume()
{
- bool result=false;
+ LOG_EVENT_TRACE;
- UpdateRequested();
- UpdateWhilePaused();
+ // Only resume if we're PAUSED
+ bool resume = false;
{
- boost::unique_lock< boost::mutex > lock( mMutex );
+ ConditionalWait::ScopedLock lock( mUpdateThreadWaitCondition );
+ if( mState == State::PAUSED )
+ {
+ resume = true;
+ mState = State::RUNNING;
+ mUpdateThreadResuming = TRUE;
+ }
+ }
- mReplaceSurfaceRequest.SetSurface(newSurface);
- mReplaceSurfaceRequested = true;
+ // Not atomic, but does not matter here as we just want to ensure we only resume if we're paused
+ if( resume )
+ {
+ LOG_EVENT( "RESUMING" );
- // Unlock the render thread sleeping on requests
- mRenderRequestSleepCondition.notify_one();
+ mFrameTime.Resume();
- // Lock event thread until request has been processed
- mRenderRequestFinishedCondition.wait(lock);// wait unlocks the mutex on entry, and locks again on exit.
+ // Start up Update thread again
+ mUpdateThreadWaitCondition.Notify();
- mReplaceSurfaceRequested = false;
- result = mReplaceSurfaceRequest.GetReplaceCompleted();
+ // Can lock so we do not want to have a lock when calling this to avoid deadlocks
+ AddPerformanceMarker( PerformanceInterface::RESUME);
}
-
- return result;
}
-
-void ThreadSynchronization::UpdateReadyToRun()
+void ThreadSynchronization::UpdateRequest()
{
- bool wokenFromPause( false );
+ LOG_EVENT_TRACE;
- // atomic check first to avoid mutex lock in 99.99% of cases
- if( mPaused )
+ bool update = false;
{
- boost::unique_lock< boost::mutex > lock( mMutex );
-
- // wait while paused
- while( mPaused && !mAllowUpdateWhilePaused )
+ ConditionalWait::ScopedLock lock( mUpdateThreadWaitCondition );
+ if( mState == State::SLEEPING )
{
- // this will automatically unlock mMutex
- mPausedCondition.wait( lock );
-
- wokenFromPause = true;
+ mState = State::RUNNING;
+ update = true;
}
+ mTryToSleepCount = 0;
}
- if ( !wokenFromPause )
+ if( update )
{
- // Wait for the next Sync
- WaitSync();
+ LOG_EVENT( "UPDATE REQUEST" );
+ mUpdateThreadWaitCondition.Notify();
}
+}
+
+void ThreadSynchronization::UpdateOnce()
+{
+ LOG_EVENT_TRACE;
+ LOG_EVENT( "UPDATE ONCE" );
- AddPerformanceMarker( PerformanceInterface::UPDATE_START );
+ mUpdateThreadWaitCondition.Notify();
}
-bool ThreadSynchronization::UpdateSyncWithRender( bool notifyEvent, bool& renderNeedsUpdate )
+void ThreadSynchronization::ReplaceSurface( RenderSurface* newSurface )
{
- AddPerformanceMarker( PerformanceInterface::UPDATE_END );
+ LOG_EVENT_TRACE;
- // Do the notifications first so the event thread can start processing them
- if( notifyEvent && mRunning )
+ State::Type previousState( State::STOPPED );
{
- // Tell the event-thread to wake up (if asleep) and send a notification event to Core
- mNotificationTrigger.Trigger();
+ ConditionalWait::ScopedLock lock( mUpdateThreadWaitCondition );
+ previousState = mState;
+ mState = State::REPLACING_SURFACE;
}
- boost::unique_lock< boost::mutex > lock( mMutex );
+ {
+ ConditionalWait::ScopedLock lock( mEventThreadWaitCondition );
+ mEventThreadSurfaceReplaced = FALSE;
+ }
- // Another frame was prepared for rendering; increment counter
- ++mUpdateReadyCount;
- DALI_ASSERT_DEBUG( mUpdateReadyCount <= mMaximumUpdateCount );
+ {
+ ConditionalWait::ScopedLock lock( mRenderThreadWaitCondition );
+ mReplaceSurfaceRequest.SetSurface( newSurface );
+ mRenderThreadReplacingSurface = TRUE;
+ }
- // Notify the render-thread that an update has completed
- mUpdateFinishedCondition.notify_one();
+ // Notify the RenderThread in case it's waiting
+ mRenderThreadWaitCondition.Notify();
- // The update-thread must wait until a frame has been rendered, when mMaximumUpdateCount is reached
- while( mRunning && ( mMaximumUpdateCount == mUpdateReadyCount ) )
{
- // Wait will atomically add the thread to the set of threads waiting on
- // the condition variable mRenderFinishedCondition and unlock the mutex.
- mRenderFinishedCondition.wait( lock );
- }
+ ConditionalWait::ScopedLock lock( mEventThreadWaitCondition );
+
+ // Wait for RenderThread to replace the surface
+ while( ! mEventThreadSurfaceReplaced )
+ {
+ LOG_EVENT( "Waiting for Surface to be Replaced" );
- renderNeedsUpdate = mUpdateRequired;
+ mEventThreadWaitCondition.Wait( lock );
+ }
+ }
- // Flag is used to during UpdateThread::Stop() to exit the update/render loops
- return mRunning;
+ {
+ ConditionalWait::ScopedLock lock( mUpdateThreadWaitCondition );
+ mState = previousState;
+ }
+ mUpdateThreadWaitCondition.Notify();
}
-void ThreadSynchronization::UpdateWaitForAllRenderingToFinish()
+void ThreadSynchronization::SetRenderRefreshRate( unsigned int numberOfVSyncsPerRender )
{
- boost::unique_lock< boost::mutex > lock( mMutex );
+ LOG_EVENT_TRACE;
+ LOG_EVENT( "SET RENDER REFRESH RATE" );
- // Wait for all of the prepared frames to be rendered
- while ( mRunning && ( 0u != mUpdateReadyCount ) && !mUpdateRequested )
- {
- // Wait will atomically add the thread to the set of threads waiting on
- // the condition variable mRenderFinishedCondition and unlock the mutex.
- mRenderFinishedCondition.wait( lock );
- }
+ mNumberOfVSyncsPerRender = numberOfVSyncsPerRender;
}
-bool ThreadSynchronization::UpdateTryToSleep()
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// UPDATE THREAD
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+bool ThreadSynchronization::UpdateReady( bool notifyEvent, bool runUpdate, float& lastFrameDeltaSeconds, unsigned int& lastSyncTimeMilliseconds, unsigned int& nextSyncTimeMilliseconds )
{
- if ( !mUpdateRequired && !mUpdateRequested )
+ LOG_UPDATE_TRACE;
+
+ State::Type state = State::STOPPED;
{
- // there's nothing to update in the scene, so wait for render to finish processing
- UpdateWaitForAllRenderingToFinish();
+ ConditionalWait::ScopedLock updateLock( mUpdateThreadWaitCondition );
+ state = mState;
}
- boost::mutex sleepMutex;
- boost::unique_lock< boost::mutex > lock( sleepMutex );
-
- while( mRunning && !mUpdateRequired && !mUpdateRequested )
+ switch( state )
{
- //
- // Going to sleep
- //
+ case State::STOPPED:
+ {
+ StopAllThreads();
+ return false; // Stop update-thread
+ }
- // 1. put VSync thread to sleep.
- mVSyncSleep = true;
+ case State::INITIALISING:
+ {
+ UpdateInitialising();
+ break;
+ }
- // 2. inform frame time
- mFrameTime.Sleep();
+ case State::PAUSED:
+ {
+ LOG_UPDATE_TRACE_FMT( "PAUSED" );
- // 3. block thread and wait for wakeup event
- mUpdateSleepCondition.wait( lock );
+ // Just pause the VSyncThread, locks so we shouldn't have a scoped-lock when calling this
+ PauseVSyncThread();
+ }
+ // No break, fall through
- //
- // Woken up
- //
+ case State::RUNNING:
+ {
+ LOG_UPDATE_TRACE_FMT( "RUNNING" );
+
+ if( IsUpdateThreadResuming() )
+ {
+ LOG_UPDATE( "Restarting VSyncThread" );
+
+ {
+ ConditionalWait::ScopedLock updateLock( mUpdateThreadWaitCondition );
+ mUpdateThreadResuming = FALSE;
+ }
+
+ // Restart the VSyncThread, locks so we shouldn't have a scoped-lock when calling this
+ RunVSyncThread();
+ }
+
+ if( notifyEvent )
+ {
+ LOG_UPDATE( "Notify Event Thread" );
+
+ // Do the notifications first so the event thread can start processing them
+ // Tell the event-thread to wake up (if asleep) and send a notification event to Core
+ mNotificationTrigger.Trigger();
+ }
+
+ // Inform render thread
+ {
+ ConditionalWait::ScopedLock lock( mRenderThreadWaitCondition );
+ ++mUpdateAheadOfRender;
+ LOG_UPDATE_COUNTER_UPDATE( "updateAheadOfRender(%d)", mUpdateAheadOfRender );
+ }
+ mRenderThreadWaitCondition.Notify();
+
+ // Wait if we've reached the maximum-ahead-of-render count.
+ while( MaximumUpdateAheadOfRenderReached() )
+ {
+ LOG_UPDATE( "Maximum Update Ahead of Render: WAIT" );
+
+ mRenderThreadWaitCondition.Notify(); // Notify the render thread in case it was waiting
+
+ {
+ // Ensure we did not stop while we were waiting previously.
+ ConditionalWait::ScopedLock updateLock( mUpdateThreadWaitCondition );
+ if( mState == State::STOPPED )
+ {
+ break; // Break out of while loop
+ }
+ mUpdateThreadWaitCondition.Wait( updateLock );
+ }
+ }
+
+ // Ensure we have had at least 1 V-Sync before we continue
+ // Ensure we didn't stop while we were previously waiting
+ {
+ ConditionalWait::ScopedLock updateLock( mUpdateThreadWaitCondition );
+ if( ( mState != State::STOPPED ) &&
+ ( mVSyncAheadOfUpdate == 0 ) )
+ {
+ LOG_VSYNC_COUNTER_UPDATE( " vSyncAheadOfUpdate(%d) WAIT", mVSyncAheadOfUpdate );
+ mUpdateThreadWaitCondition.Wait( updateLock );
+ }
+ else
+ {
+ LOG_VSYNC_COUNTER_UPDATE( " vSyncAheadOfUpdate(%d)", mVSyncAheadOfUpdate );
+ }
+ mVSyncAheadOfUpdate = 0;
+ }
+
+ // Try to sleep if we do not require any more updates
+ UpdateTryToSleep( runUpdate );
+
+ break;
+ }
- // 1. inform frame timer
- mFrameTime.WakeUp();
+ case State::SLEEPING:
+ case State::REPLACING_SURFACE:
+ {
+ break;
+ }
+ }
- // 2. wake VSync thread.
- mVSyncSleep = false;
- mVSyncSleepCondition.notify_one();
+ // Ensure we didn't stop while we were waiting
+ if( IsUpdateThreadStopping() )
+ {
+ // Locks so we shouldn't have a scoped-lock when calling this
+ StopAllThreads();
+ return false; // Stop update-thread
}
- mUpdateRequested = false;
+ // Just wait if we're replacing the surface as the render-thread is busy
+ UpdateWaitIfReplacingSurface();
- return mRunning;
+ mFrameTime.PredictNextSyncTime( lastFrameDeltaSeconds, lastSyncTimeMilliseconds, nextSyncTimeMilliseconds );
+
+ return true; // Keep update-thread running
}
-bool ThreadSynchronization::RenderSyncWithRequest(RenderRequest*& requestPtr)
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// RENDER THREAD
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+bool ThreadSynchronization::RenderReady( RenderRequest*& requestPtr )
{
- boost::unique_lock< boost::mutex > lock( mMutex );
+ LOG_RENDER_TRACE;
- // Wait for a replace surface request
- mRenderRequestSleepCondition.wait(lock);
+ if( ! IsRenderThreadReplacingSurface() ) // Call to this function locks so should not be called if we have a scoped-lock
+ {
+ if( ! mRenderThreadInitialised )
+ {
+ LOG_RENDER( "Initialised" );
+
+ mRenderThreadInitialised = TRUE;
- // write any new requests
- if( mReplaceSurfaceRequested )
+ // Notify event thread that this thread is up and running, this locks so we should have a scoped-lock
+ NotifyThreadInitialised();
+ }
+ else
+ {
+ if( mRenderThreadSurfaceReplaced )
+ {
+ mRenderThreadSurfaceReplaced = FALSE;
+ }
+ else
+ {
+ // decrement update-ahead-of-render
+ ConditionalWait::ScopedLock renderLock( mRenderThreadWaitCondition );
+ --mUpdateAheadOfRender;
+ }
+ }
+
+ // Check if we've had an update, if we haven't then we just wait
+ // Ensure we do not wait if we're supposed to stop
+ {
+ ConditionalWait::ScopedLock renderLock( mRenderThreadWaitCondition );
+ if( mUpdateAheadOfRender <= 0 && ! mRenderThreadStop )
+ {
+ LOG_UPDATE_COUNTER_RENDER( "updateAheadOfRender(%d) WAIT", mUpdateAheadOfRender );
+ mRenderThreadWaitCondition.Wait( renderLock );
+ }
+ else
+ {
+ LOG_UPDATE_COUNTER_RENDER( "updateAheadOfRender(%d)", mUpdateAheadOfRender );
+ }
+ }
+ }
+ else
{
+ LOG_RENDER( "Just Rendered, now Replacing surface" );
+
+ // ... also decrement update-ahead-of-render
+ ConditionalWait::ScopedLock renderLock( mRenderThreadWaitCondition );
+ --mUpdateAheadOfRender;
+ }
+
+ // We may have been asked to replace the surface while we were waiting so check again here
+ if( IsRenderThreadReplacingSurface() )
+ {
+ // Replacing surface
+ LOG_RENDER( "REPLACE SURFACE" );
+
+ ConditionalWait::ScopedLock renderLock( mRenderThreadWaitCondition );
requestPtr = &mReplaceSurfaceRequest;
+ mRenderThreadReplacingSurface = FALSE;
+ mRenderThreadSurfaceReplaced = FALSE;
}
- mReplaceSurfaceRequested = false;
- return mRunning;
+
+ return IsRenderThreadRunning(); // Call to this function locks so should not be called if we have a scoped-lock
}
-bool ThreadSynchronization::RenderSyncWithUpdate(RenderRequest*& requestPtr)
+void ThreadSynchronization::RenderInformSurfaceReplaced()
{
- boost::unique_lock< boost::mutex > lock( mMutex );
+ LOG_RENDER_TRACE;
- // Wait for update to produce a buffer, or for the mRunning state to change
- while ( mRunning && ( 0u == mUpdateReadyCount ) )
+ mRenderThreadSurfaceReplaced = TRUE;
{
- // Wait will atomically add the thread to the set of threads waiting on
- // the condition variable mUpdateFinishedCondition and unlock the mutex.
- mUpdateFinishedCondition.wait( lock );
+ ConditionalWait::ScopedLock lock( mEventThreadWaitCondition );
+ mEventThreadSurfaceReplaced = TRUE;
}
+ mEventThreadWaitCondition.Notify();
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// V-SYNC THREAD
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+bool ThreadSynchronization::VSyncReady( bool validSync, unsigned int frameNumber, unsigned int seconds, unsigned int microseconds, unsigned int& numberOfVSyncsPerRender )
+{
+ LOG_VSYNC_TRACE;
- if( mRunning )
{
- AddPerformanceMarker( PerformanceInterface::RENDER_START );
+ ConditionalWait::ScopedLock vSyncLock( mVSyncThreadWaitCondition );
+ if( numberOfVSyncsPerRender != mNumberOfVSyncsPerRender )
+ {
+ numberOfVSyncsPerRender = mNumberOfVSyncsPerRender; // save it back
+ mFrameTime.SetMinimumFrameTimeInterval( mNumberOfVSyncsPerRender * TIME_PER_FRAME_IN_MICROSECONDS );
+ }
+
+ if( validSync )
+ {
+ mFrameTime.SetSyncTime( frameNumber );
+ }
}
- // write any new requests
- if( mReplaceSurfaceRequested )
+ if( ! mVSyncThreadInitialised )
{
- requestPtr = &mReplaceSurfaceRequest;
+ LOG_VSYNC( "Initialised" );
+
+ mVSyncThreadInitialised = TRUE;
+
+ // Notify event thread that this thread is up and running, this locks so we should have a scoped-lock
+ NotifyThreadInitialised();
+ }
+ else
+ {
+ // Increment v-sync-ahead-of-update count and inform update-thread
+ {
+ ConditionalWait::ScopedLock lock( mUpdateThreadWaitCondition );
+ ++mVSyncAheadOfUpdate;
+ LOG_VSYNC_COUNTER_VSYNC( " vSyncAheadOfUpdate(%d)", mVSyncAheadOfUpdate );
+ }
+ mUpdateThreadWaitCondition.Notify();
+ }
+
+ // Ensure update-thread has set us to run before continuing
+ // Ensure we do not wait if we're supposed to stop
+ {
+ ConditionalWait::ScopedLock vSyncLock( mVSyncThreadWaitCondition );
+ while( ! mVSyncThreadRunning && ! mVSyncThreadStop )
+ {
+ LOG_VSYNC( "WAIT" );
+ mVSyncThreadWaitCondition.Wait( vSyncLock );
+ }
}
- mReplaceSurfaceRequested = false;
- // Flag is used to during UpdateThread::Stop() to exit the update/render loops
- return mRunning;
+ return IsVSyncThreadRunning(); // Call to this function locks so should not be called if we have a scoped-lock
}
-void ThreadSynchronization::RenderFinished( bool updateRequired, bool requestProcessed )
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// ALL THREADS: Performance Marker
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+void ThreadSynchronization::AddPerformanceMarker( PerformanceInterface::MarkerType type )
{
+ if( mPerformanceInterface )
{
- boost::unique_lock< boost::mutex > lock( mMutex );
+ mPerformanceInterface->AddMarker( type );
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+//
+// PRIVATE METHODS
+//
+///////////////////////////////////////////////////////////////////////////////////////////////////
- // Set the flag to say if update needs to run again.
- mUpdateRequired = updateRequired;
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Called by ALL Threads
+///////////////////////////////////////////////////////////////////////////////////////////////////
- // A frame has been rendered; decrement counter
- --mUpdateReadyCount;
- DALI_ASSERT_DEBUG( mUpdateReadyCount < mMaximumUpdateCount );
+void ThreadSynchronization::NotifyThreadInitialised()
+{
+ {
+ ConditionalWait::ScopedLock lock( mEventThreadWaitCondition );
+ ++mNumberOfThreadsStarted;
}
+ mEventThreadWaitCondition.Notify();
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Called by Update Thread
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+void ThreadSynchronization::UpdateInitialising()
+{
+ LOG_UPDATE_TRACE;
- // Notify the update-thread that a render has completed
- mRenderFinishedCondition.notify_one();
+ // Notify event thread that this thread is up and running, locks so we shouldn't have a scoped-lock when calling this
+ NotifyThreadInitialised();
- if( requestProcessed )
+ // Wait for first thread-sync point
{
- // Notify the event thread that a request has completed
- mRenderRequestFinishedCondition.notify_one();
+ ConditionalWait::ScopedLock updateLock( mUpdateThreadWaitCondition );
+
+ while( mState == State::INITIALISING )
+ {
+ mUpdateThreadWaitCondition.Wait( updateLock );
+ }
}
- AddPerformanceMarker( PerformanceInterface::RENDER_END );
+ // Locks so we shouldn't have a scoped-lock when calling this
+ RunVSyncThread();
}
-void ThreadSynchronization::WaitSync()
+void ThreadSynchronization::UpdateTryToSleep( bool runUpdate )
{
- // Block until the start of a new sync.
- // If we're experiencing slowdown and are behind by more than a frame
- // then we should wait for the next frame
+ LOG_UPDATE_TRACE;
- unsigned int updateFrameNumber = mSyncFrameNumber;
+ if( ! runUpdate )
+ {
+ LOG_UPDATE( "TryToSleep" );
+
+ if( ++mTryToSleepCount >= 3 )
+ {
+ LOG_UPDATE( "Going to sleep" );
- boost::unique_lock< boost::mutex > lock( mMutex );
+ // Locks so we shouldn't have a scoped-lock when calling this
+ PauseVSyncThread();
- while ( mRunning && ( updateFrameNumber == mSyncFrameNumber ) )
+ // Render thread will automatically wait as it relies on update-ahead-of-render count
+
+ // Change the state
+ {
+ ConditionalWait::ScopedLock updateLock( mUpdateThreadWaitCondition );
+
+ // Ensure we weren't stopped while we have been processing
+ if( mState != State::STOPPED )
+ {
+ mState = State::SLEEPING;
+ }
+ }
+
+ // Inform FrameTime that we're going to sleep
+ mFrameTime.Sleep();
+
+ // Wait while we're SLEEPING
+ {
+ ConditionalWait::ScopedLock updateLock( mUpdateThreadWaitCondition );
+ while( mState == State::SLEEPING )
+ {
+ mUpdateThreadWaitCondition.Wait( updateLock );
+ }
+ }
+
+ ////////////////////////
+ // WAKE UP
+ ////////////////////////
+
+ LOG_UPDATE( "Waking Up" );
+
+ // Clear V-Sync-ahead-of-update-count
+ {
+ ConditionalWait::ScopedLock updateLock( mUpdateThreadWaitCondition );
+ mVSyncAheadOfUpdate = 0;
+ }
+
+ // Restart the v-sync-thread, locks so we shouldn't have a scoped-lock
+ RunVSyncThread();
+
+ // Reset try-to-sleep count
+ mTryToSleepCount = 0;
+
+ // Inform frame timer that we've woken up
+ mFrameTime.WakeUp();
+ }
+ }
+ else
{
- // Wait will atomically add the thread to the set of threads waiting on
- // the condition variable mVSyncReceivedCondition and unlock the mutex.
- mVSyncReceivedCondition.wait( lock );
+ mTryToSleepCount = 0;
}
-
- // reset update while paused flag
- mAllowUpdateWhilePaused = false;
}
-bool ThreadSynchronization::VSyncNotifierSyncWithUpdateAndRender( bool validSync, unsigned int frameNumber, unsigned int seconds, unsigned int microseconds, unsigned int& numberOfVSyncsPerRender )
+void ThreadSynchronization::UpdateWaitIfReplacingSurface()
{
- // This may have changed since the last sync. Update VSyncNotifier's copy here if so.
- if( numberOfVSyncsPerRender != mNumberOfVSyncsPerRender )
+ bool replacingSurface = false;
{
- numberOfVSyncsPerRender = mNumberOfVSyncsPerRender; // save it back
- mFrameTime.SetMinimumFrameTimeInterval( mNumberOfVSyncsPerRender * TIME_PER_FRAME_IN_MICROSECONDS );
+ ConditionalWait::ScopedLock updateLock( mUpdateThreadWaitCondition );
+ replacingSurface = ( mState == State::REPLACING_SURFACE );
}
-
- if( validSync )
+ while( replacingSurface )
{
- mFrameTime.SetSyncTime( frameNumber );
- }
-
- boost::unique_lock< boost::mutex > lock( mMutex );
+ LOG_UPDATE_TRACE_FMT( "REPLACING SURFACE" );
- mSyncFrameNumber = frameNumber;
- mSyncSeconds = seconds;
- mSyncMicroseconds = microseconds;
+ // Locks so should not be called while we have a scoped-lock
+ PauseVSyncThread();
- mVSyncReceivedCondition.notify_all();
+ // One last check before we actually wait in case the state has changed since we checked earlier
+ {
+ ConditionalWait::ScopedLock updateLock( mUpdateThreadWaitCondition );
+ replacingSurface = ( mState == State::REPLACING_SURFACE );
+ if( replacingSurface )
+ {
+ mUpdateThreadWaitCondition.Wait( updateLock );
+ }
+ }
- AddPerformanceMarker( PerformanceInterface::VSYNC );
+ {
+ ConditionalWait::ScopedLock updateLock( mUpdateThreadWaitCondition );
+ mVSyncAheadOfUpdate = 0;
+ }
- while( mRunning && // sleep on condition variable WHILE still running
- !mAllowUpdateWhilePaused && // AND NOT allowing updates while paused
- ( mVSyncSleep || mPaused ) ) // AND sleeping OR paused
- {
- // Wait will atomically add the thread to the set of threads waiting on
- // the condition variable mVSyncSleepCondition and unlock the mutex.
- mVSyncSleepCondition.wait( lock );
+ // Locks so should not be called while we have a scoped-lock
+ RunVSyncThread();
}
+}
+
+bool ThreadSynchronization::IsUpdateThreadResuming()
+{
+ ConditionalWait::ScopedLock updateLock( mUpdateThreadWaitCondition );
+ return mUpdateThreadResuming;
+}
- return mRunning;
+bool ThreadSynchronization::IsUpdateThreadStopping()
+{
+ ConditionalWait::ScopedLock updateLock( mUpdateThreadWaitCondition );
+ return ( mState == State::STOPPED );
}
-unsigned int ThreadSynchronization::GetFrameNumber() const
+bool ThreadSynchronization::MaximumUpdateAheadOfRenderReached()
{
- return mSyncFrameNumber;
+ ConditionalWait::ScopedLock lock( mRenderThreadWaitCondition );
+ return mUpdateAheadOfRender >= mMaximumUpdateCount;
}
-uint64_t ThreadSynchronization::GetTimeMicroseconds()
+void ThreadSynchronization::StopAllThreads()
{
- uint64_t currentTime(0);
+ LOG_UPDATE_TRACE;
- {
- boost::unique_lock< boost::mutex > lock( mMutex );
+ // Lock so we shouldn't have a scoped-lock when calling these methods
+ StopVSyncThread();
+ StopRenderThread();
+}
- currentTime = mSyncSeconds;
- currentTime *= MICROSECONDS_PER_SECOND;
- currentTime += mSyncMicroseconds;
+void ThreadSynchronization::RunVSyncThread()
+{
+ {
+ ConditionalWait::ScopedLock lock( mVSyncThreadWaitCondition );
+ mVSyncThreadRunning = TRUE;
}
+ mVSyncThreadWaitCondition.Notify();
+}
- return currentTime;
+void ThreadSynchronization::PauseVSyncThread()
+{
+ ConditionalWait::ScopedLock lock( mVSyncThreadWaitCondition );
+ mVSyncThreadRunning = FALSE;
}
-void ThreadSynchronization::SetRenderRefreshRate( unsigned int numberOfVSyncsPerRender )
+void ThreadSynchronization::StopVSyncThread()
{
- mNumberOfVSyncsPerRender = numberOfVSyncsPerRender;
+ {
+ ConditionalWait::ScopedLock lock( mVSyncThreadWaitCondition );
+ mVSyncThreadStop = TRUE;
+ }
+ mVSyncThreadWaitCondition.Notify();
}
-inline void ThreadSynchronization::AddPerformanceMarker( PerformanceInterface::MarkerType type )
+void ThreadSynchronization::StopRenderThread()
{
- if( mPerformanceInterface )
{
- mPerformanceInterface->AddMarker( type );
+ ConditionalWait::ScopedLock lock( mRenderThreadWaitCondition );
+ mRenderThreadStop = TRUE;
}
+ mRenderThreadWaitCondition.Notify();
}
-void ThreadSynchronization::PredictNextSyncTime(
- float& lastFrameDeltaSeconds,
- unsigned int& lastSyncTimeMilliseconds,
- unsigned int& nextSyncTimeMilliseconds )
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Called by V-Sync Thread
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+bool ThreadSynchronization::IsVSyncThreadRunning()
{
- mFrameTime.PredictNextSyncTime( lastFrameDeltaSeconds, lastSyncTimeMilliseconds, nextSyncTimeMilliseconds );
+ ConditionalWait::ScopedLock lock( mVSyncThreadWaitCondition );
+ return ! mVSyncThreadStop;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Called by Render Thread
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+bool ThreadSynchronization::IsRenderThreadRunning()
+{
+ ConditionalWait::ScopedLock lock( mRenderThreadWaitCondition );
+ return ! mRenderThreadStop;
+}
+
+bool ThreadSynchronization::IsRenderThreadReplacingSurface()
+{
+ ConditionalWait::ScopedLock lock( mRenderThreadWaitCondition );
+ return mRenderThreadReplacingSurface;
}
} // namespace Adaptor
-#ifndef __DALI_INTERNAL_UPDATE_RENDER_SYNCHRONIZATION_H__
-#define __DALI_INTERNAL_UPDATE_RENDER_SYNCHRONIZATION_H__
+#ifndef __DALI_INTERNAL_THREAD_SYNCHRONIZATION_H__
+#define __DALI_INTERNAL_THREAD_SYNCHRONIZATION_H__
/*
* Copyright (c) 2015 Samsung Electronics Co., Ltd.
*/
// EXTERNAL INCLUDES
-#include <stdint.h>
-#include <boost/thread.hpp>
-#include <boost/thread/condition_variable.hpp>
// INTERNAL INCLUDES
#include <base/interfaces/performance-interface.h>
+#include <base/conditional-wait.h>
#include <trigger-event-interface.h>
#include <base/frame-time.h>
#include <base/render-thread.h>
class RenderSurface;
-namespace Integration
-{
-
-class Core;
-class PlatformAbstraction;
-
-} // namespace Integration
-
namespace Internal
{
*/
~ThreadSynchronization();
+ /////////////////////////////////////////////////////////////////////////////////////////////////
+ // Called by the Event Thread
+ /////////////////////////////////////////////////////////////////////////////////////////////////
+
/**
- * Start the threads
+ * Initialises the ThreadSynchronisation class. The expectation is that this function will be
+ * called when all threads are about to be set up and that Start will be called when
+ * the the scene is actually prepared and ready to be displayed. This way our first update
+ * will have the actual scene we require.
+ *
+ * @note Should only be called by the Event Thread.
+ */
+ void Initialise();
+
+ /**
+ * Starts running all threads. This waits until all threads are up and running.
+ *
+ * @pre A call to Initialise has been made.
+ *
+ * @note Should only be called by the Event Thread.
*/
void Start();
/**
- * Stop the threads
+ * Stop the threads.
+ *
+ * @note Should only be called by the Event Thread.
*/
void Stop();
/**
- * Pause the controller (and threads)
+ * Pause the controller (and threads).
+ *
+ * @note Should only be called by the Event Thread.
*/
void Pause();
/**
- * Resume the controller (and threads)
+ * Resume the controller (and threads).
+ *
+ * @note Should only be called by the Event Thread.
*/
void Resume();
/**
- * Resume the frame time predictor
- */
- void ResumeFrameTime();
-
- /**
* Wake update thread if sleeping. If the update thread is not sleeping
- * this becomes a noop.
+ * this becomes a no-op.
* Called when an update is requested by Core.
* i.e. when a batch of messages have been queued for the next update.
+ *
+ * @note Should only be called by the Event Thread.
*/
- void UpdateRequested();
+ void UpdateRequest();
/**
* Update once (even if paused)
+ *
+ * @note Should only be called by the Event Thread.
*/
- void UpdateWhilePaused();
+ void UpdateOnce();
/**
* 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.
+ *
+ * @note Should only be called by the Event Thread.
*/
- bool ReplaceSurface( RenderSurface* newSurface );
+ void 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.
+ * Set the refresh rate for rendering
*
- * @param[in] newSurface The new surface for rendering.
+ * @param[in] numberOfVSyncsPerRender The number of vsync frames per render
+ *
+ * @note Should only be called by the Event Thread.
*/
- bool NewSurface( RenderSurface* newSurface );
+ void SetRenderRefreshRate( unsigned int numberOfVSyncsPerRender );
+
+ /////////////////////////////////////////////////////////////////////////////////////////////////
+ // Called by the Update Thread
+ /////////////////////////////////////////////////////////////////////////////////////////////////
/**
- * Called by Update thread before it runs the update. This is the point where we can pause
+ * Called by Update thread when it is ready to run the update.
+ *
+ * @param[in] notifyEvent Whether the event thread should be woken up.
+ * @param[in] runUpdate Whether to run another update. If false, then the update-thread will attempt to sleep.
+ * @param[out] lastFrameDeltaSeconds The delta, in seconds (with float precision), between the last two renders.
+ * @param[out] lastSyncTimeMilliseconds The time, in milliseconds, of the last Sync.
+ * @param[out] nextSyncTimeMilliseconds The estimated time, in milliseconds, at the next Sync.
+ * @return true if updating should continue, false if the update-thread should quit.
+ *
+ * @note Should only be called by the Update thread.
*/
- void UpdateReadyToRun();
+ bool UpdateReady( bool notifyEvent, bool runUpdate, float& lastFrameDeltaSeconds, unsigned int& lastSyncTimeMilliseconds, unsigned int& nextSyncTimeMilliseconds );
+
+ /////////////////////////////////////////////////////////////////////////////////////////////////
+ // Called by the Render Thread
+ /////////////////////////////////////////////////////////////////////////////////////////////////
/**
- * Called after an update has completed, to inform render-thread a buffer is ready to render.
- * The function also waits for a free buffer to become available before returning.
- * @pre Called by update thread only.
- * @param[in] notifyEvent Whether the event thread should be woken up.
- * @param[out] renderNeedsUpdate Whether the render task requires another update.
- * @return True if updating should continue, false if the update-thread should quit.
+ * Called by the Render thread when it is ready to render.
+ *
+ * @param[in] request Pointer to set if there are any requests. This does not need to be freed by the caller.
+ *
+ * @note Should only be called by the Render thread.
+ * @note If there is a request, then the Render thread should NOT perform a Render and only process the request
*/
- bool UpdateSyncWithRender( bool notifyEvent, bool& renderNeedsUpdate );
+ bool RenderReady( RenderRequest*& request );
/**
- * Called by update thread to wait for all rendering to finish.
- * Used by update to check the status of the final render before pausing.
- * @pre Called by update thread only.
+ * Called by the Render thread to inform the synchronization class that the surface has been replaced.
+ *
+ * @note Should only be called by the Render thread.
*/
- void UpdateWaitForAllRenderingToFinish();
+ void RenderInformSurfaceReplaced();
+
+ /////////////////////////////////////////////////////////////////////////////////////////////////
+ // Called by the V-Sync Thread
+ /////////////////////////////////////////////////////////////////////////////////////////////////
/**
- * Try block the update-thread when there's nothing to update.
- * @return True if updating should continue, false if the update-thread should quit.
+ * Called by the VSync notifier thread so it can sleep if Update/Render threads are sleeping/paused
+ *
+ * @param[in] validSync True if the sync was valid (@see VSyncMonitor::DoSync)
+ * @param[in] frameNumber The current frame number
+ * @param[in] seconds The current time
+ * @param[in] microseconds The current time
+ * @param[out] numberOfVSyncsPerRender The number of frames per render.
+ * @return true if VSync monitoring/notifications should continue.
+ *
+ * @note Should only be called by the VSync thread.
+ * @note The first call to this method should be BEFORE the actual VSync so a thread-sync point can be established (and startup time is not delayed).
*/
- bool UpdateTryToSleep();
+ bool VSyncReady( bool validSync, unsigned int frameNumber, unsigned int seconds, unsigned int microseconds, unsigned int& numberOfVSyncsPerRender );
+
+ /////////////////////////////////////////////////////////////////////////////////////////////////
+ // Called by ALL Threads
+ /////////////////////////////////////////////////////////////////////////////////////////////////
/**
- * 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
+ * Helper to add a performance marker to the performance server (if it's active)
+ * @param type performance marker type
*/
- bool RenderSyncWithRequest(RenderRequest*& request );
+ void AddPerformanceMarker( PerformanceInterface::MarkerType type );
+
+private:
+
+ // Undefined copy constructor.
+ ThreadSynchronization( const ThreadSynchronization& );
+
+ // Undefined assignment operator.
+ ThreadSynchronization& operator=( const ThreadSynchronization& );
+
+ /////////////////////////////////////////////////////////////////////////////////////////////////
+ // Called by ALL Threads
+ /////////////////////////////////////////////////////////////////////////////////////////////////
/**
- * 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
- * @return True if rendering should continue, false if the render-thread should quit.
+ * Called by the update, render & v-sync thread when they up and running.
+ * This will lock the mutex in mEventThreadWaitCondition.
*/
- bool RenderSyncWithUpdate( RenderRequest*& request);
+ inline void NotifyThreadInitialised();
+
+ /////////////////////////////////////////////////////////////////////////////////////////////////
+ // Called by Update Thread
+ /////////////////////////////////////////////////////////////////////////////////////////////////
/**
- * 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.
- * @param[in] updateRequired Whether a further update is required.
- * @param[in] requestProcessed True if a render request was processed this frame
+ * Called by the update-thread when the state is State::INITIALISING.
+ * Calls methods that lock and locks itself so should NOT be called while a scoped-lock is held.
*/
- void RenderFinished( bool updateRequired, bool requestProcessed );
+ void UpdateInitialising();
/**
- * Called by the render/update threads to wait for a Synchronization
+ * Called by the update thread to attempt to sleep.
+ * @param[in] runUpdate Whether to run another update. If false, then the update-thread will attempt to sleep.
*/
- void WaitSync();
+ void UpdateTryToSleep( bool runUpdate );
/**
- * Called by the VSync notifier thread so it can sleep if Update/Render threads are sleeping/paused
- * @param[in] validSync True if the sync was valid (@see VSyncMonitor::DoSync)
- * @param[in] frameNumber The current frame number
- * @param[in] seconds The current time
- * @param[in] microseconds The current time
- * @param[out] numberOfVSyncsPerRender The number of frames per render.
- * @return true if VSync monitoring/notifications should continue.
+ * Called by the update thread to wait while the render-surface is being replaced.
+ * Calls methods that lock and locks itself so should NOT be called while a scoped-lock is held.
*/
- bool VSyncNotifierSyncWithUpdateAndRender( bool validSync, unsigned int frameNumber, unsigned int seconds, unsigned int microseconds, unsigned int& numberOfVSyncsPerRender );
+ void UpdateWaitIfReplacingSurface();
/**
- * Sets the expected minimum frame time interval.
- * @param[in] interval The interval in microseconds.
+ * Called by the update thread to check if we're just resuming.
+ * This will lock the mutex in mUpdateThreadWaitCondition.
*/
- void SetMinimumFrameTimeInterval( unsigned int timeInterval );
+ inline bool IsUpdateThreadResuming();
/**
- * Predicts when the next render time will occur.
+ * Called by the update thread to check if the update thread should be running.
+ * This will lock the mutex in mUpdateThreadWaitCondition.
*
- * @param[out] lastFrameDeltaSeconds The delta, in seconds (with float precision), between the last two renders.
- * @param[out] lastSyncTimeMilliseconds The time, in milliseconds, of the last Sync.
- * @param[out] nextSyncTimeMilliseconds The estimated time, in milliseconds, at the next Sync.
- *
- * @note Should only be called once per tick, from the update thread.
+ * @return True if we're stopping, false otherwise.
*/
- void PredictNextSyncTime( float& lastFrameDeltaSeconds,
- unsigned int& lastSyncTimeMilliseconds,
- unsigned int& nextSyncTimeMilliseconds );
+ inline bool IsUpdateThreadStopping();
/**
- * Retrieves the last sync frame number.
- * This is a count of the number of synchronised update/render
- * frames, not a count of hardware VSync frames.
+ * Called by the update thread to check if we've filled all update buffers.
+ * This will lock the mutex in mRenderThreadWaitCondition.
*
- * @return The sync frame number.
+ * @return True if all update buffers are full.
*/
- unsigned int GetFrameNumber() const;
+ inline bool MaximumUpdateAheadOfRenderReached();
/**
- * Retrieves the time (in microseconds) at the last VSync
- * @return The VSync timestamp in microseconds.
+ * Called by the update thread when we are about to stop.
+ * This will call other functions which lock various conditional wait mutexes.
*/
- uint64_t GetTimeMicroseconds();
+ inline void StopAllThreads();
/**
- * Set the refresh rate for rendering
- * @param[in] numberOfVSyncsPerRender The number of vsync frames per render
+ * Runs the V-Sync Thread.
+ * This will lock the mutex in mVSyncThreadWaitCondition.
*/
- void SetRenderRefreshRate( unsigned int numberOfVSyncsPerRender );
+ inline void RunVSyncThread();
-private:
+ /**
+ * Pauses the V-Sync Thread.
+ * This will lock the mutex in mVSyncThreadWaitCondition.
+ */
+ inline void PauseVSyncThread();
- // Undefined copy constructor.
- ThreadSynchronization( const ThreadSynchronization& );
+ /**
+ * Stops the V-Sync Thread.
+ * This will lock the mutex in mVSyncThreadWaitCondition.
+ */
+ inline void StopVSyncThread();
- // Undefined assignment operator.
- ThreadSynchronization& operator=( const ThreadSynchronization& );
+ /**
+ * Stops the Render Thread.
+ * This will lock the mutex in mRenderThreadWaitCondition.
+ */
+ inline void StopRenderThread();
+
+ /////////////////////////////////////////////////////////////////////////////////////////////////
+ // Called by V-Sync Thread
+ /////////////////////////////////////////////////////////////////////////////////////////////////
/**
- * Helper to add a performance marker to the performance server (if its active)
- * @param type performance marker type
+ * Checks if the V-Sync thread should be running.
+ * This will lock the mutex in mVSyncThreadWaitCondition.
+ *
+ * @return true if running, false otherwise.
*/
- void AddPerformanceMarker( PerformanceInterface::MarkerType type );
+ inline bool IsVSyncThreadRunning();
-private:
+ /////////////////////////////////////////////////////////////////////////////////////////////////
+ // Called by Render Thread
+ /////////////////////////////////////////////////////////////////////////////////////////////////
- const unsigned int mMaximumUpdateCount; ///< How many frames may be prepared, ahead of the rendering.
+ /**
+ * Checks if the Render thread should be running.
+ * This will lock the mutex in mRenderThreadWaitCondition.
+ *
+ * @return true if running, false otherwise.
+ */
+ inline bool IsRenderThreadRunning();
- unsigned int mNumberOfVSyncsPerRender; ///< How many frames for each update/render cycle.
+ /**
+ * Checks if the render thread should be replacing the surface
+ * This will lock the mutex in mRenderThreadWaitCondition.
+ *
+ * @return true if the render thread should be replacing the surface, false otherwise.
+ */
+ inline bool IsRenderThreadReplacingSurface();
+
+private:
- volatile unsigned int mUpdateReadyCount; ///< Incremented after each update, decremented after each render (protected by mMutex)
- // ARM CPUs perform aligned 32 bit read/writes atomically, so the following variables do not require mutex protection on modification
- volatile int mRunning; ///< Used during UpdateThread::Stop() to exit the update & render loops
- volatile int mUpdateRequired; ///< Used to inform the update thread, that render requires another update
- volatile int mPaused; ///< The paused flag
- volatile int mUpdateRequested; ///< An update has been requested
- volatile int mAllowUpdateWhilePaused; ///< whether to allow (one) update while paused
- volatile int mVSyncSleep; ///< Set true when the VSync thread should sleep
- volatile unsigned int mSyncFrameNumber; ///< Frame number of latest Sync
- volatile unsigned int mSyncSeconds; ///< Timestamp (seconds) of latest Sync
- volatile unsigned int mSyncMicroseconds; ///< Timestamp (microseconds) of latest Sync
-
- boost::mutex mMutex; ///< This mutex must be locked before reading/writing mUpdateReadyCount
- boost::condition_variable mUpdateFinishedCondition; ///< The render thread waits for this condition
- boost::condition_variable mUpdateSleepCondition; ///< The update thread waits for this condition when sleeping
- boost::condition_variable mRenderFinishedCondition; ///< The update thread waits for this condition
- 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 mRenderRequestSleepCondition; ///< The render thread waits for this condition
- boost::condition_variable mRenderRequestFinishedCondition;///< The controller waits for this condition
+ struct State
+ {
+ enum Type
+ {
+ STOPPED,
+ INITIALISING,
+ RUNNING,
+ PAUSED,
+ SLEEPING,
+ REPLACING_SURFACE
+ };
+ };
FrameTime mFrameTime; ///< Frame timer predicts next vsync time
TriggerEventInterface& mNotificationTrigger; ///< Reference to notification event trigger
PerformanceInterface* mPerformanceInterface; ///< The performance logging interface
+ ReplaceSurfaceRequest mReplaceSurfaceRequest; ///< Holder for a replace surface request
+
+ ConditionalWait mUpdateThreadWaitCondition; ///< The wait condition for the update-thread.
+ ConditionalWait mRenderThreadWaitCondition; ///< The wait condition for the render-thread.
+ ConditionalWait mVSyncThreadWaitCondition; ///< The wait condition for the v-sync-thread.
+ ConditionalWait mEventThreadWaitCondition; ///< The wait condition for the event-thread.
+
+ const int mMaximumUpdateCount; ///< How many frames may be prepared, ahead of the rendering.
+ unsigned int mNumberOfVSyncsPerRender; ///< How many frames for each update/render cycle.
+
+ unsigned int mTryToSleepCount; ///< Count to ensure we don't go to sleep too early
+
+ volatile State::Type mState; ///< The current state of synchronisation (set & read by both the event & update threads).
+
+ volatile int mVSyncAheadOfUpdate; ///< The number of frames vsync is ahead of update (set & read by both the v-sync & update threads).
+ volatile int mUpdateAheadOfRender; ///< The number of frames update is ahead of render (set & read by both the update & render threads).
+ volatile int mNumberOfThreadsStarted; ///< The number of threads that are initialised and running (set by update, v-sync & render threads, read by event-thread).
+
+ //
+ // NOTE: cannot use booleans as these are used from multiple threads, must use variable with machine word size for atomic read/write
+ //
+
+ volatile unsigned int mUpdateThreadResuming; ///< Whether the update-thread is resuming.
+
+ volatile unsigned int mVSyncThreadRunning; ///< Whether the V-Sync thread is running (set by the update-thread, read by v-sync-thread).
+ volatile unsigned int mVSyncThreadStop; ///< Whether the V-Sync thread should be stopped (set by the update-thread, read by the v-sync-thread).
+
+ volatile unsigned int mRenderThreadStop; ///< Whether the render-thread should be stopped (set by the update-thread, read by the render-thread).
+ volatile unsigned int mRenderThreadReplacingSurface;///< Whether the render-thread should replace the surface (set by the event & render threads, read by the render-thread).
- ReplaceSurfaceRequest mReplaceSurfaceRequest; ///< Holder for a replace surface request
- bool mReplaceSurfaceRequested; ///< True if there is a new replace surface request
+ volatile unsigned int mEventThreadSurfaceReplaced; ///< Checked by the event-thread & set by the render-thread when the surface has been replaced (set by the event & render threads, read by the event-thread).
+ unsigned int mVSyncThreadInitialised; ///< Whether the V-Sync thread has been initialised (only used by v-sync-thread).
+ unsigned int mRenderThreadInitialised; ///< Whether the render-thread has been initialised (only used by the render-thread).
+ unsigned int mRenderThreadSurfaceReplaced; ///< Whether the render-thread has replaced the surface (only used by render-thread).
}; // class ThreadSynchronization
} // namespace Adaptor
} // namespace Dali
-#endif // __DALI_INTERNAL_UPDATE_RENDER_SYNCHRONIZATION_H__
+#endif // __DALI_INTERNAL_THREAD_SYNCHRONIZATION_H__
UpdateThread::UpdateThread( ThreadSynchronization& sync,
AdaptorInternalServices& adaptorInterfaces,
const EnvironmentOptions& environmentOptions )
-: mThreadSync( sync ),
+: mThreadSynchronization( sync ),
mCore( adaptorInterfaces.GetCore()),
mFpsTrackingSeconds( fabsf( environmentOptions.GetFrameRateLoggingFrequency() ) ),
mFrameCount( 0.0f ),
bool UpdateThread::Run()
{
DALI_LOG_INFO( gUpdateLogFilter, Debug::Verbose, "UpdateThread::Run()\n");
- Integration::UpdateStatus status;
- // install a function for logging
+ // Install a function for logging
mEnvironmentOptions.InstallLogFunction();
- bool running( true );
+ Integration::UpdateStatus status;
+ bool runUpdate = true;
+ float lastFrameDelta( 0.0f );
+ unsigned int lastSyncTime( 0 );
+ unsigned int nextSyncTime( 0 );
// Update loop, we stay inside here while the update-thread is running
- while ( running )
+ // We also get the last delta and the predict when this update will be rendered
+ while ( mThreadSynchronization.UpdateReady( status.NeedsNotification(), runUpdate, lastFrameDelta, lastSyncTime, nextSyncTime ) )
{
- DALI_LOG_INFO( gUpdateLogFilter, Debug::Verbose, "UpdateThread::Run. 1 - Sync()\n");
-
- // Inform synchronization object update is ready to run, this will pause update thread if required.
- mThreadSync.UpdateReadyToRun();
- DALI_LOG_INFO( gUpdateLogFilter, Debug::Verbose, "UpdateThread::Run. 2 - Ready()\n");
+ DALI_LOG_INFO( gUpdateLogFilter, Debug::Verbose, "UpdateThread::Run. 1 - UpdateReady(delta:%f, lastSync:%u, nextSync:%u)\n", lastFrameDelta, lastSyncTime, nextSyncTime);
- // get the last delta and the predict when this update will be rendered
- float lastFrameDelta( 0.0f );
- unsigned int lastSyncTime( 0 );
- unsigned int nextSyncTime( 0 );
- mThreadSync.PredictNextSyncTime( lastFrameDelta, lastSyncTime, nextSyncTime );
-
- DALI_LOG_INFO( gUpdateLogFilter, Debug::Verbose, "UpdateThread::Run. 3 - Update(delta:%f, lastSync:%u, nextSync:%u)\n", lastFrameDelta, lastSyncTime, nextSyncTime);
+ DALI_LOG_INFO( gUpdateLogFilter, Debug::Verbose, "UpdateThread::Run. 2 - Core.Update()\n");
+ mThreadSynchronization.AddPerformanceMarker( PerformanceInterface::UPDATE_START );
mCore.Update( lastFrameDelta, lastSyncTime, nextSyncTime, status );
+ mThreadSynchronization.AddPerformanceMarker( PerformanceInterface::UPDATE_END );
if( mFpsTrackingSeconds > 0.f )
{
FPSTracking(status.SecondsFromLastFrame());
}
- bool renderNeedsUpdate;
-
- // tell the synchronisation class that a buffer has been written to,
- // and to wait until there is a free buffer to write to
- running = mThreadSync.UpdateSyncWithRender( status.NeedsNotification(), renderNeedsUpdate );
- DALI_LOG_INFO( gUpdateLogFilter, Debug::Verbose, "UpdateThread::Run. 4 - UpdateSyncWithRender complete\n");
+ unsigned int keepUpdatingStatus = status.KeepUpdating();
- if( running )
+ // Optional logging of update/render status
+ if ( mStatusLogInterval )
{
- unsigned int keepUpdatingStatus = status.KeepUpdating();
-
- // Optional logging of update/render status
- if ( mStatusLogInterval )
- {
- UpdateStatusLogging( keepUpdatingStatus, renderNeedsUpdate );
- }
+ UpdateStatusLogging( keepUpdatingStatus );
+ }
- // 2 things can keep update running.
- // - The status of the last update
- // - The status of the last render
- bool runUpdate = (Integration::KeepUpdating::NOT_REQUESTED != keepUpdatingStatus) || renderNeedsUpdate;
+ // 2 things can keep update running.
+ // - The status of the last update
+ // - The status of the last render
+ runUpdate = (Integration::KeepUpdating::NOT_REQUESTED != keepUpdatingStatus);
- if( !runUpdate )
- {
- DALI_LOG_INFO( gUpdateLogFilter, Debug::Verbose, "UpdateThread::Run. 5 - Nothing to update, trying to sleep\n");
+ DALI_LOG_INFO( gUpdateLogFilter, Debug::Verbose, "UpdateThread::Run. 3 - runUpdate(%d)\n", runUpdate );
- running = mThreadSync.UpdateTryToSleep();
- }
- }
+ // Reset time variables
+ lastFrameDelta = 0.0f;
+ lastSyncTime = 0;
+ nextSyncTime = 0;
}
- // uninstall a function for logging
+ // Uninstall the logging function
mEnvironmentOptions.UnInstallLogFunction();
return true;
}
}
-void UpdateThread::UpdateStatusLogging( unsigned int keepUpdatingStatus, bool renderNeedsUpdate )
+void UpdateThread::UpdateStatusLogging( unsigned int keepUpdatingStatus )
{
DALI_ASSERT_ALWAYS( mStatusLogInterval );
oss += "<Render task waiting for completion> ";
}
- if ( renderNeedsUpdate )
- {
- oss += "<Render needs Update> ";
- }
-
DALI_LOG_UPDATE_STATUS( "%s\n", oss.c_str());
}
}
/**
* Optionally output the update thread status.
* @param[in] keepUpdatingStatus Whether the update-thread requested further updates.
- * @param[in] renderNeedsUpdate Whether the render-thread requested another update.
*/
- void UpdateStatusLogging( unsigned int keepUpdatingStatus, bool renderNeedsUpdate );
+ void UpdateStatusLogging( unsigned int keepUpdatingStatus );
/**
* Helper for the thread calling the entry function
private: // Data
- ThreadSynchronization& mThreadSync; ///< Used to synchronize all the threads
+ ThreadSynchronization& mThreadSynchronization; ///< Used to synchronize all the threads
Dali::Integration::Core& mCore; ///< Dali core reference
// CLASS HEADER
#include "vsync-notifier.h"
+// EXTERNAL INCLUDES
+#include <unistd.h>
#include <dali/integration-api/core.h>
#include <dali/integration-api/platform-abstraction.h>
VSyncNotifier::VSyncNotifier( ThreadSynchronization& sync,
AdaptorInternalServices& adaptorInterfaces,
const EnvironmentOptions& environmentOptions )
-: mThreadSync( sync ),
+: mThreadSynchronization( sync ),
mCore( adaptorInterfaces.GetCore() ),
mPlatformAbstraction( adaptorInterfaces.GetPlatformAbstractionInterface() ),
mVSyncMonitor( adaptorInterfaces.GetVSyncMonitorInterface() ),
unsigned int seconds( 0u );
unsigned int microseconds( 0u );
- bool running( true );
- while( running )
+ bool validSync( true );
+ while( mThreadSynchronization.VSyncReady( validSync, frameNumber++, currentSeconds, currentMicroseconds, mNumberOfVSyncsPerRender ) )
{
- DALI_LOG_INFO( gSyncLogFilter, Debug::General, "VSyncNotifier::Run. 1 Start loop \n");
-
- bool validSync( true );
+ DALI_LOG_INFO( gSyncLogFilter, Debug::General, "VSyncNotifier::Run. 1 SyncWithUpdateAndRender(frame#:%d, current Sec:%u current uSec:%u)\n", frameNumber-1, currentSeconds, currentMicroseconds);
// Hardware VSyncs available?
if( mVSyncMonitor->UseHardware() )
DALI_LOG_INFO( gSyncLogFilter, Debug::General, "VSyncNotifier::Run. 2 Start software sync (%d frames, %u microseconds) \n", mNumberOfVSyncsPerRender, sleepTimeInMicroseconds);
usleep( sleepTimeInMicroseconds );
}
-
- DALI_LOG_INFO( gSyncLogFilter, Debug::General, "VSyncNotifier::Run. 3 SyncWithUpdateAndRender(frame#:%d, current Sec:%u current uSec:%u)\n", frameNumber+1, currentSeconds, currentMicroseconds);
-
- running = mThreadSync.VSyncNotifierSyncWithUpdateAndRender( validSync, ++frameNumber, currentSeconds, currentMicroseconds, mNumberOfVSyncsPerRender );
- // The number of vsyncs per render may have been modified by this call.
+ mThreadSynchronization.AddPerformanceMarker( PerformanceInterface::VSYNC );
}
// uninstall a function for logging
private:
- ThreadSynchronization& mThreadSync; ///< Used to synchronize all the threads
+ ThreadSynchronization& mThreadSynchronization; ///< Used to synchronize all the threads
Dali::Integration::Core& mCore; ///< Dali core reference
Integration::PlatformAbstraction& mPlatformAbstraction; ///< The platform abstraction for retrieving the current time etc.
VSyncMonitorInterface* mVSyncMonitor; ///< VSyncMonitor interface
PositionSize size = mSurface->GetPositionSize();
mCore->SurfaceResized( size.width, size.height );
- // Start all the threads
- mThreadController->Start();
+ // Initialize the thread controller
+ mThreadController->Initialize();
mState = RUNNING;
// Only resume the adaptor if we are in the suspended state.
if( PAUSED == mState )
{
- // We put ResumeFrameTime first, as this was originally called at the start of mCore->Resume()
- // If there were events pending, mCore->Resume() will call
- // RenderController->RequestUpdate()
- // ThreadController->RequestUpdate()
- // ThreadSynchronization->RequestUpdate()
- // and we should have reset the frame timers before allowing Core->Update() to be called.
- //@todo Should we call ThreadController->Resume before mCore->Resume()?
-
- mThreadController->ResumeFrameTime();
+ // Resume core first
mCore->Resume();
- mThreadController->Resume();
mState = RUNNING;
}
ProcessCoreEvents(); // Ensure any outstanding messages are processed
+
+ // Ensure our first update includes the processed messages
+ mThreadController->Resume();
}
}
void Adaptor::NotifySceneCreated()
{
GetCore().SceneCreated();
+
+ // Start thread controller after the scene has been created
+ mThreadController->Start();
}
void Adaptor::NotifyLanguageChanged()
/* Do nothing in main implementation */
}
- void PostRender( unsigned int timeDelta )
+ void PostRender()
{
/* Do nothing in main implementation */
}
* @param[in] egl The Egl interface
* @param[in] glAbstraction OpenGLES abstraction interface
* @param[in] displayConnection display connection
- * @param[in] deltaTime Time (in microseconds) since PostRender was last called.
* @param[in] replacingSurface True if the surface is being replaced.
*/
- virtual void PostRender( EglInterface& egl, Integration::GlAbstraction& glAbstraction, DisplayConnection* displayConnection, unsigned int deltaTime, bool replacingSurface ) = 0;
+ virtual void PostRender( EglInterface& egl, Integration::GlAbstraction& glAbstraction, DisplayConnection* displayConnection, bool replacingSurface ) = 0;
/**
* @brief Invoked by render thread when the thread should be stop
/**
* @copydoc Dali::RenderSurface::PostRender()
*/
- virtual void PostRender( EglInterface& egl, Integration::GlAbstraction& glAbstraction, DisplayConnection* displayConnection, unsigned int deltaTime, bool replacingSurface ) = 0;
+ virtual void PostRender( EglInterface& egl, Integration::GlAbstraction& glAbstraction, DisplayConnection* displayConnection, bool replacingSurface ) = 0;
/**
* @copydoc Dali::RenderSurface::ReleaseLock()
/**
* @copydoc Dali::RenderSurface::PostRender()
*/
- virtual void PostRender( EglInterface& egl, Integration::GlAbstraction& glAbstraction, DisplayConnection* displayConnection, unsigned int deltaTime, bool replacingSurface );
+ virtual void PostRender( EglInterface& egl, Integration::GlAbstraction& glAbstraction, DisplayConnection* displayConnection, bool replacingSurface );
/**
* @copydoc Dali::RenderSurface::StopRender()
/**
* @copydoc Dali::RenderSurface::PostRender()
*/
- virtual void PostRender( EglInterface& egl, Integration::GlAbstraction& glAbstraction, DisplayConnection* displayConnection, unsigned int deltaTime, bool replacingSurface ) = 0;
+ virtual void PostRender( EglInterface& egl, Integration::GlAbstraction& glAbstraction, DisplayConnection* displayConnection, bool replacingSurface ) = 0;
/**
* @copydoc Dali::RenderSurface::ReleaseLock()
/**
* @copydoc Dali::RenderSurface::PostRender()
*/
- virtual void PostRender( EglInterface& egl, Integration::GlAbstraction& glAbstraction, DisplayConnection* displayConnection, unsigned int deltaTime, bool replacingSurface );
+ virtual void PostRender( EglInterface& egl, Integration::GlAbstraction& glAbstraction, DisplayConnection* displayConnection, bool replacingSurface );
/**
* @copydoc Dali::RenderSurface::StopRender()
return true;
}
-void PixmapRenderSurface::PostRender( EglInterface& egl, Integration::GlAbstraction& glAbstraction, DisplayConnection* displayConnection, unsigned int deltaTime, bool replacingSurface )
+void PixmapRenderSurface::PostRender( EglInterface& egl, Integration::GlAbstraction& glAbstraction, DisplayConnection* displayConnection, bool replacingSurface )
{
// flush gl instruction queue
glAbstraction.Flush();
return true;
}
-void WindowRenderSurface::PostRender( EglInterface& egl, Integration::GlAbstraction& glAbstraction, DisplayConnection* displayConnection, unsigned int deltaTime, bool replacingSurface )
+void WindowRenderSurface::PostRender( EglInterface& egl, Integration::GlAbstraction& glAbstraction, DisplayConnection* displayConnection, bool replacingSurface )
{
Internal::Adaptor::EglImplementation& eglImpl = static_cast<Internal::Adaptor::EglImplementation&>( egl );
eglImpl.SwapBuffers();
/**
* @copydoc Dali::RenderSurface::PostRender()
*/
- virtual void PostRender( EglInterface& egl, Integration::GlAbstraction& glAbstraction, DisplayConnection* displayConnection, unsigned int deltaTime, bool replacingSurface );
+ virtual void PostRender( EglInterface& egl, Integration::GlAbstraction& glAbstraction, DisplayConnection* displayConnection, bool replacingSurface );
/**
* @copydoc Dali::RenderSurface::StopRender()
return true;
}
-void PixmapRenderSurface::PostRender( EglInterface& egl, Integration::GlAbstraction& glAbstraction, DisplayConnection* displayConnection, unsigned int deltaTime, bool replacingSurface )
+void PixmapRenderSurface::PostRender( EglInterface& egl, Integration::GlAbstraction& glAbstraction, DisplayConnection* displayConnection, bool replacingSurface )
{
// flush gl instruction queue
glAbstraction.Flush();
return true;
}
-void WindowRenderSurface::PostRender( EglInterface& egl, Integration::GlAbstraction& glAbstraction, DisplayConnection* displayConnection, unsigned int deltaTime, bool replacingSurface )
+void WindowRenderSurface::PostRender( EglInterface& egl, Integration::GlAbstraction& glAbstraction, DisplayConnection* displayConnection, bool replacingSurface )
{
Internal::Adaptor::EglImplementation& eglImpl = static_cast<Internal::Adaptor::EglImplementation&>( egl );
eglImpl.SwapBuffers();
/**
* @copydoc Dali::RenderSurface::PostRender()
*/
- virtual void PostRender( EglInterface& egl, Integration::GlAbstraction& glAbstraction, DisplayConnection* displayConnection, unsigned int deltaTime, bool replacingSurface );
+ virtual void PostRender( EglInterface& egl, Integration::GlAbstraction& glAbstraction, DisplayConnection* displayConnection, bool replacingSurface );
/**
* @copydoc Dali::RenderSurface::StopRender()
{
}
-void TestGlAbstraction::PostRender(unsigned int timeDelta)
+void TestGlAbstraction::PostRender()
{
}
void Initialize();
void PreRender();
- void PostRender(unsigned int timeDelta);
+ void PostRender();
/* OpenGL ES 2.0 */