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 ) )
390 LOG_VSYNC_COUNTER_UPDATE( " vSyncAheadOfUpdate(%d) WAIT", mVSyncAheadOfUpdate );
391 mUpdateThreadWaitCondition.Wait( updateLock );
395 LOG_VSYNC_COUNTER_UPDATE( " vSyncAheadOfUpdate(%d)", mVSyncAheadOfUpdate );
397 mVSyncAheadOfUpdate = 0;
400 // Try to sleep if we do not require any more updates
401 UpdateTryToSleep( runUpdate );
406 case State::SLEEPING:
407 case State::REPLACING_SURFACE:
413 // Ensure we didn't stop while we were waiting
414 if( IsUpdateThreadStopping() )
416 // Locks so we shouldn't have a scoped-lock when calling this
418 return false; // Stop update-thread
421 // Just wait if we're replacing the surface as the render-thread is busy
422 UpdateWaitIfReplacingSurface();
424 mFrameTime.PredictNextSyncTime( lastFrameDeltaSeconds, lastSyncTimeMilliseconds, nextSyncTimeMilliseconds );
426 return true; // Keep update-thread running
429 ///////////////////////////////////////////////////////////////////////////////////////////////////
431 ///////////////////////////////////////////////////////////////////////////////////////////////////
433 bool ThreadSynchronization::RenderReady( RenderRequest*& requestPtr )
437 if( ! IsRenderThreadReplacingSurface() ) // Call to this function locks so should not be called if we have a scoped-lock
439 if( ! mRenderThreadInitialised )
441 LOG_RENDER( "Initialised" );
443 mRenderThreadInitialised = TRUE;
445 // Notify event thread that this thread is up and running, this locks so we should have a scoped-lock
446 NotifyThreadInitialised();
450 if( mRenderThreadSurfaceReplaced )
452 mRenderThreadSurfaceReplaced = FALSE;
456 // decrement update-ahead-of-render
457 ConditionalWait::ScopedLock renderLock( mRenderThreadWaitCondition );
458 --mUpdateAheadOfRender;
462 // Check if we've had an update, if we haven't then we just wait
463 // Ensure we do not wait if we're supposed to stop
465 ConditionalWait::ScopedLock renderLock( mRenderThreadWaitCondition );
466 if( mUpdateAheadOfRender <= 0 && ! mRenderThreadStop )
468 LOG_UPDATE_COUNTER_RENDER( "updateAheadOfRender(%d) WAIT", mUpdateAheadOfRender );
469 mRenderThreadWaitCondition.Wait( renderLock );
473 LOG_UPDATE_COUNTER_RENDER( "updateAheadOfRender(%d)", mUpdateAheadOfRender );
479 LOG_RENDER( "Just Rendered, now Replacing surface" );
481 // ... also decrement update-ahead-of-render
482 ConditionalWait::ScopedLock renderLock( mRenderThreadWaitCondition );
483 --mUpdateAheadOfRender;
486 // We may have been asked to replace the surface while we were waiting so check again here
487 if( IsRenderThreadReplacingSurface() )
490 LOG_RENDER( "REPLACE SURFACE" );
492 ConditionalWait::ScopedLock renderLock( mRenderThreadWaitCondition );
493 requestPtr = &mReplaceSurfaceRequest;
494 mRenderThreadReplacingSurface = FALSE;
495 mRenderThreadSurfaceReplaced = FALSE;
498 return IsRenderThreadRunning(); // Call to this function locks so should not be called if we have a scoped-lock
501 void ThreadSynchronization::RenderInformSurfaceReplaced()
505 mRenderThreadSurfaceReplaced = TRUE;
507 ConditionalWait::ScopedLock lock( mEventThreadWaitCondition );
508 mEventThreadSurfaceReplaced = TRUE;
510 mEventThreadWaitCondition.Notify();
513 ///////////////////////////////////////////////////////////////////////////////////////////////////
515 ///////////////////////////////////////////////////////////////////////////////////////////////////
517 bool ThreadSynchronization::VSyncReady( bool validSync, unsigned int frameNumber, unsigned int seconds, unsigned int microseconds, unsigned int& numberOfVSyncsPerRender )
522 ConditionalWait::ScopedLock vSyncLock( mVSyncThreadWaitCondition );
523 if( numberOfVSyncsPerRender != mNumberOfVSyncsPerRender )
525 numberOfVSyncsPerRender = mNumberOfVSyncsPerRender; // save it back
526 mFrameTime.SetMinimumFrameTimeInterval( mNumberOfVSyncsPerRender * TIME_PER_FRAME_IN_MICROSECONDS );
531 mFrameTime.SetSyncTime( frameNumber );
535 if( ! mVSyncThreadInitialised )
537 LOG_VSYNC( "Initialised" );
539 mVSyncThreadInitialised = TRUE;
541 // Notify event thread that this thread is up and running, this locks so we should have a scoped-lock
542 NotifyThreadInitialised();
546 // Increment v-sync-ahead-of-update count and inform update-thread
548 ConditionalWait::ScopedLock lock( mUpdateThreadWaitCondition );
549 ++mVSyncAheadOfUpdate;
550 LOG_VSYNC_COUNTER_VSYNC( " vSyncAheadOfUpdate(%d)", mVSyncAheadOfUpdate );
552 mUpdateThreadWaitCondition.Notify();
555 // Ensure update-thread has set us to run before continuing
556 // Ensure we do not wait if we're supposed to stop
558 ConditionalWait::ScopedLock vSyncLock( mVSyncThreadWaitCondition );
559 while( ! mVSyncThreadRunning && ! mVSyncThreadStop )
562 mVSyncThreadWaitCondition.Wait( vSyncLock );
566 return IsVSyncThreadRunning(); // Call to this function locks so should not be called if we have a scoped-lock
569 ///////////////////////////////////////////////////////////////////////////////////////////////////
570 // ALL THREADS: Performance Marker
571 ///////////////////////////////////////////////////////////////////////////////////////////////////
573 void ThreadSynchronization::AddPerformanceMarker( PerformanceInterface::MarkerType type )
575 if( mPerformanceInterface )
577 mPerformanceInterface->AddMarker( type );
581 ///////////////////////////////////////////////////////////////////////////////////////////////////
585 ///////////////////////////////////////////////////////////////////////////////////////////////////
587 ///////////////////////////////////////////////////////////////////////////////////////////////////
588 // Called by ALL Threads
589 ///////////////////////////////////////////////////////////////////////////////////////////////////
591 void ThreadSynchronization::NotifyThreadInitialised()
594 ConditionalWait::ScopedLock lock( mEventThreadWaitCondition );
595 ++mNumberOfThreadsStarted;
597 mEventThreadWaitCondition.Notify();
600 ///////////////////////////////////////////////////////////////////////////////////////////////////
601 // Called by Update Thread
602 ///////////////////////////////////////////////////////////////////////////////////////////////////
604 void ThreadSynchronization::UpdateInitialising()
608 // Notify event thread that this thread is up and running, locks so we shouldn't have a scoped-lock when calling this
609 NotifyThreadInitialised();
611 // Wait for first thread-sync point
613 ConditionalWait::ScopedLock updateLock( mUpdateThreadWaitCondition );
615 while( mState == State::INITIALISING )
617 mUpdateThreadWaitCondition.Wait( updateLock );
621 // Locks so we shouldn't have a scoped-lock when calling this
625 void ThreadSynchronization::UpdateTryToSleep( bool runUpdate )
631 LOG_UPDATE( "TryToSleep" );
633 if( ++mTryToSleepCount >= 3 )
635 LOG_UPDATE( "Going to sleep" );
637 // Locks so we shouldn't have a scoped-lock when calling this
640 // Render thread will automatically wait as it relies on update-ahead-of-render count
644 ConditionalWait::ScopedLock updateLock( mUpdateThreadWaitCondition );
646 // Ensure we weren't stopped while we have been processing
647 if( mState != State::STOPPED )
649 mState = State::SLEEPING;
653 // Inform FrameTime that we're going to sleep
656 // Wait while we're SLEEPING
658 ConditionalWait::ScopedLock updateLock( mUpdateThreadWaitCondition );
659 while( mState == State::SLEEPING )
661 mUpdateThreadWaitCondition.Wait( updateLock );
665 ////////////////////////
667 ////////////////////////
669 LOG_UPDATE( "Waking Up" );
671 // Clear V-Sync-ahead-of-update-count
673 ConditionalWait::ScopedLock updateLock( mUpdateThreadWaitCondition );
674 mVSyncAheadOfUpdate = 0;
677 // Restart the v-sync-thread, locks so we shouldn't have a scoped-lock
680 // Reset try-to-sleep count
681 mTryToSleepCount = 0;
683 // Inform frame timer that we've woken up
689 mTryToSleepCount = 0;
693 void ThreadSynchronization::UpdateWaitIfReplacingSurface()
695 bool replacingSurface = false;
697 ConditionalWait::ScopedLock updateLock( mUpdateThreadWaitCondition );
698 replacingSurface = ( mState == State::REPLACING_SURFACE );
700 while( replacingSurface )
702 LOG_UPDATE_TRACE_FMT( "REPLACING SURFACE" );
704 // Locks so should not be called while we have a scoped-lock
707 // One last check before we actually wait in case the state has changed since we checked earlier
709 ConditionalWait::ScopedLock updateLock( mUpdateThreadWaitCondition );
710 replacingSurface = ( mState == State::REPLACING_SURFACE );
711 if( replacingSurface )
713 mUpdateThreadWaitCondition.Wait( updateLock );
718 ConditionalWait::ScopedLock updateLock( mUpdateThreadWaitCondition );
719 mVSyncAheadOfUpdate = 0;
722 // Locks so should not be called while we have a scoped-lock
727 bool ThreadSynchronization::IsUpdateThreadResuming()
729 ConditionalWait::ScopedLock updateLock( mUpdateThreadWaitCondition );
730 return mUpdateThreadResuming;
733 bool ThreadSynchronization::IsUpdateThreadStopping()
735 ConditionalWait::ScopedLock updateLock( mUpdateThreadWaitCondition );
736 return ( mState == State::STOPPED );
739 bool ThreadSynchronization::MaximumUpdateAheadOfRenderReached()
741 ConditionalWait::ScopedLock lock( mRenderThreadWaitCondition );
742 return mUpdateAheadOfRender >= mMaximumUpdateCount;
745 void ThreadSynchronization::StopAllThreads()
749 // Lock so we shouldn't have a scoped-lock when calling these methods
754 void ThreadSynchronization::RunVSyncThread()
757 ConditionalWait::ScopedLock lock( mVSyncThreadWaitCondition );
758 mVSyncThreadRunning = TRUE;
760 mVSyncThreadWaitCondition.Notify();
763 void ThreadSynchronization::PauseVSyncThread()
765 ConditionalWait::ScopedLock lock( mVSyncThreadWaitCondition );
766 mVSyncThreadRunning = FALSE;
769 void ThreadSynchronization::StopVSyncThread()
772 ConditionalWait::ScopedLock lock( mVSyncThreadWaitCondition );
773 mVSyncThreadStop = TRUE;
775 mVSyncThreadWaitCondition.Notify();
778 void ThreadSynchronization::StopRenderThread()
781 ConditionalWait::ScopedLock lock( mRenderThreadWaitCondition );
782 mRenderThreadStop = TRUE;
784 mRenderThreadWaitCondition.Notify();
787 ///////////////////////////////////////////////////////////////////////////////////////////////////
788 // Called by V-Sync Thread
789 ///////////////////////////////////////////////////////////////////////////////////////////////////
791 bool ThreadSynchronization::IsVSyncThreadRunning()
793 ConditionalWait::ScopedLock lock( mVSyncThreadWaitCondition );
794 return ! mVSyncThreadStop;
797 ///////////////////////////////////////////////////////////////////////////////////////////////////
798 // Called by Render Thread
799 ///////////////////////////////////////////////////////////////////////////////////////////////////
801 bool ThreadSynchronization::IsRenderThreadRunning()
803 ConditionalWait::ScopedLock lock( mRenderThreadWaitCondition );
804 return ! mRenderThreadStop;
807 bool ThreadSynchronization::IsRenderThreadReplacingSurface()
809 ConditionalWait::ScopedLock lock( mRenderThreadWaitCondition );
810 return mRenderThreadReplacingSurface;
813 } // namespace Adaptor
815 } // namespace Internal