72aa9da0b5c846ffedd82dde016fee5fa6fe6147
[platform/core/uifw/dali-adaptor.git] / dali / internal / adaptor / common / combined-update-render-controller.cpp
1 /*
2  * Copyright (c) 2018 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 <dali/internal/adaptor/common/combined-update-render-controller.h>
20
21 // EXTERNAL INCLUDES
22 #include <errno.h>
23 #include <dali/integration-api/platform-abstraction.h>
24
25 // INTERNAL INCLUDES
26 #include <dali/integration-api/trigger-event-factory.h>
27 #include <dali/internal/adaptor/common/combined-update-render-controller-debug.h>
28 #include <dali/internal/system/common/environment-options.h>
29 #include <dali/internal/system/common/time-service.h>
30 #include <dali/internal/adaptor/common/adaptor-internal-services.h>
31 #include <dali/devel-api/adaptor-framework/thread-settings.h>
32
33 namespace Dali
34 {
35
36 namespace Internal
37 {
38
39 namespace Adaptor
40 {
41
42 namespace
43 {
44
45 const unsigned int CREATED_THREAD_COUNT = 1u;
46
47 const int CONTINUOUS = -1;
48 const int ONCE = 1;
49
50 const unsigned int TRUE = 1u;
51 const unsigned int FALSE = 0u;
52
53 const unsigned int MILLISECONDS_PER_SECOND( 1e+3 );
54 const float        NANOSECONDS_TO_SECOND( 1e-9f );
55 const unsigned int NANOSECONDS_PER_SECOND( 1e+9 );
56 const unsigned int NANOSECONDS_PER_MILLISECOND( 1e+6 );
57
58 // The following values will get calculated at compile time
59 const float        DEFAULT_FRAME_DURATION_IN_SECONDS( 1.0f / 60.0f );
60 const uint64_t DEFAULT_FRAME_DURATION_IN_MILLISECONDS( DEFAULT_FRAME_DURATION_IN_SECONDS * MILLISECONDS_PER_SECOND );
61 const uint64_t DEFAULT_FRAME_DURATION_IN_NANOSECONDS( DEFAULT_FRAME_DURATION_IN_SECONDS * NANOSECONDS_PER_SECOND );
62
63 /**
64  * Handles the use case when an update-request is received JUST before we process a sleep-request. If we did not have an update-request count then
65  * there is a danger that, on the event-thread we could have:
66  *  1) An update-request where we do nothing as Update/Render thread still running.
67  *  2) Quickly followed by a sleep-request being handled where we pause the Update/Render Thread (even though we have an update to process).
68  *
69  * Using a counter means we increment the counter on an update-request, and decrement it on a sleep-request. This handles the above scenario because:
70  *  1) MAIN THREAD:           Update Request: COUNTER = 1
71  *  2) UPDATE/RENDER THREAD:  Do Update/Render, then no Updates required -> Sleep Trigger
72  *  3) MAIN THREAD:           Update Request: COUNTER = 2
73  *  4) MAIN THREAD:           Sleep Request:  COUNTER = 1 -> We do not sleep just yet
74  *
75  * Also ensures we preserve battery life by only doing ONE update when the above use case is not triggered.
76  *  1) MAIN THREAD:           Update Request: COUNTER = 1
77  *  2) UPDATE/RENDER THREAD:  Do Update/Render, then no Updates required -> Sleep Trigger
78  *  3) MAIN THREAD:           Sleep Request:  COUNTER = 0 -> Go to sleep
79  */
80 const unsigned int MAXIMUM_UPDATE_REQUESTS = 2;
81 } // unnamed namespace
82
83 ///////////////////////////////////////////////////////////////////////////////////////////////////
84 // EVENT THREAD
85 ///////////////////////////////////////////////////////////////////////////////////////////////////
86
87 CombinedUpdateRenderController::CombinedUpdateRenderController( AdaptorInternalServices& adaptorInterfaces, const EnvironmentOptions& environmentOptions )
88 : mFpsTracker( environmentOptions ),
89   mUpdateStatusLogger( environmentOptions ),
90   mRenderHelper( adaptorInterfaces ),
91   mEventThreadSemaphore(),
92   mUpdateRenderThreadWaitCondition(),
93   mAdaptorInterfaces( adaptorInterfaces ),
94   mPerformanceInterface( adaptorInterfaces.GetPerformanceInterface() ),
95   mCore( adaptorInterfaces.GetCore() ),
96   mEnvironmentOptions( environmentOptions ),
97   mNotificationTrigger( adaptorInterfaces.GetProcessCoreEventsTrigger() ),
98   mSleepTrigger( NULL ),
99   mUpdateRenderThread( NULL ),
100   mDefaultFrameDelta( 0.0f ),
101   mDefaultFrameDurationMilliseconds( 0u ),
102   mDefaultFrameDurationNanoseconds( 0u ),
103   mDefaultHalfFrameNanoseconds( 0u ),
104   mUpdateRequestCount( 0u ),
105   mRunning( FALSE ),
106   mUpdateRenderRunCount( 0 ),
107   mDestroyUpdateRenderThread( FALSE ),
108   mUpdateRenderThreadCanSleep( FALSE ),
109   mPendingRequestUpdate( FALSE ),
110   mUseElapsedTimeAfterWait( FALSE ),
111   mNewSurface( NULL ),
112   mPostRendering( FALSE ),
113   mSurfaceResized( FALSE ),
114   mForceClear( FALSE )
115 {
116   LOG_EVENT_TRACE;
117
118   // Initialise frame delta/duration variables first
119   SetRenderRefreshRate( environmentOptions.GetRenderRefreshRate() );
120
121   // Set the thread-synchronization interface on the render-surface
122   RenderSurface* currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
123   if( currentSurface )
124   {
125     currentSurface->SetThreadSynchronization( *this );
126   }
127
128   TriggerEventFactoryInterface& triggerFactory = mAdaptorInterfaces.GetTriggerEventFactoryInterface();
129   mSleepTrigger = triggerFactory.CreateTriggerEvent( MakeCallback( this, &CombinedUpdateRenderController::ProcessSleepRequest ), TriggerEventInterface::KEEP_ALIVE_AFTER_TRIGGER );
130
131   sem_init( &mEventThreadSemaphore, 0, 0 ); // Initialize to 0 so that it just waits if sem_post has not been called
132 }
133
134 CombinedUpdateRenderController::~CombinedUpdateRenderController()
135 {
136   LOG_EVENT_TRACE;
137
138   Stop();
139
140   delete mSleepTrigger;
141 }
142
143 void CombinedUpdateRenderController::Initialize()
144 {
145   LOG_EVENT_TRACE;
146
147   // Ensure Update/Render Thread not already created
148   DALI_ASSERT_ALWAYS( ! mUpdateRenderThread );
149
150   // Create Update/Render Thread
151   mUpdateRenderThread = new pthread_t();
152   int error = pthread_create( mUpdateRenderThread, NULL, InternalUpdateRenderThreadEntryFunc, this );
153   DALI_ASSERT_ALWAYS( !error && "Return code from pthread_create() when creating UpdateRenderThread" );
154
155   // The Update/Render thread will now run and initialise EGL etc. and will then wait for Start to be called
156   // When this function returns, the application initialisation on the event thread should occur
157 }
158
159 void CombinedUpdateRenderController::Start()
160 {
161   LOG_EVENT_TRACE;
162
163   DALI_ASSERT_ALWAYS( !mRunning && mUpdateRenderThread );
164
165   // Wait until all threads created in Initialise are up and running
166   for( unsigned int i = 0; i < CREATED_THREAD_COUNT; ++i )
167   {
168     sem_wait( &mEventThreadSemaphore );
169   }
170
171   mRenderHelper.Start();
172
173   mRunning = TRUE;
174
175   LOG_EVENT( "Startup Complete, starting Update/Render Thread" );
176
177   RunUpdateRenderThread( CONTINUOUS, false /* No animation progression */ );
178 }
179
180 void CombinedUpdateRenderController::Pause()
181 {
182   LOG_EVENT_TRACE;
183
184   mRunning = FALSE;
185
186   PauseUpdateRenderThread();
187
188   AddPerformanceMarker( PerformanceInterface::PAUSED );
189 }
190
191 void CombinedUpdateRenderController::Resume()
192 {
193   LOG_EVENT_TRACE;
194
195   if( !mRunning && IsUpdateRenderThreadPaused() )
196   {
197     LOG_EVENT( "Resuming" );
198
199     RunUpdateRenderThread( CONTINUOUS, true /* Animation progression required while we were paused */ );
200
201     AddPerformanceMarker( PerformanceInterface::RESUME );
202
203     mRunning = TRUE;
204     mForceClear = TRUE;
205   }
206 }
207
208 void CombinedUpdateRenderController::Stop()
209 {
210   LOG_EVENT_TRACE;
211
212   // Stop Rendering and the Update/Render Thread
213   mRenderHelper.Stop();
214
215   StopUpdateRenderThread();
216
217   if( mUpdateRenderThread )
218   {
219     LOG_EVENT( "Destroying UpdateRenderThread" );
220
221     // wait for the thread to finish
222     pthread_join( *mUpdateRenderThread, NULL );
223
224     delete mUpdateRenderThread;
225     mUpdateRenderThread = NULL;
226   }
227
228   mRunning = FALSE;
229 }
230
231 void CombinedUpdateRenderController::RequestUpdate()
232 {
233   LOG_EVENT_TRACE;
234
235   // Increment the update-request count to the maximum
236   if( mUpdateRequestCount < MAXIMUM_UPDATE_REQUESTS )
237   {
238     ++mUpdateRequestCount;
239   }
240
241   if( mRunning && IsUpdateRenderThreadPaused() )
242   {
243     LOG_EVENT( "Processing" );
244
245     RunUpdateRenderThread( CONTINUOUS, false /* No animation progression */ );
246   }
247
248   ConditionalWait::ScopedLock updateLock( mUpdateRenderThreadWaitCondition );
249   mPendingRequestUpdate = TRUE;
250 }
251
252 void CombinedUpdateRenderController::RequestUpdateOnce()
253 {
254   // Increment the update-request count to the maximum
255   if( mUpdateRequestCount < MAXIMUM_UPDATE_REQUESTS )
256   {
257     ++mUpdateRequestCount;
258   }
259
260   if( IsUpdateRenderThreadPaused() )
261   {
262     LOG_EVENT_TRACE;
263
264     // Run Update/Render once
265     RunUpdateRenderThread( ONCE, false /* No animation progression */ );
266   }
267 }
268
269 void CombinedUpdateRenderController::ReplaceSurface( RenderSurface* newSurface )
270 {
271   LOG_EVENT_TRACE;
272
273   // Set the ThreadSyncronizationInterface on the new surface
274   newSurface->SetThreadSynchronization( *this );
275
276   LOG_EVENT( "Starting to replace the surface, event-thread blocked" );
277
278   // Start replacing the surface.
279   {
280     ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
281     mPostRendering = FALSE; // Clear the post-rendering flag as Update/Render thread will replace the surface now
282     mNewSurface = newSurface;
283     mUpdateRenderThreadWaitCondition.Notify( lock );
284   }
285
286   // Wait until the surface has been replaced
287   sem_wait( &mEventThreadSemaphore );
288
289   LOG_EVENT( "Surface replaced, event-thread continuing" );
290 }
291
292 void CombinedUpdateRenderController::ResizeSurface()
293 {
294   LOG_EVENT_TRACE;
295
296   LOG_EVENT( "Resize the surface" );
297
298   {
299     ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
300     mPostRendering = FALSE; // Clear the post-rendering flag as Update/Render thread will resize the surface now
301     mSurfaceResized = TRUE;
302     mUpdateRenderThreadWaitCondition.Notify( lock );
303   }
304 }
305
306 void CombinedUpdateRenderController::SetRenderRefreshRate( unsigned int numberOfFramesPerRender )
307 {
308   // Not protected by lock, but written to rarely so not worth adding a lock when reading
309   mDefaultFrameDelta                  = numberOfFramesPerRender * DEFAULT_FRAME_DURATION_IN_SECONDS;
310   mDefaultFrameDurationMilliseconds   = uint64_t( numberOfFramesPerRender ) * DEFAULT_FRAME_DURATION_IN_MILLISECONDS;
311   mDefaultFrameDurationNanoseconds    = uint64_t( numberOfFramesPerRender ) * DEFAULT_FRAME_DURATION_IN_NANOSECONDS;
312   mDefaultHalfFrameNanoseconds        = mDefaultFrameDurationNanoseconds / 2u;
313
314   LOG_EVENT( "mDefaultFrameDelta(%.6f), mDefaultFrameDurationMilliseconds(%lld), mDefaultFrameDurationNanoseconds(%lld)", mDefaultFrameDelta, mDefaultFrameDurationMilliseconds, mDefaultFrameDurationNanoseconds );
315 }
316
317 ///////////////////////////////////////////////////////////////////////////////////////////////////
318 // EVENT THREAD
319 ///////////////////////////////////////////////////////////////////////////////////////////////////
320
321 void CombinedUpdateRenderController::RunUpdateRenderThread( int numberOfCycles, bool useElapsedTime )
322 {
323   ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
324   mUpdateRenderRunCount = numberOfCycles;
325   mUpdateRenderThreadCanSleep = FALSE;
326   mUseElapsedTimeAfterWait = useElapsedTime;
327   LOG_COUNTER_EVENT( "mUpdateRenderRunCount: %d, mUseElapsedTimeAfterWait: %d", mUpdateRenderRunCount, mUseElapsedTimeAfterWait );
328   mUpdateRenderThreadWaitCondition.Notify( lock );
329 }
330
331 void CombinedUpdateRenderController::PauseUpdateRenderThread()
332 {
333   ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
334   mUpdateRenderRunCount = 0;
335 }
336
337 void CombinedUpdateRenderController::StopUpdateRenderThread()
338 {
339   ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
340   mDestroyUpdateRenderThread = TRUE;
341   mUpdateRenderThreadWaitCondition.Notify( lock );
342 }
343
344 bool CombinedUpdateRenderController::IsUpdateRenderThreadPaused()
345 {
346   ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
347   return ( mUpdateRenderRunCount != CONTINUOUS ) || // Report paused if NOT continuously running
348          mUpdateRenderThreadCanSleep;               // Report paused if sleeping
349 }
350
351 void CombinedUpdateRenderController::ProcessSleepRequest()
352 {
353   LOG_EVENT_TRACE;
354
355   // Decrement Update request count
356   if( mUpdateRequestCount > 0 )
357   {
358     --mUpdateRequestCount;
359   }
360
361   // Can sleep if our update-request count is 0
362   // Update/Render thread can choose to carry on updating if it determines more update/renders are required
363   if( mUpdateRequestCount == 0 )
364   {
365     LOG_EVENT( "Going to sleep" );
366
367     ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
368     mUpdateRenderThreadCanSleep = TRUE;
369   }
370 }
371
372 ///////////////////////////////////////////////////////////////////////////////////////////////////
373 // UPDATE/RENDER THREAD
374 ///////////////////////////////////////////////////////////////////////////////////////////////////
375
376 void CombinedUpdateRenderController::UpdateRenderThread()
377 {
378   SetThreadName("RenderThread\0");
379
380   // Install a function for logging
381   mEnvironmentOptions.InstallLogFunction();
382
383   // Install a function for tracing
384   mEnvironmentOptions.InstallTraceFunction();
385
386   LOG_UPDATE_RENDER( "THREAD CREATED" );
387
388   mRenderHelper.InitializeEgl();
389
390   // tell core it has a context
391   mCore.ContextCreated();
392
393   NotifyThreadInitialised();
394
395   // Update time
396   uint64_t lastFrameTime;
397   TimeService::GetNanoseconds( lastFrameTime );
398
399   LOG_UPDATE_RENDER( "THREAD INITIALISED" );
400
401   bool useElapsedTime = true;
402   bool updateRequired = true;
403   uint64_t timeToSleepUntil = 0;
404   int extraFramesDropped = 0;
405
406   const unsigned int renderToFboInterval = mEnvironmentOptions.GetRenderToFboInterval();
407   const bool renderToFboEnabled = 0u != renderToFboInterval;
408   unsigned int frameCount = 0u;
409
410   while( UpdateRenderReady( useElapsedTime, updateRequired, timeToSleepUntil ) )
411   {
412     LOG_UPDATE_RENDER_TRACE;
413
414     // Performance statistics are logged upon a VSYNC tick so use this point for a VSync marker
415     AddPerformanceMarker( PerformanceInterface::VSYNC );
416
417     uint64_t currentFrameStartTime = 0;
418     TimeService::GetNanoseconds( currentFrameStartTime );
419
420     const uint64_t timeSinceLastFrame = currentFrameStartTime - lastFrameTime;
421
422     // Optional FPS Tracking when continuously rendering
423     if( useElapsedTime && mFpsTracker.Enabled() )
424     {
425       float absoluteTimeSinceLastRender = timeSinceLastFrame * NANOSECONDS_TO_SECOND;
426       mFpsTracker.Track( absoluteTimeSinceLastRender );
427     }
428
429     lastFrameTime = currentFrameStartTime; // Store frame start time
430
431     //////////////////////////////
432     // REPLACE SURFACE
433     //////////////////////////////
434
435     RenderSurface* newSurface = ShouldSurfaceBeReplaced();
436     if( DALI_UNLIKELY( newSurface ) )
437     {
438       LOG_UPDATE_RENDER_TRACE_FMT( "Replacing Surface" );
439       mRenderHelper.ReplaceSurface( newSurface );
440       SurfaceReplaced();
441     }
442
443     const bool isRenderingToFbo = renderToFboEnabled && ( ( 0u == frameCount ) || ( 0u != frameCount % renderToFboInterval ) );
444     ++frameCount;
445
446     //////////////////////////////
447     // UPDATE
448     //////////////////////////////
449
450     const unsigned int currentTime = currentFrameStartTime / NANOSECONDS_PER_MILLISECOND;
451     const unsigned int nextFrameTime = currentTime + mDefaultFrameDurationMilliseconds;
452
453     uint64_t noOfFramesSinceLastUpdate = 1;
454     float frameDelta = 0.0f;
455     if( useElapsedTime )
456     {
457       // If using the elapsed time, then calculate frameDelta as a multiple of mDefaultFrameDelta
458       noOfFramesSinceLastUpdate += extraFramesDropped;
459
460       frameDelta = mDefaultFrameDelta * noOfFramesSinceLastUpdate;
461     }
462     LOG_UPDATE_RENDER( "timeSinceLastFrame(%llu) noOfFramesSinceLastUpdate(%u) frameDelta(%.6f)", timeSinceLastFrame, noOfFramesSinceLastUpdate, frameDelta );
463
464     Integration::UpdateStatus updateStatus;
465
466     AddPerformanceMarker( PerformanceInterface::UPDATE_START );
467     mCore.Update( frameDelta,
468                   currentTime,
469                   nextFrameTime,
470                   updateStatus,
471                   renderToFboEnabled,
472                   isRenderingToFbo );
473     AddPerformanceMarker( PerformanceInterface::UPDATE_END );
474
475     unsigned int keepUpdatingStatus = updateStatus.KeepUpdating();
476
477     // Tell the event-thread to wake up (if asleep) and send a notification event to Core if required
478     if( updateStatus.NeedsNotification() )
479     {
480       mNotificationTrigger.Trigger();
481       LOG_UPDATE_RENDER( "Notification Triggered" );
482     }
483
484     // Check resize
485     bool surfaceResized = ShouldSurfaceBeResized();
486     if( DALI_UNLIKELY( surfaceResized ) )
487     {
488       // RenderHelper::ResizeSurface() should be called right after a viewport is changed.
489       if( updateStatus.SurfaceRectChanged() )
490       {
491         LOG_UPDATE_RENDER_TRACE_FMT( "Resizing Surface" );
492         mRenderHelper.ResizeSurface();
493         SurfaceResized();
494       }
495     }
496
497     // Optional logging of update/render status
498     mUpdateStatusLogger.Log( keepUpdatingStatus );
499
500     //////////////////////////////
501     // RENDER
502     //////////////////////////////
503
504     mRenderHelper.ConsumeEvents();
505     mRenderHelper.PreRender();
506
507     Integration::RenderStatus renderStatus;
508
509     AddPerformanceMarker( PerformanceInterface::RENDER_START );
510     mCore.Render( renderStatus, mForceClear );
511     AddPerformanceMarker( PerformanceInterface::RENDER_END );
512
513     mForceClear = false;
514
515     if( renderStatus.NeedsPostRender() )
516     {
517       mRenderHelper.PostRender( isRenderingToFbo );
518     }
519
520     // Trigger event thread to request Update/Render thread to sleep if update not required
521     if( ( Integration::KeepUpdating::NOT_REQUESTED == keepUpdatingStatus ) &&
522         ! renderStatus.NeedsUpdate() )
523     {
524       mSleepTrigger->Trigger();
525       updateRequired = false;
526       LOG_UPDATE_RENDER( "Sleep Triggered" );
527     }
528     else
529     {
530       updateRequired = true;
531     }
532
533     //////////////////////////////
534     // FRAME TIME
535     //////////////////////////////
536
537     extraFramesDropped = 0;
538
539     if (timeToSleepUntil == 0)
540     {
541       // If this is the first frame after the thread is initialized or resumed, we
542       // use the actual time the current frame starts from to calculate the time to
543       // sleep until the next frame.
544       timeToSleepUntil = currentFrameStartTime + mDefaultFrameDurationNanoseconds;
545     }
546     else
547     {
548       // Otherwise, always use the sleep-until time calculated in the last frame to
549       // calculate the time to sleep until the next frame. In this way, if there is
550       // any time gap between the current frame and the next frame, or if update or
551       // rendering in the current frame takes too much time so that the specified
552       // sleep-until time has already passed, it will try to keep the frames syncing
553       // by shortening the duration of the next frame.
554       timeToSleepUntil += mDefaultFrameDurationNanoseconds;
555
556       // Check the current time at the end of the frame
557       uint64_t currentFrameEndTime = 0;
558       TimeService::GetNanoseconds( currentFrameEndTime );
559       while ( currentFrameEndTime > timeToSleepUntil + mDefaultFrameDurationNanoseconds )
560       {
561          // We are more than one frame behind already, so just drop the next frames
562          // until the sleep-until time is later than the current time so that we can
563          // catch up.
564          timeToSleepUntil += mDefaultFrameDurationNanoseconds;
565          extraFramesDropped++;
566       }
567     }
568
569     // Render to FBO is intended to measure fps above 60 so sleep is not wanted.
570     if( 0u == renderToFboInterval )
571     {
572       // Sleep until at least the the default frame duration has elapsed. This will return immediately if the specified end-time has already passed.
573       TimeService::SleepUntil( timeToSleepUntil );
574     }
575   }
576
577   // Inform core of context destruction & shutdown EGL
578   mCore.ContextDestroyed();
579   mRenderHelper.ShutdownEgl();
580
581   LOG_UPDATE_RENDER( "THREAD DESTROYED" );
582
583   // Uninstall the logging function
584   mEnvironmentOptions.UnInstallLogFunction();
585 }
586
587 bool CombinedUpdateRenderController::UpdateRenderReady( bool& useElapsedTime, bool updateRequired, uint64_t& timeToSleepUntil )
588 {
589   useElapsedTime = true;
590
591   ConditionalWait::ScopedLock updateLock( mUpdateRenderThreadWaitCondition );
592   while( ( ! mUpdateRenderRunCount || // Should try to wait if event-thread has paused the Update/Render thread
593            ( mUpdateRenderThreadCanSleep && ! updateRequired && ! mPendingRequestUpdate ) ) && // Ensure we wait if we're supposed to be sleeping AND do not require another update
594          ! mDestroyUpdateRenderThread && // Ensure we don't wait if the update-render-thread is supposed to be destroyed
595          ! mNewSurface &&  // Ensure we don't wait if we need to replace the surface
596          ! mSurfaceResized ) // Ensure we don't wait if we need to resize the surface
597   {
598     LOG_UPDATE_RENDER( "WAIT: mUpdateRenderRunCount:       %d", mUpdateRenderRunCount );
599     LOG_UPDATE_RENDER( "      mUpdateRenderThreadCanSleep: %d, updateRequired: %d, mPendingRequestUpdate: %d", mUpdateRenderThreadCanSleep, updateRequired, mPendingRequestUpdate );
600     LOG_UPDATE_RENDER( "      mDestroyUpdateRenderThread:  %d", mDestroyUpdateRenderThread );
601     LOG_UPDATE_RENDER( "      mNewSurface:                 %d", mNewSurface );
602     LOG_UPDATE_RENDER( "      mSurfaceResized:             %d", mSurfaceResized );
603
604     // Reset the time when the thread is waiting, so the sleep-until time for
605     // the first frame after resuming should be based on the actual start time
606     // of the first frame.
607     timeToSleepUntil = 0;
608
609     mUpdateRenderThreadWaitCondition.Wait( updateLock );
610
611     if( ! mUseElapsedTimeAfterWait )
612     {
613       useElapsedTime = false;
614     }
615   }
616
617   LOG_COUNTER_UPDATE_RENDER( "mUpdateRenderRunCount:       %d", mUpdateRenderRunCount );
618   LOG_COUNTER_UPDATE_RENDER( "mUpdateRenderThreadCanSleep: %d, updateRequired: %d, mPendingRequestUpdate: %d", mUpdateRenderThreadCanSleep, updateRequired, mPendingRequestUpdate );
619   LOG_COUNTER_UPDATE_RENDER( "mDestroyUpdateRenderThread:  %d", mDestroyUpdateRenderThread );
620   LOG_COUNTER_UPDATE_RENDER( "mNewSurface:                 %d", mNewSurface );
621   LOG_COUNTER_UPDATE_RENDER( "mSurfaceResized:             %d", mSurfaceResized );
622
623   mUseElapsedTimeAfterWait = FALSE;
624   mUpdateRenderThreadCanSleep = FALSE;
625   mPendingRequestUpdate = FALSE;
626
627   // If we've been asked to run Update/Render cycles a finite number of times then decrement so we wait after the
628   // requested number of cycles
629   if( mUpdateRenderRunCount > 0 )
630   {
631     --mUpdateRenderRunCount;
632   }
633
634   // Keep the update-render thread alive if this thread is NOT to be destroyed
635   return ! mDestroyUpdateRenderThread;
636 }
637
638 RenderSurface* CombinedUpdateRenderController::ShouldSurfaceBeReplaced()
639 {
640   ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
641
642   RenderSurface* newSurface = mNewSurface;
643   mNewSurface = NULL;
644
645   return newSurface;
646 }
647
648 void CombinedUpdateRenderController::SurfaceReplaced()
649 {
650   // Just increment the semaphore
651   sem_post( &mEventThreadSemaphore );
652 }
653
654 bool CombinedUpdateRenderController::ShouldSurfaceBeResized()
655 {
656   ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
657   return mSurfaceResized;
658 }
659
660 void CombinedUpdateRenderController::SurfaceResized()
661 {
662   ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
663   mSurfaceResized = FALSE;
664 }
665
666 ///////////////////////////////////////////////////////////////////////////////////////////////////
667 // ALL THREADS
668 ///////////////////////////////////////////////////////////////////////////////////////////////////
669
670 void CombinedUpdateRenderController::NotifyThreadInitialised()
671 {
672   // Just increment the semaphore
673   sem_post( &mEventThreadSemaphore );
674 }
675
676 void CombinedUpdateRenderController::AddPerformanceMarker( PerformanceInterface::MarkerType type )
677 {
678   if( mPerformanceInterface )
679   {
680     mPerformanceInterface->AddMarker( type );
681   }
682 }
683
684 /////////////////////////////////////////////////////////////////////////////////////////////////
685 // POST RENDERING: EVENT THREAD
686 /////////////////////////////////////////////////////////////////////////////////////////////////
687
688 void CombinedUpdateRenderController::PostRenderComplete()
689 {
690   ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
691   mPostRendering = FALSE;
692   mUpdateRenderThreadWaitCondition.Notify( lock );
693 }
694
695 ///////////////////////////////////////////////////////////////////////////////////////////////////
696 // POST RENDERING: RENDER THREAD
697 ///////////////////////////////////////////////////////////////////////////////////////////////////
698
699 void CombinedUpdateRenderController::PostRenderStarted()
700 {
701   ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
702   mPostRendering = TRUE;
703 }
704
705 void CombinedUpdateRenderController::PostRenderWaitForCompletion()
706 {
707   ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
708   while( mPostRendering &&
709          ! mNewSurface &&                // We should NOT wait if we're replacing the surface
710          ! mSurfaceResized &&            // We should NOT wait if we're resizing the surface
711          ! mDestroyUpdateRenderThread )
712   {
713     mUpdateRenderThreadWaitCondition.Wait( lock );
714   }
715 }
716
717 } // namespace Adaptor
718
719 } // namespace Internal
720
721 } // namespace Dali