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(),
97 mUpdateRenderThreadWaitCondition(),
98 mAdaptorInterfaces( adaptorInterfaces ),
99 mPerformanceInterface( adaptorInterfaces.GetPerformanceInterface() ),
100 mCore( adaptorInterfaces.GetCore() ),
101 mEnvironmentOptions( environmentOptions ),
102 mNotificationTrigger( adaptorInterfaces.GetProcessCoreEventsTrigger() ),
103 mSleepTrigger( NULL ),
104 mPreRenderCallback( NULL ),
105 mUpdateRenderThread( NULL ),
106 mDefaultFrameDelta( 0.0f ),
107 mDefaultFrameDurationMilliseconds( 0u ),
108 mDefaultFrameDurationNanoseconds( 0u ),
109 mDefaultHalfFrameNanoseconds( 0u ),
110 mUpdateRequestCount( 0u ),
112 mThreadMode( threadMode ),
113 mUpdateRenderRunCount( 0 ),
114 mDestroyUpdateRenderThread( FALSE ),
115 mUpdateRenderThreadCanSleep( FALSE ),
116 mPendingRequestUpdate( FALSE ),
117 mUseElapsedTimeAfterWait( FALSE ),
119 mDeletedSurface( nullptr ),
120 mPostRendering( FALSE ),
121 mSurfaceResized( FALSE ),
122 mForceClear( FALSE ),
123 mUploadWithoutRendering( FALSE ),
124 mFirstFrameAfterResume( FALSE )
128 // Initialise frame delta/duration variables first
129 SetRenderRefreshRate( environmentOptions.GetRenderRefreshRate() );
131 // Set the thread-synchronization interface on the render-surface
132 Dali::RenderSurfaceInterface* currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
135 currentSurface->SetThreadSynchronization( *this );
138 mSleepTrigger = TriggerEventFactory::CreateTriggerEvent( MakeCallback( this, &CombinedUpdateRenderController::ProcessSleepRequest ), TriggerEventInterface::KEEP_ALIVE_AFTER_TRIGGER );
140 // Initialize to 0 so that it just waits if sem_post has not been called
141 sem_init( &mEventThreadSemaphore, 0, 0 );
142 sem_init( &mGraphicsInitializeSemaphore, 0, 0 );
145 CombinedUpdateRenderController::~CombinedUpdateRenderController()
151 delete mPreRenderCallback;
152 delete mSleepTrigger;
155 void CombinedUpdateRenderController::Initialize()
159 // Ensure Update/Render Thread not already created
160 DALI_ASSERT_ALWAYS( ! mUpdateRenderThread );
162 // Create Update/Render Thread
163 mUpdateRenderThread = new pthread_t();
164 int error = pthread_create( mUpdateRenderThread, NULL, InternalUpdateRenderThreadEntryFunc, this );
165 DALI_ASSERT_ALWAYS( !error && "Return code from pthread_create() when creating UpdateRenderThread" );
167 // The Update/Render thread will now run and initialise the graphics interface etc. and will then wait for Start to be called
168 // When this function returns, the application initialisation on the event thread should occur
171 void CombinedUpdateRenderController::Start()
175 DALI_ASSERT_ALWAYS( !mRunning && mUpdateRenderThread );
177 // Wait until all threads created in Initialise are up and running
178 for( unsigned int i = 0; i < CREATED_THREAD_COUNT; ++i )
180 sem_wait( &mEventThreadSemaphore );
183 Dali::RenderSurfaceInterface* currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
186 currentSurface->StartRender();
191 LOG_EVENT( "Startup Complete, starting Update/Render Thread" );
193 RunUpdateRenderThread( CONTINUOUS, AnimationProgression::NONE, UpdateMode::NORMAL );
195 DALI_LOG_RELEASE_INFO( "CombinedUpdateRenderController::Start\n" );
198 void CombinedUpdateRenderController::Pause()
204 PauseUpdateRenderThread();
206 AddPerformanceMarker( PerformanceInterface::PAUSED );
208 DALI_LOG_RELEASE_INFO( "CombinedUpdateRenderController::Pause\n" );
211 void CombinedUpdateRenderController::Resume()
215 if( !mRunning && IsUpdateRenderThreadPaused() )
217 LOG_EVENT( "Resuming" );
219 RunUpdateRenderThread( CONTINUOUS, AnimationProgression::USE_ELAPSED_TIME, UpdateMode::NORMAL );
221 AddPerformanceMarker( PerformanceInterface::RESUME );
225 mFirstFrameAfterResume = TRUE;
227 DALI_LOG_RELEASE_INFO( "CombinedUpdateRenderController::Resume\n" );
231 DALI_LOG_RELEASE_INFO( "CombinedUpdateRenderController::Resume: Already resumed [%d, %d, %d]\n", mRunning, mUpdateRenderRunCount, mUpdateRenderThreadCanSleep );
235 void CombinedUpdateRenderController::Stop()
239 // Stop Rendering and the Update/Render Thread
240 Dali::RenderSurfaceInterface* currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
243 currentSurface->StopRender();
246 StopUpdateRenderThread();
248 if( mUpdateRenderThread )
250 LOG_EVENT( "Destroying UpdateRenderThread" );
252 // wait for the thread to finish
253 pthread_join( *mUpdateRenderThread, NULL );
255 delete mUpdateRenderThread;
256 mUpdateRenderThread = NULL;
261 DALI_LOG_RELEASE_INFO( "CombinedUpdateRenderController::Stop\n" );
264 void CombinedUpdateRenderController::RequestUpdate()
268 // Increment the update-request count to the maximum
269 if( mUpdateRequestCount < MAXIMUM_UPDATE_REQUESTS )
271 ++mUpdateRequestCount;
274 if( mRunning && IsUpdateRenderThreadPaused() )
276 LOG_EVENT( "Processing" );
278 RunUpdateRenderThread( CONTINUOUS, AnimationProgression::NONE, UpdateMode::NORMAL );
281 ConditionalWait::ScopedLock updateLock( mUpdateRenderThreadWaitCondition );
282 mPendingRequestUpdate = TRUE;
285 void CombinedUpdateRenderController::RequestUpdateOnce( UpdateMode updateMode )
287 // Increment the update-request count to the maximum
288 if( mUpdateRequestCount < MAXIMUM_UPDATE_REQUESTS )
290 ++mUpdateRequestCount;
293 if( IsUpdateRenderThreadPaused() || updateMode == UpdateMode::FORCE_RENDER )
297 // Run Update/Render once
298 RunUpdateRenderThread( ONCE, AnimationProgression::NONE, updateMode );
302 void CombinedUpdateRenderController::ReplaceSurface( Dali::RenderSurfaceInterface* newSurface )
306 if( mUpdateRenderThread )
308 // Set the ThreadSyncronizationInterface on the new surface
309 newSurface->SetThreadSynchronization( *this );
311 LOG_EVENT( "Starting to replace the surface, event-thread blocked" );
313 // Start replacing the surface.
315 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
316 mPostRendering = FALSE; // Clear the post-rendering flag as Update/Render thread will replace the surface now
317 mNewSurface = newSurface;
318 mUpdateRenderThreadWaitCondition.Notify( lock );
321 // Wait until the surface has been replaced
322 sem_wait( &mEventThreadSemaphore );
324 LOG_EVENT( "Surface replaced, event-thread continuing" );
328 void CombinedUpdateRenderController::DeleteSurface( Dali::RenderSurfaceInterface* surface )
332 if( mUpdateRenderThread )
334 LOG_EVENT( "Starting to delete the surface, event-thread blocked" );
336 // Start replacing the surface.
338 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
339 mPostRendering = FALSE; // Clear the post-rendering flag as Update/Render thread will delete the surface now
340 mDeletedSurface = surface;
341 mUpdateRenderThreadWaitCondition.Notify( lock );
344 // Wait until the surface has been deleted
345 sem_wait( &mEventThreadSemaphore );
347 LOG_EVENT( "Surface deleted, event-thread continuing" );
351 void CombinedUpdateRenderController::WaitForGraphicsInitialization()
355 if( mUpdateRenderThread )
357 LOG_EVENT( "Waiting for graphics initialisation, event-thread blocked" );
359 // Wait until the graphics has been initialised
360 sem_wait( &mGraphicsInitializeSemaphore );
362 LOG_EVENT( "graphics initialised, event-thread continuing" );
366 void CombinedUpdateRenderController::ResizeSurface()
370 LOG_EVENT( "Resize the surface" );
373 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
374 mPostRendering = FALSE; // Clear the post-rendering flag as Update/Render thread will resize the surface now
375 mSurfaceResized = TRUE;
376 mUpdateRenderThreadWaitCondition.Notify( lock );
380 void CombinedUpdateRenderController::SetRenderRefreshRate( unsigned int numberOfFramesPerRender )
382 // Not protected by lock, but written to rarely so not worth adding a lock when reading
383 mDefaultFrameDelta = numberOfFramesPerRender * DEFAULT_FRAME_DURATION_IN_SECONDS;
384 mDefaultFrameDurationMilliseconds = uint64_t( numberOfFramesPerRender ) * DEFAULT_FRAME_DURATION_IN_MILLISECONDS;
385 mDefaultFrameDurationNanoseconds = uint64_t( numberOfFramesPerRender ) * DEFAULT_FRAME_DURATION_IN_NANOSECONDS;
386 mDefaultHalfFrameNanoseconds = mDefaultFrameDurationNanoseconds / 2u;
388 LOG_EVENT( "mDefaultFrameDelta(%.6f), mDefaultFrameDurationMilliseconds(%lld), mDefaultFrameDurationNanoseconds(%lld)", mDefaultFrameDelta, mDefaultFrameDurationMilliseconds, mDefaultFrameDurationNanoseconds );
391 void CombinedUpdateRenderController::SetPreRenderCallback( CallbackBase* callback )
394 LOG_EVENT( "Set PreRender Callback" );
396 ConditionalWait::ScopedLock updateLock( mUpdateRenderThreadWaitCondition );
397 if( mPreRenderCallback )
399 delete mPreRenderCallback;
401 mPreRenderCallback = callback;
404 void CombinedUpdateRenderController::AddSurface( Dali::RenderSurfaceInterface* surface )
407 LOG_EVENT( "Surface is added" );
408 if( mUpdateRenderThread )
410 // Set the ThreadSyncronizationInterface on the added surface
411 surface->SetThreadSynchronization( *this );
415 ///////////////////////////////////////////////////////////////////////////////////////////////////
417 ///////////////////////////////////////////////////////////////////////////////////////////////////
419 void CombinedUpdateRenderController::RunUpdateRenderThread( int numberOfCycles, AnimationProgression animationProgression, UpdateMode updateMode )
421 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
423 switch( mThreadMode )
425 case ThreadMode::NORMAL:
427 mUpdateRenderRunCount = numberOfCycles;
428 mUseElapsedTimeAfterWait = ( animationProgression == AnimationProgression::USE_ELAPSED_TIME );
431 case ThreadMode::RUN_IF_REQUESTED:
433 if( updateMode != UpdateMode::FORCE_RENDER )
435 // Render only if the update mode is FORCE_RENDER which means the application requests it.
436 // We don't want to awake the update thread.
440 mUpdateRenderRunCount++; // Increase the update request count
441 mUseElapsedTimeAfterWait = TRUE; // The elapsed time should be used. We want animations to proceed.
446 mUpdateRenderThreadCanSleep = FALSE;
447 mUploadWithoutRendering = ( updateMode == UpdateMode::SKIP_RENDER );
448 LOG_COUNTER_EVENT( "mUpdateRenderRunCount: %d, mUseElapsedTimeAfterWait: %d", mUpdateRenderRunCount, mUseElapsedTimeAfterWait );
449 mUpdateRenderThreadWaitCondition.Notify( lock );
452 void CombinedUpdateRenderController::PauseUpdateRenderThread()
454 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
455 mUpdateRenderRunCount = 0;
458 void CombinedUpdateRenderController::StopUpdateRenderThread()
460 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
461 mDestroyUpdateRenderThread = TRUE;
462 mUpdateRenderThreadWaitCondition.Notify( lock );
465 bool CombinedUpdateRenderController::IsUpdateRenderThreadPaused()
467 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
469 if( mThreadMode == ThreadMode::RUN_IF_REQUESTED )
471 return !mRunning || mUpdateRenderThreadCanSleep;
474 return ( mUpdateRenderRunCount != CONTINUOUS ) || // Report paused if NOT continuously running
475 mUpdateRenderThreadCanSleep; // Report paused if sleeping
478 void CombinedUpdateRenderController::ProcessSleepRequest()
482 // Decrement Update request count
483 if( mUpdateRequestCount > 0 )
485 --mUpdateRequestCount;
488 // Can sleep if our update-request count is 0
489 // Update/Render thread can choose to carry on updating if it determines more update/renders are required
490 if( mUpdateRequestCount == 0 )
492 LOG_EVENT( "Going to sleep" );
494 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
495 mUpdateRenderThreadCanSleep = TRUE;
499 ///////////////////////////////////////////////////////////////////////////////////////////////////
500 // UPDATE/RENDER THREAD
501 ///////////////////////////////////////////////////////////////////////////////////////////////////
503 void CombinedUpdateRenderController::UpdateRenderThread()
505 SetThreadName("RenderThread\0");
507 // Install a function for logging
508 mEnvironmentOptions.InstallLogFunction();
510 // Install a function for tracing
511 mEnvironmentOptions.InstallTraceFunction();
513 LOG_UPDATE_RENDER( "THREAD CREATED" );
515 // Initialize EGL & OpenGL
516 Dali::DisplayConnection& displayConnection = mAdaptorInterfaces.GetDisplayConnectionInterface();
517 displayConnection.Initialize();
519 // EGL has been initialised at this point
520 NotifyGraphicsInitialised();
522 RenderSurfaceInterface* currentSurface = nullptr;
524 GraphicsInterface& graphics = mAdaptorInterfaces.GetGraphicsInterface();
525 EglGraphics* eglGraphics = static_cast<EglGraphics *>(&graphics);
527 // This will only be created once
528 EglInterface* eglInterface = &eglGraphics->GetEglInterface();
530 Internal::Adaptor::EglImplementation& eglImpl = static_cast<Internal::Adaptor::EglImplementation&>( *eglInterface );
532 // Try to use OpenGL es 3.0
533 // ChooseConfig returns false here when the device only support gles 2.0.
534 // Because eglChooseConfig with gles 3.0 setting fails when the device only support gles 2.0 and Our default setting is gles 3.0.
535 if( !eglImpl.ChooseConfig( true, COLOR_DEPTH_32 ) )
537 // Retry to use OpenGL es 2.0
538 eglGraphics->SetGlesVersion( 20 );
539 eglImpl.ChooseConfig( true, COLOR_DEPTH_32 );
542 // Check whether surfaceless context is supported
543 bool isSurfacelessContextSupported = eglImpl.IsSurfacelessContextSupported();
544 eglGraphics->SetIsSurfacelessContextSupported( isSurfacelessContextSupported );
546 if ( isSurfacelessContextSupported )
548 // Create a surfaceless OpenGL context for shared resources
549 eglImpl.CreateContext();
550 eglImpl.MakeContextCurrent( EGL_NO_SURFACE, eglImpl.GetContext() );
554 currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
557 currentSurface->InitializeGraphics();
558 currentSurface->MakeContextCurrent();
562 eglGraphics->GetGlesInterface().ContextCreated();
564 // Tell core it has a context
565 mCore.ContextCreated();
567 NotifyThreadInitialised();
570 uint64_t lastFrameTime;
571 TimeService::GetNanoseconds( lastFrameTime );
573 LOG_UPDATE_RENDER( "THREAD INITIALISED" );
575 bool useElapsedTime = true;
576 bool updateRequired = true;
577 uint64_t timeToSleepUntil = 0;
578 int extraFramesDropped = 0;
580 const unsigned int renderToFboInterval = mEnvironmentOptions.GetRenderToFboInterval();
581 const bool renderToFboEnabled = 0u != renderToFboInterval;
582 unsigned int frameCount = 0u;
584 while( UpdateRenderReady( useElapsedTime, updateRequired, timeToSleepUntil ) )
586 LOG_UPDATE_RENDER_TRACE;
588 // Performance statistics are logged upon a VSYNC tick so use this point for a VSync marker
589 AddPerformanceMarker( PerformanceInterface::VSYNC );
591 uint64_t currentFrameStartTime = 0;
592 TimeService::GetNanoseconds( currentFrameStartTime );
594 uint64_t timeSinceLastFrame = currentFrameStartTime - lastFrameTime;
596 // Optional FPS Tracking when continuously rendering
597 if( useElapsedTime && mFpsTracker.Enabled() )
599 float absoluteTimeSinceLastRender = timeSinceLastFrame * NANOSECONDS_TO_SECOND;
600 mFpsTracker.Track( absoluteTimeSinceLastRender );
603 lastFrameTime = currentFrameStartTime; // Store frame start time
605 //////////////////////////////
607 //////////////////////////////
609 Dali::RenderSurfaceInterface* newSurface = ShouldSurfaceBeReplaced();
610 if( DALI_UNLIKELY( newSurface ) )
612 LOG_UPDATE_RENDER_TRACE_FMT( "Replacing Surface" );
613 // This is designed for replacing pixmap surfaces, but should work for window as well
614 // we need to delete the surface and renderable (pixmap / window)
615 // Then create a new pixmap/window and new surface
616 // If the new surface has a different display connection, then the context will be lost
617 mAdaptorInterfaces.GetDisplayConnectionInterface().Initialize();
618 newSurface->InitializeGraphics();
619 newSurface->MakeContextCurrent();
620 // TODO: ReplaceGraphicsSurface doesn't work, InitializeGraphics()
621 // already creates new surface window, the surface and the context.
622 // We probably don't need ReplaceGraphicsSurface at all.
623 // newSurface->ReplaceGraphicsSurface();
627 const bool isRenderingToFbo = renderToFboEnabled && ( ( 0u == frameCount ) || ( 0u != frameCount % renderToFboInterval ) );
630 //////////////////////////////
632 //////////////////////////////
634 const unsigned int currentTime = currentFrameStartTime / NANOSECONDS_PER_MILLISECOND;
635 const unsigned int nextFrameTime = currentTime + mDefaultFrameDurationMilliseconds;
637 uint64_t noOfFramesSinceLastUpdate = 1;
638 float frameDelta = 0.0f;
641 if( mThreadMode == ThreadMode::RUN_IF_REQUESTED )
643 extraFramesDropped = 0;
644 while( timeSinceLastFrame >= mDefaultFrameDurationNanoseconds )
646 timeSinceLastFrame -= mDefaultFrameDurationNanoseconds;
647 extraFramesDropped++;
651 // If using the elapsed time, then calculate frameDelta as a multiple of mDefaultFrameDelta
652 noOfFramesSinceLastUpdate += extraFramesDropped;
654 frameDelta = mDefaultFrameDelta * noOfFramesSinceLastUpdate;
656 LOG_UPDATE_RENDER( "timeSinceLastFrame(%llu) noOfFramesSinceLastUpdate(%u) frameDelta(%.6f)", timeSinceLastFrame, noOfFramesSinceLastUpdate, frameDelta );
658 Integration::UpdateStatus updateStatus;
660 AddPerformanceMarker( PerformanceInterface::UPDATE_START );
661 mCore.Update( frameDelta,
667 AddPerformanceMarker( PerformanceInterface::UPDATE_END );
669 unsigned int keepUpdatingStatus = updateStatus.KeepUpdating();
671 // Tell the event-thread to wake up (if asleep) and send a notification event to Core if required
672 if( updateStatus.NeedsNotification() )
674 mNotificationTrigger.Trigger();
675 LOG_UPDATE_RENDER( "Notification Triggered" );
679 bool surfaceResized = false;
680 bool shouldSurfaceBeResized = ShouldSurfaceBeResized();
681 if( DALI_UNLIKELY( shouldSurfaceBeResized ) )
683 if( updateStatus.SurfaceRectChanged() )
685 LOG_UPDATE_RENDER_TRACE_FMT( "Resizing Surface" );
687 surfaceResized = true;
691 // Optional logging of update/render status
692 mUpdateStatusLogger.Log( keepUpdatingStatus );
694 //////////////////////////////
696 //////////////////////////////
698 mAdaptorInterfaces.GetDisplayConnectionInterface().ConsumeEvents();
700 if( mPreRenderCallback != NULL )
702 bool keepCallback = CallbackBase::ExecuteReturn<bool>(*mPreRenderCallback);
705 delete mPreRenderCallback;
706 mPreRenderCallback = NULL;
710 if( eglImpl.IsSurfacelessContextSupported() )
712 // Make the shared surfaceless context as current before rendering
713 eglImpl.MakeContextCurrent( EGL_NO_SURFACE, eglImpl.GetContext() );
716 if( mFirstFrameAfterResume )
718 // mFirstFrameAfterResume is set to true when the thread is resumed
719 // Let eglImplementation know the first frame after thread initialized or resumed.
720 eglImpl.SetFirstFrameAfterResume();
721 mFirstFrameAfterResume = FALSE;
724 Integration::RenderStatus renderStatus;
726 AddPerformanceMarker( PerformanceInterface::RENDER_START );
728 // Upload shared resources
729 mCore.PreRender( renderStatus, mForceClear, mUploadWithoutRendering );
731 if ( !mUploadWithoutRendering )
733 // Go through each window
734 WindowContainer windows;
735 mAdaptorInterfaces.GetWindowContainerInterface( windows );
737 for( auto&& window : windows )
739 Dali::Integration::Scene scene = window->GetScene();
740 Dali::RenderSurfaceInterface* windowSurface = window->GetSurface();
742 if ( scene && windowSurface )
744 Integration::RenderStatus windowRenderStatus;
746 windowSurface->InitializeGraphics();
748 // clear previous frame damaged render items rects, buffer history is tracked on surface level
749 mDamagedRects.clear();
751 // If user damaged areas are not set
752 if (!eglImpl.DamageAreasSet())
754 // Collect damage rects
755 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( &mEventThreadSemaphore );
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( &mEventThreadSemaphore );
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