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