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