2 * Copyright (c) 2015 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 #include "thread-synchronization.h"
22 #include <base/interfaces/adaptor-internal-services.h>
23 #include <base/thread-synchronization-debug.h>
36 const unsigned int TIME_PER_FRAME_IN_MICROSECONDS = 16667;
37 const int TOTAL_THREAD_COUNT = 3;
38 const unsigned int TRUE = 1u;
39 const unsigned int FALSE = 0u;
40 } // unnamed namespace
42 ThreadSynchronization::ThreadSynchronization( AdaptorInternalServices& adaptorInterfaces, unsigned int numberOfVSyncsPerRender)
43 : mFrameTime( adaptorInterfaces.GetPlatformAbstractionInterface() ),
44 mNotificationTrigger( adaptorInterfaces.GetProcessCoreEventsTrigger() ),
45 mPerformanceInterface( adaptorInterfaces.GetPerformanceInterface() ),
46 mReplaceSurfaceRequest(),
47 mUpdateThreadWaitCondition(),
48 mRenderThreadWaitCondition(),
49 mVSyncThreadWaitCondition(),
50 mEventThreadWaitCondition(),
51 mMaximumUpdateCount( adaptorInterfaces.GetCore().GetMaximumUpdateCount()),
52 mNumberOfVSyncsPerRender( numberOfVSyncsPerRender ),
53 mTryToSleepCount( 0u ),
54 mState( State::STOPPED ),
55 mVSyncAheadOfUpdate( 0u ),
56 mUpdateAheadOfRender( 0u ),
57 mNumberOfThreadsStarted( 0u ),
58 mUpdateThreadResuming( FALSE ),
59 mVSyncThreadRunning( FALSE ),
60 mVSyncThreadStop( FALSE ),
61 mRenderThreadStop( FALSE ),
62 mRenderThreadReplacingSurface( FALSE ),
63 mEventThreadSurfaceReplaced( FALSE ),
64 mVSyncThreadInitialised( FALSE ),
65 mRenderThreadInitialised( FALSE ),
66 mRenderThreadSurfaceReplaced( FALSE )
70 ThreadSynchronization::~ThreadSynchronization()
74 ///////////////////////////////////////////////////////////////////////////////////////////////////
76 ///////////////////////////////////////////////////////////////////////////////////////////////////
78 void ThreadSynchronization::Initialise()
82 ConditionalWait::ScopedLock lock( mUpdateThreadWaitCondition );
83 if( mState == State::STOPPED )
85 LOG_EVENT( "INITIALISING" );
86 mState = State::INITIALISING;
90 void ThreadSynchronization::Start()
96 ConditionalWait::ScopedLock lock( mUpdateThreadWaitCondition );
97 if( mState == State::INITIALISING )
103 // Not atomic, but does not matter here as we just want to ensure we only start from State::INITIALISING
106 LOG_EVENT( "STARTING" );
107 mFrameTime.SetMinimumFrameTimeInterval( mNumberOfVSyncsPerRender * TIME_PER_FRAME_IN_MICROSECONDS );
110 ConditionalWait::ScopedLock lock( mEventThreadWaitCondition );
111 while( mNumberOfThreadsStarted < TOTAL_THREAD_COUNT )
113 mEventThreadWaitCondition.Wait( lock );
118 ConditionalWait::ScopedLock lock( mUpdateThreadWaitCondition );
119 mState = State::RUNNING;
121 mUpdateThreadWaitCondition.Notify();
125 void ThreadSynchronization::Stop()
131 ConditionalWait::ScopedLock lock( mUpdateThreadWaitCondition );
132 if( mState != State::STOPPED )
135 mState = State::STOPPED;
139 // Not atomic, but does not matter here as we just want to ensure we do not stop more than once
142 LOG_EVENT( "STOPPING" );
144 // Notify update-thread so that it continues and sets up the other threads to stop as well
145 mUpdateThreadWaitCondition.Notify();
147 mFrameTime.Suspend();
151 void ThreadSynchronization::Pause()
155 bool addPerformanceMarker = false;
157 // Only pause if we're RUNNING or SLEEPING
158 ConditionalWait::ScopedLock lock( mUpdateThreadWaitCondition );
159 if( ( mState == State::RUNNING ) ||
160 ( mState == State::SLEEPING ) )
162 LOG_EVENT( "PAUSING" );
164 mState = State::PAUSED;
166 mUpdateThreadResuming = FALSE;
168 mFrameTime.Suspend();
170 addPerformanceMarker = true;
174 if( addPerformanceMarker )
176 // Can lock so we do not want to have a lock when calling this to avoid deadlocks
177 AddPerformanceMarker( PerformanceInterface::PAUSED );
181 void ThreadSynchronization::Resume()
185 // Only resume if we're PAUSED
188 ConditionalWait::ScopedLock lock( mUpdateThreadWaitCondition );
189 if( mState == State::PAUSED )
192 mState = State::RUNNING;
193 mUpdateThreadResuming = TRUE;
197 // Not atomic, but does not matter here as we just want to ensure we only resume if we're paused
200 LOG_EVENT( "RESUMING" );
204 // Start up Update thread again
205 mUpdateThreadWaitCondition.Notify();
207 // Can lock so we do not want to have a lock when calling this to avoid deadlocks
208 AddPerformanceMarker( PerformanceInterface::RESUME);
212 void ThreadSynchronization::UpdateRequest()
218 ConditionalWait::ScopedLock lock( mUpdateThreadWaitCondition );
219 if( mState == State::SLEEPING )
221 mState = State::RUNNING;
224 mTryToSleepCount = 0;
229 LOG_EVENT( "UPDATE REQUEST" );
230 mUpdateThreadWaitCondition.Notify();
234 void ThreadSynchronization::UpdateOnce()
237 LOG_EVENT( "UPDATE ONCE" );
239 // If we're sleeping then change state to running as this will also wake up the v-sync-thread
241 ConditionalWait::ScopedLock lock( mUpdateThreadWaitCondition );
242 if( mState == State::SLEEPING )
244 mState = State::RUNNING;
248 mUpdateThreadWaitCondition.Notify();
251 void ThreadSynchronization::ReplaceSurface( RenderSurface* newSurface )
255 State::Type previousState( State::STOPPED );
257 ConditionalWait::ScopedLock lock( mUpdateThreadWaitCondition );
258 previousState = mState;
259 mState = State::REPLACING_SURFACE;
263 ConditionalWait::ScopedLock lock( mEventThreadWaitCondition );
264 mEventThreadSurfaceReplaced = FALSE;
268 ConditionalWait::ScopedLock lock( mRenderThreadWaitCondition );
269 mReplaceSurfaceRequest.SetSurface( newSurface );
270 mRenderThreadReplacingSurface = TRUE;
273 // Notify the RenderThread in case it's waiting
274 mRenderThreadWaitCondition.Notify();
277 ConditionalWait::ScopedLock lock( mEventThreadWaitCondition );
279 // Wait for RenderThread to replace the surface
280 while( ! mEventThreadSurfaceReplaced )
282 LOG_EVENT( "Waiting for Surface to be Replaced" );
284 mEventThreadWaitCondition.Wait( lock );
289 ConditionalWait::ScopedLock lock( mUpdateThreadWaitCondition );
290 mState = previousState;
292 mUpdateThreadWaitCondition.Notify();
295 void ThreadSynchronization::SetRenderRefreshRate( unsigned int numberOfVSyncsPerRender )
298 LOG_EVENT( "SET RENDER REFRESH RATE" );
300 mNumberOfVSyncsPerRender = numberOfVSyncsPerRender;
303 ///////////////////////////////////////////////////////////////////////////////////////////////////
305 ///////////////////////////////////////////////////////////////////////////////////////////////////
307 bool ThreadSynchronization::UpdateReady( bool notifyEvent, bool runUpdate, float& lastFrameDeltaSeconds, unsigned int& lastSyncTimeMilliseconds, unsigned int& nextSyncTimeMilliseconds )
311 State::Type state = State::STOPPED;
313 ConditionalWait::ScopedLock updateLock( mUpdateThreadWaitCondition );
322 return false; // Stop update-thread
325 case State::INITIALISING:
327 UpdateInitialising();
333 LOG_UPDATE_TRACE_FMT( "PAUSED" );
335 // Just pause the VSyncThread, locks so we shouldn't have a scoped-lock when calling this
338 // No break, fall through
342 LOG_UPDATE_TRACE_FMT( "RUNNING" );
344 if( IsUpdateThreadResuming() )
346 LOG_UPDATE( "Restarting VSyncThread" );
349 ConditionalWait::ScopedLock updateLock( mUpdateThreadWaitCondition );
350 mUpdateThreadResuming = FALSE;
353 // Restart the VSyncThread, locks so we shouldn't have a scoped-lock when calling this
359 LOG_UPDATE( "Notify Event Thread" );
361 // Do the notifications first so the event thread can start processing them
362 // Tell the event-thread to wake up (if asleep) and send a notification event to Core
363 mNotificationTrigger.Trigger();
366 // Inform render thread
368 ConditionalWait::ScopedLock lock( mRenderThreadWaitCondition );
369 ++mUpdateAheadOfRender;
370 LOG_UPDATE_COUNTER_UPDATE( "updateAheadOfRender(%d)", mUpdateAheadOfRender );
372 mRenderThreadWaitCondition.Notify();
374 // Wait if we've reached the maximum-ahead-of-render count.
375 while( MaximumUpdateAheadOfRenderReached() )
377 LOG_UPDATE( "Maximum Update Ahead of Render: WAIT" );
379 mRenderThreadWaitCondition.Notify(); // Notify the render thread in case it was waiting
382 // Ensure we did not stop while we were waiting previously.
383 ConditionalWait::ScopedLock updateLock( mUpdateThreadWaitCondition );
384 if( mState == State::STOPPED )
386 break; // Break out of while loop
388 mUpdateThreadWaitCondition.Wait( updateLock );
392 // Ensure we have had at least 1 V-Sync before we continue
393 // Ensure we didn't stop while we were previously waiting
395 ConditionalWait::ScopedLock updateLock( mUpdateThreadWaitCondition );
396 if( ( mState != State::STOPPED ) &&
397 ( mVSyncAheadOfUpdate == 0 ) &&
398 ( !mUpdateThreadResuming ) ) // Ensure we don't wait if the update-thread is JUST resuming
400 LOG_VSYNC_COUNTER_UPDATE( " vSyncAheadOfUpdate(%d) WAIT", mVSyncAheadOfUpdate );
401 mUpdateThreadWaitCondition.Wait( updateLock );
405 LOG_VSYNC_COUNTER_UPDATE( " vSyncAheadOfUpdate(%d)", mVSyncAheadOfUpdate );
407 mVSyncAheadOfUpdate = 0;
410 // Try to sleep if we do not require any more updates
411 UpdateTryToSleep( runUpdate );
416 case State::SLEEPING:
417 case State::REPLACING_SURFACE:
423 // Ensure we didn't stop while we were waiting
424 if( IsUpdateThreadStopping() )
426 // Locks so we shouldn't have a scoped-lock when calling this
428 return false; // Stop update-thread
431 // Just wait if we're replacing the surface as the render-thread is busy
432 UpdateWaitIfReplacingSurface();
434 mFrameTime.PredictNextSyncTime( lastFrameDeltaSeconds, lastSyncTimeMilliseconds, nextSyncTimeMilliseconds );
436 return true; // Keep update-thread running
439 ///////////////////////////////////////////////////////////////////////////////////////////////////
441 ///////////////////////////////////////////////////////////////////////////////////////////////////
443 bool ThreadSynchronization::RenderReady( RenderRequest*& requestPtr )
447 if( ! IsRenderThreadReplacingSurface() ) // Call to this function locks so should not be called if we have a scoped-lock
449 if( ! mRenderThreadInitialised )
451 LOG_RENDER( "Initialised" );
453 mRenderThreadInitialised = TRUE;
455 // Notify event thread that this thread is up and running, this locks so we should have a scoped-lock
456 NotifyThreadInitialised();
460 if( mRenderThreadSurfaceReplaced )
462 mRenderThreadSurfaceReplaced = FALSE;
466 // decrement update-ahead-of-render
467 ConditionalWait::ScopedLock renderLock( mRenderThreadWaitCondition );
468 --mUpdateAheadOfRender;
472 // Check if we've had an update, if we haven't then we just wait
473 // Ensure we do not wait if we're supposed to stop
475 ConditionalWait::ScopedLock renderLock( mRenderThreadWaitCondition );
476 if( mUpdateAheadOfRender <= 0 && ! mRenderThreadStop )
478 LOG_UPDATE_COUNTER_RENDER( "updateAheadOfRender(%d) WAIT", mUpdateAheadOfRender );
479 mRenderThreadWaitCondition.Wait( renderLock );
483 LOG_UPDATE_COUNTER_RENDER( "updateAheadOfRender(%d)", mUpdateAheadOfRender );
489 LOG_RENDER( "Just Rendered, now Replacing surface" );
491 // ... also decrement update-ahead-of-render
492 ConditionalWait::ScopedLock renderLock( mRenderThreadWaitCondition );
493 --mUpdateAheadOfRender;
496 // We may have been asked to replace the surface while we were waiting so check again here
497 if( IsRenderThreadReplacingSurface() )
500 LOG_RENDER( "REPLACE SURFACE" );
502 ConditionalWait::ScopedLock renderLock( mRenderThreadWaitCondition );
503 requestPtr = &mReplaceSurfaceRequest;
504 mRenderThreadReplacingSurface = FALSE;
505 mRenderThreadSurfaceReplaced = FALSE;
508 return IsRenderThreadRunning(); // Call to this function locks so should not be called if we have a scoped-lock
511 void ThreadSynchronization::RenderInformSurfaceReplaced()
515 mRenderThreadSurfaceReplaced = TRUE;
517 ConditionalWait::ScopedLock lock( mEventThreadWaitCondition );
518 mEventThreadSurfaceReplaced = TRUE;
520 mEventThreadWaitCondition.Notify();
523 ///////////////////////////////////////////////////////////////////////////////////////////////////
525 ///////////////////////////////////////////////////////////////////////////////////////////////////
527 bool ThreadSynchronization::VSyncReady( bool validSync, unsigned int frameNumber, unsigned int seconds, unsigned int microseconds, unsigned int& numberOfVSyncsPerRender )
532 ConditionalWait::ScopedLock vSyncLock( mVSyncThreadWaitCondition );
533 if( numberOfVSyncsPerRender != mNumberOfVSyncsPerRender )
535 numberOfVSyncsPerRender = mNumberOfVSyncsPerRender; // save it back
536 mFrameTime.SetMinimumFrameTimeInterval( mNumberOfVSyncsPerRender * TIME_PER_FRAME_IN_MICROSECONDS );
541 mFrameTime.SetSyncTime( frameNumber );
545 if( ! mVSyncThreadInitialised )
547 LOG_VSYNC( "Initialised" );
549 mVSyncThreadInitialised = TRUE;
551 // Notify event thread that this thread is up and running, this locks so we should have a scoped-lock
552 NotifyThreadInitialised();
556 // Increment v-sync-ahead-of-update count and inform update-thread
558 ConditionalWait::ScopedLock lock( mUpdateThreadWaitCondition );
559 ++mVSyncAheadOfUpdate;
560 LOG_VSYNC_COUNTER_VSYNC( " vSyncAheadOfUpdate(%d)", mVSyncAheadOfUpdate );
562 mUpdateThreadWaitCondition.Notify();
565 // Ensure update-thread has set us to run before continuing
566 // Ensure we do not wait if we're supposed to stop
568 ConditionalWait::ScopedLock vSyncLock( mVSyncThreadWaitCondition );
569 while( ! mVSyncThreadRunning && ! mVSyncThreadStop )
572 mVSyncThreadWaitCondition.Wait( vSyncLock );
576 return IsVSyncThreadRunning(); // Call to this function locks so should not be called if we have a scoped-lock
579 ///////////////////////////////////////////////////////////////////////////////////////////////////
580 // ALL THREADS: Performance Marker
581 ///////////////////////////////////////////////////////////////////////////////////////////////////
583 void ThreadSynchronization::AddPerformanceMarker( PerformanceInterface::MarkerType type )
585 if( mPerformanceInterface )
587 mPerformanceInterface->AddMarker( type );
591 ///////////////////////////////////////////////////////////////////////////////////////////////////
595 ///////////////////////////////////////////////////////////////////////////////////////////////////
597 ///////////////////////////////////////////////////////////////////////////////////////////////////
598 // Called by ALL Threads
599 ///////////////////////////////////////////////////////////////////////////////////////////////////
601 void ThreadSynchronization::NotifyThreadInitialised()
604 ConditionalWait::ScopedLock lock( mEventThreadWaitCondition );
605 ++mNumberOfThreadsStarted;
607 mEventThreadWaitCondition.Notify();
610 ///////////////////////////////////////////////////////////////////////////////////////////////////
611 // Called by Update Thread
612 ///////////////////////////////////////////////////////////////////////////////////////////////////
614 void ThreadSynchronization::UpdateInitialising()
618 // Notify event thread that this thread is up and running, locks so we shouldn't have a scoped-lock when calling this
619 NotifyThreadInitialised();
621 // Wait for first thread-sync point
623 ConditionalWait::ScopedLock updateLock( mUpdateThreadWaitCondition );
625 while( mState == State::INITIALISING )
627 mUpdateThreadWaitCondition.Wait( updateLock );
631 // Locks so we shouldn't have a scoped-lock when calling this
635 void ThreadSynchronization::UpdateTryToSleep( bool runUpdate )
640 ! IsUpdateThreadResuming() ) // Locks so we shouldn't have a lock, we shouldn't try to sleep if we're JUST resuming
642 LOG_UPDATE( "TryToSleep" );
644 if( ++mTryToSleepCount >= 3 )
646 LOG_UPDATE( "Going to sleep" );
648 // Locks so we shouldn't have a scoped-lock when calling this
651 // Render thread will automatically wait as it relies on update-ahead-of-render count
655 ConditionalWait::ScopedLock updateLock( mUpdateThreadWaitCondition );
657 // Ensure we weren't stopped while we have been processing
658 if( mState != State::STOPPED )
660 mState = State::SLEEPING;
664 // Inform FrameTime that we're going to sleep
667 // Wait while we're SLEEPING
669 ConditionalWait::ScopedLock updateLock( mUpdateThreadWaitCondition );
670 while( mState == State::SLEEPING )
672 mUpdateThreadWaitCondition.Wait( updateLock );
676 ////////////////////////
678 ////////////////////////
680 LOG_UPDATE( "Waking Up" );
682 // Clear V-Sync-ahead-of-update-count
684 ConditionalWait::ScopedLock updateLock( mUpdateThreadWaitCondition );
685 mVSyncAheadOfUpdate = 0;
688 // Restart the v-sync-thread, locks so we shouldn't have a scoped-lock
691 // Reset try-to-sleep count
692 mTryToSleepCount = 0;
694 // Inform frame timer that we've woken up
700 mTryToSleepCount = 0;
704 void ThreadSynchronization::UpdateWaitIfReplacingSurface()
706 bool replacingSurface = false;
708 ConditionalWait::ScopedLock updateLock( mUpdateThreadWaitCondition );
709 replacingSurface = ( mState == State::REPLACING_SURFACE );
711 while( replacingSurface )
713 LOG_UPDATE_TRACE_FMT( "REPLACING SURFACE" );
715 // Locks so should not be called while we have a scoped-lock
718 // One last check before we actually wait in case the state has changed since we checked earlier
720 ConditionalWait::ScopedLock updateLock( mUpdateThreadWaitCondition );
721 replacingSurface = ( mState == State::REPLACING_SURFACE );
722 if( replacingSurface )
724 mUpdateThreadWaitCondition.Wait( updateLock );
729 ConditionalWait::ScopedLock updateLock( mUpdateThreadWaitCondition );
730 mVSyncAheadOfUpdate = 0;
733 // Locks so should not be called while we have a scoped-lock
738 bool ThreadSynchronization::IsUpdateThreadResuming()
740 ConditionalWait::ScopedLock updateLock( mUpdateThreadWaitCondition );
741 return mUpdateThreadResuming;
744 bool ThreadSynchronization::IsUpdateThreadStopping()
746 ConditionalWait::ScopedLock updateLock( mUpdateThreadWaitCondition );
747 return ( mState == State::STOPPED );
750 bool ThreadSynchronization::MaximumUpdateAheadOfRenderReached()
752 ConditionalWait::ScopedLock lock( mRenderThreadWaitCondition );
753 return mUpdateAheadOfRender >= mMaximumUpdateCount;
756 void ThreadSynchronization::StopAllThreads()
760 // Lock so we shouldn't have a scoped-lock when calling these methods
765 void ThreadSynchronization::RunVSyncThread()
768 ConditionalWait::ScopedLock lock( mVSyncThreadWaitCondition );
769 mVSyncThreadRunning = TRUE;
771 mVSyncThreadWaitCondition.Notify();
774 void ThreadSynchronization::PauseVSyncThread()
776 ConditionalWait::ScopedLock lock( mVSyncThreadWaitCondition );
777 mVSyncThreadRunning = FALSE;
780 void ThreadSynchronization::StopVSyncThread()
783 ConditionalWait::ScopedLock lock( mVSyncThreadWaitCondition );
784 mVSyncThreadStop = TRUE;
786 mVSyncThreadWaitCondition.Notify();
789 void ThreadSynchronization::StopRenderThread()
792 ConditionalWait::ScopedLock lock( mRenderThreadWaitCondition );
793 mRenderThreadStop = TRUE;
795 mRenderThreadWaitCondition.Notify();
798 ///////////////////////////////////////////////////////////////////////////////////////////////////
799 // Called by V-Sync Thread
800 ///////////////////////////////////////////////////////////////////////////////////////////////////
802 bool ThreadSynchronization::IsVSyncThreadRunning()
804 ConditionalWait::ScopedLock lock( mVSyncThreadWaitCondition );
805 return ! mVSyncThreadStop;
808 ///////////////////////////////////////////////////////////////////////////////////////////////////
809 // Called by Render Thread
810 ///////////////////////////////////////////////////////////////////////////////////////////////////
812 bool ThreadSynchronization::IsRenderThreadRunning()
814 ConditionalWait::ScopedLock lock( mRenderThreadWaitCondition );
815 return ! mRenderThreadStop;
818 bool ThreadSynchronization::IsRenderThreadReplacingSurface()
820 ConditionalWait::ScopedLock lock( mRenderThreadWaitCondition );
821 return mRenderThreadReplacingSurface;
824 } // namespace Adaptor
826 } // namespace Internal