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