Fix flickering issue when the window is resized
[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( "Resize the surface" );
296
297   {
298     ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
299     mPostRendering = FALSE; // Clear the post-rendering flag as Update/Render thread will resize the surface now
300     mSurfaceResized = TRUE;
301     mUpdateRenderThreadWaitCondition.Notify( lock );
302   }
303 }
304
305 void CombinedUpdateRenderController::SetRenderRefreshRate( unsigned int numberOfFramesPerRender )
306 {
307   // Not protected by lock, but written to rarely so not worth adding a lock when reading
308   mDefaultFrameDelta                  = numberOfFramesPerRender * DEFAULT_FRAME_DURATION_IN_SECONDS;
309   mDefaultFrameDurationMilliseconds   = uint64_t( numberOfFramesPerRender ) * DEFAULT_FRAME_DURATION_IN_MILLISECONDS;
310   mDefaultFrameDurationNanoseconds    = uint64_t( numberOfFramesPerRender ) * DEFAULT_FRAME_DURATION_IN_NANOSECONDS;
311   mDefaultHalfFrameNanoseconds        = mDefaultFrameDurationNanoseconds / 2u;
312
313   LOG_EVENT( "mDefaultFrameDelta(%.6f), mDefaultFrameDurationMilliseconds(%lld), mDefaultFrameDurationNanoseconds(%lld)", mDefaultFrameDelta, mDefaultFrameDurationMilliseconds, mDefaultFrameDurationNanoseconds );
314 }
315
316 ///////////////////////////////////////////////////////////////////////////////////////////////////
317 // EVENT THREAD
318 ///////////////////////////////////////////////////////////////////////////////////////////////////
319
320 void CombinedUpdateRenderController::RunUpdateRenderThread( int numberOfCycles, bool useElapsedTime )
321 {
322   ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
323   mUpdateRenderRunCount = numberOfCycles;
324   mUpdateRenderThreadCanSleep = FALSE;
325   mUseElapsedTimeAfterWait = useElapsedTime;
326   LOG_COUNTER_EVENT( "mUpdateRenderRunCount: %d, mUseElapsedTimeAfterWait: %d", mUpdateRenderRunCount, mUseElapsedTimeAfterWait );
327   mUpdateRenderThreadWaitCondition.Notify( lock );
328 }
329
330 void CombinedUpdateRenderController::PauseUpdateRenderThread()
331 {
332   ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
333   mUpdateRenderRunCount = 0;
334 }
335
336 void CombinedUpdateRenderController::StopUpdateRenderThread()
337 {
338   ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
339   mDestroyUpdateRenderThread = TRUE;
340   mUpdateRenderThreadWaitCondition.Notify( lock );
341 }
342
343 bool CombinedUpdateRenderController::IsUpdateRenderThreadPaused()
344 {
345   ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
346   return ( mUpdateRenderRunCount != CONTINUOUS ) || // Report paused if NOT continuously running
347          mUpdateRenderThreadCanSleep;               // Report paused if sleeping
348 }
349
350 void CombinedUpdateRenderController::ProcessSleepRequest()
351 {
352   LOG_EVENT_TRACE;
353
354   // Decrement Update request count
355   if( mUpdateRequestCount > 0 )
356   {
357     --mUpdateRequestCount;
358   }
359
360   // Can sleep if our update-request count is 0
361   // Update/Render thread can choose to carry on updating if it determines more update/renders are required
362   if( mUpdateRequestCount == 0 )
363   {
364     LOG_EVENT( "Going to sleep" );
365
366     ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
367     mUpdateRenderThreadCanSleep = TRUE;
368   }
369 }
370
371 ///////////////////////////////////////////////////////////////////////////////////////////////////
372 // UPDATE/RENDER THREAD
373 ///////////////////////////////////////////////////////////////////////////////////////////////////
374
375 void CombinedUpdateRenderController::UpdateRenderThread()
376 {
377   // Install a function for logging
378   mEnvironmentOptions.InstallLogFunction();
379
380   // Install a function for tracing
381   mEnvironmentOptions.InstallTraceFunction();
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     const bool isRenderingToFbo = renderToFboEnabled && ( ( 0u == frameCount ) || ( 0u != frameCount % renderToFboInterval ) );
441     ++frameCount;
442
443     //////////////////////////////
444     // UPDATE
445     //////////////////////////////
446
447     const unsigned int currentTime = currentFrameStartTime / NANOSECONDS_PER_MILLISECOND;
448     const unsigned int nextFrameTime = currentTime + mDefaultFrameDurationMilliseconds;
449
450     uint64_t noOfFramesSinceLastUpdate = 1;
451     float frameDelta = 0.0f;
452     if( useElapsedTime )
453     {
454       // If using the elapsed time, then calculate frameDelta as a multiple of mDefaultFrameDelta
455       noOfFramesSinceLastUpdate += extraFramesDropped;
456
457       frameDelta = mDefaultFrameDelta * noOfFramesSinceLastUpdate;
458     }
459     LOG_UPDATE_RENDER( "timeSinceLastFrame(%llu) noOfFramesSinceLastUpdate(%u) frameDelta(%.6f)", timeSinceLastFrame, noOfFramesSinceLastUpdate, frameDelta );
460
461     Integration::UpdateStatus updateStatus;
462
463     AddPerformanceMarker( PerformanceInterface::UPDATE_START );
464     mCore.Update( frameDelta,
465                   currentTime,
466                   nextFrameTime,
467                   updateStatus,
468                   renderToFboEnabled,
469                   isRenderingToFbo );
470     AddPerformanceMarker( PerformanceInterface::UPDATE_END );
471
472     unsigned int keepUpdatingStatus = updateStatus.KeepUpdating();
473
474     // Tell the event-thread to wake up (if asleep) and send a notification event to Core if required
475     if( updateStatus.NeedsNotification() )
476     {
477       mNotificationTrigger.Trigger();
478       LOG_UPDATE_RENDER( "Notification Triggered" );
479     }
480
481     // Check resize
482     bool surfaceResized = ShouldSurfaceBeResized();
483     if( DALI_UNLIKELY( surfaceResized ) )
484     {
485       // RenderHelper::ResizeSurface() should be called right after a viewport is changed.
486       if( updateStatus.SurfaceRectChanged() )
487       {
488         LOG_UPDATE_RENDER_TRACE_FMT( "Resizing Surface" );
489         mRenderHelper.ResizeSurface();
490         SurfaceResized();
491       }
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, mForceClear );
508     AddPerformanceMarker( PerformanceInterface::RENDER_END );
509
510     mForceClear = false;
511
512     if( renderStatus.NeedsPostRender() )
513     {
514       mRenderHelper.PostRender( isRenderingToFbo );
515     }
516
517     // Trigger event thread to request Update/Render thread to sleep if update not required
518     if( ( Integration::KeepUpdating::NOT_REQUESTED == keepUpdatingStatus ) &&
519         ! renderStatus.NeedsUpdate() )
520     {
521       mSleepTrigger->Trigger();
522       updateRequired = false;
523       LOG_UPDATE_RENDER( "Sleep Triggered" );
524     }
525     else
526     {
527       updateRequired = true;
528     }
529
530     //////////////////////////////
531     // FRAME TIME
532     //////////////////////////////
533
534     extraFramesDropped = 0;
535
536     if (timeToSleepUntil == 0)
537     {
538       // If this is the first frame after the thread is initialized or resumed, we
539       // use the actual time the current frame starts from to calculate the time to
540       // sleep until the next frame.
541       timeToSleepUntil = currentFrameStartTime + mDefaultFrameDurationNanoseconds;
542     }
543     else
544     {
545       // Otherwise, always use the sleep-until time calculated in the last frame to
546       // calculate the time to sleep until the next frame. In this way, if there is
547       // any time gap between the current frame and the next frame, or if update or
548       // rendering in the current frame takes too much time so that the specified
549       // sleep-until time has already passed, it will try to keep the frames syncing
550       // by shortening the duration of the next frame.
551       timeToSleepUntil += mDefaultFrameDurationNanoseconds;
552
553       // Check the current time at the end of the frame
554       uint64_t currentFrameEndTime = 0;
555       TimeService::GetNanoseconds( currentFrameEndTime );
556       while ( currentFrameEndTime > timeToSleepUntil + mDefaultFrameDurationNanoseconds )
557       {
558          // We are more than one frame behind already, so just drop the next frames
559          // until the sleep-until time is later than the current time so that we can
560          // catch up.
561          timeToSleepUntil += mDefaultFrameDurationNanoseconds;
562          extraFramesDropped++;
563       }
564     }
565
566     // Render to FBO is intended to measure fps above 60 so sleep is not wanted.
567     if( 0u == renderToFboInterval )
568     {
569       // Sleep until at least the the default frame duration has elapsed. This will return immediately if the specified end-time has already passed.
570       TimeService::SleepUntil( timeToSleepUntil );
571     }
572   }
573
574   // Inform core of context destruction & shutdown EGL
575   mCore.ContextDestroyed();
576   mRenderHelper.ShutdownEgl();
577
578   LOG_UPDATE_RENDER( "THREAD DESTROYED" );
579
580   // Uninstall the logging function
581   mEnvironmentOptions.UnInstallLogFunction();
582 }
583
584 bool CombinedUpdateRenderController::UpdateRenderReady( bool& useElapsedTime, bool updateRequired, uint64_t& timeToSleepUntil )
585 {
586   useElapsedTime = true;
587
588   ConditionalWait::ScopedLock updateLock( mUpdateRenderThreadWaitCondition );
589   while( ( ! mUpdateRenderRunCount || // Should try to wait if event-thread has paused the Update/Render thread
590            ( mUpdateRenderThreadCanSleep && ! updateRequired && ! mPendingRequestUpdate ) ) && // Ensure we wait if we're supposed to be sleeping AND do not require another update
591          ! mDestroyUpdateRenderThread && // Ensure we don't wait if the update-render-thread is supposed to be destroyed
592          ! mNewSurface &&  // Ensure we don't wait if we need to replace the surface
593          ! mSurfaceResized ) // Ensure we don't wait if we need to resize the surface
594   {
595     LOG_UPDATE_RENDER( "WAIT: mUpdateRenderRunCount:       %d", mUpdateRenderRunCount );
596     LOG_UPDATE_RENDER( "      mUpdateRenderThreadCanSleep: %d, updateRequired: %d, mPendingRequestUpdate: %d", mUpdateRenderThreadCanSleep, updateRequired, mPendingRequestUpdate );
597     LOG_UPDATE_RENDER( "      mDestroyUpdateRenderThread:  %d", mDestroyUpdateRenderThread );
598     LOG_UPDATE_RENDER( "      mNewSurface:                 %d", mNewSurface );
599     LOG_UPDATE_RENDER( "      mSurfaceResized:             %d", mSurfaceResized );
600
601     // Reset the time when the thread is waiting, so the sleep-until time for
602     // the first frame after resuming should be based on the actual start time
603     // of the first frame.
604     timeToSleepUntil = 0;
605
606     mUpdateRenderThreadWaitCondition.Wait( updateLock );
607
608     if( ! mUseElapsedTimeAfterWait )
609     {
610       useElapsedTime = false;
611     }
612   }
613
614   LOG_COUNTER_UPDATE_RENDER( "mUpdateRenderRunCount:       %d", mUpdateRenderRunCount );
615   LOG_COUNTER_UPDATE_RENDER( "mUpdateRenderThreadCanSleep: %d, updateRequired: %d, mPendingRequestUpdate: %d", mUpdateRenderThreadCanSleep, updateRequired, mPendingRequestUpdate );
616   LOG_COUNTER_UPDATE_RENDER( "mDestroyUpdateRenderThread:  %d", mDestroyUpdateRenderThread );
617   LOG_COUNTER_UPDATE_RENDER( "mNewSurface:                 %d", mNewSurface );
618   LOG_COUNTER_UPDATE_RENDER( "mSurfaceResized:             %d", mSurfaceResized );
619
620   mUseElapsedTimeAfterWait = FALSE;
621   mUpdateRenderThreadCanSleep = FALSE;
622   mPendingRequestUpdate = FALSE;
623
624   // If we've been asked to run Update/Render cycles a finite number of times then decrement so we wait after the
625   // requested number of cycles
626   if( mUpdateRenderRunCount > 0 )
627   {
628     --mUpdateRenderRunCount;
629   }
630
631   // Keep the update-render thread alive if this thread is NOT to be destroyed
632   return ! mDestroyUpdateRenderThread;
633 }
634
635 RenderSurface* CombinedUpdateRenderController::ShouldSurfaceBeReplaced()
636 {
637   ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
638
639   RenderSurface* newSurface = mNewSurface;
640   mNewSurface = NULL;
641
642   return newSurface;
643 }
644
645 void CombinedUpdateRenderController::SurfaceReplaced()
646 {
647   // Just increment the semaphore
648   sem_post( &mEventThreadSemaphore );
649 }
650
651 bool CombinedUpdateRenderController::ShouldSurfaceBeResized()
652 {
653   ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
654   return mSurfaceResized;
655 }
656
657 void CombinedUpdateRenderController::SurfaceResized()
658 {
659   ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
660   mSurfaceResized = FALSE;
661 }
662
663 ///////////////////////////////////////////////////////////////////////////////////////////////////
664 // ALL THREADS
665 ///////////////////////////////////////////////////////////////////////////////////////////////////
666
667 void CombinedUpdateRenderController::NotifyThreadInitialised()
668 {
669   // Just increment the semaphore
670   sem_post( &mEventThreadSemaphore );
671 }
672
673 void CombinedUpdateRenderController::AddPerformanceMarker( PerformanceInterface::MarkerType type )
674 {
675   if( mPerformanceInterface )
676   {
677     mPerformanceInterface->AddMarker( type );
678   }
679 }
680
681 /////////////////////////////////////////////////////////////////////////////////////////////////
682 // POST RENDERING: EVENT THREAD
683 /////////////////////////////////////////////////////////////////////////////////////////////////
684
685 void CombinedUpdateRenderController::PostRenderComplete()
686 {
687   ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
688   mPostRendering = FALSE;
689   mUpdateRenderThreadWaitCondition.Notify( lock );
690 }
691
692 ///////////////////////////////////////////////////////////////////////////////////////////////////
693 // POST RENDERING: RENDER THREAD
694 ///////////////////////////////////////////////////////////////////////////////////////////////////
695
696 void CombinedUpdateRenderController::PostRenderStarted()
697 {
698   ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
699   mPostRendering = TRUE;
700 }
701
702 void CombinedUpdateRenderController::PostRenderWaitForCompletion()
703 {
704   ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
705   while( mPostRendering &&
706          ! mNewSurface &&                // We should NOT wait if we're replacing the surface
707          ! mSurfaceResized &&            // We should NOT wait if we're resizing the surface
708          ! mDestroyUpdateRenderThread )
709   {
710     mUpdateRenderThreadWaitCondition.Wait( lock );
711   }
712 }
713
714 } // namespace Adaptor
715
716 } // namespace Internal
717
718 } // namespace Dali