Performance Improvements: Remove unnecessary SwapBuffer calls
[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
380   while( UpdateRenderReady( useElapsedTime, updateRequired ) )
381   {
382     LOG_UPDATE_RENDER_TRACE;
383
384     // Performance statistics are logged upon a VSYNC tick so use this point for a VSync marker
385     AddPerformanceMarker( PerformanceInterface::VSYNC );
386
387     uint64_t currentFrameStartTime = 0;
388     TimeService::GetNanoseconds( currentFrameStartTime );
389
390     const uint64_t timeSinceLastFrame = currentFrameStartTime - lastFrameTime;
391
392     // Optional FPS Tracking when continuously rendering
393     if( useElapsedTime && mFpsTracker.Enabled() )
394     {
395       float absoluteTimeSinceLastRender = timeSinceLastFrame * NANOSECONDS_TO_SECOND;
396       mFpsTracker.Track( absoluteTimeSinceLastRender );
397     }
398
399     lastFrameTime = currentFrameStartTime; // Store frame start time
400
401     //////////////////////////////
402     // REPLACE SURFACE
403     //////////////////////////////
404
405     RenderSurface* newSurface = ShouldSurfaceBeReplaced();
406     if( DALI_UNLIKELY( newSurface ) )
407     {
408       LOG_UPDATE_RENDER_TRACE_FMT( "Replacing Surface" );
409       mRenderHelper.ReplaceSurface( newSurface );
410       SurfaceReplaced();
411     }
412
413     //////////////////////////////
414     // UPDATE
415     //////////////////////////////
416
417     const unsigned int currentTime = currentFrameStartTime / NANOSECONDS_PER_MILLISECOND;
418     const unsigned int nextFrameTime = currentTime + mDefaultFrameDurationMilliseconds;
419
420     uint64_t noOfFramesSinceLastUpdate = 1;
421     float frameDelta = 0.0f;
422     if( useElapsedTime )
423     {
424       // If using the elapsed time, then calculate frameDelta as a multiple of mDefaultFrameDelta
425       // Round up if remainder is more than half the default frame time
426       noOfFramesSinceLastUpdate = ( timeSinceLastFrame + mDefaultHalfFrameNanoseconds) / mDefaultFrameDurationNanoseconds;
427       frameDelta = mDefaultFrameDelta * noOfFramesSinceLastUpdate;
428     }
429     LOG_UPDATE_RENDER( "timeSinceLastFrame(%llu) noOfFramesSinceLastUpdate(%u) frameDelta(%.6f)", timeSinceLastFrame, noOfFramesSinceLastUpdate, frameDelta );
430
431     Integration::UpdateStatus updateStatus;
432
433     AddPerformanceMarker( PerformanceInterface::UPDATE_START );
434     mCore.Update( frameDelta, currentTime, nextFrameTime, updateStatus );
435     AddPerformanceMarker( PerformanceInterface::UPDATE_END );
436
437     unsigned int keepUpdatingStatus = updateStatus.KeepUpdating();
438
439     // Tell the event-thread to wake up (if asleep) and send a notification event to Core if required
440     if( updateStatus.NeedsNotification() )
441     {
442       mNotificationTrigger.Trigger();
443       LOG_UPDATE_RENDER( "Notification Triggered" );
444     }
445
446     // Optional logging of update/render status
447     mUpdateStatusLogger.Log( keepUpdatingStatus );
448
449     //////////////////////////////
450     // RENDER
451     //////////////////////////////
452
453     mRenderHelper.ConsumeEvents();
454     mRenderHelper.PreRender();
455
456     Integration::RenderStatus renderStatus;
457
458     AddPerformanceMarker( PerformanceInterface::RENDER_START );
459     mCore.Render( renderStatus );
460     AddPerformanceMarker( PerformanceInterface::RENDER_END );
461
462     if( renderStatus.NeedsPostRender() )
463     {
464       mRenderHelper.PostRender();
465     }
466
467     // Trigger event thread to request Update/Render thread to sleep if update not required
468     if( ( Integration::KeepUpdating::NOT_REQUESTED == keepUpdatingStatus ) &&
469         ! renderStatus.NeedsUpdate() )
470     {
471       mSleepTrigger->Trigger();
472       updateRequired = false;
473       LOG_UPDATE_RENDER( "Sleep Triggered" );
474     }
475     else
476     {
477       updateRequired = true;
478     }
479
480     //////////////////////////////
481     // FRAME TIME
482     //////////////////////////////
483
484     // Sleep until at least the the default frame duration has elapsed. This will return immediately if the specified end-time has already passed.
485     TimeService::SleepUntil( currentFrameStartTime + mDefaultFrameDurationNanoseconds );
486   }
487
488   // Inform core of context destruction & shutdown EGL
489   mCore.ContextDestroyed();
490   mRenderHelper.ShutdownEgl();
491
492   LOG_UPDATE_RENDER( "THREAD DESTROYED" );
493
494   // Uninstall the logging function
495   mEnvironmentOptions.UnInstallLogFunction();
496 }
497
498 bool CombinedUpdateRenderController::UpdateRenderReady( bool& useElapsedTime, bool updateRequired )
499 {
500   useElapsedTime = true;
501
502   ConditionalWait::ScopedLock updateLock( mUpdateRenderThreadWaitCondition );
503   while( ( ! mUpdateRenderRunCount || // Should try to wait if event-thread has paused the Update/Render thread
504            ( mUpdateRenderThreadCanSleep && ! updateRequired && ! mPendingRequestUpdate ) ) && // Ensure we wait if we're supposed to be sleeping AND do not require another update
505          ! mDestroyUpdateRenderThread && // Ensure we don't wait if the update-render-thread is supposed to be destroyed
506          ! mNewSurface ) // Ensure we don't wait if we need to replace the surface
507   {
508     LOG_UPDATE_RENDER( "WAIT: mUpdateRenderRunCount:       %d", mUpdateRenderRunCount );
509     LOG_UPDATE_RENDER( "      mUpdateRenderThreadCanSleep: %d, updateRequired: %d, mPendingRequestUpdate: %d", mUpdateRenderThreadCanSleep, updateRequired, mPendingRequestUpdate );
510     LOG_UPDATE_RENDER( "      mDestroyUpdateRenderThread:  %d", mDestroyUpdateRenderThread );
511     LOG_UPDATE_RENDER( "      mNewSurface:                 %d", mNewSurface );
512
513     mUpdateRenderThreadWaitCondition.Wait( updateLock );
514
515     if( ! mUseElapsedTimeAfterWait )
516     {
517       useElapsedTime = false;
518     }
519   }
520
521   LOG_COUNTER_UPDATE_RENDER( "mUpdateRenderRunCount:       %d", mUpdateRenderRunCount );
522   LOG_COUNTER_UPDATE_RENDER( "mUpdateRenderThreadCanSleep: %d, updateRequired: %d, mPendingRequestUpdate: %d", mUpdateRenderThreadCanSleep, updateRequired, mPendingRequestUpdate );
523   LOG_COUNTER_UPDATE_RENDER( "mDestroyUpdateRenderThread:  %d", mDestroyUpdateRenderThread );
524   LOG_COUNTER_UPDATE_RENDER( "mNewSurface:                 %d", mNewSurface );
525
526   mUseElapsedTimeAfterWait = FALSE;
527   mUpdateRenderThreadCanSleep = FALSE;
528   mPendingRequestUpdate = FALSE;
529
530   // If we've been asked to run Update/Render cycles a finite number of times then decrement so we wait after the
531   // requested number of cycles
532   if( mUpdateRenderRunCount > 0 )
533   {
534     --mUpdateRenderRunCount;
535   }
536
537   // Keep the update-render thread alive if this thread is NOT to be destroyed
538   return ! mDestroyUpdateRenderThread;
539 }
540
541 RenderSurface* CombinedUpdateRenderController::ShouldSurfaceBeReplaced()
542 {
543   ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
544
545   RenderSurface* newSurface = mNewSurface;
546   mNewSurface = NULL;
547
548   return newSurface;
549 }
550
551 void CombinedUpdateRenderController::SurfaceReplaced()
552 {
553   // Just increment the semaphore
554   sem_post( &mEventThreadSemaphore );
555 }
556
557 ///////////////////////////////////////////////////////////////////////////////////////////////////
558 // ALL THREADS
559 ///////////////////////////////////////////////////////////////////////////////////////////////////
560
561 void CombinedUpdateRenderController::NotifyThreadInitialised()
562 {
563   // Just increment the semaphore
564   sem_post( &mEventThreadSemaphore );
565 }
566
567 void CombinedUpdateRenderController::AddPerformanceMarker( PerformanceInterface::MarkerType type )
568 {
569   if( mPerformanceInterface )
570   {
571     mPerformanceInterface->AddMarker( type );
572   }
573 }
574
575 /////////////////////////////////////////////////////////////////////////////////////////////////
576 // POST RENDERING: EVENT THREAD
577 /////////////////////////////////////////////////////////////////////////////////////////////////
578
579 void CombinedUpdateRenderController::PostRenderComplete()
580 {
581   ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
582   mPostRendering = FALSE;
583   mUpdateRenderThreadWaitCondition.Notify( lock );
584 }
585
586 ///////////////////////////////////////////////////////////////////////////////////////////////////
587 // POST RENDERING: RENDER THREAD
588 ///////////////////////////////////////////////////////////////////////////////////////////////////
589
590 void CombinedUpdateRenderController::PostRenderStarted()
591 {
592   ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
593   mPostRendering = TRUE;
594 }
595
596 void CombinedUpdateRenderController::PostRenderWaitForCompletion()
597 {
598   ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
599   while( mPostRendering &&
600          ! mNewSurface &&                // We should NOT wait if we're replacing the surface
601          ! mDestroyUpdateRenderThread )
602   {
603     mUpdateRenderThreadWaitCondition.Wait( lock );
604   }
605 }
606
607 } // namespace Adaptor
608
609 } // namespace Internal
610
611 } // namespace Dali