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