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 GlImplementation& gles = eglGraphics->GetGlesInterface();
565 gles.ContextCreated();
566 eglGraphics->SetGlesVersion( gles.GetGlesVersion() );
568 // Tell core it has a context
569 mCore.ContextCreated();
571 NotifyThreadInitialised();
574 uint64_t lastFrameTime;
575 TimeService::GetNanoseconds( lastFrameTime );
577 LOG_UPDATE_RENDER( "THREAD INITIALISED" );
579 bool useElapsedTime = true;
580 bool updateRequired = true;
581 uint64_t timeToSleepUntil = 0;
582 int extraFramesDropped = 0;
584 const unsigned int renderToFboInterval = mEnvironmentOptions.GetRenderToFboInterval();
585 const bool renderToFboEnabled = 0u != renderToFboInterval;
586 unsigned int frameCount = 0u;
588 while( UpdateRenderReady( useElapsedTime, updateRequired, timeToSleepUntil ) )
590 LOG_UPDATE_RENDER_TRACE;
592 // Performance statistics are logged upon a VSYNC tick so use this point for a VSync marker
593 AddPerformanceMarker( PerformanceInterface::VSYNC );
595 uint64_t currentFrameStartTime = 0;
596 TimeService::GetNanoseconds( currentFrameStartTime );
598 uint64_t timeSinceLastFrame = currentFrameStartTime - lastFrameTime;
600 // Optional FPS Tracking when continuously rendering
601 if( useElapsedTime && mFpsTracker.Enabled() )
603 float absoluteTimeSinceLastRender = timeSinceLastFrame * NANOSECONDS_TO_SECOND;
604 mFpsTracker.Track( absoluteTimeSinceLastRender );
607 lastFrameTime = currentFrameStartTime; // Store frame start time
609 //////////////////////////////
611 //////////////////////////////
613 Dali::RenderSurfaceInterface* newSurface = ShouldSurfaceBeReplaced();
614 if( DALI_UNLIKELY( newSurface ) )
616 LOG_UPDATE_RENDER_TRACE_FMT( "Replacing Surface" );
617 // This is designed for replacing pixmap surfaces, but should work for window as well
618 // we need to delete the surface and renderable (pixmap / window)
619 // Then create a new pixmap/window and new surface
620 // If the new surface has a different display connection, then the context will be lost
621 mAdaptorInterfaces.GetDisplayConnectionInterface().Initialize();
622 newSurface->InitializeGraphics();
623 newSurface->MakeContextCurrent();
624 // TODO: ReplaceGraphicsSurface doesn't work, InitializeGraphics()
625 // already creates new surface window, the surface and the context.
626 // We probably don't need ReplaceGraphicsSurface at all.
627 // newSurface->ReplaceGraphicsSurface();
631 const bool isRenderingToFbo = renderToFboEnabled && ( ( 0u == frameCount ) || ( 0u != frameCount % renderToFboInterval ) );
634 //////////////////////////////
636 //////////////////////////////
638 const unsigned int currentTime = currentFrameStartTime / NANOSECONDS_PER_MILLISECOND;
639 const unsigned int nextFrameTime = currentTime + mDefaultFrameDurationMilliseconds;
641 uint64_t noOfFramesSinceLastUpdate = 1;
642 float frameDelta = 0.0f;
645 if( mThreadMode == ThreadMode::RUN_IF_REQUESTED )
647 extraFramesDropped = 0;
648 while( timeSinceLastFrame >= mDefaultFrameDurationNanoseconds )
650 timeSinceLastFrame -= mDefaultFrameDurationNanoseconds;
651 extraFramesDropped++;
655 // If using the elapsed time, then calculate frameDelta as a multiple of mDefaultFrameDelta
656 noOfFramesSinceLastUpdate += extraFramesDropped;
658 frameDelta = mDefaultFrameDelta * noOfFramesSinceLastUpdate;
660 LOG_UPDATE_RENDER( "timeSinceLastFrame(%llu) noOfFramesSinceLastUpdate(%u) frameDelta(%.6f)", timeSinceLastFrame, noOfFramesSinceLastUpdate, frameDelta );
662 Integration::UpdateStatus updateStatus;
664 AddPerformanceMarker( PerformanceInterface::UPDATE_START );
665 mCore.Update( frameDelta,
671 AddPerformanceMarker( PerformanceInterface::UPDATE_END );
673 unsigned int keepUpdatingStatus = updateStatus.KeepUpdating();
675 // Tell the event-thread to wake up (if asleep) and send a notification event to Core if required
676 if( updateStatus.NeedsNotification() )
678 mNotificationTrigger.Trigger();
679 LOG_UPDATE_RENDER( "Notification Triggered" );
683 bool surfaceResized = false;
684 bool shouldSurfaceBeResized = ShouldSurfaceBeResized();
685 if( DALI_UNLIKELY( shouldSurfaceBeResized ) )
687 if( updateStatus.SurfaceRectChanged() )
689 LOG_UPDATE_RENDER_TRACE_FMT( "Resizing Surface" );
691 surfaceResized = true;
695 // Optional logging of update/render status
696 mUpdateStatusLogger.Log( keepUpdatingStatus );
698 //////////////////////////////
700 //////////////////////////////
702 mAdaptorInterfaces.GetDisplayConnectionInterface().ConsumeEvents();
704 if( mPreRenderCallback != NULL )
706 bool keepCallback = CallbackBase::ExecuteReturn<bool>(*mPreRenderCallback);
709 delete mPreRenderCallback;
710 mPreRenderCallback = NULL;
714 if( eglImpl.IsSurfacelessContextSupported() )
716 // Make the shared surfaceless context as current before rendering
717 eglImpl.MakeContextCurrent( EGL_NO_SURFACE, eglImpl.GetContext() );
720 if( mFirstFrameAfterResume )
722 // mFirstFrameAfterResume is set to true when the thread is resumed
723 // Let eglImplementation know the first frame after thread initialized or resumed.
724 eglImpl.SetFirstFrameAfterResume();
725 mFirstFrameAfterResume = FALSE;
728 Integration::RenderStatus renderStatus;
730 AddPerformanceMarker( PerformanceInterface::RENDER_START );
732 // Upload shared resources
733 mCore.PreRender( renderStatus, mForceClear, mUploadWithoutRendering );
735 if ( !mUploadWithoutRendering )
737 // Go through each window
738 WindowContainer windows;
739 mAdaptorInterfaces.GetWindowContainerInterface( windows );
741 for( auto&& window : windows )
743 Dali::Integration::Scene scene = window->GetScene();
744 Dali::RenderSurfaceInterface* windowSurface = window->GetSurface();
746 if ( scene && windowSurface )
748 Integration::RenderStatus windowRenderStatus;
750 windowSurface->InitializeGraphics();
752 // clear previous frame damaged render items rects, buffer history is tracked on surface level
753 mDamagedRects.clear();
755 // Collect damage rects
756 mCore.PreRender( scene, mDamagedRects );
758 // Render off-screen frame buffers first if any
759 mCore.RenderScene( windowRenderStatus, scene, true );
761 Rect<int> clippingRect; // Empty for fbo rendering
763 // Switch to the EGL context of the surface, merge damaged areas for previous frames
764 windowSurface->PreRender( surfaceResized, mDamagedRects, clippingRect ); // Switch GL context
766 if (clippingRect.IsEmpty())
768 mDamagedRects.clear();
771 // Render the surface
772 mCore.RenderScene( windowRenderStatus, scene, false, clippingRect );
774 if( windowRenderStatus.NeedsPostRender() )
776 windowSurface->PostRender( false, false, surfaceResized, mDamagedRects ); // Swap Buffer with damage
782 mCore.PostRender( mUploadWithoutRendering );
784 //////////////////////////////
786 //////////////////////////////
788 Dali::RenderSurfaceInterface* deletedSurface = ShouldSurfaceBeDeleted();
789 if( DALI_UNLIKELY( deletedSurface ) )
791 LOG_UPDATE_RENDER_TRACE_FMT( "Deleting Surface" );
793 deletedSurface->DestroySurface();
798 AddPerformanceMarker( PerformanceInterface::RENDER_END );
802 // Trigger event thread to request Update/Render thread to sleep if update not required
803 if( ( Integration::KeepUpdating::NOT_REQUESTED == keepUpdatingStatus ) && !renderStatus.NeedsUpdate() )
805 mSleepTrigger->Trigger();
806 updateRequired = false;
807 LOG_UPDATE_RENDER( "Sleep Triggered" );
811 updateRequired = true;
814 //////////////////////////////
816 //////////////////////////////
818 extraFramesDropped = 0;
820 if (timeToSleepUntil == 0)
822 // If this is the first frame after the thread is initialized or resumed, we
823 // use the actual time the current frame starts from to calculate the time to
824 // sleep until the next frame.
825 timeToSleepUntil = currentFrameStartTime + mDefaultFrameDurationNanoseconds;
829 // Otherwise, always use the sleep-until time calculated in the last frame to
830 // calculate the time to sleep until the next frame. In this way, if there is
831 // any time gap between the current frame and the next frame, or if update or
832 // rendering in the current frame takes too much time so that the specified
833 // sleep-until time has already passed, it will try to keep the frames syncing
834 // by shortening the duration of the next frame.
835 timeToSleepUntil += mDefaultFrameDurationNanoseconds;
837 // Check the current time at the end of the frame
838 uint64_t currentFrameEndTime = 0;
839 TimeService::GetNanoseconds( currentFrameEndTime );
840 while ( currentFrameEndTime > timeToSleepUntil + mDefaultFrameDurationNanoseconds )
842 // We are more than one frame behind already, so just drop the next frames
843 // until the sleep-until time is later than the current time so that we can
845 timeToSleepUntil += mDefaultFrameDurationNanoseconds;
846 extraFramesDropped++;
850 // Render to FBO is intended to measure fps above 60 so sleep is not wanted.
851 if( 0u == renderToFboInterval )
853 // Sleep until at least the the default frame duration has elapsed. This will return immediately if the specified end-time has already passed.
854 TimeService::SleepUntil( timeToSleepUntil );
858 // Inform core of context destruction
859 mCore.ContextDestroyed();
861 WindowContainer windows;
862 mAdaptorInterfaces.GetWindowContainerInterface( windows );
865 for( auto&& window : windows )
867 Dali::RenderSurfaceInterface* surface = window->GetSurface();
868 surface->DestroySurface();
872 eglInterface->TerminateGles();
874 LOG_UPDATE_RENDER( "THREAD DESTROYED" );
876 // Uninstall the logging function
877 mEnvironmentOptions.UnInstallLogFunction();
880 bool CombinedUpdateRenderController::UpdateRenderReady( bool& useElapsedTime, bool updateRequired, uint64_t& timeToSleepUntil )
882 useElapsedTime = true;
884 ConditionalWait::ScopedLock updateLock( mUpdateRenderThreadWaitCondition );
885 while( ( ! mUpdateRenderRunCount || // Should try to wait if event-thread has paused the Update/Render thread
886 ( mUpdateRenderThreadCanSleep && ! updateRequired && ! mPendingRequestUpdate ) ) && // Ensure we wait if we're supposed to be sleeping AND do not require another update
887 ! mDestroyUpdateRenderThread && // Ensure we don't wait if the update-render-thread is supposed to be destroyed
888 ! mNewSurface && // Ensure we don't wait if we need to replace the surface
889 ! mDeletedSurface && // Ensure we don't wait if we need to delete the surface
890 ! mSurfaceResized ) // Ensure we don't wait if we need to resize the surface
892 LOG_UPDATE_RENDER( "WAIT: mUpdateRenderRunCount: %d", mUpdateRenderRunCount );
893 LOG_UPDATE_RENDER( " mUpdateRenderThreadCanSleep: %d, updateRequired: %d, mPendingRequestUpdate: %d", mUpdateRenderThreadCanSleep, updateRequired, mPendingRequestUpdate );
894 LOG_UPDATE_RENDER( " mDestroyUpdateRenderThread: %d", mDestroyUpdateRenderThread );
895 LOG_UPDATE_RENDER( " mNewSurface: %d", mNewSurface );
896 LOG_UPDATE_RENDER( " mDeletedSurface: %d", mDeletedSurface );
897 LOG_UPDATE_RENDER( " mSurfaceResized: %d", mSurfaceResized );
899 // Reset the time when the thread is waiting, so the sleep-until time for
900 // the first frame after resuming should be based on the actual start time
901 // of the first frame.
902 timeToSleepUntil = 0;
904 mUpdateRenderThreadWaitCondition.Wait( updateLock );
906 if( ! mUseElapsedTimeAfterWait )
908 useElapsedTime = false;
912 LOG_COUNTER_UPDATE_RENDER( "mUpdateRenderRunCount: %d", mUpdateRenderRunCount );
913 LOG_COUNTER_UPDATE_RENDER( "mUpdateRenderThreadCanSleep: %d, updateRequired: %d, mPendingRequestUpdate: %d", mUpdateRenderThreadCanSleep, updateRequired, mPendingRequestUpdate );
914 LOG_COUNTER_UPDATE_RENDER( "mDestroyUpdateRenderThread: %d", mDestroyUpdateRenderThread );
915 LOG_COUNTER_UPDATE_RENDER( "mNewSurface: %d", mNewSurface );
916 LOG_COUNTER_UPDATE_RENDER( "mDeletedSurface: %d", mDeletedSurface );
917 LOG_COUNTER_UPDATE_RENDER( "mSurfaceResized: %d", mSurfaceResized );
919 mUseElapsedTimeAfterWait = FALSE;
920 mUpdateRenderThreadCanSleep = FALSE;
921 mPendingRequestUpdate = FALSE;
923 // If we've been asked to run Update/Render cycles a finite number of times then decrement so we wait after the
924 // requested number of cycles
925 if( mUpdateRenderRunCount > 0 )
927 --mUpdateRenderRunCount;
930 // Keep the update-render thread alive if this thread is NOT to be destroyed
931 return ! mDestroyUpdateRenderThread;
934 Dali::RenderSurfaceInterface* CombinedUpdateRenderController::ShouldSurfaceBeReplaced()
936 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
938 Dali::RenderSurfaceInterface* newSurface = mNewSurface;
944 void CombinedUpdateRenderController::SurfaceReplaced()
946 // Just increment the semaphore
947 sem_post( &mSurfaceSemaphore );
950 Dali::RenderSurfaceInterface* CombinedUpdateRenderController::ShouldSurfaceBeDeleted()
952 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
954 Dali::RenderSurfaceInterface* deletedSurface = mDeletedSurface;
955 mDeletedSurface = NULL;
957 return deletedSurface;
960 void CombinedUpdateRenderController::SurfaceDeleted()
962 // Just increment the semaphore
963 sem_post( &mSurfaceSemaphore );
966 bool CombinedUpdateRenderController::ShouldSurfaceBeResized()
968 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
969 return mSurfaceResized;
972 void CombinedUpdateRenderController::SurfaceResized()
974 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
975 mSurfaceResized = FALSE;
978 ///////////////////////////////////////////////////////////////////////////////////////////////////
980 ///////////////////////////////////////////////////////////////////////////////////////////////////
982 void CombinedUpdateRenderController::NotifyThreadInitialised()
984 // Just increment the semaphore
985 sem_post( &mEventThreadSemaphore );
988 void CombinedUpdateRenderController::NotifyGraphicsInitialised()
990 sem_post( &mGraphicsInitializeSemaphore );
993 void CombinedUpdateRenderController::AddPerformanceMarker( PerformanceInterface::MarkerType type )
995 if( mPerformanceInterface )
997 mPerformanceInterface->AddMarker( type );
1001 /////////////////////////////////////////////////////////////////////////////////////////////////
1002 // POST RENDERING: EVENT THREAD
1003 /////////////////////////////////////////////////////////////////////////////////////////////////
1005 void CombinedUpdateRenderController::PostRenderComplete()
1007 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
1008 mPostRendering = FALSE;
1009 mUpdateRenderThreadWaitCondition.Notify( lock );
1012 ///////////////////////////////////////////////////////////////////////////////////////////////////
1013 // POST RENDERING: RENDER THREAD
1014 ///////////////////////////////////////////////////////////////////////////////////////////////////
1016 void CombinedUpdateRenderController::PostRenderStarted()
1018 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
1019 mPostRendering = TRUE;
1022 void CombinedUpdateRenderController::PostRenderWaitForCompletion()
1024 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
1025 while( mPostRendering &&
1026 ! mNewSurface && // We should NOT wait if we're replacing the surface
1027 ! mDeletedSurface && // We should NOT wait if we're deleting the surface
1028 ! mDestroyUpdateRenderThread )
1030 mUpdateRenderThreadWaitCondition.Wait( lock );
1034 } // namespace Adaptor
1036 } // namespace Internal