8e124a82b4601ece559d745b488c3b6f82c24b23
[platform/core/uifw/dali-adaptor.git] / dali / internal / adaptor / common / combined-update-render-controller.cpp
1 /*
2  * Copyright (c) 2018 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 <dali/internal/adaptor/common/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 <dali/integration-api/trigger-event-factory.h>
27 #include <dali/internal/adaptor/common/combined-update-render-controller-debug.h>
28 #include <dali/internal/system/common/environment-options.h>
29 #include <dali/internal/system/common/time-service.h>
30 #include <dali/internal/adaptor/common/adaptor-internal-services.h>
31 #include <dali/devel-api/adaptor-framework/thread-settings.h>
32
33 namespace Dali
34 {
35
36 namespace Internal
37 {
38
39 namespace Adaptor
40 {
41
42 namespace
43 {
44
45 const unsigned int CREATED_THREAD_COUNT = 1u;
46
47 const int CONTINUOUS = -1;
48 const int ONCE = 1;
49
50 const unsigned int TRUE = 1u;
51 const unsigned int FALSE = 0u;
52
53 const unsigned int MILLISECONDS_PER_SECOND( 1e+3 );
54 const float        NANOSECONDS_TO_SECOND( 1e-9f );
55 const unsigned int NANOSECONDS_PER_SECOND( 1e+9 );
56 const unsigned int NANOSECONDS_PER_MILLISECOND( 1e+6 );
57
58 // The following values will get calculated at compile time
59 const float        DEFAULT_FRAME_DURATION_IN_SECONDS( 1.0f / 60.0f );
60 const uint64_t DEFAULT_FRAME_DURATION_IN_MILLISECONDS( DEFAULT_FRAME_DURATION_IN_SECONDS * MILLISECONDS_PER_SECOND );
61 const uint64_t DEFAULT_FRAME_DURATION_IN_NANOSECONDS( DEFAULT_FRAME_DURATION_IN_SECONDS * NANOSECONDS_PER_SECOND );
62
63 /**
64  * 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
65  * there is a danger that, on the event-thread we could have:
66  *  1) An update-request where we do nothing as Update/Render thread still running.
67  *  2) Quickly followed by a sleep-request being handled where we pause the Update/Render Thread (even though we have an update to process).
68  *
69  * 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:
70  *  1) MAIN THREAD:           Update Request: COUNTER = 1
71  *  2) UPDATE/RENDER THREAD:  Do Update/Render, then no Updates required -> Sleep Trigger
72  *  3) MAIN THREAD:           Update Request: COUNTER = 2
73  *  4) MAIN THREAD:           Sleep Request:  COUNTER = 1 -> We do not sleep just yet
74  *
75  * Also ensures we preserve battery life by only doing ONE update when the above use case is not triggered.
76  *  1) MAIN THREAD:           Update Request: COUNTER = 1
77  *  2) UPDATE/RENDER THREAD:  Do Update/Render, then no Updates required -> Sleep Trigger
78  *  3) MAIN THREAD:           Sleep Request:  COUNTER = 0 -> Go to sleep
79  */
80 const unsigned int MAXIMUM_UPDATE_REQUESTS = 2;
81 } // unnamed namespace
82
83 ///////////////////////////////////////////////////////////////////////////////////////////////////
84 // EVENT THREAD
85 ///////////////////////////////////////////////////////////////////////////////////////////////////
86
87 CombinedUpdateRenderController::CombinedUpdateRenderController( AdaptorInternalServices& adaptorInterfaces, const EnvironmentOptions& environmentOptions )
88 : mFpsTracker( environmentOptions ),
89   mUpdateStatusLogger( environmentOptions ),
90   mEventThreadSemaphore(),
91   mUpdateRenderThreadWaitCondition(),
92   mAdaptorInterfaces( adaptorInterfaces ),
93   mPerformanceInterface( adaptorInterfaces.GetPerformanceInterface() ),
94   mCore( adaptorInterfaces.GetCore() ),
95   mEnvironmentOptions( environmentOptions ),
96   mNotificationTrigger( adaptorInterfaces.GetProcessCoreEventsTrigger() ),
97   mSleepTrigger( NULL ),
98   mPreRenderCallback( NULL ),
99   mUpdateRenderThread( NULL ),
100   mDefaultFrameDelta( 0.0f ),
101   mDefaultFrameDurationMilliseconds( 0u ),
102   mDefaultFrameDurationNanoseconds( 0u ),
103   mDefaultHalfFrameNanoseconds( 0u ),
104   mUpdateRequestCount( 0u ),
105   mRunning( FALSE ),
106   mUpdateRenderRunCount( 0 ),
107   mDestroyUpdateRenderThread( FALSE ),
108   mUpdateRenderThreadCanSleep( FALSE ),
109   mPendingRequestUpdate( FALSE ),
110   mUseElapsedTimeAfterWait( FALSE ),
111   mNewSurface( NULL ),
112   mPostRendering( FALSE ),
113   mSurfaceResized( FALSE ),
114   mForceClear( FALSE )
115 {
116   LOG_EVENT_TRACE;
117
118   // Initialise frame delta/duration variables first
119   SetRenderRefreshRate( environmentOptions.GetRenderRefreshRate() );
120
121   // Set the thread-synchronization interface on the render-surface
122   RenderSurface* currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
123   if( currentSurface )
124   {
125     currentSurface->SetThreadSynchronization( *this );
126   }
127
128   TriggerEventFactoryInterface& triggerFactory = mAdaptorInterfaces.GetTriggerEventFactoryInterface();
129   mSleepTrigger = triggerFactory.CreateTriggerEvent( MakeCallback( this, &CombinedUpdateRenderController::ProcessSleepRequest ), TriggerEventInterface::KEEP_ALIVE_AFTER_TRIGGER );
130
131   sem_init( &mEventThreadSemaphore, 0, 0 ); // Initialize to 0 so that it just waits if sem_post has not been called
132 }
133
134 CombinedUpdateRenderController::~CombinedUpdateRenderController()
135 {
136   LOG_EVENT_TRACE;
137
138   Stop();
139
140   delete mPreRenderCallback;
141   delete mSleepTrigger;
142 }
143
144 void CombinedUpdateRenderController::Initialize()
145 {
146   LOG_EVENT_TRACE;
147
148   // Ensure Update/Render Thread not already created
149   DALI_ASSERT_ALWAYS( ! mUpdateRenderThread );
150
151   // Create Update/Render Thread
152   mUpdateRenderThread = new pthread_t();
153   int error = pthread_create( mUpdateRenderThread, NULL, InternalUpdateRenderThreadEntryFunc, this );
154   DALI_ASSERT_ALWAYS( !error && "Return code from pthread_create() when creating UpdateRenderThread" );
155
156   // The Update/Render thread will now run and initialise the graphics interface etc. and will then wait for Start to be called
157   // When this function returns, the application initialisation on the event thread should occur
158 }
159
160 void CombinedUpdateRenderController::Start()
161 {
162   LOG_EVENT_TRACE;
163
164   DALI_ASSERT_ALWAYS( !mRunning && mUpdateRenderThread );
165
166   // Wait until all threads created in Initialise are up and running
167   for( unsigned int i = 0; i < CREATED_THREAD_COUNT; ++i )
168   {
169     sem_wait( &mEventThreadSemaphore );
170   }
171
172   RenderSurface* currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
173   if( currentSurface )
174   {
175     currentSurface->StartRender();
176   }
177
178   mRunning = TRUE;
179
180   LOG_EVENT( "Startup Complete, starting Update/Render Thread" );
181
182   RunUpdateRenderThread( CONTINUOUS, false /* No animation progression */ );
183
184   DALI_LOG_RELEASE_INFO( "CombinedUpdateRenderController::Start\n" );
185 }
186
187 void CombinedUpdateRenderController::Pause()
188 {
189   LOG_EVENT_TRACE;
190
191   mRunning = FALSE;
192
193   PauseUpdateRenderThread();
194
195   AddPerformanceMarker( PerformanceInterface::PAUSED );
196
197   DALI_LOG_RELEASE_INFO( "CombinedUpdateRenderController::Pause\n" );
198 }
199
200 void CombinedUpdateRenderController::Resume()
201 {
202   LOG_EVENT_TRACE;
203
204   if( !mRunning && IsUpdateRenderThreadPaused() )
205   {
206     LOG_EVENT( "Resuming" );
207
208     RunUpdateRenderThread( CONTINUOUS, true /* Animation progression required while we were paused */ );
209
210     AddPerformanceMarker( PerformanceInterface::RESUME );
211
212     mRunning = TRUE;
213     mForceClear = TRUE;
214   }
215
216   DALI_LOG_RELEASE_INFO( "CombinedUpdateRenderController::Resume\n" );
217 }
218
219 void CombinedUpdateRenderController::Stop()
220 {
221   LOG_EVENT_TRACE;
222
223   // Stop Rendering and the Update/Render Thread
224   RenderSurface* currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
225   if( currentSurface )
226   {
227     currentSurface->StopRender();
228   }
229
230   StopUpdateRenderThread();
231
232   if( mUpdateRenderThread )
233   {
234     LOG_EVENT( "Destroying UpdateRenderThread" );
235
236     // wait for the thread to finish
237     pthread_join( *mUpdateRenderThread, NULL );
238
239     delete mUpdateRenderThread;
240     mUpdateRenderThread = NULL;
241   }
242
243   mRunning = FALSE;
244
245   DALI_LOG_RELEASE_INFO( "CombinedUpdateRenderController::Stop\n" );
246 }
247
248 void CombinedUpdateRenderController::RequestUpdate()
249 {
250   LOG_EVENT_TRACE;
251
252   // Increment the update-request count to the maximum
253   if( mUpdateRequestCount < MAXIMUM_UPDATE_REQUESTS )
254   {
255     ++mUpdateRequestCount;
256   }
257
258   if( mRunning && IsUpdateRenderThreadPaused() )
259   {
260     LOG_EVENT( "Processing" );
261
262     RunUpdateRenderThread( CONTINUOUS, false /* No animation progression */ );
263   }
264
265   ConditionalWait::ScopedLock updateLock( mUpdateRenderThreadWaitCondition );
266   mPendingRequestUpdate = TRUE;
267 }
268
269 void CombinedUpdateRenderController::RequestUpdateOnce()
270 {
271   // Increment the update-request count to the maximum
272   if( mUpdateRequestCount < MAXIMUM_UPDATE_REQUESTS )
273   {
274     ++mUpdateRequestCount;
275   }
276
277   if( IsUpdateRenderThreadPaused() )
278   {
279     LOG_EVENT_TRACE;
280
281     // Run Update/Render once
282     RunUpdateRenderThread( ONCE, false /* No animation progression */ );
283   }
284 }
285
286 void CombinedUpdateRenderController::ReplaceSurface( RenderSurface* newSurface )
287 {
288   LOG_EVENT_TRACE;
289
290   // Set the ThreadSyncronizationInterface on the new surface
291   newSurface->SetThreadSynchronization( *this );
292
293   LOG_EVENT( "Starting to replace the surface, event-thread blocked" );
294
295   // Start replacing the surface.
296   {
297     ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
298     mPostRendering = FALSE; // Clear the post-rendering flag as Update/Render thread will replace the surface now
299     mNewSurface = newSurface;
300     mUpdateRenderThreadWaitCondition.Notify( lock );
301   }
302
303   // Wait until the surface has been replaced
304   sem_wait( &mEventThreadSemaphore );
305
306   LOG_EVENT( "Surface replaced, event-thread continuing" );
307 }
308
309 void CombinedUpdateRenderController::ResizeSurface()
310 {
311   LOG_EVENT_TRACE;
312
313   LOG_EVENT( "Resize the surface" );
314
315   {
316     ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
317     mPostRendering = FALSE; // Clear the post-rendering flag as Update/Render thread will resize the surface now
318     mSurfaceResized = TRUE;
319     mUpdateRenderThreadWaitCondition.Notify( lock );
320   }
321 }
322
323 void CombinedUpdateRenderController::SetRenderRefreshRate( unsigned int numberOfFramesPerRender )
324 {
325   // Not protected by lock, but written to rarely so not worth adding a lock when reading
326   mDefaultFrameDelta                  = numberOfFramesPerRender * DEFAULT_FRAME_DURATION_IN_SECONDS;
327   mDefaultFrameDurationMilliseconds   = uint64_t( numberOfFramesPerRender ) * DEFAULT_FRAME_DURATION_IN_MILLISECONDS;
328   mDefaultFrameDurationNanoseconds    = uint64_t( numberOfFramesPerRender ) * DEFAULT_FRAME_DURATION_IN_NANOSECONDS;
329   mDefaultHalfFrameNanoseconds        = mDefaultFrameDurationNanoseconds / 2u;
330
331   LOG_EVENT( "mDefaultFrameDelta(%.6f), mDefaultFrameDurationMilliseconds(%lld), mDefaultFrameDurationNanoseconds(%lld)", mDefaultFrameDelta, mDefaultFrameDurationMilliseconds, mDefaultFrameDurationNanoseconds );
332 }
333
334 void CombinedUpdateRenderController::SetPreRenderCallback( CallbackBase* callback )
335 {
336   LOG_EVENT_TRACE;
337   LOG_EVENT( "Set PreRender Callback" );
338
339   ConditionalWait::ScopedLock updateLock( mUpdateRenderThreadWaitCondition );
340   if( mPreRenderCallback )
341   {
342     delete mPreRenderCallback;
343   }
344   mPreRenderCallback = callback;
345 }
346
347 ///////////////////////////////////////////////////////////////////////////////////////////////////
348 // EVENT THREAD
349 ///////////////////////////////////////////////////////////////////////////////////////////////////
350
351 void CombinedUpdateRenderController::RunUpdateRenderThread( int numberOfCycles, bool useElapsedTime )
352 {
353   ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
354   mUpdateRenderRunCount = numberOfCycles;
355   mUpdateRenderThreadCanSleep = FALSE;
356   mUseElapsedTimeAfterWait = useElapsedTime;
357   LOG_COUNTER_EVENT( "mUpdateRenderRunCount: %d, mUseElapsedTimeAfterWait: %d", mUpdateRenderRunCount, mUseElapsedTimeAfterWait );
358   mUpdateRenderThreadWaitCondition.Notify( lock );
359 }
360
361 void CombinedUpdateRenderController::PauseUpdateRenderThread()
362 {
363   ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
364   mUpdateRenderRunCount = 0;
365 }
366
367 void CombinedUpdateRenderController::StopUpdateRenderThread()
368 {
369   ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
370   mDestroyUpdateRenderThread = TRUE;
371   mUpdateRenderThreadWaitCondition.Notify( lock );
372 }
373
374 bool CombinedUpdateRenderController::IsUpdateRenderThreadPaused()
375 {
376   ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
377   return ( mUpdateRenderRunCount != CONTINUOUS ) || // Report paused if NOT continuously running
378          mUpdateRenderThreadCanSleep;               // Report paused if sleeping
379 }
380
381 void CombinedUpdateRenderController::ProcessSleepRequest()
382 {
383   LOG_EVENT_TRACE;
384
385   // Decrement Update request count
386   if( mUpdateRequestCount > 0 )
387   {
388     --mUpdateRequestCount;
389   }
390
391   // Can sleep if our update-request count is 0
392   // Update/Render thread can choose to carry on updating if it determines more update/renders are required
393   if( mUpdateRequestCount == 0 )
394   {
395     LOG_EVENT( "Going to sleep" );
396
397     ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
398     mUpdateRenderThreadCanSleep = TRUE;
399   }
400 }
401
402 ///////////////////////////////////////////////////////////////////////////////////////////////////
403 // UPDATE/RENDER THREAD
404 ///////////////////////////////////////////////////////////////////////////////////////////////////
405
406 void CombinedUpdateRenderController::UpdateRenderThread()
407 {
408   SetThreadName("RenderThread\0");
409
410   // Install a function for logging
411   mEnvironmentOptions.InstallLogFunction();
412
413   // Install a function for tracing
414   mEnvironmentOptions.InstallTraceFunction();
415
416   LOG_UPDATE_RENDER( "THREAD CREATED" );
417
418   RenderSurface* currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
419   if( currentSurface )
420   {
421     currentSurface->InitializeGraphics( mAdaptorInterfaces.GetGraphicsInterface(), mAdaptorInterfaces.GetDisplayConnectionInterface() );
422   }
423
424   // Tell core it has a context
425   mCore.ContextCreated();
426
427   NotifyThreadInitialised();
428
429   // Update time
430   uint64_t lastFrameTime;
431   TimeService::GetNanoseconds( lastFrameTime );
432
433   LOG_UPDATE_RENDER( "THREAD INITIALISED" );
434
435   bool useElapsedTime = true;
436   bool updateRequired = true;
437   uint64_t timeToSleepUntil = 0;
438   int extraFramesDropped = 0;
439
440   const unsigned int renderToFboInterval = mEnvironmentOptions.GetRenderToFboInterval();
441   const bool renderToFboEnabled = 0u != renderToFboInterval;
442   unsigned int frameCount = 0u;
443
444   while( UpdateRenderReady( useElapsedTime, updateRequired, timeToSleepUntil ) )
445   {
446     LOG_UPDATE_RENDER_TRACE;
447
448     // Performance statistics are logged upon a VSYNC tick so use this point for a VSync marker
449     AddPerformanceMarker( PerformanceInterface::VSYNC );
450
451     uint64_t currentFrameStartTime = 0;
452     TimeService::GetNanoseconds( currentFrameStartTime );
453
454     const uint64_t timeSinceLastFrame = currentFrameStartTime - lastFrameTime;
455
456     // Optional FPS Tracking when continuously rendering
457     if( useElapsedTime && mFpsTracker.Enabled() )
458     {
459       float absoluteTimeSinceLastRender = timeSinceLastFrame * NANOSECONDS_TO_SECOND;
460       mFpsTracker.Track( absoluteTimeSinceLastRender );
461     }
462
463     lastFrameTime = currentFrameStartTime; // Store frame start time
464
465     //////////////////////////////
466     // REPLACE SURFACE
467     //////////////////////////////
468
469     RenderSurface* newSurface = ShouldSurfaceBeReplaced();
470     if( DALI_UNLIKELY( newSurface ) )
471     {
472       LOG_UPDATE_RENDER_TRACE_FMT( "Replacing Surface" );
473
474       // This is designed for replacing pixmap surfaces, but should work for window as well
475       // we need to delete the surface and renderable (pixmap / window)
476       // Then create a new pixmap/window and new surface
477       // If the new surface has a different display connection, then the context will be lost
478
479       mAdaptorInterfaces.GetDisplayConnectionInterface().Initialize();
480       newSurface->InitializeGraphics( mAdaptorInterfaces.GetGraphicsInterface(), mAdaptorInterfaces.GetDisplayConnectionInterface() );
481       newSurface->ReplaceGraphicsSurface();
482       SurfaceReplaced();
483     }
484
485     const bool isRenderingToFbo = renderToFboEnabled && ( ( 0u == frameCount ) || ( 0u != frameCount % renderToFboInterval ) );
486     ++frameCount;
487
488     //////////////////////////////
489     // UPDATE
490     //////////////////////////////
491
492     const unsigned int currentTime = currentFrameStartTime / NANOSECONDS_PER_MILLISECOND;
493     const unsigned int nextFrameTime = currentTime + mDefaultFrameDurationMilliseconds;
494
495     uint64_t noOfFramesSinceLastUpdate = 1;
496     float frameDelta = 0.0f;
497     if( useElapsedTime )
498     {
499       // If using the elapsed time, then calculate frameDelta as a multiple of mDefaultFrameDelta
500       noOfFramesSinceLastUpdate += extraFramesDropped;
501
502       frameDelta = mDefaultFrameDelta * noOfFramesSinceLastUpdate;
503     }
504     LOG_UPDATE_RENDER( "timeSinceLastFrame(%llu) noOfFramesSinceLastUpdate(%u) frameDelta(%.6f)", timeSinceLastFrame, noOfFramesSinceLastUpdate, frameDelta );
505
506     Integration::UpdateStatus updateStatus;
507
508     AddPerformanceMarker( PerformanceInterface::UPDATE_START );
509     mCore.Update( frameDelta,
510                   currentTime,
511                   nextFrameTime,
512                   updateStatus,
513                   renderToFboEnabled,
514                   isRenderingToFbo );
515     AddPerformanceMarker( PerformanceInterface::UPDATE_END );
516
517     unsigned int keepUpdatingStatus = updateStatus.KeepUpdating();
518
519     // Tell the event-thread to wake up (if asleep) and send a notification event to Core if required
520     if( updateStatus.NeedsNotification() )
521     {
522       mNotificationTrigger.Trigger();
523       LOG_UPDATE_RENDER( "Notification Triggered" );
524     }
525
526     // Check resize
527     bool surfaceResized = false;
528     bool shouldSurfaceBeResized = ShouldSurfaceBeResized();
529     if( DALI_UNLIKELY( shouldSurfaceBeResized ) )
530     {
531       if( updateStatus.SurfaceRectChanged() )
532       {
533         LOG_UPDATE_RENDER_TRACE_FMT( "Resizing Surface" );
534         SurfaceResized();
535         surfaceResized = true;
536       }
537     }
538
539     // Optional logging of update/render status
540     mUpdateStatusLogger.Log( keepUpdatingStatus );
541
542     //////////////////////////////
543     // RENDER
544     //////////////////////////////
545
546     mAdaptorInterfaces.GetDisplayConnectionInterface().ConsumeEvents();
547
548     if( mPreRenderCallback != NULL )
549     {
550       bool keepCallback = CallbackBase::ExecuteReturn<bool>(*mPreRenderCallback);
551       if( ! keepCallback )
552       {
553         delete mPreRenderCallback;
554         mPreRenderCallback = NULL;
555       }
556     }
557
558     currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
559     if( currentSurface )
560     {
561       currentSurface->PreRender( surfaceResized );
562     }
563
564     Integration::RenderStatus renderStatus;
565
566     AddPerformanceMarker( PerformanceInterface::RENDER_START );
567     mCore.Render( renderStatus, mForceClear );
568     AddPerformanceMarker( PerformanceInterface::RENDER_END );
569
570     mForceClear = false;
571
572     if( renderStatus.NeedsPostRender() )
573     {
574       if( currentSurface )
575       {
576         currentSurface->PostRender( isRenderingToFbo, ( mNewSurface != NULL ), surfaceResized );
577       }
578     }
579
580     // Trigger event thread to request Update/Render thread to sleep if update not required
581     if( ( Integration::KeepUpdating::NOT_REQUESTED == keepUpdatingStatus ) && !renderStatus.NeedsUpdate() )
582     {
583       mSleepTrigger->Trigger();
584       updateRequired = false;
585       LOG_UPDATE_RENDER( "Sleep Triggered" );
586     }
587     else
588     {
589       updateRequired = true;
590     }
591
592     //////////////////////////////
593     // FRAME TIME
594     //////////////////////////////
595
596     extraFramesDropped = 0;
597
598     if (timeToSleepUntil == 0)
599     {
600       // If this is the first frame after the thread is initialized or resumed, we
601       // use the actual time the current frame starts from to calculate the time to
602       // sleep until the next frame.
603       timeToSleepUntil = currentFrameStartTime + mDefaultFrameDurationNanoseconds;
604     }
605     else
606     {
607       // Otherwise, always use the sleep-until time calculated in the last frame to
608       // calculate the time to sleep until the next frame. In this way, if there is
609       // any time gap between the current frame and the next frame, or if update or
610       // rendering in the current frame takes too much time so that the specified
611       // sleep-until time has already passed, it will try to keep the frames syncing
612       // by shortening the duration of the next frame.
613       timeToSleepUntil += mDefaultFrameDurationNanoseconds;
614
615       // Check the current time at the end of the frame
616       uint64_t currentFrameEndTime = 0;
617       TimeService::GetNanoseconds( currentFrameEndTime );
618       while ( currentFrameEndTime > timeToSleepUntil + mDefaultFrameDurationNanoseconds )
619       {
620          // We are more than one frame behind already, so just drop the next frames
621          // until the sleep-until time is later than the current time so that we can
622          // catch up.
623          timeToSleepUntil += mDefaultFrameDurationNanoseconds;
624          extraFramesDropped++;
625       }
626     }
627
628     // Render to FBO is intended to measure fps above 60 so sleep is not wanted.
629     if( 0u == renderToFboInterval )
630     {
631       // Sleep until at least the the default frame duration has elapsed. This will return immediately if the specified end-time has already passed.
632       TimeService::SleepUntil( timeToSleepUntil );
633     }
634   }
635
636   // Inform core of context destruction & shutdown EGL
637   mCore.ContextDestroyed();
638   currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
639   if( currentSurface )
640   {
641     currentSurface->DestroySurface();
642     currentSurface = nullptr;
643   }
644
645   LOG_UPDATE_RENDER( "THREAD DESTROYED" );
646
647   // Uninstall the logging function
648   mEnvironmentOptions.UnInstallLogFunction();
649 }
650
651 bool CombinedUpdateRenderController::UpdateRenderReady( bool& useElapsedTime, bool updateRequired, uint64_t& timeToSleepUntil )
652 {
653   useElapsedTime = true;
654
655   ConditionalWait::ScopedLock updateLock( mUpdateRenderThreadWaitCondition );
656   while( ( ! mUpdateRenderRunCount || // Should try to wait if event-thread has paused the Update/Render thread
657            ( mUpdateRenderThreadCanSleep && ! updateRequired && ! mPendingRequestUpdate ) ) && // Ensure we wait if we're supposed to be sleeping AND do not require another update
658          ! mDestroyUpdateRenderThread && // Ensure we don't wait if the update-render-thread is supposed to be destroyed
659          ! mNewSurface &&  // Ensure we don't wait if we need to replace the surface
660          ! mSurfaceResized ) // Ensure we don't wait if we need to resize the surface
661   {
662     LOG_UPDATE_RENDER( "WAIT: mUpdateRenderRunCount:       %d", mUpdateRenderRunCount );
663     LOG_UPDATE_RENDER( "      mUpdateRenderThreadCanSleep: %d, updateRequired: %d, mPendingRequestUpdate: %d", mUpdateRenderThreadCanSleep, updateRequired, mPendingRequestUpdate );
664     LOG_UPDATE_RENDER( "      mDestroyUpdateRenderThread:  %d", mDestroyUpdateRenderThread );
665     LOG_UPDATE_RENDER( "      mNewSurface:                 %d", mNewSurface );
666     LOG_UPDATE_RENDER( "      mSurfaceResized:             %d", mSurfaceResized );
667
668     // Reset the time when the thread is waiting, so the sleep-until time for
669     // the first frame after resuming should be based on the actual start time
670     // of the first frame.
671     timeToSleepUntil = 0;
672
673     mUpdateRenderThreadWaitCondition.Wait( updateLock );
674
675     if( ! mUseElapsedTimeAfterWait )
676     {
677       useElapsedTime = false;
678     }
679   }
680
681   LOG_COUNTER_UPDATE_RENDER( "mUpdateRenderRunCount:       %d", mUpdateRenderRunCount );
682   LOG_COUNTER_UPDATE_RENDER( "mUpdateRenderThreadCanSleep: %d, updateRequired: %d, mPendingRequestUpdate: %d", mUpdateRenderThreadCanSleep, updateRequired, mPendingRequestUpdate );
683   LOG_COUNTER_UPDATE_RENDER( "mDestroyUpdateRenderThread:  %d", mDestroyUpdateRenderThread );
684   LOG_COUNTER_UPDATE_RENDER( "mNewSurface:                 %d", mNewSurface );
685   LOG_COUNTER_UPDATE_RENDER( "mSurfaceResized:             %d", mSurfaceResized );
686
687   mUseElapsedTimeAfterWait = FALSE;
688   mUpdateRenderThreadCanSleep = FALSE;
689   mPendingRequestUpdate = FALSE;
690
691   // If we've been asked to run Update/Render cycles a finite number of times then decrement so we wait after the
692   // requested number of cycles
693   if( mUpdateRenderRunCount > 0 )
694   {
695     --mUpdateRenderRunCount;
696   }
697
698   // Keep the update-render thread alive if this thread is NOT to be destroyed
699   return ! mDestroyUpdateRenderThread;
700 }
701
702 RenderSurface* CombinedUpdateRenderController::ShouldSurfaceBeReplaced()
703 {
704   ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
705
706   RenderSurface* newSurface = mNewSurface;
707   mNewSurface = NULL;
708
709   return newSurface;
710 }
711
712 void CombinedUpdateRenderController::SurfaceReplaced()
713 {
714   // Just increment the semaphore
715   sem_post( &mEventThreadSemaphore );
716 }
717
718 bool CombinedUpdateRenderController::ShouldSurfaceBeResized()
719 {
720   ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
721   return mSurfaceResized;
722 }
723
724 void CombinedUpdateRenderController::SurfaceResized()
725 {
726   ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
727   mSurfaceResized = FALSE;
728 }
729
730 ///////////////////////////////////////////////////////////////////////////////////////////////////
731 // ALL THREADS
732 ///////////////////////////////////////////////////////////////////////////////////////////////////
733
734 void CombinedUpdateRenderController::NotifyThreadInitialised()
735 {
736   // Just increment the semaphore
737   sem_post( &mEventThreadSemaphore );
738 }
739
740 void CombinedUpdateRenderController::AddPerformanceMarker( PerformanceInterface::MarkerType type )
741 {
742   if( mPerformanceInterface )
743   {
744     mPerformanceInterface->AddMarker( type );
745   }
746 }
747
748 /////////////////////////////////////////////////////////////////////////////////////////////////
749 // POST RENDERING: EVENT THREAD
750 /////////////////////////////////////////////////////////////////////////////////////////////////
751
752 void CombinedUpdateRenderController::PostRenderComplete()
753 {
754   ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
755   mPostRendering = FALSE;
756   mUpdateRenderThreadWaitCondition.Notify( lock );
757 }
758
759 ///////////////////////////////////////////////////////////////////////////////////////////////////
760 // POST RENDERING: RENDER THREAD
761 ///////////////////////////////////////////////////////////////////////////////////////////////////
762
763 void CombinedUpdateRenderController::PostRenderStarted()
764 {
765   ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
766   mPostRendering = TRUE;
767 }
768
769 void CombinedUpdateRenderController::PostRenderWaitForCompletion()
770 {
771   ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
772   while( mPostRendering &&
773          ! mNewSurface &&                // We should NOT wait if we're replacing the surface
774          ! mSurfaceResized &&            // We should NOT wait if we're resizing the surface
775          ! mDestroyUpdateRenderThread )
776   {
777     mUpdateRenderThreadWaitCondition.Wait( lock );
778   }
779 }
780
781 } // namespace Adaptor
782
783 } // namespace Internal
784
785 } // namespace Dali