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