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 mUpdateThreadWaitCondition.Notify();
242 void ThreadSynchronization::ReplaceSurface( RenderSurface* newSurface )
246 State::Type previousState( State::STOPPED );
248 ConditionalWait::ScopedLock lock( mUpdateThreadWaitCondition );
249 previousState = mState;
250 mState = State::REPLACING_SURFACE;
254 ConditionalWait::ScopedLock lock( mEventThreadWaitCondition );
255 mEventThreadSurfaceReplaced = FALSE;
259 ConditionalWait::ScopedLock lock( mRenderThreadWaitCondition );
260 mReplaceSurfaceRequest.SetSurface( newSurface );
261 mRenderThreadReplacingSurface = TRUE;
264 // Notify the RenderThread in case it's waiting
265 mRenderThreadWaitCondition.Notify();
268 ConditionalWait::ScopedLock lock( mEventThreadWaitCondition );
270 // Wait for RenderThread to replace the surface
271 while( ! mEventThreadSurfaceReplaced )
273 LOG_EVENT( "Waiting for Surface to be Replaced" );
275 mEventThreadWaitCondition.Wait( lock );
280 ConditionalWait::ScopedLock lock( mUpdateThreadWaitCondition );
281 mState = previousState;
283 mUpdateThreadWaitCondition.Notify();
286 void ThreadSynchronization::SetRenderRefreshRate( unsigned int numberOfVSyncsPerRender )
289 LOG_EVENT( "SET RENDER REFRESH RATE" );
291 mNumberOfVSyncsPerRender = numberOfVSyncsPerRender;
294 ///////////////////////////////////////////////////////////////////////////////////////////////////
296 ///////////////////////////////////////////////////////////////////////////////////////////////////
298 bool ThreadSynchronization::UpdateReady( bool notifyEvent, bool runUpdate, float& lastFrameDeltaSeconds, unsigned int& lastSyncTimeMilliseconds, unsigned int& nextSyncTimeMilliseconds )
302 State::Type state = State::STOPPED;
304 ConditionalWait::ScopedLock updateLock( mUpdateThreadWaitCondition );
313 return false; // Stop update-thread
316 case State::INITIALISING:
318 UpdateInitialising();
324 LOG_UPDATE_TRACE_FMT( "PAUSED" );
326 // Just pause the VSyncThread, locks so we shouldn't have a scoped-lock when calling this
329 // No break, fall through
333 LOG_UPDATE_TRACE_FMT( "RUNNING" );
335 if( IsUpdateThreadResuming() )
337 LOG_UPDATE( "Restarting VSyncThread" );
340 ConditionalWait::ScopedLock updateLock( mUpdateThreadWaitCondition );
341 mUpdateThreadResuming = FALSE;
344 // Restart the VSyncThread, locks so we shouldn't have a scoped-lock when calling this
350 LOG_UPDATE( "Notify Event Thread" );
352 // Do the notifications first so the event thread can start processing them
353 // Tell the event-thread to wake up (if asleep) and send a notification event to Core
354 mNotificationTrigger.Trigger();
357 // Inform render thread
359 ConditionalWait::ScopedLock lock( mRenderThreadWaitCondition );
360 ++mUpdateAheadOfRender;
361 LOG_UPDATE_COUNTER_UPDATE( "updateAheadOfRender(%d)", mUpdateAheadOfRender );
363 mRenderThreadWaitCondition.Notify();
365 // Wait if we've reached the maximum-ahead-of-render count.
366 while( MaximumUpdateAheadOfRenderReached() )
368 LOG_UPDATE( "Maximum Update Ahead of Render: WAIT" );
370 mRenderThreadWaitCondition.Notify(); // Notify the render thread in case it was waiting
373 // Ensure we did not stop while we were waiting previously.
374 ConditionalWait::ScopedLock updateLock( mUpdateThreadWaitCondition );
375 if( mState == State::STOPPED )
377 break; // Break out of while loop
379 mUpdateThreadWaitCondition.Wait( updateLock );
383 // Ensure we have had at least 1 V-Sync before we continue
384 // Ensure we didn't stop while we were previously waiting
386 ConditionalWait::ScopedLock updateLock( mUpdateThreadWaitCondition );
387 if( ( mState != State::STOPPED ) &&
388 ( mVSyncAheadOfUpdate == 0 ) &&
389 ( !mUpdateThreadResuming ) ) // Ensure we don't wait if the update-thread is JUST resuming
391 LOG_VSYNC_COUNTER_UPDATE( " vSyncAheadOfUpdate(%d) WAIT", mVSyncAheadOfUpdate );
392 mUpdateThreadWaitCondition.Wait( updateLock );
396 LOG_VSYNC_COUNTER_UPDATE( " vSyncAheadOfUpdate(%d)", mVSyncAheadOfUpdate );
398 mVSyncAheadOfUpdate = 0;
401 // Try to sleep if we do not require any more updates
402 UpdateTryToSleep( runUpdate );
407 case State::SLEEPING:
408 case State::REPLACING_SURFACE:
414 // Ensure we didn't stop while we were waiting
415 if( IsUpdateThreadStopping() )
417 // Locks so we shouldn't have a scoped-lock when calling this
419 return false; // Stop update-thread
422 // Just wait if we're replacing the surface as the render-thread is busy
423 UpdateWaitIfReplacingSurface();
425 mFrameTime.PredictNextSyncTime( lastFrameDeltaSeconds, lastSyncTimeMilliseconds, nextSyncTimeMilliseconds );
427 return true; // Keep update-thread running
430 ///////////////////////////////////////////////////////////////////////////////////////////////////
432 ///////////////////////////////////////////////////////////////////////////////////////////////////
434 bool ThreadSynchronization::RenderReady( RenderRequest*& requestPtr )
438 if( ! IsRenderThreadReplacingSurface() ) // Call to this function locks so should not be called if we have a scoped-lock
440 if( ! mRenderThreadInitialised )
442 LOG_RENDER( "Initialised" );
444 mRenderThreadInitialised = TRUE;
446 // Notify event thread that this thread is up and running, this locks so we should have a scoped-lock
447 NotifyThreadInitialised();
451 if( mRenderThreadSurfaceReplaced )
453 mRenderThreadSurfaceReplaced = FALSE;
457 // decrement update-ahead-of-render
458 ConditionalWait::ScopedLock renderLock( mRenderThreadWaitCondition );
459 --mUpdateAheadOfRender;
463 // Check if we've had an update, if we haven't then we just wait
464 // Ensure we do not wait if we're supposed to stop
466 ConditionalWait::ScopedLock renderLock( mRenderThreadWaitCondition );
467 if( mUpdateAheadOfRender <= 0 && ! mRenderThreadStop )
469 LOG_UPDATE_COUNTER_RENDER( "updateAheadOfRender(%d) WAIT", mUpdateAheadOfRender );
470 mRenderThreadWaitCondition.Wait( renderLock );
474 LOG_UPDATE_COUNTER_RENDER( "updateAheadOfRender(%d)", mUpdateAheadOfRender );
480 LOG_RENDER( "Just Rendered, now Replacing surface" );
482 // ... also decrement update-ahead-of-render
483 ConditionalWait::ScopedLock renderLock( mRenderThreadWaitCondition );
484 --mUpdateAheadOfRender;
487 // We may have been asked to replace the surface while we were waiting so check again here
488 if( IsRenderThreadReplacingSurface() )
491 LOG_RENDER( "REPLACE SURFACE" );
493 ConditionalWait::ScopedLock renderLock( mRenderThreadWaitCondition );
494 requestPtr = &mReplaceSurfaceRequest;
495 mRenderThreadReplacingSurface = FALSE;
496 mRenderThreadSurfaceReplaced = FALSE;
499 return IsRenderThreadRunning(); // Call to this function locks so should not be called if we have a scoped-lock
502 void ThreadSynchronization::RenderInformSurfaceReplaced()
506 mRenderThreadSurfaceReplaced = TRUE;
508 ConditionalWait::ScopedLock lock( mEventThreadWaitCondition );
509 mEventThreadSurfaceReplaced = TRUE;
511 mEventThreadWaitCondition.Notify();
514 ///////////////////////////////////////////////////////////////////////////////////////////////////
516 ///////////////////////////////////////////////////////////////////////////////////////////////////
518 bool ThreadSynchronization::VSyncReady( bool validSync, unsigned int frameNumber, unsigned int seconds, unsigned int microseconds, unsigned int& numberOfVSyncsPerRender )
523 ConditionalWait::ScopedLock vSyncLock( mVSyncThreadWaitCondition );
524 if( numberOfVSyncsPerRender != mNumberOfVSyncsPerRender )
526 numberOfVSyncsPerRender = mNumberOfVSyncsPerRender; // save it back
527 mFrameTime.SetMinimumFrameTimeInterval( mNumberOfVSyncsPerRender * TIME_PER_FRAME_IN_MICROSECONDS );
532 mFrameTime.SetSyncTime( frameNumber );
536 if( ! mVSyncThreadInitialised )
538 LOG_VSYNC( "Initialised" );
540 mVSyncThreadInitialised = TRUE;
542 // Notify event thread that this thread is up and running, this locks so we should have a scoped-lock
543 NotifyThreadInitialised();
547 // Increment v-sync-ahead-of-update count and inform update-thread
549 ConditionalWait::ScopedLock lock( mUpdateThreadWaitCondition );
550 ++mVSyncAheadOfUpdate;
551 LOG_VSYNC_COUNTER_VSYNC( " vSyncAheadOfUpdate(%d)", mVSyncAheadOfUpdate );
553 mUpdateThreadWaitCondition.Notify();
556 // Ensure update-thread has set us to run before continuing
557 // Ensure we do not wait if we're supposed to stop
559 ConditionalWait::ScopedLock vSyncLock( mVSyncThreadWaitCondition );
560 while( ! mVSyncThreadRunning && ! mVSyncThreadStop )
563 mVSyncThreadWaitCondition.Wait( vSyncLock );
567 return IsVSyncThreadRunning(); // Call to this function locks so should not be called if we have a scoped-lock
570 ///////////////////////////////////////////////////////////////////////////////////////////////////
571 // ALL THREADS: Performance Marker
572 ///////////////////////////////////////////////////////////////////////////////////////////////////
574 void ThreadSynchronization::AddPerformanceMarker( PerformanceInterface::MarkerType type )
576 if( mPerformanceInterface )
578 mPerformanceInterface->AddMarker( type );
582 ///////////////////////////////////////////////////////////////////////////////////////////////////
586 ///////////////////////////////////////////////////////////////////////////////////////////////////
588 ///////////////////////////////////////////////////////////////////////////////////////////////////
589 // Called by ALL Threads
590 ///////////////////////////////////////////////////////////////////////////////////////////////////
592 void ThreadSynchronization::NotifyThreadInitialised()
595 ConditionalWait::ScopedLock lock( mEventThreadWaitCondition );
596 ++mNumberOfThreadsStarted;
598 mEventThreadWaitCondition.Notify();
601 ///////////////////////////////////////////////////////////////////////////////////////////////////
602 // Called by Update Thread
603 ///////////////////////////////////////////////////////////////////////////////////////////////////
605 void ThreadSynchronization::UpdateInitialising()
609 // Notify event thread that this thread is up and running, locks so we shouldn't have a scoped-lock when calling this
610 NotifyThreadInitialised();
612 // Wait for first thread-sync point
614 ConditionalWait::ScopedLock updateLock( mUpdateThreadWaitCondition );
616 while( mState == State::INITIALISING )
618 mUpdateThreadWaitCondition.Wait( updateLock );
622 // Locks so we shouldn't have a scoped-lock when calling this
626 void ThreadSynchronization::UpdateTryToSleep( bool runUpdate )
631 ! IsUpdateThreadResuming() ) // Locks so we shouldn't have a lock, we shouldn't try to sleep if we're JUST resuming
633 LOG_UPDATE( "TryToSleep" );
635 if( ++mTryToSleepCount >= 3 )
637 LOG_UPDATE( "Going to sleep" );
639 // Locks so we shouldn't have a scoped-lock when calling this
642 // Render thread will automatically wait as it relies on update-ahead-of-render count
646 ConditionalWait::ScopedLock updateLock( mUpdateThreadWaitCondition );
648 // Ensure we weren't stopped while we have been processing
649 if( mState != State::STOPPED )
651 mState = State::SLEEPING;
655 // Inform FrameTime that we're going to sleep
658 // Wait while we're SLEEPING
660 ConditionalWait::ScopedLock updateLock( mUpdateThreadWaitCondition );
661 while( mState == State::SLEEPING )
663 mUpdateThreadWaitCondition.Wait( updateLock );
667 ////////////////////////
669 ////////////////////////
671 LOG_UPDATE( "Waking Up" );
673 // Clear V-Sync-ahead-of-update-count
675 ConditionalWait::ScopedLock updateLock( mUpdateThreadWaitCondition );
676 mVSyncAheadOfUpdate = 0;
679 // Restart the v-sync-thread, locks so we shouldn't have a scoped-lock
682 // Reset try-to-sleep count
683 mTryToSleepCount = 0;
685 // Inform frame timer that we've woken up
691 mTryToSleepCount = 0;
695 void ThreadSynchronization::UpdateWaitIfReplacingSurface()
697 bool replacingSurface = false;
699 ConditionalWait::ScopedLock updateLock( mUpdateThreadWaitCondition );
700 replacingSurface = ( mState == State::REPLACING_SURFACE );
702 while( replacingSurface )
704 LOG_UPDATE_TRACE_FMT( "REPLACING SURFACE" );
706 // Locks so should not be called while we have a scoped-lock
709 // One last check before we actually wait in case the state has changed since we checked earlier
711 ConditionalWait::ScopedLock updateLock( mUpdateThreadWaitCondition );
712 replacingSurface = ( mState == State::REPLACING_SURFACE );
713 if( replacingSurface )
715 mUpdateThreadWaitCondition.Wait( updateLock );
720 ConditionalWait::ScopedLock updateLock( mUpdateThreadWaitCondition );
721 mVSyncAheadOfUpdate = 0;
724 // Locks so should not be called while we have a scoped-lock
729 bool ThreadSynchronization::IsUpdateThreadResuming()
731 ConditionalWait::ScopedLock updateLock( mUpdateThreadWaitCondition );
732 return mUpdateThreadResuming;
735 bool ThreadSynchronization::IsUpdateThreadStopping()
737 ConditionalWait::ScopedLock updateLock( mUpdateThreadWaitCondition );
738 return ( mState == State::STOPPED );
741 bool ThreadSynchronization::MaximumUpdateAheadOfRenderReached()
743 ConditionalWait::ScopedLock lock( mRenderThreadWaitCondition );
744 return mUpdateAheadOfRender >= mMaximumUpdateCount;
747 void ThreadSynchronization::StopAllThreads()
751 // Lock so we shouldn't have a scoped-lock when calling these methods
756 void ThreadSynchronization::RunVSyncThread()
759 ConditionalWait::ScopedLock lock( mVSyncThreadWaitCondition );
760 mVSyncThreadRunning = TRUE;
762 mVSyncThreadWaitCondition.Notify();
765 void ThreadSynchronization::PauseVSyncThread()
767 ConditionalWait::ScopedLock lock( mVSyncThreadWaitCondition );
768 mVSyncThreadRunning = FALSE;
771 void ThreadSynchronization::StopVSyncThread()
774 ConditionalWait::ScopedLock lock( mVSyncThreadWaitCondition );
775 mVSyncThreadStop = TRUE;
777 mVSyncThreadWaitCondition.Notify();
780 void ThreadSynchronization::StopRenderThread()
783 ConditionalWait::ScopedLock lock( mRenderThreadWaitCondition );
784 mRenderThreadStop = TRUE;
786 mRenderThreadWaitCondition.Notify();
789 ///////////////////////////////////////////////////////////////////////////////////////////////////
790 // Called by V-Sync Thread
791 ///////////////////////////////////////////////////////////////////////////////////////////////////
793 bool ThreadSynchronization::IsVSyncThreadRunning()
795 ConditionalWait::ScopedLock lock( mVSyncThreadWaitCondition );
796 return ! mVSyncThreadStop;
799 ///////////////////////////////////////////////////////////////////////////////////////////////////
800 // Called by Render Thread
801 ///////////////////////////////////////////////////////////////////////////////////////////////////
803 bool ThreadSynchronization::IsRenderThreadRunning()
805 ConditionalWait::ScopedLock lock( mRenderThreadWaitCondition );
806 return ! mRenderThreadStop;
809 bool ThreadSynchronization::IsRenderThreadReplacingSurface()
811 ConditionalWait::ScopedLock lock( mRenderThreadWaitCondition );
812 return mRenderThreadReplacingSurface;
815 } // namespace Adaptor
817 } // namespace Internal