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