2 * Copyright (c) 2020 Samsung Electronics Co., Ltd.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 #include <dali/internal/adaptor/common/combined-update-render-controller.h>
23 #include <dali/integration-api/platform-abstraction.h>
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>
50 const unsigned int CREATED_THREAD_COUNT = 1u;
52 const int CONTINUOUS = -1;
55 const unsigned int TRUE = 1u;
56 const unsigned int FALSE = 0u;
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 );
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 );
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).
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
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
85 const unsigned int MAXIMUM_UPDATE_REQUESTS = 2;
86 } // unnamed namespace
88 ///////////////////////////////////////////////////////////////////////////////////////////////////
90 ///////////////////////////////////////////////////////////////////////////////////////////////////
92 CombinedUpdateRenderController::CombinedUpdateRenderController( AdaptorInternalServices& adaptorInterfaces, const EnvironmentOptions& environmentOptions, ThreadMode threadMode )
93 : mFpsTracker( environmentOptions ),
94 mUpdateStatusLogger( environmentOptions ),
95 mEventThreadSemaphore(),
96 mGraphicsInitializeSemaphore(),
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 ),
113 mThreadMode( threadMode ),
114 mUpdateRenderRunCount( 0 ),
115 mDestroyUpdateRenderThread( FALSE ),
116 mUpdateRenderThreadCanSleep( FALSE ),
117 mPendingRequestUpdate( FALSE ),
118 mUseElapsedTimeAfterWait( FALSE ),
120 mDeletedSurface( nullptr ),
121 mPostRendering( FALSE ),
122 mSurfaceResized( FALSE ),
123 mForceClear( FALSE ),
124 mUploadWithoutRendering( FALSE ),
125 mFirstFrameAfterResume( FALSE )
129 // Initialise frame delta/duration variables first
130 SetRenderRefreshRate( environmentOptions.GetRenderRefreshRate() );
132 // Set the thread-synchronization interface on the render-surface
133 Dali::RenderSurfaceInterface* currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
136 currentSurface->SetThreadSynchronization( *this );
139 mSleepTrigger = TriggerEventFactory::CreateTriggerEvent( MakeCallback( this, &CombinedUpdateRenderController::ProcessSleepRequest ), TriggerEventInterface::KEEP_ALIVE_AFTER_TRIGGER );
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 );
147 CombinedUpdateRenderController::~CombinedUpdateRenderController()
153 delete mPreRenderCallback;
154 delete mSleepTrigger;
157 void CombinedUpdateRenderController::Initialize()
161 // Ensure Update/Render Thread not already created
162 DALI_ASSERT_ALWAYS( ! mUpdateRenderThread );
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" );
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
173 void CombinedUpdateRenderController::Start()
177 DALI_ASSERT_ALWAYS( !mRunning && mUpdateRenderThread );
179 // Wait until all threads created in Initialise are up and running
180 for( unsigned int i = 0; i < CREATED_THREAD_COUNT; ++i )
182 sem_wait( &mEventThreadSemaphore );
185 Dali::RenderSurfaceInterface* currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
188 currentSurface->StartRender();
193 LOG_EVENT( "Startup Complete, starting Update/Render Thread" );
195 RunUpdateRenderThread( CONTINUOUS, AnimationProgression::NONE, UpdateMode::NORMAL );
197 DALI_LOG_RELEASE_INFO( "CombinedUpdateRenderController::Start\n" );
200 void CombinedUpdateRenderController::Pause()
206 PauseUpdateRenderThread();
208 AddPerformanceMarker( PerformanceInterface::PAUSED );
210 DALI_LOG_RELEASE_INFO( "CombinedUpdateRenderController::Pause\n" );
213 void CombinedUpdateRenderController::Resume()
217 if( !mRunning && IsUpdateRenderThreadPaused() )
219 LOG_EVENT( "Resuming" );
221 RunUpdateRenderThread( CONTINUOUS, AnimationProgression::USE_ELAPSED_TIME, UpdateMode::NORMAL );
223 AddPerformanceMarker( PerformanceInterface::RESUME );
227 mFirstFrameAfterResume = TRUE;
229 DALI_LOG_RELEASE_INFO( "CombinedUpdateRenderController::Resume\n" );
233 DALI_LOG_RELEASE_INFO( "CombinedUpdateRenderController::Resume: Already resumed [%d, %d, %d]\n", mRunning, mUpdateRenderRunCount, mUpdateRenderThreadCanSleep );
237 void CombinedUpdateRenderController::Stop()
241 // Stop Rendering and the Update/Render Thread
242 Dali::RenderSurfaceInterface* currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
245 currentSurface->StopRender();
248 StopUpdateRenderThread();
250 if( mUpdateRenderThread )
252 LOG_EVENT( "Destroying UpdateRenderThread" );
254 // wait for the thread to finish
255 pthread_join( *mUpdateRenderThread, NULL );
257 delete mUpdateRenderThread;
258 mUpdateRenderThread = NULL;
263 DALI_LOG_RELEASE_INFO( "CombinedUpdateRenderController::Stop\n" );
266 void CombinedUpdateRenderController::RequestUpdate()
270 // Increment the update-request count to the maximum
271 if( mUpdateRequestCount < MAXIMUM_UPDATE_REQUESTS )
273 ++mUpdateRequestCount;
276 if( mRunning && IsUpdateRenderThreadPaused() )
278 LOG_EVENT( "Processing" );
280 RunUpdateRenderThread( CONTINUOUS, AnimationProgression::NONE, UpdateMode::NORMAL );
283 ConditionalWait::ScopedLock updateLock( mUpdateRenderThreadWaitCondition );
284 mPendingRequestUpdate = TRUE;
287 void CombinedUpdateRenderController::RequestUpdateOnce( UpdateMode updateMode )
289 // Increment the update-request count to the maximum
290 if( mUpdateRequestCount < MAXIMUM_UPDATE_REQUESTS )
292 ++mUpdateRequestCount;
295 if( IsUpdateRenderThreadPaused() || updateMode == UpdateMode::FORCE_RENDER )
299 // Run Update/Render once
300 RunUpdateRenderThread( ONCE, AnimationProgression::NONE, updateMode );
304 void CombinedUpdateRenderController::ReplaceSurface( Dali::RenderSurfaceInterface* newSurface )
308 if( mUpdateRenderThread )
310 // Set the ThreadSyncronizationInterface on the new surface
311 newSurface->SetThreadSynchronization( *this );
313 LOG_EVENT( "Starting to replace the surface, event-thread blocked" );
315 // Start replacing the surface.
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 );
323 // Wait until the surface has been replaced
324 sem_wait( &mSurfaceSemaphore );
326 LOG_EVENT( "Surface replaced, event-thread continuing" );
330 void CombinedUpdateRenderController::DeleteSurface( Dali::RenderSurfaceInterface* surface )
334 if( mUpdateRenderThread )
336 LOG_EVENT( "Starting to delete the surface, event-thread blocked" );
338 // Start replacing the surface.
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 );
346 // Wait until the surface has been deleted
347 sem_wait( &mSurfaceSemaphore );
349 LOG_EVENT( "Surface deleted, event-thread continuing" );
353 void CombinedUpdateRenderController::WaitForGraphicsInitialization()
357 if( mUpdateRenderThread )
359 LOG_EVENT( "Waiting for graphics initialisation, event-thread blocked" );
361 // Wait until the graphics has been initialised
362 sem_wait( &mGraphicsInitializeSemaphore );
364 LOG_EVENT( "graphics initialised, event-thread continuing" );
368 void CombinedUpdateRenderController::ResizeSurface()
372 LOG_EVENT( "Resize the surface" );
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 );
382 void CombinedUpdateRenderController::SetRenderRefreshRate( unsigned int numberOfFramesPerRender )
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;
390 LOG_EVENT( "mDefaultFrameDelta(%.6f), mDefaultFrameDurationMilliseconds(%lld), mDefaultFrameDurationNanoseconds(%lld)", mDefaultFrameDelta, mDefaultFrameDurationMilliseconds, mDefaultFrameDurationNanoseconds );
393 void CombinedUpdateRenderController::SetPreRenderCallback( CallbackBase* callback )
396 LOG_EVENT( "Set PreRender Callback" );
398 ConditionalWait::ScopedLock updateLock( mUpdateRenderThreadWaitCondition );
399 if( mPreRenderCallback )
401 delete mPreRenderCallback;
403 mPreRenderCallback = callback;
406 void CombinedUpdateRenderController::AddSurface( Dali::RenderSurfaceInterface* surface )
409 LOG_EVENT( "Surface is added" );
410 if( mUpdateRenderThread )
412 // Set the ThreadSyncronizationInterface on the added surface
413 surface->SetThreadSynchronization( *this );
417 ///////////////////////////////////////////////////////////////////////////////////////////////////
419 ///////////////////////////////////////////////////////////////////////////////////////////////////
421 void CombinedUpdateRenderController::RunUpdateRenderThread( int numberOfCycles, AnimationProgression animationProgression, UpdateMode updateMode )
423 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
425 switch( mThreadMode )
427 case ThreadMode::NORMAL:
429 mUpdateRenderRunCount = numberOfCycles;
430 mUseElapsedTimeAfterWait = ( animationProgression == AnimationProgression::USE_ELAPSED_TIME );
433 case ThreadMode::RUN_IF_REQUESTED:
435 if( updateMode != UpdateMode::FORCE_RENDER )
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.
442 mUpdateRenderRunCount++; // Increase the update request count
443 mUseElapsedTimeAfterWait = TRUE; // The elapsed time should be used. We want animations to proceed.
448 mUpdateRenderThreadCanSleep = FALSE;
449 mUploadWithoutRendering = ( updateMode == UpdateMode::SKIP_RENDER );
450 LOG_COUNTER_EVENT( "mUpdateRenderRunCount: %d, mUseElapsedTimeAfterWait: %d", mUpdateRenderRunCount, mUseElapsedTimeAfterWait );
451 mUpdateRenderThreadWaitCondition.Notify( lock );
454 void CombinedUpdateRenderController::PauseUpdateRenderThread()
456 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
457 mUpdateRenderRunCount = 0;
460 void CombinedUpdateRenderController::StopUpdateRenderThread()
462 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
463 mDestroyUpdateRenderThread = TRUE;
464 mUpdateRenderThreadWaitCondition.Notify( lock );
467 bool CombinedUpdateRenderController::IsUpdateRenderThreadPaused()
469 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
471 if( mThreadMode == ThreadMode::RUN_IF_REQUESTED )
473 return !mRunning || mUpdateRenderThreadCanSleep;
476 return ( mUpdateRenderRunCount != CONTINUOUS ) || // Report paused if NOT continuously running
477 mUpdateRenderThreadCanSleep; // Report paused if sleeping
480 void CombinedUpdateRenderController::ProcessSleepRequest()
484 // Decrement Update request count
485 if( mUpdateRequestCount > 0 )
487 --mUpdateRequestCount;
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 )
494 LOG_EVENT( "Going to sleep" );
496 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
497 mUpdateRenderThreadCanSleep = TRUE;
501 ///////////////////////////////////////////////////////////////////////////////////////////////////
502 // UPDATE/RENDER THREAD
503 ///////////////////////////////////////////////////////////////////////////////////////////////////
505 void CombinedUpdateRenderController::UpdateRenderThread()
507 SetThreadName("RenderThread\0");
509 // Install a function for logging
510 mEnvironmentOptions.InstallLogFunction();
512 // Install a function for tracing
513 mEnvironmentOptions.InstallTraceFunction();
515 LOG_UPDATE_RENDER( "THREAD CREATED" );
517 // Initialize EGL & OpenGL
518 Dali::DisplayConnection& displayConnection = mAdaptorInterfaces.GetDisplayConnectionInterface();
519 displayConnection.Initialize();
521 // EGL has been initialised at this point
522 NotifyGraphicsInitialised();
524 RenderSurfaceInterface* currentSurface = nullptr;
526 GraphicsInterface& graphics = mAdaptorInterfaces.GetGraphicsInterface();
527 EglGraphics* eglGraphics = static_cast<EglGraphics *>(&graphics);
529 // This will only be created once
530 EglInterface* eglInterface = &eglGraphics->GetEglInterface();
532 Internal::Adaptor::EglImplementation& eglImpl = static_cast<Internal::Adaptor::EglImplementation&>( *eglInterface );
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 ) )
539 // Retry to use OpenGL es 2.0
540 eglGraphics->SetGlesVersion( 20 );
541 eglImpl.ChooseConfig( true, COLOR_DEPTH_32 );
544 // Check whether surfaceless context is supported
545 bool isSurfacelessContextSupported = eglImpl.IsSurfacelessContextSupported();
546 eglGraphics->SetIsSurfacelessContextSupported( isSurfacelessContextSupported );
548 if ( isSurfacelessContextSupported )
550 // Create a surfaceless OpenGL context for shared resources
551 eglImpl.CreateContext();
552 eglImpl.MakeContextCurrent( EGL_NO_SURFACE, eglImpl.GetContext() );
556 currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
559 currentSurface->InitializeGraphics();
560 currentSurface->MakeContextCurrent();
564 eglGraphics->GetGlesInterface().ContextCreated();
566 // Tell core it has a context
567 mCore.ContextCreated();
569 NotifyThreadInitialised();
572 uint64_t lastFrameTime;
573 TimeService::GetNanoseconds( lastFrameTime );
575 LOG_UPDATE_RENDER( "THREAD INITIALISED" );
577 bool useElapsedTime = true;
578 bool updateRequired = true;
579 uint64_t timeToSleepUntil = 0;
580 int extraFramesDropped = 0;
582 const unsigned int renderToFboInterval = mEnvironmentOptions.GetRenderToFboInterval();
583 const bool renderToFboEnabled = 0u != renderToFboInterval;
584 unsigned int frameCount = 0u;
586 while( UpdateRenderReady( useElapsedTime, updateRequired, timeToSleepUntil ) )
588 LOG_UPDATE_RENDER_TRACE;
590 // Performance statistics are logged upon a VSYNC tick so use this point for a VSync marker
591 AddPerformanceMarker( PerformanceInterface::VSYNC );
593 uint64_t currentFrameStartTime = 0;
594 TimeService::GetNanoseconds( currentFrameStartTime );
596 uint64_t timeSinceLastFrame = currentFrameStartTime - lastFrameTime;
598 // Optional FPS Tracking when continuously rendering
599 if( useElapsedTime && mFpsTracker.Enabled() )
601 float absoluteTimeSinceLastRender = timeSinceLastFrame * NANOSECONDS_TO_SECOND;
602 mFpsTracker.Track( absoluteTimeSinceLastRender );
605 lastFrameTime = currentFrameStartTime; // Store frame start time
607 //////////////////////////////
609 //////////////////////////////
611 Dali::RenderSurfaceInterface* newSurface = ShouldSurfaceBeReplaced();
612 if( DALI_UNLIKELY( newSurface ) )
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();
629 const bool isRenderingToFbo = renderToFboEnabled && ( ( 0u == frameCount ) || ( 0u != frameCount % renderToFboInterval ) );
632 //////////////////////////////
634 //////////////////////////////
636 const unsigned int currentTime = currentFrameStartTime / NANOSECONDS_PER_MILLISECOND;
637 const unsigned int nextFrameTime = currentTime + mDefaultFrameDurationMilliseconds;
639 uint64_t noOfFramesSinceLastUpdate = 1;
640 float frameDelta = 0.0f;
643 if( mThreadMode == ThreadMode::RUN_IF_REQUESTED )
645 extraFramesDropped = 0;
646 while( timeSinceLastFrame >= mDefaultFrameDurationNanoseconds )
648 timeSinceLastFrame -= mDefaultFrameDurationNanoseconds;
649 extraFramesDropped++;
653 // If using the elapsed time, then calculate frameDelta as a multiple of mDefaultFrameDelta
654 noOfFramesSinceLastUpdate += extraFramesDropped;
656 frameDelta = mDefaultFrameDelta * noOfFramesSinceLastUpdate;
658 LOG_UPDATE_RENDER( "timeSinceLastFrame(%llu) noOfFramesSinceLastUpdate(%u) frameDelta(%.6f)", timeSinceLastFrame, noOfFramesSinceLastUpdate, frameDelta );
660 Integration::UpdateStatus updateStatus;
662 AddPerformanceMarker( PerformanceInterface::UPDATE_START );
663 mCore.Update( frameDelta,
669 AddPerformanceMarker( PerformanceInterface::UPDATE_END );
671 unsigned int keepUpdatingStatus = updateStatus.KeepUpdating();
673 // Tell the event-thread to wake up (if asleep) and send a notification event to Core if required
674 if( updateStatus.NeedsNotification() )
676 mNotificationTrigger.Trigger();
677 LOG_UPDATE_RENDER( "Notification Triggered" );
681 bool surfaceResized = false;
682 bool shouldSurfaceBeResized = ShouldSurfaceBeResized();
683 if( DALI_UNLIKELY( shouldSurfaceBeResized ) )
685 if( updateStatus.SurfaceRectChanged() )
687 LOG_UPDATE_RENDER_TRACE_FMT( "Resizing Surface" );
689 surfaceResized = true;
693 // Optional logging of update/render status
694 mUpdateStatusLogger.Log( keepUpdatingStatus );
696 //////////////////////////////
698 //////////////////////////////
700 mAdaptorInterfaces.GetDisplayConnectionInterface().ConsumeEvents();
702 if( mPreRenderCallback != NULL )
704 bool keepCallback = CallbackBase::ExecuteReturn<bool>(*mPreRenderCallback);
707 delete mPreRenderCallback;
708 mPreRenderCallback = NULL;
712 if( eglImpl.IsSurfacelessContextSupported() )
714 // Make the shared surfaceless context as current before rendering
715 eglImpl.MakeContextCurrent( EGL_NO_SURFACE, eglImpl.GetContext() );
718 if( mFirstFrameAfterResume )
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;
726 Integration::RenderStatus renderStatus;
728 AddPerformanceMarker( PerformanceInterface::RENDER_START );
730 // Upload shared resources
731 mCore.PreRender( renderStatus, mForceClear, mUploadWithoutRendering );
733 if ( !mUploadWithoutRendering )
735 // Go through each window
736 WindowContainer windows;
737 mAdaptorInterfaces.GetWindowContainerInterface( windows );
739 for( auto&& window : windows )
741 Dali::Integration::Scene scene = window->GetScene();
742 Dali::RenderSurfaceInterface* windowSurface = window->GetSurface();
744 if ( scene && windowSurface )
746 Integration::RenderStatus windowRenderStatus;
748 windowSurface->InitializeGraphics();
750 // clear previous frame damaged render items rects, buffer history is tracked on surface level
751 mDamagedRects.clear();
753 // Collect damage rects
754 mCore.PreRender( scene, mDamagedRects );
756 // Render off-screen frame buffers first if any
757 mCore.RenderScene( windowRenderStatus, scene, true );
759 Rect<int> clippingRect; // Empty for fbo rendering
761 // Switch to the EGL context of the surface, merge damaged areas for previous frames
762 windowSurface->PreRender( surfaceResized, mDamagedRects, clippingRect ); // Switch GL context
764 if (clippingRect.IsEmpty())
766 mDamagedRects.clear();
769 // Render the surface
770 mCore.RenderScene( windowRenderStatus, scene, false, clippingRect );
772 if( windowRenderStatus.NeedsPostRender() )
774 windowSurface->PostRender( false, false, surfaceResized, mDamagedRects ); // Swap Buffer with damage
780 mCore.PostRender( mUploadWithoutRendering );
782 //////////////////////////////
784 //////////////////////////////
786 Dali::RenderSurfaceInterface* deletedSurface = ShouldSurfaceBeDeleted();
787 if( DALI_UNLIKELY( deletedSurface ) )
789 LOG_UPDATE_RENDER_TRACE_FMT( "Deleting Surface" );
791 deletedSurface->DestroySurface();
796 AddPerformanceMarker( PerformanceInterface::RENDER_END );
800 // Trigger event thread to request Update/Render thread to sleep if update not required
801 if( ( Integration::KeepUpdating::NOT_REQUESTED == keepUpdatingStatus ) && !renderStatus.NeedsUpdate() )
803 mSleepTrigger->Trigger();
804 updateRequired = false;
805 LOG_UPDATE_RENDER( "Sleep Triggered" );
809 updateRequired = true;
812 //////////////////////////////
814 //////////////////////////////
816 extraFramesDropped = 0;
818 if (timeToSleepUntil == 0)
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;
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;
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 )
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
843 timeToSleepUntil += mDefaultFrameDurationNanoseconds;
844 extraFramesDropped++;
848 // Render to FBO is intended to measure fps above 60 so sleep is not wanted.
849 if( 0u == renderToFboInterval )
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 );
856 // Inform core of context destruction
857 mCore.ContextDestroyed();
859 WindowContainer windows;
860 mAdaptorInterfaces.GetWindowContainerInterface( windows );
863 for( auto&& window : windows )
865 Dali::RenderSurfaceInterface* surface = window->GetSurface();
866 surface->DestroySurface();
870 eglInterface->TerminateGles();
872 LOG_UPDATE_RENDER( "THREAD DESTROYED" );
874 // Uninstall the logging function
875 mEnvironmentOptions.UnInstallLogFunction();
878 bool CombinedUpdateRenderController::UpdateRenderReady( bool& useElapsedTime, bool updateRequired, uint64_t& timeToSleepUntil )
880 useElapsedTime = true;
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
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 );
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;
902 mUpdateRenderThreadWaitCondition.Wait( updateLock );
904 if( ! mUseElapsedTimeAfterWait )
906 useElapsedTime = false;
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 );
917 mUseElapsedTimeAfterWait = FALSE;
918 mUpdateRenderThreadCanSleep = FALSE;
919 mPendingRequestUpdate = FALSE;
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 )
925 --mUpdateRenderRunCount;
928 // Keep the update-render thread alive if this thread is NOT to be destroyed
929 return ! mDestroyUpdateRenderThread;
932 Dali::RenderSurfaceInterface* CombinedUpdateRenderController::ShouldSurfaceBeReplaced()
934 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
936 Dali::RenderSurfaceInterface* newSurface = mNewSurface;
942 void CombinedUpdateRenderController::SurfaceReplaced()
944 // Just increment the semaphore
945 sem_post( &mSurfaceSemaphore );
948 Dali::RenderSurfaceInterface* CombinedUpdateRenderController::ShouldSurfaceBeDeleted()
950 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
952 Dali::RenderSurfaceInterface* deletedSurface = mDeletedSurface;
953 mDeletedSurface = NULL;
955 return deletedSurface;
958 void CombinedUpdateRenderController::SurfaceDeleted()
960 // Just increment the semaphore
961 sem_post( &mSurfaceSemaphore );
964 bool CombinedUpdateRenderController::ShouldSurfaceBeResized()
966 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
967 return mSurfaceResized;
970 void CombinedUpdateRenderController::SurfaceResized()
972 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
973 mSurfaceResized = FALSE;
976 ///////////////////////////////////////////////////////////////////////////////////////////////////
978 ///////////////////////////////////////////////////////////////////////////////////////////////////
980 void CombinedUpdateRenderController::NotifyThreadInitialised()
982 // Just increment the semaphore
983 sem_post( &mEventThreadSemaphore );
986 void CombinedUpdateRenderController::NotifyGraphicsInitialised()
988 sem_post( &mGraphicsInitializeSemaphore );
991 void CombinedUpdateRenderController::AddPerformanceMarker( PerformanceInterface::MarkerType type )
993 if( mPerformanceInterface )
995 mPerformanceInterface->AddMarker( type );
999 /////////////////////////////////////////////////////////////////////////////////////////////////
1000 // POST RENDERING: EVENT THREAD
1001 /////////////////////////////////////////////////////////////////////////////////////////////////
1003 void CombinedUpdateRenderController::PostRenderComplete()
1005 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
1006 mPostRendering = FALSE;
1007 mUpdateRenderThreadWaitCondition.Notify( lock );
1010 ///////////////////////////////////////////////////////////////////////////////////////////////////
1011 // POST RENDERING: RENDER THREAD
1012 ///////////////////////////////////////////////////////////////////////////////////////////////////
1014 void CombinedUpdateRenderController::PostRenderStarted()
1016 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
1017 mPostRendering = TRUE;
1020 void CombinedUpdateRenderController::PostRenderWaitForCompletion()
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 )
1028 mUpdateRenderThreadWaitCondition.Wait( lock );
1032 } // namespace Adaptor
1034 } // namespace Internal