738da28c4ca3d341e1e3fc1b80f7ad978f9f97d1
[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   // Increment the update-request count to the maximum
249   if( mUpdateRequestCount < MAXIMUM_UPDATE_REQUESTS )
250   {
251     ++mUpdateRequestCount;
252   }
253
254   if( IsUpdateRenderThreadPaused() )
255   {
256     LOG_EVENT_TRACE;
257
258     // Run Update/Render once
259     RunUpdateRenderThread( ONCE, false /* No animation progression */ );
260   }
261 }
262
263 void CombinedUpdateRenderController::ReplaceSurface( RenderSurface* newSurface )
264 {
265   LOG_EVENT_TRACE;
266
267   // Set the ThreadSyncronizationInterface on the new surface
268   newSurface->SetThreadSynchronization( *this );
269
270   LOG_EVENT( "Starting to replace the surface, event-thread blocked" );
271
272   // Start replacing the surface.
273   {
274     ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
275     mPostRendering = FALSE; // Clear the post-rendering flag as Update/Render thread will replace the surface now
276     mNewSurface = newSurface;
277     mUpdateRenderThreadWaitCondition.Notify( lock );
278   }
279
280   // Wait until the surface has been replaced
281   sem_wait( &mEventThreadSemaphore );
282
283   LOG_EVENT( "Surface replaced, event-thread continuing" );
284 }
285
286 void CombinedUpdateRenderController::SetRenderRefreshRate( unsigned int numberOfFramesPerRender )
287 {
288   // Not protected by lock, but written to rarely so not worth adding a lock when reading
289   mDefaultFrameDelta                  = numberOfFramesPerRender * DEFAULT_FRAME_DURATION_IN_SECONDS;
290   mDefaultFrameDurationMilliseconds   = (uint64_t)numberOfFramesPerRender * DEFAULT_FRAME_DURATION_IN_MILLISECONDS;
291   mDefaultFrameDurationNanoseconds    = (uint64_t)numberOfFramesPerRender * DEFAULT_FRAME_DURATION_IN_NANOSECONDS;
292   mDefaultHalfFrameNanoseconds        = mDefaultFrameDurationNanoseconds / 2;
293
294   LOG_EVENT( "mDefaultFrameDelta(%.6f), mDefaultFrameDurationMilliseconds(%lld), mDefaultFrameDurationNanoseconds(%lld)", mDefaultFrameDelta, mDefaultFrameDurationMilliseconds, mDefaultFrameDurationNanoseconds );
295 }
296
297 ///////////////////////////////////////////////////////////////////////////////////////////////////
298 // EVENT THREAD
299 ///////////////////////////////////////////////////////////////////////////////////////////////////
300
301 void CombinedUpdateRenderController::RunUpdateRenderThread( int numberOfCycles, bool useElapsedTime )
302 {
303   ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
304   mUpdateRenderRunCount = numberOfCycles;
305   mUpdateRenderThreadCanSleep = FALSE;
306   mUseElapsedTimeAfterWait = useElapsedTime;
307   LOG_COUNTER_EVENT( "mUpdateRenderRunCount: %d, mUseElapsedTimeAfterWait: %d", mUpdateRenderRunCount, mUseElapsedTimeAfterWait );
308   mUpdateRenderThreadWaitCondition.Notify( lock );
309 }
310
311 void CombinedUpdateRenderController::PauseUpdateRenderThread()
312 {
313   ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
314   mUpdateRenderRunCount = 0;
315 }
316
317 void CombinedUpdateRenderController::StopUpdateRenderThread()
318 {
319   ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
320   mDestroyUpdateRenderThread = TRUE;
321   mUpdateRenderThreadWaitCondition.Notify( lock );
322 }
323
324 bool CombinedUpdateRenderController::IsUpdateRenderThreadPaused()
325 {
326   ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
327   return ( mUpdateRenderRunCount != CONTINUOUS ) || // Report paused if NOT continuously running
328          mUpdateRenderThreadCanSleep;               // Report paused if sleeping
329 }
330
331 void CombinedUpdateRenderController::ProcessSleepRequest()
332 {
333   LOG_EVENT_TRACE;
334
335   // Decrement Update request count
336   if( mUpdateRequestCount > 0 )
337   {
338     --mUpdateRequestCount;
339   }
340
341   // Can sleep if our update-request count is 0
342   // Update/Render thread can choose to carry on updating if it determines more update/renders are required
343   if( mUpdateRequestCount == 0 )
344   {
345     LOG_EVENT( "Going to sleep" );
346
347     ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
348     mUpdateRenderThreadCanSleep = TRUE;
349   }
350 }
351
352 ///////////////////////////////////////////////////////////////////////////////////////////////////
353 // UPDATE/RENDER THREAD
354 ///////////////////////////////////////////////////////////////////////////////////////////////////
355
356 void CombinedUpdateRenderController::UpdateRenderThread()
357 {
358   // Install a function for logging
359   mEnvironmentOptions.InstallLogFunction();
360
361   LOG_UPDATE_RENDER( "THREAD CREATED" );
362
363   mRenderHelper.InitializeEgl();
364
365   // tell core it has a context
366   mCore.ContextCreated();
367
368   NotifyThreadInitialised();
369
370   // Update time
371   uint64_t lastFrameTime;
372   TimeService::GetNanoseconds( lastFrameTime );
373
374   LOG_UPDATE_RENDER( "THREAD INITIALISED" );
375
376   bool useElapsedTime = true;
377   bool updateRequired = true;
378
379   while( UpdateRenderReady( useElapsedTime, updateRequired ) )
380   {
381     LOG_UPDATE_RENDER_TRACE;
382
383     // Performance statistics are logged upon a VSYNC tick so use this point for a VSync marker
384     AddPerformanceMarker( PerformanceInterface::VSYNC );
385
386     uint64_t currentFrameStartTime = 0;
387     TimeService::GetNanoseconds( currentFrameStartTime );
388
389     const uint64_t timeSinceLastFrame = currentFrameStartTime - lastFrameTime;
390
391     // Optional FPS Tracking when continuously rendering
392     if( useElapsedTime && mFpsTracker.Enabled() )
393     {
394       float absoluteTimeSinceLastRender = timeSinceLastFrame * NANOSECONDS_TO_SECOND;
395       mFpsTracker.Track( absoluteTimeSinceLastRender );
396     }
397
398     lastFrameTime = currentFrameStartTime; // Store frame start time
399
400     //////////////////////////////
401     // REPLACE SURFACE
402     //////////////////////////////
403
404     RenderSurface* newSurface = ShouldSurfaceBeReplaced();
405     if( DALI_UNLIKELY( newSurface ) )
406     {
407       LOG_UPDATE_RENDER_TRACE_FMT( "Replacing Surface" );
408       mRenderHelper.ReplaceSurface( newSurface );
409       SurfaceReplaced();
410     }
411
412     //////////////////////////////
413     // UPDATE
414     //////////////////////////////
415
416     const unsigned int currentTime = currentFrameStartTime / NANOSECONDS_PER_MILLISECOND;
417     const unsigned int nextFrameTime = currentTime + mDefaultFrameDurationMilliseconds;
418
419     uint64_t noOfFramesSinceLastUpdate = 1;
420     float frameDelta = 0.0f;
421     if( useElapsedTime )
422     {
423       // If using the elapsed time, then calculate frameDelta as a multiple of mDefaultFrameDelta
424       // Round up if remainder is more than half the default frame time
425       noOfFramesSinceLastUpdate = ( timeSinceLastFrame + mDefaultHalfFrameNanoseconds) / mDefaultFrameDurationNanoseconds;
426       frameDelta = mDefaultFrameDelta * noOfFramesSinceLastUpdate;
427     }
428     LOG_UPDATE_RENDER( "timeSinceLastFrame(%llu) noOfFramesSinceLastUpdate(%u) frameDelta(%.6f)", timeSinceLastFrame, noOfFramesSinceLastUpdate, frameDelta );
429
430     Integration::UpdateStatus updateStatus;
431
432     AddPerformanceMarker( PerformanceInterface::UPDATE_START );
433     mCore.Update( frameDelta, currentTime, nextFrameTime, updateStatus );
434     AddPerformanceMarker( PerformanceInterface::UPDATE_END );
435
436     unsigned int keepUpdatingStatus = updateStatus.KeepUpdating();
437
438     // Tell the event-thread to wake up (if asleep) and send a notification event to Core if required
439     if( updateStatus.NeedsNotification() )
440     {
441       mNotificationTrigger.Trigger();
442       LOG_UPDATE_RENDER( "Notification Triggered" );
443     }
444
445     // Optional logging of update/render status
446     mUpdateStatusLogger.Log( keepUpdatingStatus );
447
448     //////////////////////////////
449     // RENDER
450     //////////////////////////////
451
452     mRenderHelper.ConsumeEvents();
453     mRenderHelper.PreRender();
454
455     Integration::RenderStatus renderStatus;
456
457     AddPerformanceMarker( PerformanceInterface::RENDER_START );
458     mCore.Render( renderStatus );
459     AddPerformanceMarker( PerformanceInterface::RENDER_END );
460
461     mRenderHelper.PostRender();
462
463     // Trigger event thread to request Update/Render thread to sleep if update not required
464     if( ( Integration::KeepUpdating::NOT_REQUESTED == keepUpdatingStatus ) &&
465         ! renderStatus.NeedsUpdate() )
466     {
467       mSleepTrigger->Trigger();
468       updateRequired = false;
469       LOG_UPDATE_RENDER( "Sleep Triggered" );
470     }
471     else
472     {
473       updateRequired = true;
474     }
475
476     //////////////////////////////
477     // FRAME TIME
478     //////////////////////////////
479
480     // Sleep until at least the the default frame duration has elapsed. This will return immediately if the specified end-time has already passed.
481     TimeService::SleepUntil( currentFrameStartTime + mDefaultFrameDurationNanoseconds );
482   }
483
484   // Inform core of context destruction & shutdown EGL
485   mCore.ContextDestroyed();
486   mRenderHelper.ShutdownEgl();
487
488   LOG_UPDATE_RENDER( "THREAD DESTROYED" );
489
490   // Uninstall the logging function
491   mEnvironmentOptions.UnInstallLogFunction();
492 }
493
494 bool CombinedUpdateRenderController::UpdateRenderReady( bool& useElapsedTime, bool updateRequired )
495 {
496   useElapsedTime = true;
497
498   ConditionalWait::ScopedLock updateLock( mUpdateRenderThreadWaitCondition );
499   while( ( ! mUpdateRenderRunCount || // Should try to wait if event-thread has paused the Update/Render thread
500            ( mUpdateRenderThreadCanSleep && ! updateRequired && ! mPendingRequestUpdate ) ) && // Ensure we wait if we're supposed to be sleeping AND do not require another update
501          ! mDestroyUpdateRenderThread && // Ensure we don't wait if the update-render-thread is supposed to be destroyed
502          ! mNewSurface ) // Ensure we don't wait if we need to replace the surface
503   {
504     LOG_UPDATE_RENDER( "WAIT: mUpdateRenderRunCount:       %d", mUpdateRenderRunCount );
505     LOG_UPDATE_RENDER( "      mUpdateRenderThreadCanSleep: %d, updateRequired: %d, mPendingRequestUpdate: %d", mUpdateRenderThreadCanSleep, updateRequired, mPendingRequestUpdate );
506     LOG_UPDATE_RENDER( "      mDestroyUpdateRenderThread:  %d", mDestroyUpdateRenderThread );
507     LOG_UPDATE_RENDER( "      mNewSurface:                 %d", mNewSurface );
508
509     mUpdateRenderThreadWaitCondition.Wait( updateLock );
510
511     if( ! mUseElapsedTimeAfterWait )
512     {
513       useElapsedTime = false;
514     }
515   }
516
517   LOG_COUNTER_UPDATE_RENDER( "mUpdateRenderRunCount:       %d", mUpdateRenderRunCount );
518   LOG_COUNTER_UPDATE_RENDER( "mUpdateRenderThreadCanSleep: %d, updateRequired: %d, mPendingRequestUpdate: %d", mUpdateRenderThreadCanSleep, updateRequired, mPendingRequestUpdate );
519   LOG_COUNTER_UPDATE_RENDER( "mDestroyUpdateRenderThread:  %d", mDestroyUpdateRenderThread );
520   LOG_COUNTER_UPDATE_RENDER( "mNewSurface:                 %d", mNewSurface );
521
522   mUseElapsedTimeAfterWait = FALSE;
523   mUpdateRenderThreadCanSleep = FALSE;
524   mPendingRequestUpdate = FALSE;
525
526   // If we've been asked to run Update/Render cycles a finite number of times then decrement so we wait after the
527   // requested number of cycles
528   if( mUpdateRenderRunCount > 0 )
529   {
530     --mUpdateRenderRunCount;
531   }
532
533   // Keep the update-render thread alive if this thread is NOT to be destroyed
534   return ! mDestroyUpdateRenderThread;
535 }
536
537 RenderSurface* CombinedUpdateRenderController::ShouldSurfaceBeReplaced()
538 {
539   ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
540
541   RenderSurface* newSurface = mNewSurface;
542   mNewSurface = NULL;
543
544   return newSurface;
545 }
546
547 void CombinedUpdateRenderController::SurfaceReplaced()
548 {
549   // Just increment the semaphore
550   sem_post( &mEventThreadSemaphore );
551 }
552
553 ///////////////////////////////////////////////////////////////////////////////////////////////////
554 // ALL THREADS
555 ///////////////////////////////////////////////////////////////////////////////////////////////////
556
557 void CombinedUpdateRenderController::NotifyThreadInitialised()
558 {
559   // Just increment the semaphore
560   sem_post( &mEventThreadSemaphore );
561 }
562
563 void CombinedUpdateRenderController::AddPerformanceMarker( PerformanceInterface::MarkerType type )
564 {
565   if( mPerformanceInterface )
566   {
567     mPerformanceInterface->AddMarker( type );
568   }
569 }
570
571 /////////////////////////////////////////////////////////////////////////////////////////////////
572 // POST RENDERING: EVENT THREAD
573 /////////////////////////////////////////////////////////////////////////////////////////////////
574
575 void CombinedUpdateRenderController::PostRenderComplete()
576 {
577   ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
578   mPostRendering = FALSE;
579   mUpdateRenderThreadWaitCondition.Notify( lock );
580 }
581
582 ///////////////////////////////////////////////////////////////////////////////////////////////////
583 // POST RENDERING: RENDER THREAD
584 ///////////////////////////////////////////////////////////////////////////////////////////////////
585
586 void CombinedUpdateRenderController::PostRenderStarted()
587 {
588   ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
589   mPostRendering = TRUE;
590 }
591
592 void CombinedUpdateRenderController::PostRenderWaitForCompletion()
593 {
594   ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
595   while( mPostRendering &&
596          ! mNewSurface &&                // We should NOT wait if we're replacing the surface
597          ! mDestroyUpdateRenderThread )
598   {
599     mUpdateRenderThreadWaitCondition.Wait( lock );
600   }
601 }
602
603 } // namespace Adaptor
604
605 } // namespace Internal
606
607 } // namespace Dali