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