Dali 3D User Interface Engine
thread-synchronization.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2015 Samsung Electronics Co., Ltd.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  *
16  */
17 
18 // CLASS HEADER
19 #include "thread-synchronization.h"
20 
21 // INTERNAL INCLUDES
24 
25 namespace Dali
26 {
27 
28 namespace Internal
29 {
30 
31 namespace Adaptor
32 {
33 
34 namespace
35 {
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
41 
42 ThreadSynchronization::ThreadSynchronization( AdaptorInternalServices& adaptorInterfaces, unsigned int numberOfVSyncsPerRender)
43 : mFrameTime(),
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  mRenderThreadPostRendering( FALSE ),
64  mEventThreadSurfaceReplaced( FALSE ),
65  mVSyncThreadInitialised( FALSE ),
66  mRenderThreadInitialised( FALSE ),
67  mRenderThreadSurfaceReplaced( FALSE )
68 {
69 }
70 
72 {
73 }
74 
76 // EVENT THREAD
78 
80 {
82 
84  if( mState == State::STOPPED )
85  {
86  LOG_EVENT( "INITIALISING" );
88  }
89 }
90 
92 {
94 
95  bool start = false;
96  {
99  {
100  start = true;
101  }
102  }
103 
104  // Not atomic, but does not matter here as we just want to ensure we only start from State::INITIALISING
105  if( start )
106  {
107  LOG_EVENT( "STARTING" );
109 
110  {
113  {
115  }
116  }
117 
118  {
121  }
123  }
124 }
125 
127 {
129 
130  bool stop = false;
131  {
133  if( mState != State::STOPPED )
134  {
135  stop = true;
137  }
138  }
139 
140  // Not atomic, but does not matter here as we just want to ensure we do not stop more than once
141  if( stop )
142  {
143  LOG_EVENT( "STOPPING" );
144 
145  // Notify update-thread so that it continues and sets up the other threads to stop as well
147 
149  }
150 }
151 
153 {
155 
156  bool addPerformanceMarker = false;
157  {
158  // Only pause if we're RUNNING or SLEEPING
160  if( ( mState == State::RUNNING ) ||
161  ( mState == State::SLEEPING ) )
162  {
163  LOG_EVENT( "PAUSING" );
164 
166 
168 
170 
171  addPerformanceMarker = true;
172  }
173  }
174 
175  if( addPerformanceMarker )
176  {
177  // Can lock so we do not want to have a lock when calling this to avoid deadlocks
179  }
180 }
181 
183 {
185 
186  // Only resume if we're PAUSED
187  bool resume = false;
188  {
190  if( mState == State::PAUSED )
191  {
192  resume = true;
195  }
196  }
197 
198  // Not atomic, but does not matter here as we just want to ensure we only resume if we're paused
199  if( resume )
200  {
201  LOG_EVENT( "RESUMING" );
202 
203  mFrameTime.Resume();
204 
205  // Start up Update thread again
207 
208  // Can lock so we do not want to have a lock when calling this to avoid deadlocks
210  }
211 }
212 
214 {
216 
217  bool update = false;
218  {
220  if( mState == State::SLEEPING )
221  {
223  update = true;
224  }
225  mTryToSleepCount = 0;
226  }
227 
228  if( update )
229  {
230  LOG_EVENT( "UPDATE REQUEST" );
232  }
233 }
234 
236 {
238  LOG_EVENT( "UPDATE ONCE" );
239 
240  // If we're sleeping then change state to running as this will also wake up the v-sync-thread
241  {
243  if( mState == State::SLEEPING )
244  {
246  }
247  }
248 
250 }
251 
252 void ThreadSynchronization::ReplaceSurface( RenderSurface* newSurface )
253 {
255 
256  State::Type previousState( State::STOPPED );
257  {
259  previousState = mState;
261  }
262 
263  {
266  }
267 
268  {
270  mReplaceSurfaceRequest.SetSurface( newSurface );
272  }
273 
274  // Notify the RenderThread in case it's waiting
276 
277  {
279 
280  // Wait for RenderThread to replace the surface
281  while( ! mEventThreadSurfaceReplaced )
282  {
283  LOG_EVENT( "Waiting for Surface to be Replaced" );
284 
286  }
287  }
288 
289  {
291  mState = previousState;
292  }
294 }
295 
296 void ThreadSynchronization::SetRenderRefreshRate( unsigned int numberOfVSyncsPerRender )
297 {
299  LOG_EVENT( "SET RENDER REFRESH RATE" );
300 
301  mNumberOfVSyncsPerRender = numberOfVSyncsPerRender;
302 }
303 
305 // UPDATE THREAD
307 
308 bool ThreadSynchronization::UpdateReady( bool notifyEvent, bool runUpdate, float& lastFrameDeltaSeconds, unsigned int& lastSyncTimeMilliseconds, unsigned int& nextSyncTimeMilliseconds )
309 {
311 
312  State::Type state = State::STOPPED;
313  {
315  state = mState;
316  }
317 
318  switch( state )
319  {
320  case State::STOPPED:
321  {
322  StopAllThreads();
323  return false; // Stop update-thread
324  }
325 
326  case State::INITIALISING:
327  {
329  break;
330  }
331 
332  case State::PAUSED:
333  {
334  LOG_UPDATE_TRACE_FMT( "PAUSED" );
335 
336  // Just pause the VSyncThread, locks so we shouldn't have a scoped-lock when calling this
338  }
339  // No break, fall through
340 
341  case State::RUNNING:
342  {
343  LOG_UPDATE_TRACE_FMT( "RUNNING" );
344 
345  if( IsUpdateThreadResuming() )
346  {
347  LOG_UPDATE( "Restarting VSyncThread" );
348 
349  {
352  }
353 
354  // Restart the VSyncThread, locks so we shouldn't have a scoped-lock when calling this
355  RunVSyncThread();
356  }
357 
358  if( notifyEvent )
359  {
360  LOG_UPDATE( "Notify Event Thread" );
361 
362  // Do the notifications first so the event thread can start processing them
363  // Tell the event-thread to wake up (if asleep) and send a notification event to Core
364  mNotificationTrigger.Trigger();
365  }
366 
367  // Inform render thread
368  {
373  LOG_UPDATE_COUNTER_UPDATE( "updateAheadOfRender(%d)", mUpdateAheadOfRender );
374  }
376 
377  // Wait if we've reached the maximum-ahead-of-render count.
379  {
380  LOG_UPDATE( "Maximum Update Ahead of Render: WAIT" );
381 
382  mRenderThreadWaitCondition.Notify(); // Notify the render thread in case it was waiting
383 
384  {
385  // Ensure we did not stop while we were waiting previously.
387  if( mState == State::STOPPED )
388  {
389  break; // Break out of while loop
390  }
391  mUpdateThreadWaitCondition.Wait( updateLock );
392  }
393  }
394 
395  // Ensure we have had at least 1 V-Sync before we continue
396  // Ensure we didn't stop while we were previously waiting
397  {
399  if( ( mState != State::STOPPED ) &&
400  ( mVSyncAheadOfUpdate == 0 ) &&
401  ( !mUpdateThreadResuming ) ) // Ensure we don't wait if the update-thread is JUST resuming
402  {
403  LOG_VSYNC_COUNTER_UPDATE( " vSyncAheadOfUpdate(%d) WAIT", mVSyncAheadOfUpdate );
404  mUpdateThreadWaitCondition.Wait( updateLock );
405  }
406  else
407  {
408  LOG_VSYNC_COUNTER_UPDATE( " vSyncAheadOfUpdate(%d)", mVSyncAheadOfUpdate );
409  }
411  }
412 
413  // Try to sleep if we do not require any more updates
414  UpdateTryToSleep( runUpdate );
415 
416  break;
417  }
418 
419  case State::SLEEPING:
421  {
422  break;
423  }
424  }
425 
426  // Ensure we didn't stop while we were waiting
427  if( IsUpdateThreadStopping() )
428  {
429  // Locks so we shouldn't have a scoped-lock when calling this
430  StopAllThreads();
431  return false; // Stop update-thread
432  }
433 
434  // Just wait if we're replacing the surface as the render-thread is busy
436 
437  mFrameTime.PredictNextSyncTime( lastFrameDeltaSeconds, lastSyncTimeMilliseconds, nextSyncTimeMilliseconds );
438 
439  return true; // Keep update-thread running
440 }
441 
443 // RENDER THREAD
445 
447 {
449 
450  if( ! IsRenderThreadReplacingSurface() ) // Call to this function locks so should not be called if we have a scoped-lock
451  {
453  {
454  LOG_RENDER( "Initialised" );
455 
457 
458  // Notify event thread that this thread is up and running, this locks so we should have a scoped-lock
460  }
461  else
462  {
464  {
466  }
467  }
468 
469  // Check if we've had an update, if we haven't then we just wait
470  // Ensure we do not wait if we're supposed to stop
471  {
474  {
475  do
476  {
477  LOG_UPDATE_COUNTER_RENDER( "updateAheadOfRender(%d) WAIT", mUpdateAheadOfRender );
478  mRenderThreadWaitCondition.Wait( renderLock );
480  }
481  else
482  {
483  LOG_UPDATE_COUNTER_RENDER( "updateAheadOfRender(%d)", mUpdateAheadOfRender );
484  }
485  }
486  }
487 
488  // We may have been asked to replace the surface while we were waiting so check again here
490  {
491  // Replacing surface
492  LOG_RENDER( "REPLACE SURFACE" );
493 
495  requestPtr = &mReplaceSurfaceRequest;
498  }
499 
500  return IsRenderThreadRunning(); // Call to this function locks so should not be called if we have a scoped-lock
501 }
502 
504 {
505  // A frame has been rendered; decrement counter
508 
509  LOG_RENDER( "mUpdateAheadOfRender %d\n", mUpdateAheadOfRender );
512 }
513 
515 {
517 
519  {
522  }
524 }
525 
527 // V-SYNC THREAD
529 
530 bool ThreadSynchronization::VSyncReady( bool validSync, unsigned int frameNumber, unsigned int seconds, unsigned int microseconds, unsigned int& numberOfVSyncsPerRender )
531 {
533 
534  // Ensure we do not process an invalid v-sync
535  if( validSync )
536  {
537  bool minimumFrameTimeIntervalChanged = false;
538  {
540  if( numberOfVSyncsPerRender != mNumberOfVSyncsPerRender )
541  {
542  numberOfVSyncsPerRender = mNumberOfVSyncsPerRender; // save it back
543  minimumFrameTimeIntervalChanged = true;
544  }
545  }
546 
547  if( minimumFrameTimeIntervalChanged )
548  {
550  }
551 
552  mFrameTime.SetSyncTime( frameNumber );
553 
555  {
556  LOG_VSYNC( "Initialised" );
557 
559 
560  // Notify event thread that this thread is up and running, this locks so we should have a scoped-lock
562  }
563  else
564  {
565  // Increment v-sync-ahead-of-update count and inform update-thread
566  {
569  LOG_VSYNC_COUNTER_VSYNC( " vSyncAheadOfUpdate(%d)", mVSyncAheadOfUpdate );
570  }
572  }
573 
574  // Ensure update-thread has set us to run before continuing
575  // Ensure we do not wait if we're supposed to stop
576  {
578  while( ! mVSyncThreadRunning && ! mVSyncThreadStop )
579  {
580  LOG_VSYNC( "WAIT" );
581  mVSyncThreadWaitCondition.Wait( vSyncLock );
582  }
583  }
584  }
585  else
586  {
587  LOG_VSYNC( "INVALID SYNC" );
588 
589  // Later we still check if the v-sync thread is supposed to keep running so we can still stop the thread if we are supposed to
590  }
591 
592  return IsVSyncThreadRunning(); // Call to this function locks so should not be called if we have a scoped-lock
593 }
594 
596 // POST RENDERING: EVENT THREAD
598 
600 {
602 
603  {
606  }
608 }
609 
611 // POST RENDERING: RENDER THREAD
613 
615 {
617 
620 }
621 
623 {
625 
628  ! mRenderThreadReplacingSurface ) // We should NOT wait if we're replacing the surface
629  {
630  LOG_RENDER( "WAIT" );
632  }
633 }
634 
636 // ALL THREADS: Performance Marker
638 
640 {
642  {
644  }
645 }
646 
648 //
649 // PRIVATE METHODS
650 //
652 
654 // Called by ALL Threads
656 
658 {
659  {
662  }
664 }
665 
667 // Called by Update Thread
669 
671 {
673 
674  // Notify event thread that this thread is up and running, locks so we shouldn't have a scoped-lock when calling this
676 
677  // Wait for first thread-sync point
678  {
680 
681  while( mState == State::INITIALISING )
682  {
683  mUpdateThreadWaitCondition.Wait( updateLock );
684  }
685  }
686 
687  // Locks so we shouldn't have a scoped-lock when calling this
688  RunVSyncThread();
689 }
690 
692 {
694 
695  if( ! runUpdate &&
696  ! IsUpdateThreadResuming() ) // Locks so we shouldn't have a lock, we shouldn't try to sleep if we're JUST resuming
697  {
698  LOG_UPDATE( "TryToSleep" );
699 
700  if( ++mTryToSleepCount >= 3 )
701  {
702  LOG_UPDATE( "Going to sleep" );
703 
704  // Locks so we shouldn't have a scoped-lock when calling this
706 
707  // Render thread will automatically wait as it relies on update-ahead-of-render count
708 
709  // Change the state
710  {
712 
713  // Ensure we weren't stopped while we have been processing
714  if( mState != State::STOPPED )
715  {
717  }
718  }
719 
720  // Inform FrameTime that we're going to sleep
721  mFrameTime.Sleep();
722 
723  // Wait while we're SLEEPING
724  {
726  while( mState == State::SLEEPING )
727  {
728  mUpdateThreadWaitCondition.Wait( updateLock );
729  }
730  }
731 
733  // WAKE UP
735 
736  LOG_UPDATE( "Waking Up" );
737 
738  // Clear V-Sync-ahead-of-update-count
739  {
742  }
743 
744  // Restart the v-sync-thread, locks so we shouldn't have a scoped-lock
745  RunVSyncThread();
746 
747  // Reset try-to-sleep count
748  mTryToSleepCount = 0;
749 
750  // Inform frame timer that we've woken up
751  mFrameTime.WakeUp();
752  }
753  }
754  else
755  {
756  mTryToSleepCount = 0;
757  }
758 }
759 
761 {
762  bool replacingSurface = false;
763  {
765  replacingSurface = ( mState == State::REPLACING_SURFACE );
766  }
767  while( replacingSurface )
768  {
769  LOG_UPDATE_TRACE_FMT( "REPLACING SURFACE" );
770 
771  // Locks so should not be called while we have a scoped-lock
773 
774  // One last check before we actually wait in case the state has changed since we checked earlier
775  {
777  replacingSurface = ( mState == State::REPLACING_SURFACE );
778  if( replacingSurface )
779  {
780  mUpdateThreadWaitCondition.Wait( updateLock );
781  }
782  }
783 
784  {
787  }
788 
789  // Locks so should not be called while we have a scoped-lock
790  RunVSyncThread();
791  }
792 }
793 
795 {
797  return mUpdateThreadResuming;
798 }
799 
801 {
803  return ( mState == State::STOPPED );
804 }
805 
807 {
810 }
811 
813 {
815 
816  // Lock so we shouldn't have a scoped-lock when calling these methods
817  StopVSyncThread();
819 }
820 
822 {
823  {
826  }
828 }
829 
831 {
834 }
835 
837 {
838  {
841  }
843 }
844 
846 {
847  {
850  }
852 }
853 
855 // Called by V-Sync Thread
857 
859 {
861  return ! mVSyncThreadStop;
862 }
863 
865 // Called by Render Thread
867 
869 {
871  return ! mRenderThreadStop;
872 }
873 
875 {
878 }
879 
880 } // namespace Adaptor
881 
882 } // namespace Internal
883 
884 } // namespace Dali
Dali Docs Home
Read more about Dali