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