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>
26 #include <dali/integration-api/adaptor-framework/trigger-event-factory.h>
27 #include <dali/devel-api/adaptor-framework/thread-settings.h>
28 #include <dali/internal/adaptor/common/adaptor-internal-services.h>
29 #include <dali/internal/adaptor/common/combined-update-render-controller-debug.h>
30 #include <dali/internal/graphics/gles/egl-graphics.h>
31 #include <dali/internal/graphics/gles/egl-implementation.h>
32 #include <dali/internal/graphics/common/graphics-interface.h>
33 #include <dali/internal/system/common/environment-options.h>
34 #include <dali/internal/system/common/time-service.h>
35 #include <dali/internal/window-system/common/window-impl.h>
49 const unsigned int CREATED_THREAD_COUNT = 1u;
51 const int CONTINUOUS = -1;
54 const unsigned int TRUE = 1u;
55 const unsigned int FALSE = 0u;
57 const unsigned int MILLISECONDS_PER_SECOND( 1e+3 );
58 const float NANOSECONDS_TO_SECOND( 1e-9f );
59 const unsigned int NANOSECONDS_PER_SECOND( 1e+9 );
60 const unsigned int NANOSECONDS_PER_MILLISECOND( 1e+6 );
62 // The following values will get calculated at compile time
63 const float DEFAULT_FRAME_DURATION_IN_SECONDS( 1.0f / 60.0f );
64 const uint64_t DEFAULT_FRAME_DURATION_IN_MILLISECONDS( DEFAULT_FRAME_DURATION_IN_SECONDS * MILLISECONDS_PER_SECOND );
65 const uint64_t DEFAULT_FRAME_DURATION_IN_NANOSECONDS( DEFAULT_FRAME_DURATION_IN_SECONDS * NANOSECONDS_PER_SECOND );
68 * 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
69 * there is a danger that, on the event-thread we could have:
70 * 1) An update-request where we do nothing as Update/Render thread still running.
71 * 2) Quickly followed by a sleep-request being handled where we pause the Update/Render Thread (even though we have an update to process).
73 * 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:
74 * 1) MAIN THREAD: Update Request: COUNTER = 1
75 * 2) UPDATE/RENDER THREAD: Do Update/Render, then no Updates required -> Sleep Trigger
76 * 3) MAIN THREAD: Update Request: COUNTER = 2
77 * 4) MAIN THREAD: Sleep Request: COUNTER = 1 -> We do not sleep just yet
79 * Also ensures we preserve battery life by only doing ONE update when the above use case is not triggered.
80 * 1) MAIN THREAD: Update Request: COUNTER = 1
81 * 2) UPDATE/RENDER THREAD: Do Update/Render, then no Updates required -> Sleep Trigger
82 * 3) MAIN THREAD: Sleep Request: COUNTER = 0 -> Go to sleep
84 const unsigned int MAXIMUM_UPDATE_REQUESTS = 2;
85 } // unnamed namespace
87 ///////////////////////////////////////////////////////////////////////////////////////////////////
89 ///////////////////////////////////////////////////////////////////////////////////////////////////
91 CombinedUpdateRenderController::CombinedUpdateRenderController( AdaptorInternalServices& adaptorInterfaces, const EnvironmentOptions& environmentOptions )
92 : mFpsTracker( environmentOptions ),
93 mUpdateStatusLogger( environmentOptions ),
94 mEventThreadSemaphore(),
95 mGraphicsInitializeSemaphore(),
96 mUpdateRenderThreadWaitCondition(),
97 mAdaptorInterfaces( adaptorInterfaces ),
98 mPerformanceInterface( adaptorInterfaces.GetPerformanceInterface() ),
99 mCore( adaptorInterfaces.GetCore() ),
100 mEnvironmentOptions( environmentOptions ),
101 mNotificationTrigger( adaptorInterfaces.GetProcessCoreEventsTrigger() ),
102 mSleepTrigger( NULL ),
103 mPreRenderCallback( NULL ),
104 mUpdateRenderThread( NULL ),
105 mDefaultFrameDelta( 0.0f ),
106 mDefaultFrameDurationMilliseconds( 0u ),
107 mDefaultFrameDurationNanoseconds( 0u ),
108 mDefaultHalfFrameNanoseconds( 0u ),
109 mUpdateRequestCount( 0u ),
111 mUpdateRenderRunCount( 0 ),
112 mDestroyUpdateRenderThread( FALSE ),
113 mUpdateRenderThreadCanSleep( FALSE ),
114 mPendingRequestUpdate( FALSE ),
115 mUseElapsedTimeAfterWait( FALSE ),
117 mPostRendering( FALSE ),
118 mSurfaceResized( FALSE ),
119 mForceClear( FALSE ),
120 mUploadWithoutRendering( FALSE ),
121 mFirstFrameAfterResume( FALSE )
125 // Initialise frame delta/duration variables first
126 SetRenderRefreshRate( environmentOptions.GetRenderRefreshRate() );
128 // Set the thread-synchronization interface on the render-surface
129 Dali::RenderSurfaceInterface* currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
132 currentSurface->SetThreadSynchronization( *this );
135 mSleepTrigger = TriggerEventFactory::CreateTriggerEvent( MakeCallback( this, &CombinedUpdateRenderController::ProcessSleepRequest ), TriggerEventInterface::KEEP_ALIVE_AFTER_TRIGGER );
137 // Initialize to 0 so that it just waits if sem_post has not been called
138 sem_init( &mEventThreadSemaphore, 0, 0 );
139 sem_init( &mGraphicsInitializeSemaphore, 0, 0 );
142 CombinedUpdateRenderController::~CombinedUpdateRenderController()
148 delete mPreRenderCallback;
149 delete mSleepTrigger;
152 void CombinedUpdateRenderController::Initialize()
156 // Ensure Update/Render Thread not already created
157 DALI_ASSERT_ALWAYS( ! mUpdateRenderThread );
159 // Create Update/Render Thread
160 mUpdateRenderThread = new pthread_t();
161 int error = pthread_create( mUpdateRenderThread, NULL, InternalUpdateRenderThreadEntryFunc, this );
162 DALI_ASSERT_ALWAYS( !error && "Return code from pthread_create() when creating UpdateRenderThread" );
164 // The Update/Render thread will now run and initialise the graphics interface etc. and will then wait for Start to be called
165 // When this function returns, the application initialisation on the event thread should occur
168 void CombinedUpdateRenderController::Start()
172 DALI_ASSERT_ALWAYS( !mRunning && mUpdateRenderThread );
174 // Wait until all threads created in Initialise are up and running
175 for( unsigned int i = 0; i < CREATED_THREAD_COUNT; ++i )
177 sem_wait( &mEventThreadSemaphore );
180 Dali::RenderSurfaceInterface* currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
183 currentSurface->StartRender();
188 LOG_EVENT( "Startup Complete, starting Update/Render Thread" );
190 RunUpdateRenderThread( CONTINUOUS, AnimationProgression::NONE, UpdateMode::NORMAL );
192 DALI_LOG_RELEASE_INFO( "CombinedUpdateRenderController::Start\n" );
195 void CombinedUpdateRenderController::Pause()
201 PauseUpdateRenderThread();
203 AddPerformanceMarker( PerformanceInterface::PAUSED );
205 DALI_LOG_RELEASE_INFO( "CombinedUpdateRenderController::Pause\n" );
208 void CombinedUpdateRenderController::Resume()
212 if( !mRunning && IsUpdateRenderThreadPaused() )
214 LOG_EVENT( "Resuming" );
216 RunUpdateRenderThread( CONTINUOUS, AnimationProgression::USE_ELAPSED_TIME, UpdateMode::NORMAL );
218 AddPerformanceMarker( PerformanceInterface::RESUME );
222 mFirstFrameAfterResume = TRUE;
224 DALI_LOG_RELEASE_INFO( "CombinedUpdateRenderController::Resume\n" );
228 DALI_LOG_RELEASE_INFO( "CombinedUpdateRenderController::Resume: Already resumed [%d, %d, %d]\n", mRunning, mUpdateRenderRunCount, mUpdateRenderThreadCanSleep );
232 void CombinedUpdateRenderController::Stop()
236 // Stop Rendering and the Update/Render Thread
237 Dali::RenderSurfaceInterface* currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
240 currentSurface->StopRender();
243 StopUpdateRenderThread();
245 if( mUpdateRenderThread )
247 LOG_EVENT( "Destroying UpdateRenderThread" );
249 // wait for the thread to finish
250 pthread_join( *mUpdateRenderThread, NULL );
252 delete mUpdateRenderThread;
253 mUpdateRenderThread = NULL;
258 DALI_LOG_RELEASE_INFO( "CombinedUpdateRenderController::Stop\n" );
261 void CombinedUpdateRenderController::RequestUpdate()
265 // Increment the update-request count to the maximum
266 if( mUpdateRequestCount < MAXIMUM_UPDATE_REQUESTS )
268 ++mUpdateRequestCount;
271 if( mRunning && IsUpdateRenderThreadPaused() )
273 LOG_EVENT( "Processing" );
275 RunUpdateRenderThread( CONTINUOUS, AnimationProgression::NONE, UpdateMode::NORMAL );
278 ConditionalWait::ScopedLock updateLock( mUpdateRenderThreadWaitCondition );
279 mPendingRequestUpdate = TRUE;
282 void CombinedUpdateRenderController::RequestUpdateOnce( UpdateMode updateMode )
284 // Increment the update-request count to the maximum
285 if( mUpdateRequestCount < MAXIMUM_UPDATE_REQUESTS )
287 ++mUpdateRequestCount;
290 if( IsUpdateRenderThreadPaused() )
294 // Run Update/Render once
295 RunUpdateRenderThread( ONCE, AnimationProgression::NONE, updateMode );
299 void CombinedUpdateRenderController::ReplaceSurface( Dali::RenderSurfaceInterface* newSurface )
303 if( mUpdateRenderThread )
305 // Set the ThreadSyncronizationInterface on the new surface
306 newSurface->SetThreadSynchronization( *this );
308 LOG_EVENT( "Starting to replace the surface, event-thread blocked" );
310 // Start replacing the surface.
312 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
313 mPostRendering = FALSE; // Clear the post-rendering flag as Update/Render thread will replace the surface now
314 mNewSurface = newSurface;
315 mUpdateRenderThreadWaitCondition.Notify( lock );
318 // Wait until the surface has been replaced
319 sem_wait( &mEventThreadSemaphore );
321 LOG_EVENT( "Surface replaced, event-thread continuing" );
325 void CombinedUpdateRenderController::WaitForGraphicsInitialization()
329 if( mUpdateRenderThread )
331 LOG_EVENT( "Waiting for graphics initialisation, event-thread blocked" );
333 // Wait until the graphics has been initialised
334 sem_wait( &mGraphicsInitializeSemaphore );
336 LOG_EVENT( "graphics initialised, event-thread continuing" );
340 void CombinedUpdateRenderController::ResizeSurface()
344 LOG_EVENT( "Resize the surface" );
347 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
348 mPostRendering = FALSE; // Clear the post-rendering flag as Update/Render thread will resize the surface now
349 mSurfaceResized = TRUE;
350 mUpdateRenderThreadWaitCondition.Notify( lock );
354 void CombinedUpdateRenderController::SetRenderRefreshRate( unsigned int numberOfFramesPerRender )
356 // Not protected by lock, but written to rarely so not worth adding a lock when reading
357 mDefaultFrameDelta = numberOfFramesPerRender * DEFAULT_FRAME_DURATION_IN_SECONDS;
358 mDefaultFrameDurationMilliseconds = uint64_t( numberOfFramesPerRender ) * DEFAULT_FRAME_DURATION_IN_MILLISECONDS;
359 mDefaultFrameDurationNanoseconds = uint64_t( numberOfFramesPerRender ) * DEFAULT_FRAME_DURATION_IN_NANOSECONDS;
360 mDefaultHalfFrameNanoseconds = mDefaultFrameDurationNanoseconds / 2u;
362 LOG_EVENT( "mDefaultFrameDelta(%.6f), mDefaultFrameDurationMilliseconds(%lld), mDefaultFrameDurationNanoseconds(%lld)", mDefaultFrameDelta, mDefaultFrameDurationMilliseconds, mDefaultFrameDurationNanoseconds );
365 void CombinedUpdateRenderController::SetPreRenderCallback( CallbackBase* callback )
368 LOG_EVENT( "Set PreRender Callback" );
370 ConditionalWait::ScopedLock updateLock( mUpdateRenderThreadWaitCondition );
371 if( mPreRenderCallback )
373 delete mPreRenderCallback;
375 mPreRenderCallback = callback;
378 void CombinedUpdateRenderController::AddSurface( Dali::RenderSurfaceInterface* surface )
381 LOG_EVENT( "Surface is added" );
382 if( mUpdateRenderThread )
384 // Set the ThreadSyncronizationInterface on the added surface
385 surface->SetThreadSynchronization( *this );
389 ///////////////////////////////////////////////////////////////////////////////////////////////////
391 ///////////////////////////////////////////////////////////////////////////////////////////////////
393 void CombinedUpdateRenderController::RunUpdateRenderThread( int numberOfCycles, AnimationProgression animationProgression, UpdateMode updateMode )
395 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
396 mUpdateRenderRunCount = numberOfCycles;
397 mUpdateRenderThreadCanSleep = FALSE;
398 mUseElapsedTimeAfterWait = ( animationProgression == AnimationProgression::USE_ELAPSED_TIME );
399 mUploadWithoutRendering = ( updateMode == UpdateMode::SKIP_RENDER );
400 LOG_COUNTER_EVENT( "mUpdateRenderRunCount: %d, mUseElapsedTimeAfterWait: %d", mUpdateRenderRunCount, mUseElapsedTimeAfterWait );
401 mUpdateRenderThreadWaitCondition.Notify( lock );
404 void CombinedUpdateRenderController::PauseUpdateRenderThread()
406 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
407 mUpdateRenderRunCount = 0;
410 void CombinedUpdateRenderController::StopUpdateRenderThread()
412 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
413 mDestroyUpdateRenderThread = TRUE;
414 mUpdateRenderThreadWaitCondition.Notify( lock );
417 bool CombinedUpdateRenderController::IsUpdateRenderThreadPaused()
419 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
420 return ( mUpdateRenderRunCount != CONTINUOUS ) || // Report paused if NOT continuously running
421 mUpdateRenderThreadCanSleep; // Report paused if sleeping
424 void CombinedUpdateRenderController::ProcessSleepRequest()
428 // Decrement Update request count
429 if( mUpdateRequestCount > 0 )
431 --mUpdateRequestCount;
434 // Can sleep if our update-request count is 0
435 // Update/Render thread can choose to carry on updating if it determines more update/renders are required
436 if( mUpdateRequestCount == 0 )
438 LOG_EVENT( "Going to sleep" );
440 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
441 mUpdateRenderThreadCanSleep = TRUE;
445 ///////////////////////////////////////////////////////////////////////////////////////////////////
446 // UPDATE/RENDER THREAD
447 ///////////////////////////////////////////////////////////////////////////////////////////////////
449 void CombinedUpdateRenderController::UpdateRenderThread()
451 SetThreadName("RenderThread\0");
453 // Install a function for logging
454 mEnvironmentOptions.InstallLogFunction();
456 // Install a function for tracing
457 mEnvironmentOptions.InstallTraceFunction();
459 LOG_UPDATE_RENDER( "THREAD CREATED" );
461 // Initialize EGL & OpenGL
462 Dali::DisplayConnection& displayConnection = mAdaptorInterfaces.GetDisplayConnectionInterface();
463 displayConnection.Initialize();
465 // EGL has been initialised at this point
466 NotifyGraphicsInitialised();
468 RenderSurfaceInterface* currentSurface = nullptr;
470 GraphicsInterface& graphics = mAdaptorInterfaces.GetGraphicsInterface();
471 EglGraphics* eglGraphics = static_cast<EglGraphics *>(&graphics);
473 // This will only be created once
474 EglInterface* eglInterface = &eglGraphics->GetEglInterface();
476 Internal::Adaptor::EglImplementation& eglImpl = static_cast<Internal::Adaptor::EglImplementation&>( *eglInterface );
478 // Try to use OpenGL es 3.0
479 // ChooseConfig returns false here when the device only support gles 2.0.
480 // Because eglChooseConfig with gles 3.0 setting fails when the device only support gles 2.0 and Our default setting is gles 3.0.
481 if( !eglImpl.ChooseConfig( true, COLOR_DEPTH_32 ) )
483 // Retry to use OpenGL es 2.0
484 eglGraphics->SetGlesVersion( 20 );
485 eglImpl.ChooseConfig( true, COLOR_DEPTH_32 );
488 // Check whether surfaceless context is supported
489 bool isSurfacelessContextSupported = eglImpl.IsSurfacelessContextSupported();
490 eglGraphics->SetIsSurfacelessContextSupported( isSurfacelessContextSupported );
492 if ( isSurfacelessContextSupported )
494 // Create a surfaceless OpenGL context for shared resources
495 eglImpl.CreateContext();
496 eglImpl.MakeContextCurrent( EGL_NO_SURFACE, eglImpl.GetContext() );
500 currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
503 currentSurface->InitializeGraphics();
504 currentSurface->MakeContextCurrent();
508 eglGraphics->GetGlesInterface().ContextCreated();
510 // Tell core it has a context
511 mCore.ContextCreated();
513 NotifyThreadInitialised();
516 uint64_t lastFrameTime;
517 TimeService::GetNanoseconds( lastFrameTime );
519 LOG_UPDATE_RENDER( "THREAD INITIALISED" );
521 bool useElapsedTime = true;
522 bool updateRequired = true;
523 uint64_t timeToSleepUntil = 0;
524 int extraFramesDropped = 0;
526 const unsigned int renderToFboInterval = mEnvironmentOptions.GetRenderToFboInterval();
527 const bool renderToFboEnabled = 0u != renderToFboInterval;
528 unsigned int frameCount = 0u;
530 while( UpdateRenderReady( useElapsedTime, updateRequired, timeToSleepUntil ) )
532 LOG_UPDATE_RENDER_TRACE;
534 // Performance statistics are logged upon a VSYNC tick so use this point for a VSync marker
535 AddPerformanceMarker( PerformanceInterface::VSYNC );
537 uint64_t currentFrameStartTime = 0;
538 TimeService::GetNanoseconds( currentFrameStartTime );
540 const uint64_t timeSinceLastFrame = currentFrameStartTime - lastFrameTime;
542 // Optional FPS Tracking when continuously rendering
543 if( useElapsedTime && mFpsTracker.Enabled() )
545 float absoluteTimeSinceLastRender = timeSinceLastFrame * NANOSECONDS_TO_SECOND;
546 mFpsTracker.Track( absoluteTimeSinceLastRender );
549 lastFrameTime = currentFrameStartTime; // Store frame start time
551 //////////////////////////////
553 //////////////////////////////
555 Dali::RenderSurfaceInterface* newSurface = ShouldSurfaceBeReplaced();
556 if( DALI_UNLIKELY( newSurface ) )
558 LOG_UPDATE_RENDER_TRACE_FMT( "Replacing Surface" );
559 // This is designed for replacing pixmap surfaces, but should work for window as well
560 // we need to delete the surface and renderable (pixmap / window)
561 // Then create a new pixmap/window and new surface
562 // If the new surface has a different display connection, then the context will be lost
563 mAdaptorInterfaces.GetDisplayConnectionInterface().Initialize();
564 newSurface->InitializeGraphics();
565 newSurface->MakeContextCurrent();
566 // TODO: ReplaceGraphicsSurface doesn't work, InitializeGraphics()
567 // already creates new surface window, the surface and the context.
568 // We probably don't need ReplaceGraphicsSurface at all.
569 // newSurface->ReplaceGraphicsSurface();
573 const bool isRenderingToFbo = renderToFboEnabled && ( ( 0u == frameCount ) || ( 0u != frameCount % renderToFboInterval ) );
576 //////////////////////////////
578 //////////////////////////////
580 const unsigned int currentTime = currentFrameStartTime / NANOSECONDS_PER_MILLISECOND;
581 const unsigned int nextFrameTime = currentTime + mDefaultFrameDurationMilliseconds;
583 uint64_t noOfFramesSinceLastUpdate = 1;
584 float frameDelta = 0.0f;
587 // If using the elapsed time, then calculate frameDelta as a multiple of mDefaultFrameDelta
588 noOfFramesSinceLastUpdate += extraFramesDropped;
590 frameDelta = mDefaultFrameDelta * noOfFramesSinceLastUpdate;
592 LOG_UPDATE_RENDER( "timeSinceLastFrame(%llu) noOfFramesSinceLastUpdate(%u) frameDelta(%.6f)", timeSinceLastFrame, noOfFramesSinceLastUpdate, frameDelta );
594 Integration::UpdateStatus updateStatus;
596 AddPerformanceMarker( PerformanceInterface::UPDATE_START );
597 mCore.Update( frameDelta,
603 AddPerformanceMarker( PerformanceInterface::UPDATE_END );
605 unsigned int keepUpdatingStatus = updateStatus.KeepUpdating();
607 // Tell the event-thread to wake up (if asleep) and send a notification event to Core if required
608 if( updateStatus.NeedsNotification() )
610 mNotificationTrigger.Trigger();
611 LOG_UPDATE_RENDER( "Notification Triggered" );
615 bool surfaceResized = false;
616 bool shouldSurfaceBeResized = ShouldSurfaceBeResized();
617 if( DALI_UNLIKELY( shouldSurfaceBeResized ) )
619 if( updateStatus.SurfaceRectChanged() )
621 LOG_UPDATE_RENDER_TRACE_FMT( "Resizing Surface" );
623 surfaceResized = true;
627 // Optional logging of update/render status
628 mUpdateStatusLogger.Log( keepUpdatingStatus );
630 //////////////////////////////
632 //////////////////////////////
634 mAdaptorInterfaces.GetDisplayConnectionInterface().ConsumeEvents();
636 if( mPreRenderCallback != NULL )
638 bool keepCallback = CallbackBase::ExecuteReturn<bool>(*mPreRenderCallback);
641 delete mPreRenderCallback;
642 mPreRenderCallback = NULL;
646 if( eglImpl.IsSurfacelessContextSupported() )
648 // Make the shared surfaceless context as current before rendering
649 eglImpl.MakeContextCurrent( EGL_NO_SURFACE, eglImpl.GetContext() );
652 if( mFirstFrameAfterResume )
654 // mFirstFrameAfterResume is set to true when the thread is resumed
655 // Let eglImplementation know the first frame after thread initialized or resumed.
656 eglImpl.SetFirstFrameAfterResume();
657 mFirstFrameAfterResume = FALSE;
660 Integration::RenderStatus renderStatus;
662 AddPerformanceMarker( PerformanceInterface::RENDER_START );
664 // Upload shared resources
665 mCore.PreRender( renderStatus, mForceClear, mUploadWithoutRendering );
667 if ( !mUploadWithoutRendering )
669 // Go through each window
670 WindowContainer windows;
671 mAdaptorInterfaces.GetWindowContainerInterface( windows );
673 for ( auto&& iter = windows.begin(); iter != windows.end(); ++iter )
677 Dali::Integration::Scene scene = (*iter)->GetScene();
679 (*iter)->GetSurface()->InitializeGraphics();
681 // Render off-screen frame buffers first if any
682 mCore.RenderScene( scene, true );
684 // Switch to the EGL context of the surface
685 (*iter)->GetSurface()->PreRender( surfaceResized ); // Switch GL context
687 // Render the surface
688 mCore.RenderScene( scene, false );
690 (*iter)->GetSurface()->PostRender( false, false, surfaceResized ); // Swap Buffer
695 mCore.PostRender( mUploadWithoutRendering );
698 AddPerformanceMarker( PerformanceInterface::RENDER_END );
702 // Trigger event thread to request Update/Render thread to sleep if update not required
703 if( ( Integration::KeepUpdating::NOT_REQUESTED == keepUpdatingStatus ) && !renderStatus.NeedsUpdate() )
705 mSleepTrigger->Trigger();
706 updateRequired = false;
707 LOG_UPDATE_RENDER( "Sleep Triggered" );
711 updateRequired = true;
714 //////////////////////////////
716 //////////////////////////////
718 extraFramesDropped = 0;
720 if (timeToSleepUntil == 0)
722 // If this is the first frame after the thread is initialized or resumed, we
723 // use the actual time the current frame starts from to calculate the time to
724 // sleep until the next frame.
725 timeToSleepUntil = currentFrameStartTime + mDefaultFrameDurationNanoseconds;
729 // Otherwise, always use the sleep-until time calculated in the last frame to
730 // calculate the time to sleep until the next frame. In this way, if there is
731 // any time gap between the current frame and the next frame, or if update or
732 // rendering in the current frame takes too much time so that the specified
733 // sleep-until time has already passed, it will try to keep the frames syncing
734 // by shortening the duration of the next frame.
735 timeToSleepUntil += mDefaultFrameDurationNanoseconds;
737 // Check the current time at the end of the frame
738 uint64_t currentFrameEndTime = 0;
739 TimeService::GetNanoseconds( currentFrameEndTime );
740 while ( currentFrameEndTime > timeToSleepUntil + mDefaultFrameDurationNanoseconds )
742 // We are more than one frame behind already, so just drop the next frames
743 // until the sleep-until time is later than the current time so that we can
745 timeToSleepUntil += mDefaultFrameDurationNanoseconds;
746 extraFramesDropped++;
750 // Render to FBO is intended to measure fps above 60 so sleep is not wanted.
751 if( 0u == renderToFboInterval )
753 // Sleep until at least the the default frame duration has elapsed. This will return immediately if the specified end-time has already passed.
754 TimeService::SleepUntil( timeToSleepUntil );
758 // Inform core of context destruction & shutdown EGL
759 mCore.ContextDestroyed();
760 currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
763 currentSurface->DestroySurface();
764 currentSurface = nullptr;
767 LOG_UPDATE_RENDER( "THREAD DESTROYED" );
769 // Uninstall the logging function
770 mEnvironmentOptions.UnInstallLogFunction();
773 bool CombinedUpdateRenderController::UpdateRenderReady( bool& useElapsedTime, bool updateRequired, uint64_t& timeToSleepUntil )
775 useElapsedTime = true;
777 ConditionalWait::ScopedLock updateLock( mUpdateRenderThreadWaitCondition );
778 while( ( ! mUpdateRenderRunCount || // Should try to wait if event-thread has paused the Update/Render thread
779 ( mUpdateRenderThreadCanSleep && ! updateRequired && ! mPendingRequestUpdate ) ) && // Ensure we wait if we're supposed to be sleeping AND do not require another update
780 ! mDestroyUpdateRenderThread && // Ensure we don't wait if the update-render-thread is supposed to be destroyed
781 ! mNewSurface && // Ensure we don't wait if we need to replace the surface
782 ! mSurfaceResized ) // Ensure we don't wait if we need to resize the surface
784 LOG_UPDATE_RENDER( "WAIT: mUpdateRenderRunCount: %d", mUpdateRenderRunCount );
785 LOG_UPDATE_RENDER( " mUpdateRenderThreadCanSleep: %d, updateRequired: %d, mPendingRequestUpdate: %d", mUpdateRenderThreadCanSleep, updateRequired, mPendingRequestUpdate );
786 LOG_UPDATE_RENDER( " mDestroyUpdateRenderThread: %d", mDestroyUpdateRenderThread );
787 LOG_UPDATE_RENDER( " mNewSurface: %d", mNewSurface );
788 LOG_UPDATE_RENDER( " mSurfaceResized: %d", mSurfaceResized );
790 // Reset the time when the thread is waiting, so the sleep-until time for
791 // the first frame after resuming should be based on the actual start time
792 // of the first frame.
793 timeToSleepUntil = 0;
795 mUpdateRenderThreadWaitCondition.Wait( updateLock );
797 if( ! mUseElapsedTimeAfterWait )
799 useElapsedTime = false;
803 LOG_COUNTER_UPDATE_RENDER( "mUpdateRenderRunCount: %d", mUpdateRenderRunCount );
804 LOG_COUNTER_UPDATE_RENDER( "mUpdateRenderThreadCanSleep: %d, updateRequired: %d, mPendingRequestUpdate: %d", mUpdateRenderThreadCanSleep, updateRequired, mPendingRequestUpdate );
805 LOG_COUNTER_UPDATE_RENDER( "mDestroyUpdateRenderThread: %d", mDestroyUpdateRenderThread );
806 LOG_COUNTER_UPDATE_RENDER( "mNewSurface: %d", mNewSurface );
807 LOG_COUNTER_UPDATE_RENDER( "mSurfaceResized: %d", mSurfaceResized );
809 mUseElapsedTimeAfterWait = FALSE;
810 mUpdateRenderThreadCanSleep = FALSE;
811 mPendingRequestUpdate = FALSE;
813 // If we've been asked to run Update/Render cycles a finite number of times then decrement so we wait after the
814 // requested number of cycles
815 if( mUpdateRenderRunCount > 0 )
817 --mUpdateRenderRunCount;
820 // Keep the update-render thread alive if this thread is NOT to be destroyed
821 return ! mDestroyUpdateRenderThread;
824 Dali::RenderSurfaceInterface* CombinedUpdateRenderController::ShouldSurfaceBeReplaced()
826 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
828 Dali::RenderSurfaceInterface* newSurface = mNewSurface;
834 void CombinedUpdateRenderController::SurfaceReplaced()
836 // Just increment the semaphore
837 sem_post( &mEventThreadSemaphore );
840 bool CombinedUpdateRenderController::ShouldSurfaceBeResized()
842 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
843 return mSurfaceResized;
846 void CombinedUpdateRenderController::SurfaceResized()
848 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
849 mSurfaceResized = FALSE;
852 ///////////////////////////////////////////////////////////////////////////////////////////////////
854 ///////////////////////////////////////////////////////////////////////////////////////////////////
856 void CombinedUpdateRenderController::NotifyThreadInitialised()
858 // Just increment the semaphore
859 sem_post( &mEventThreadSemaphore );
862 void CombinedUpdateRenderController::NotifyGraphicsInitialised()
864 sem_post( &mGraphicsInitializeSemaphore );
867 void CombinedUpdateRenderController::AddPerformanceMarker( PerformanceInterface::MarkerType type )
869 if( mPerformanceInterface )
871 mPerformanceInterface->AddMarker( type );
875 /////////////////////////////////////////////////////////////////////////////////////////////////
876 // POST RENDERING: EVENT THREAD
877 /////////////////////////////////////////////////////////////////////////////////////////////////
879 void CombinedUpdateRenderController::PostRenderComplete()
881 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
882 mPostRendering = FALSE;
883 mUpdateRenderThreadWaitCondition.Notify( lock );
886 ///////////////////////////////////////////////////////////////////////////////////////////////////
887 // POST RENDERING: RENDER THREAD
888 ///////////////////////////////////////////////////////////////////////////////////////////////////
890 void CombinedUpdateRenderController::PostRenderStarted()
892 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
893 mPostRendering = TRUE;
896 void CombinedUpdateRenderController::PostRenderWaitForCompletion()
898 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
899 while( mPostRendering &&
900 ! mNewSurface && // We should NOT wait if we're replacing the surface
901 ! mDestroyUpdateRenderThread )
903 mUpdateRenderThreadWaitCondition.Wait( lock );
907 } // namespace Adaptor
909 } // namespace Internal