Use separated semaphore in CombinedUpdateRenderController
[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   eglGraphics->GetGlesInterface().ContextCreated();
565
566   // Tell core it has a context
567   mCore.ContextCreated();
568
569   NotifyThreadInitialised();
570
571   // Update time
572   uint64_t lastFrameTime;
573   TimeService::GetNanoseconds( lastFrameTime );
574
575   LOG_UPDATE_RENDER( "THREAD INITIALISED" );
576
577   bool useElapsedTime = true;
578   bool updateRequired = true;
579   uint64_t timeToSleepUntil = 0;
580   int extraFramesDropped = 0;
581
582   const unsigned int renderToFboInterval = mEnvironmentOptions.GetRenderToFboInterval();
583   const bool renderToFboEnabled = 0u != renderToFboInterval;
584   unsigned int frameCount = 0u;
585
586   while( UpdateRenderReady( useElapsedTime, updateRequired, timeToSleepUntil ) )
587   {
588     LOG_UPDATE_RENDER_TRACE;
589
590     // Performance statistics are logged upon a VSYNC tick so use this point for a VSync marker
591     AddPerformanceMarker( PerformanceInterface::VSYNC );
592
593     uint64_t currentFrameStartTime = 0;
594     TimeService::GetNanoseconds( currentFrameStartTime );
595
596     uint64_t timeSinceLastFrame = currentFrameStartTime - lastFrameTime;
597
598     // Optional FPS Tracking when continuously rendering
599     if( useElapsedTime && mFpsTracker.Enabled() )
600     {
601       float absoluteTimeSinceLastRender = timeSinceLastFrame * NANOSECONDS_TO_SECOND;
602       mFpsTracker.Track( absoluteTimeSinceLastRender );
603     }
604
605     lastFrameTime = currentFrameStartTime; // Store frame start time
606
607     //////////////////////////////
608     // REPLACE SURFACE
609     //////////////////////////////
610
611     Dali::RenderSurfaceInterface* newSurface = ShouldSurfaceBeReplaced();
612     if( DALI_UNLIKELY( newSurface ) )
613     {
614       LOG_UPDATE_RENDER_TRACE_FMT( "Replacing Surface" );
615       // This is designed for replacing pixmap surfaces, but should work for window as well
616       // we need to delete the surface and renderable (pixmap / window)
617       // Then create a new pixmap/window and new surface
618       // If the new surface has a different display connection, then the context will be lost
619       mAdaptorInterfaces.GetDisplayConnectionInterface().Initialize();
620       newSurface->InitializeGraphics();
621       newSurface->MakeContextCurrent();
622       // TODO: ReplaceGraphicsSurface doesn't work, InitializeGraphics()
623       // already creates new surface window, the surface and the context.
624       // We probably don't need ReplaceGraphicsSurface at all.
625       // newSurface->ReplaceGraphicsSurface();
626       SurfaceReplaced();
627     }
628
629     const bool isRenderingToFbo = renderToFboEnabled && ( ( 0u == frameCount ) || ( 0u != frameCount % renderToFboInterval ) );
630     ++frameCount;
631
632     //////////////////////////////
633     // UPDATE
634     //////////////////////////////
635
636     const unsigned int currentTime = currentFrameStartTime / NANOSECONDS_PER_MILLISECOND;
637     const unsigned int nextFrameTime = currentTime + mDefaultFrameDurationMilliseconds;
638
639     uint64_t noOfFramesSinceLastUpdate = 1;
640     float frameDelta = 0.0f;
641     if( useElapsedTime )
642     {
643       if( mThreadMode == ThreadMode::RUN_IF_REQUESTED )
644       {
645         extraFramesDropped = 0;
646         while( timeSinceLastFrame >= mDefaultFrameDurationNanoseconds )
647         {
648            timeSinceLastFrame -= mDefaultFrameDurationNanoseconds;
649            extraFramesDropped++;
650         }
651       }
652
653       // If using the elapsed time, then calculate frameDelta as a multiple of mDefaultFrameDelta
654       noOfFramesSinceLastUpdate += extraFramesDropped;
655
656       frameDelta = mDefaultFrameDelta * noOfFramesSinceLastUpdate;
657     }
658     LOG_UPDATE_RENDER( "timeSinceLastFrame(%llu) noOfFramesSinceLastUpdate(%u) frameDelta(%.6f)", timeSinceLastFrame, noOfFramesSinceLastUpdate, frameDelta );
659
660     Integration::UpdateStatus updateStatus;
661
662     AddPerformanceMarker( PerformanceInterface::UPDATE_START );
663     mCore.Update( frameDelta,
664                   currentTime,
665                   nextFrameTime,
666                   updateStatus,
667                   renderToFboEnabled,
668                   isRenderingToFbo );
669     AddPerformanceMarker( PerformanceInterface::UPDATE_END );
670
671     unsigned int keepUpdatingStatus = updateStatus.KeepUpdating();
672
673     // Tell the event-thread to wake up (if asleep) and send a notification event to Core if required
674     if( updateStatus.NeedsNotification() )
675     {
676       mNotificationTrigger.Trigger();
677       LOG_UPDATE_RENDER( "Notification Triggered" );
678     }
679
680     // Check resize
681     bool surfaceResized = false;
682     bool shouldSurfaceBeResized = ShouldSurfaceBeResized();
683     if( DALI_UNLIKELY( shouldSurfaceBeResized ) )
684     {
685       if( updateStatus.SurfaceRectChanged() )
686       {
687         LOG_UPDATE_RENDER_TRACE_FMT( "Resizing Surface" );
688         SurfaceResized();
689         surfaceResized = true;
690       }
691     }
692
693     // Optional logging of update/render status
694     mUpdateStatusLogger.Log( keepUpdatingStatus );
695
696     //////////////////////////////
697     // RENDER
698     //////////////////////////////
699
700     mAdaptorInterfaces.GetDisplayConnectionInterface().ConsumeEvents();
701
702     if( mPreRenderCallback != NULL )
703     {
704       bool keepCallback = CallbackBase::ExecuteReturn<bool>(*mPreRenderCallback);
705       if( ! keepCallback )
706       {
707         delete mPreRenderCallback;
708         mPreRenderCallback = NULL;
709       }
710     }
711
712     if( eglImpl.IsSurfacelessContextSupported() )
713     {
714       // Make the shared surfaceless context as current before rendering
715       eglImpl.MakeContextCurrent( EGL_NO_SURFACE, eglImpl.GetContext() );
716     }
717
718     if( mFirstFrameAfterResume )
719     {
720       // mFirstFrameAfterResume is set to true when the thread is resumed
721       // Let eglImplementation know the first frame after thread initialized or resumed.
722       eglImpl.SetFirstFrameAfterResume();
723       mFirstFrameAfterResume = FALSE;
724     }
725
726     Integration::RenderStatus renderStatus;
727
728     AddPerformanceMarker( PerformanceInterface::RENDER_START );
729
730     // Upload shared resources
731     mCore.PreRender( renderStatus, mForceClear, mUploadWithoutRendering );
732
733     if ( !mUploadWithoutRendering )
734     {
735       // Go through each window
736       WindowContainer windows;
737       mAdaptorInterfaces.GetWindowContainerInterface( windows );
738
739       for( auto&& window : windows )
740       {
741         Dali::Integration::Scene scene = window->GetScene();
742         Dali::RenderSurfaceInterface* windowSurface = window->GetSurface();
743
744         if ( scene && windowSurface )
745         {
746           Integration::RenderStatus windowRenderStatus;
747
748           windowSurface->InitializeGraphics();
749
750           // clear previous frame damaged render items rects, buffer history is tracked on surface level
751           mDamagedRects.clear();
752
753           // Collect damage rects
754           mCore.PreRender( scene, mDamagedRects );
755
756           // Render off-screen frame buffers first if any
757           mCore.RenderScene( windowRenderStatus, scene, true );
758
759           Rect<int> clippingRect; // Empty for fbo rendering
760
761           // Switch to the EGL context of the surface, merge damaged areas for previous frames
762           windowSurface->PreRender( surfaceResized, mDamagedRects, clippingRect ); // Switch GL context
763
764           if (clippingRect.IsEmpty())
765           {
766             mDamagedRects.clear();
767           }
768
769           // Render the surface
770           mCore.RenderScene( windowRenderStatus, scene, false, clippingRect );
771
772           if( windowRenderStatus.NeedsPostRender() )
773           {
774             windowSurface->PostRender( false, false, surfaceResized, mDamagedRects ); // Swap Buffer with damage
775           }
776         }
777       }
778     }
779
780     mCore.PostRender( mUploadWithoutRendering );
781
782     //////////////////////////////
783     // DELETE SURFACE
784     //////////////////////////////
785
786     Dali::RenderSurfaceInterface* deletedSurface = ShouldSurfaceBeDeleted();
787     if( DALI_UNLIKELY( deletedSurface ) )
788     {
789       LOG_UPDATE_RENDER_TRACE_FMT( "Deleting Surface" );
790
791       deletedSurface->DestroySurface();
792
793       SurfaceDeleted();
794     }
795
796     AddPerformanceMarker( PerformanceInterface::RENDER_END );
797
798     mForceClear = false;
799
800     // Trigger event thread to request Update/Render thread to sleep if update not required
801     if( ( Integration::KeepUpdating::NOT_REQUESTED == keepUpdatingStatus ) && !renderStatus.NeedsUpdate() )
802     {
803       mSleepTrigger->Trigger();
804       updateRequired = false;
805       LOG_UPDATE_RENDER( "Sleep Triggered" );
806     }
807     else
808     {
809       updateRequired = true;
810     }
811
812     //////////////////////////////
813     // FRAME TIME
814     //////////////////////////////
815
816     extraFramesDropped = 0;
817
818     if (timeToSleepUntil == 0)
819     {
820       // If this is the first frame after the thread is initialized or resumed, we
821       // use the actual time the current frame starts from to calculate the time to
822       // sleep until the next frame.
823       timeToSleepUntil = currentFrameStartTime + mDefaultFrameDurationNanoseconds;
824     }
825     else
826     {
827       // Otherwise, always use the sleep-until time calculated in the last frame to
828       // calculate the time to sleep until the next frame. In this way, if there is
829       // any time gap between the current frame and the next frame, or if update or
830       // rendering in the current frame takes too much time so that the specified
831       // sleep-until time has already passed, it will try to keep the frames syncing
832       // by shortening the duration of the next frame.
833       timeToSleepUntil += mDefaultFrameDurationNanoseconds;
834
835       // Check the current time at the end of the frame
836       uint64_t currentFrameEndTime = 0;
837       TimeService::GetNanoseconds( currentFrameEndTime );
838       while ( currentFrameEndTime > timeToSleepUntil + mDefaultFrameDurationNanoseconds )
839       {
840          // We are more than one frame behind already, so just drop the next frames
841          // until the sleep-until time is later than the current time so that we can
842          // catch up.
843          timeToSleepUntil += mDefaultFrameDurationNanoseconds;
844          extraFramesDropped++;
845       }
846     }
847
848     // Render to FBO is intended to measure fps above 60 so sleep is not wanted.
849     if( 0u == renderToFboInterval )
850     {
851       // Sleep until at least the the default frame duration has elapsed. This will return immediately if the specified end-time has already passed.
852       TimeService::SleepUntil( timeToSleepUntil );
853     }
854   }
855
856   // Inform core of context destruction
857   mCore.ContextDestroyed();
858
859   WindowContainer windows;
860   mAdaptorInterfaces.GetWindowContainerInterface( windows );
861
862   // Destroy surfaces
863   for( auto&& window : windows )
864   {
865     Dali::RenderSurfaceInterface* surface = window->GetSurface();
866     surface->DestroySurface();
867   }
868
869   // Shutdown EGL
870   eglInterface->TerminateGles();
871
872   LOG_UPDATE_RENDER( "THREAD DESTROYED" );
873
874   // Uninstall the logging function
875   mEnvironmentOptions.UnInstallLogFunction();
876 }
877
878 bool CombinedUpdateRenderController::UpdateRenderReady( bool& useElapsedTime, bool updateRequired, uint64_t& timeToSleepUntil )
879 {
880   useElapsedTime = true;
881
882   ConditionalWait::ScopedLock updateLock( mUpdateRenderThreadWaitCondition );
883   while( ( ! mUpdateRenderRunCount || // Should try to wait if event-thread has paused the Update/Render thread
884            ( mUpdateRenderThreadCanSleep && ! updateRequired && ! mPendingRequestUpdate ) ) && // Ensure we wait if we're supposed to be sleeping AND do not require another update
885          ! mDestroyUpdateRenderThread && // Ensure we don't wait if the update-render-thread is supposed to be destroyed
886          ! mNewSurface &&  // Ensure we don't wait if we need to replace the surface
887          ! mDeletedSurface && // Ensure we don't wait if we need to delete the surface
888          ! mSurfaceResized ) // Ensure we don't wait if we need to resize the surface
889   {
890     LOG_UPDATE_RENDER( "WAIT: mUpdateRenderRunCount:       %d", mUpdateRenderRunCount );
891     LOG_UPDATE_RENDER( "      mUpdateRenderThreadCanSleep: %d, updateRequired: %d, mPendingRequestUpdate: %d", mUpdateRenderThreadCanSleep, updateRequired, mPendingRequestUpdate );
892     LOG_UPDATE_RENDER( "      mDestroyUpdateRenderThread:  %d", mDestroyUpdateRenderThread );
893     LOG_UPDATE_RENDER( "      mNewSurface:                 %d", mNewSurface );
894     LOG_UPDATE_RENDER( "      mDeletedSurface:             %d", mDeletedSurface );
895     LOG_UPDATE_RENDER( "      mSurfaceResized:             %d", mSurfaceResized );
896
897     // Reset the time when the thread is waiting, so the sleep-until time for
898     // the first frame after resuming should be based on the actual start time
899     // of the first frame.
900     timeToSleepUntil = 0;
901
902     mUpdateRenderThreadWaitCondition.Wait( updateLock );
903
904     if( ! mUseElapsedTimeAfterWait )
905     {
906       useElapsedTime = false;
907     }
908   }
909
910   LOG_COUNTER_UPDATE_RENDER( "mUpdateRenderRunCount:       %d", mUpdateRenderRunCount );
911   LOG_COUNTER_UPDATE_RENDER( "mUpdateRenderThreadCanSleep: %d, updateRequired: %d, mPendingRequestUpdate: %d", mUpdateRenderThreadCanSleep, updateRequired, mPendingRequestUpdate );
912   LOG_COUNTER_UPDATE_RENDER( "mDestroyUpdateRenderThread:  %d", mDestroyUpdateRenderThread );
913   LOG_COUNTER_UPDATE_RENDER( "mNewSurface:                 %d", mNewSurface );
914   LOG_COUNTER_UPDATE_RENDER( "mDeletedSurface:             %d", mDeletedSurface );
915   LOG_COUNTER_UPDATE_RENDER( "mSurfaceResized:             %d", mSurfaceResized );
916
917   mUseElapsedTimeAfterWait = FALSE;
918   mUpdateRenderThreadCanSleep = FALSE;
919   mPendingRequestUpdate = FALSE;
920
921   // If we've been asked to run Update/Render cycles a finite number of times then decrement so we wait after the
922   // requested number of cycles
923   if( mUpdateRenderRunCount > 0 )
924   {
925     --mUpdateRenderRunCount;
926   }
927
928   // Keep the update-render thread alive if this thread is NOT to be destroyed
929   return ! mDestroyUpdateRenderThread;
930 }
931
932 Dali::RenderSurfaceInterface* CombinedUpdateRenderController::ShouldSurfaceBeReplaced()
933 {
934   ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
935
936   Dali::RenderSurfaceInterface* newSurface = mNewSurface;
937   mNewSurface = NULL;
938
939   return newSurface;
940 }
941
942 void CombinedUpdateRenderController::SurfaceReplaced()
943 {
944   // Just increment the semaphore
945   sem_post( &mSurfaceSemaphore );
946 }
947
948 Dali::RenderSurfaceInterface* CombinedUpdateRenderController::ShouldSurfaceBeDeleted()
949 {
950   ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
951
952   Dali::RenderSurfaceInterface* deletedSurface = mDeletedSurface;
953   mDeletedSurface = NULL;
954
955   return deletedSurface;
956 }
957
958 void CombinedUpdateRenderController::SurfaceDeleted()
959 {
960   // Just increment the semaphore
961   sem_post( &mSurfaceSemaphore );
962 }
963
964 bool CombinedUpdateRenderController::ShouldSurfaceBeResized()
965 {
966   ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
967   return mSurfaceResized;
968 }
969
970 void CombinedUpdateRenderController::SurfaceResized()
971 {
972   ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
973   mSurfaceResized = FALSE;
974 }
975
976 ///////////////////////////////////////////////////////////////////////////////////////////////////
977 // ALL THREADS
978 ///////////////////////////////////////////////////////////////////////////////////////////////////
979
980 void CombinedUpdateRenderController::NotifyThreadInitialised()
981 {
982   // Just increment the semaphore
983   sem_post( &mEventThreadSemaphore );
984 }
985
986 void CombinedUpdateRenderController::NotifyGraphicsInitialised()
987 {
988   sem_post( &mGraphicsInitializeSemaphore );
989 }
990
991 void CombinedUpdateRenderController::AddPerformanceMarker( PerformanceInterface::MarkerType type )
992 {
993   if( mPerformanceInterface )
994   {
995     mPerformanceInterface->AddMarker( type );
996   }
997 }
998
999 /////////////////////////////////////////////////////////////////////////////////////////////////
1000 // POST RENDERING: EVENT THREAD
1001 /////////////////////////////////////////////////////////////////////////////////////////////////
1002
1003 void CombinedUpdateRenderController::PostRenderComplete()
1004 {
1005   ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
1006   mPostRendering = FALSE;
1007   mUpdateRenderThreadWaitCondition.Notify( lock );
1008 }
1009
1010 ///////////////////////////////////////////////////////////////////////////////////////////////////
1011 // POST RENDERING: RENDER THREAD
1012 ///////////////////////////////////////////////////////////////////////////////////////////////////
1013
1014 void CombinedUpdateRenderController::PostRenderStarted()
1015 {
1016   ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
1017   mPostRendering = TRUE;
1018 }
1019
1020 void CombinedUpdateRenderController::PostRenderWaitForCompletion()
1021 {
1022   ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
1023   while( mPostRendering &&
1024          ! mNewSurface &&                // We should NOT wait if we're replacing the surface
1025          ! mDeletedSurface &&            // We should NOT wait if we're deleting the surface
1026          ! mDestroyUpdateRenderThread )
1027   {
1028     mUpdateRenderThreadWaitCondition.Wait( lock );
1029   }
1030 }
1031
1032 } // namespace Adaptor
1033
1034 } // namespace Internal
1035
1036 } // namespace Dali