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>
48 const unsigned int CREATED_THREAD_COUNT = 1u;
50 const int CONTINUOUS = -1;
53 const unsigned int TRUE = 1u;
54 const unsigned int FALSE = 0u;
56 const unsigned int MILLISECONDS_PER_SECOND( 1e+3 );
57 const float NANOSECONDS_TO_SECOND( 1e-9f );
58 const unsigned int NANOSECONDS_PER_SECOND( 1e+9 );
59 const unsigned int NANOSECONDS_PER_MILLISECOND( 1e+6 );
61 // The following values will get calculated at compile time
62 const float DEFAULT_FRAME_DURATION_IN_SECONDS( 1.0f / 60.0f );
63 const uint64_t DEFAULT_FRAME_DURATION_IN_MILLISECONDS( DEFAULT_FRAME_DURATION_IN_SECONDS * MILLISECONDS_PER_SECOND );
64 const uint64_t DEFAULT_FRAME_DURATION_IN_NANOSECONDS( DEFAULT_FRAME_DURATION_IN_SECONDS * NANOSECONDS_PER_SECOND );
67 * 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
68 * there is a danger that, on the event-thread we could have:
69 * 1) An update-request where we do nothing as Update/Render thread still running.
70 * 2) Quickly followed by a sleep-request being handled where we pause the Update/Render Thread (even though we have an update to process).
72 * 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:
73 * 1) MAIN THREAD: Update Request: COUNTER = 1
74 * 2) UPDATE/RENDER THREAD: Do Update/Render, then no Updates required -> Sleep Trigger
75 * 3) MAIN THREAD: Update Request: COUNTER = 2
76 * 4) MAIN THREAD: Sleep Request: COUNTER = 1 -> We do not sleep just yet
78 * Also ensures we preserve battery life by only doing ONE update when the above use case is not triggered.
79 * 1) MAIN THREAD: Update Request: COUNTER = 1
80 * 2) UPDATE/RENDER THREAD: Do Update/Render, then no Updates required -> Sleep Trigger
81 * 3) MAIN THREAD: Sleep Request: COUNTER = 0 -> Go to sleep
83 const unsigned int MAXIMUM_UPDATE_REQUESTS = 2;
84 } // unnamed namespace
86 ///////////////////////////////////////////////////////////////////////////////////////////////////
88 ///////////////////////////////////////////////////////////////////////////////////////////////////
90 CombinedUpdateRenderController::CombinedUpdateRenderController( AdaptorInternalServices& adaptorInterfaces, const EnvironmentOptions& environmentOptions )
91 : mFpsTracker( environmentOptions ),
92 mUpdateStatusLogger( environmentOptions ),
93 mEventThreadSemaphore(),
94 mGraphicsInitializeSemaphore(),
95 mUpdateRenderThreadWaitCondition(),
96 mAdaptorInterfaces( adaptorInterfaces ),
97 mPerformanceInterface( adaptorInterfaces.GetPerformanceInterface() ),
98 mCore( adaptorInterfaces.GetCore() ),
99 mEnvironmentOptions( environmentOptions ),
100 mNotificationTrigger( adaptorInterfaces.GetProcessCoreEventsTrigger() ),
101 mSleepTrigger( NULL ),
102 mPreRenderCallback( NULL ),
103 mUpdateRenderThread( NULL ),
104 mDefaultFrameDelta( 0.0f ),
105 mDefaultFrameDurationMilliseconds( 0u ),
106 mDefaultFrameDurationNanoseconds( 0u ),
107 mDefaultHalfFrameNanoseconds( 0u ),
108 mUpdateRequestCount( 0u ),
110 mUpdateRenderRunCount( 0 ),
111 mDestroyUpdateRenderThread( FALSE ),
112 mUpdateRenderThreadCanSleep( FALSE ),
113 mPendingRequestUpdate( FALSE ),
114 mUseElapsedTimeAfterWait( FALSE ),
116 mDeletedSurface( nullptr ),
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 Integration::RenderSurface* 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 Integration::RenderSurface* 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::DeleteSurface( Dali::RenderSurfaceInterface* surface )
329 if( mUpdateRenderThread )
331 LOG_EVENT( "Starting to delete the surface, event-thread blocked" );
333 // Start replacing the surface.
335 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
336 mPostRendering = FALSE; // Clear the post-rendering flag as Update/Render thread will delete the surface now
337 mDeletedSurface = surface;
338 mUpdateRenderThreadWaitCondition.Notify( lock );
341 // Wait until the surface has been deleted
342 sem_wait( &mEventThreadSemaphore );
344 LOG_EVENT( "Surface deleted, event-thread continuing" );
348 void CombinedUpdateRenderController::WaitForGraphicsInitialization()
352 if( mUpdateRenderThread )
354 LOG_EVENT( "Waiting for graphics initialisation, event-thread blocked" );
356 // Wait until the graphics has been initialised
357 sem_wait( &mGraphicsInitializeSemaphore );
359 LOG_EVENT( "graphics initialised, event-thread continuing" );
363 void CombinedUpdateRenderController::ResizeSurface()
367 LOG_EVENT( "Resize the surface" );
370 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
371 mPostRendering = FALSE; // Clear the post-rendering flag as Update/Render thread will resize the surface now
372 mSurfaceResized = TRUE;
373 mUpdateRenderThreadWaitCondition.Notify( lock );
377 void CombinedUpdateRenderController::SetRenderRefreshRate( unsigned int numberOfFramesPerRender )
379 // Not protected by lock, but written to rarely so not worth adding a lock when reading
380 mDefaultFrameDelta = numberOfFramesPerRender * DEFAULT_FRAME_DURATION_IN_SECONDS;
381 mDefaultFrameDurationMilliseconds = uint64_t( numberOfFramesPerRender ) * DEFAULT_FRAME_DURATION_IN_MILLISECONDS;
382 mDefaultFrameDurationNanoseconds = uint64_t( numberOfFramesPerRender ) * DEFAULT_FRAME_DURATION_IN_NANOSECONDS;
383 mDefaultHalfFrameNanoseconds = mDefaultFrameDurationNanoseconds / 2u;
385 LOG_EVENT( "mDefaultFrameDelta(%.6f), mDefaultFrameDurationMilliseconds(%lld), mDefaultFrameDurationNanoseconds(%lld)", mDefaultFrameDelta, mDefaultFrameDurationMilliseconds, mDefaultFrameDurationNanoseconds );
388 void CombinedUpdateRenderController::SetPreRenderCallback( CallbackBase* callback )
391 LOG_EVENT( "Set PreRender Callback" );
393 ConditionalWait::ScopedLock updateLock( mUpdateRenderThreadWaitCondition );
394 if( mPreRenderCallback )
396 delete mPreRenderCallback;
398 mPreRenderCallback = callback;
401 void CombinedUpdateRenderController::AddSurface( Dali::RenderSurfaceInterface* surface )
404 LOG_EVENT( "Surface is added" );
405 if( mUpdateRenderThread )
407 // Set the ThreadSyncronizationInterface on the added surface
408 surface->SetThreadSynchronization( *this );
412 ///////////////////////////////////////////////////////////////////////////////////////////////////
414 ///////////////////////////////////////////////////////////////////////////////////////////////////
416 void CombinedUpdateRenderController::RunUpdateRenderThread( int numberOfCycles, AnimationProgression animationProgression, UpdateMode updateMode )
418 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
419 mUpdateRenderRunCount = numberOfCycles;
420 mUpdateRenderThreadCanSleep = FALSE;
421 mUseElapsedTimeAfterWait = ( animationProgression == AnimationProgression::USE_ELAPSED_TIME );
422 mUploadWithoutRendering = ( updateMode == UpdateMode::SKIP_RENDER );
423 LOG_COUNTER_EVENT( "mUpdateRenderRunCount: %d, mUseElapsedTimeAfterWait: %d", mUpdateRenderRunCount, mUseElapsedTimeAfterWait );
424 mUpdateRenderThreadWaitCondition.Notify( lock );
427 void CombinedUpdateRenderController::PauseUpdateRenderThread()
429 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
430 mUpdateRenderRunCount = 0;
433 void CombinedUpdateRenderController::StopUpdateRenderThread()
435 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
436 mDestroyUpdateRenderThread = TRUE;
437 mUpdateRenderThreadWaitCondition.Notify( lock );
440 bool CombinedUpdateRenderController::IsUpdateRenderThreadPaused()
442 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
443 return ( mUpdateRenderRunCount != CONTINUOUS ) || // Report paused if NOT continuously running
444 mUpdateRenderThreadCanSleep; // Report paused if sleeping
447 void CombinedUpdateRenderController::ProcessSleepRequest()
451 // Decrement Update request count
452 if( mUpdateRequestCount > 0 )
454 --mUpdateRequestCount;
457 // Can sleep if our update-request count is 0
458 // Update/Render thread can choose to carry on updating if it determines more update/renders are required
459 if( mUpdateRequestCount == 0 )
461 LOG_EVENT( "Going to sleep" );
463 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
464 mUpdateRenderThreadCanSleep = TRUE;
468 ///////////////////////////////////////////////////////////////////////////////////////////////////
469 // UPDATE/RENDER THREAD
470 ///////////////////////////////////////////////////////////////////////////////////////////////////
472 void CombinedUpdateRenderController::UpdateRenderThread()
474 SetThreadName("RenderThread\0");
476 // Install a function for logging
477 mEnvironmentOptions.InstallLogFunction();
479 // Install a function for tracing
480 mEnvironmentOptions.InstallTraceFunction();
482 LOG_UPDATE_RENDER( "THREAD CREATED" );
484 // Initialize EGL & OpenGL
485 Dali::DisplayConnection& displayConnection = mAdaptorInterfaces.GetDisplayConnectionInterface();
486 displayConnection.Initialize();
488 // EGL has been initialised at this point
489 NotifyGraphicsInitialised();
491 RenderSurfaceInterface* currentSurface = nullptr;
493 GraphicsInterface& graphics = mAdaptorInterfaces.GetGraphicsInterface();
494 EglGraphics* eglGraphics = static_cast<EglGraphics *>(&graphics);
496 // This will only be created once
497 EglInterface* eglInterface = &eglGraphics->GetEglInterface();
499 Internal::Adaptor::EglImplementation& eglImpl = static_cast<Internal::Adaptor::EglImplementation&>( *eglInterface );
501 // Try to use OpenGL es 3.0
502 // ChooseConfig returns false here when the device only support gles 2.0.
503 // Because eglChooseConfig with gles 3.0 setting fails when the device only support gles 2.0 and Our default setting is gles 3.0.
504 if( !eglImpl.ChooseConfig( true, COLOR_DEPTH_32 ) )
506 // Retry to use OpenGL es 2.0
507 eglGraphics->SetGlesVersion( 20 );
508 eglImpl.ChooseConfig( true, COLOR_DEPTH_32 );
511 // Check whether surfaceless context is supported
512 bool isSurfacelessContextSupported = eglImpl.IsSurfacelessContextSupported();
513 eglGraphics->SetIsSurfacelessContextSupported( isSurfacelessContextSupported );
515 if ( isSurfacelessContextSupported )
517 // Create a surfaceless OpenGL context for shared resources
518 eglImpl.CreateContext();
519 eglImpl.MakeContextCurrent( EGL_NO_SURFACE, eglImpl.GetContext() );
523 currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
526 currentSurface->InitializeGraphics();
527 currentSurface->MakeContextCurrent();
531 eglGraphics->GetGlesInterface().ContextCreated();
533 // Tell core it has a context
534 mCore.ContextCreated();
536 NotifyThreadInitialised();
539 uint64_t lastFrameTime;
540 TimeService::GetNanoseconds( lastFrameTime );
542 LOG_UPDATE_RENDER( "THREAD INITIALISED" );
544 bool useElapsedTime = true;
545 bool updateRequired = true;
546 uint64_t timeToSleepUntil = 0;
547 int extraFramesDropped = 0;
549 const unsigned int renderToFboInterval = mEnvironmentOptions.GetRenderToFboInterval();
550 const bool renderToFboEnabled = 0u != renderToFboInterval;
551 unsigned int frameCount = 0u;
553 while( UpdateRenderReady( useElapsedTime, updateRequired, timeToSleepUntil ) )
555 LOG_UPDATE_RENDER_TRACE;
557 // Performance statistics are logged upon a VSYNC tick so use this point for a VSync marker
558 AddPerformanceMarker( PerformanceInterface::VSYNC );
560 uint64_t currentFrameStartTime = 0;
561 TimeService::GetNanoseconds( currentFrameStartTime );
563 const uint64_t timeSinceLastFrame = currentFrameStartTime - lastFrameTime;
565 // Optional FPS Tracking when continuously rendering
566 if( useElapsedTime && mFpsTracker.Enabled() )
568 float absoluteTimeSinceLastRender = timeSinceLastFrame * NANOSECONDS_TO_SECOND;
569 mFpsTracker.Track( absoluteTimeSinceLastRender );
572 lastFrameTime = currentFrameStartTime; // Store frame start time
574 //////////////////////////////
576 //////////////////////////////
578 Integration::RenderSurface* newSurface = ShouldSurfaceBeReplaced();
579 if( DALI_UNLIKELY( newSurface ) )
581 LOG_UPDATE_RENDER_TRACE_FMT( "Replacing Surface" );
582 // This is designed for replacing pixmap surfaces, but should work for window as well
583 // we need to delete the surface and renderable (pixmap / window)
584 // Then create a new pixmap/window and new surface
585 // If the new surface has a different display connection, then the context will be lost
586 mAdaptorInterfaces.GetDisplayConnectionInterface().Initialize();
587 newSurface->InitializeGraphics();
588 newSurface->MakeContextCurrent();
589 // TODO: ReplaceGraphicsSurface doesn't work, InitializeGraphics()
590 // already creates new surface window, the surface and the context.
591 // We probably don't need ReplaceGraphicsSurface at all.
592 // newSurface->ReplaceGraphicsSurface();
596 const bool isRenderingToFbo = renderToFboEnabled && ( ( 0u == frameCount ) || ( 0u != frameCount % renderToFboInterval ) );
599 //////////////////////////////
601 //////////////////////////////
603 const unsigned int currentTime = currentFrameStartTime / NANOSECONDS_PER_MILLISECOND;
604 const unsigned int nextFrameTime = currentTime + mDefaultFrameDurationMilliseconds;
606 uint64_t noOfFramesSinceLastUpdate = 1;
607 float frameDelta = 0.0f;
610 // If using the elapsed time, then calculate frameDelta as a multiple of mDefaultFrameDelta
611 noOfFramesSinceLastUpdate += extraFramesDropped;
613 frameDelta = mDefaultFrameDelta * noOfFramesSinceLastUpdate;
615 LOG_UPDATE_RENDER( "timeSinceLastFrame(%llu) noOfFramesSinceLastUpdate(%u) frameDelta(%.6f)", timeSinceLastFrame, noOfFramesSinceLastUpdate, frameDelta );
617 Integration::UpdateStatus updateStatus;
619 AddPerformanceMarker( PerformanceInterface::UPDATE_START );
620 mCore.Update( frameDelta,
626 AddPerformanceMarker( PerformanceInterface::UPDATE_END );
628 unsigned int keepUpdatingStatus = updateStatus.KeepUpdating();
630 // Tell the event-thread to wake up (if asleep) and send a notification event to Core if required
631 if( updateStatus.NeedsNotification() )
633 mNotificationTrigger.Trigger();
634 LOG_UPDATE_RENDER( "Notification Triggered" );
638 bool shouldSurfaceBeResized = ShouldSurfaceBeResized();
639 if( DALI_UNLIKELY( shouldSurfaceBeResized ) )
641 if( updateStatus.SurfaceRectChanged() )
643 LOG_UPDATE_RENDER_TRACE_FMT( "Resizing Surface" );
648 // Optional logging of update/render status
649 mUpdateStatusLogger.Log( keepUpdatingStatus );
651 //////////////////////////////
653 //////////////////////////////
655 mAdaptorInterfaces.GetDisplayConnectionInterface().ConsumeEvents();
657 if( mPreRenderCallback != NULL )
659 bool keepCallback = CallbackBase::ExecuteReturn<bool>(*mPreRenderCallback);
662 delete mPreRenderCallback;
663 mPreRenderCallback = NULL;
667 if( eglImpl.IsSurfacelessContextSupported() )
669 // Make the shared surfaceless context as current before rendering
670 eglImpl.MakeContextCurrent( EGL_NO_SURFACE, eglImpl.GetContext() );
673 if( mFirstFrameAfterResume )
675 // mFirstFrameAfterResume is set to true when the thread is resumed
676 // Let eglImplementation know the first frame after thread initialized or resumed.
677 eglImpl.SetFirstFrameAfterResume();
678 mFirstFrameAfterResume = FALSE;
681 Integration::RenderStatus renderStatus;
683 AddPerformanceMarker( PerformanceInterface::RENDER_START );
684 mCore.Render( renderStatus, mForceClear, mUploadWithoutRendering );
686 //////////////////////////////
688 //////////////////////////////
690 Integration::RenderSurface* deletedSurface = ShouldSurfaceBeDeleted();
691 if( DALI_UNLIKELY( deletedSurface ) )
693 LOG_UPDATE_RENDER_TRACE_FMT( "Deleting Surface" );
695 mCore.SurfaceDeleted( deletedSurface );
700 AddPerformanceMarker( PerformanceInterface::RENDER_END );
704 // Trigger event thread to request Update/Render thread to sleep if update not required
705 if( ( Integration::KeepUpdating::NOT_REQUESTED == keepUpdatingStatus ) && !renderStatus.NeedsUpdate() )
707 mSleepTrigger->Trigger();
708 updateRequired = false;
709 LOG_UPDATE_RENDER( "Sleep Triggered" );
713 updateRequired = true;
716 //////////////////////////////
718 //////////////////////////////
720 extraFramesDropped = 0;
722 if (timeToSleepUntil == 0)
724 // If this is the first frame after the thread is initialized or resumed, we
725 // use the actual time the current frame starts from to calculate the time to
726 // sleep until the next frame.
727 timeToSleepUntil = currentFrameStartTime + mDefaultFrameDurationNanoseconds;
731 // Otherwise, always use the sleep-until time calculated in the last frame to
732 // calculate the time to sleep until the next frame. In this way, if there is
733 // any time gap between the current frame and the next frame, or if update or
734 // rendering in the current frame takes too much time so that the specified
735 // sleep-until time has already passed, it will try to keep the frames syncing
736 // by shortening the duration of the next frame.
737 timeToSleepUntil += mDefaultFrameDurationNanoseconds;
739 // Check the current time at the end of the frame
740 uint64_t currentFrameEndTime = 0;
741 TimeService::GetNanoseconds( currentFrameEndTime );
742 while ( currentFrameEndTime > timeToSleepUntil + mDefaultFrameDurationNanoseconds )
744 // We are more than one frame behind already, so just drop the next frames
745 // until the sleep-until time is later than the current time so that we can
747 timeToSleepUntil += mDefaultFrameDurationNanoseconds;
748 extraFramesDropped++;
752 // Render to FBO is intended to measure fps above 60 so sleep is not wanted.
753 if( 0u == renderToFboInterval )
755 // Sleep until at least the the default frame duration has elapsed. This will return immediately if the specified end-time has already passed.
756 TimeService::SleepUntil( timeToSleepUntil );
760 // Inform core of context destruction & shutdown EGL
761 mCore.ContextDestroyed();
762 currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
765 currentSurface->DestroySurface();
766 currentSurface = nullptr;
769 LOG_UPDATE_RENDER( "THREAD DESTROYED" );
771 // Uninstall the logging function
772 mEnvironmentOptions.UnInstallLogFunction();
775 bool CombinedUpdateRenderController::UpdateRenderReady( bool& useElapsedTime, bool updateRequired, uint64_t& timeToSleepUntil )
777 useElapsedTime = true;
779 ConditionalWait::ScopedLock updateLock( mUpdateRenderThreadWaitCondition );
780 while( ( ! mUpdateRenderRunCount || // Should try to wait if event-thread has paused the Update/Render thread
781 ( mUpdateRenderThreadCanSleep && ! updateRequired && ! mPendingRequestUpdate ) ) && // Ensure we wait if we're supposed to be sleeping AND do not require another update
782 ! mDestroyUpdateRenderThread && // Ensure we don't wait if the update-render-thread is supposed to be destroyed
783 ! mNewSurface && // Ensure we don't wait if we need to replace the surface
784 ! mDeletedSurface && // Ensure we don't wait if we need to delete the surface
785 ! mSurfaceResized ) // Ensure we don't wait if we need to resize the surface
787 LOG_UPDATE_RENDER( "WAIT: mUpdateRenderRunCount: %d", mUpdateRenderRunCount );
788 LOG_UPDATE_RENDER( " mUpdateRenderThreadCanSleep: %d, updateRequired: %d, mPendingRequestUpdate: %d", mUpdateRenderThreadCanSleep, updateRequired, mPendingRequestUpdate );
789 LOG_UPDATE_RENDER( " mDestroyUpdateRenderThread: %d", mDestroyUpdateRenderThread );
790 LOG_UPDATE_RENDER( " mNewSurface: %d", mNewSurface );
791 LOG_UPDATE_RENDER( " mDeletedSurface: %d", mDeletedSurface );
792 LOG_UPDATE_RENDER( " mSurfaceResized: %d", mSurfaceResized );
794 // Reset the time when the thread is waiting, so the sleep-until time for
795 // the first frame after resuming should be based on the actual start time
796 // of the first frame.
797 timeToSleepUntil = 0;
799 mUpdateRenderThreadWaitCondition.Wait( updateLock );
801 if( ! mUseElapsedTimeAfterWait )
803 useElapsedTime = false;
807 LOG_COUNTER_UPDATE_RENDER( "mUpdateRenderRunCount: %d", mUpdateRenderRunCount );
808 LOG_COUNTER_UPDATE_RENDER( "mUpdateRenderThreadCanSleep: %d, updateRequired: %d, mPendingRequestUpdate: %d", mUpdateRenderThreadCanSleep, updateRequired, mPendingRequestUpdate );
809 LOG_COUNTER_UPDATE_RENDER( "mDestroyUpdateRenderThread: %d", mDestroyUpdateRenderThread );
810 LOG_COUNTER_UPDATE_RENDER( "mNewSurface: %d", mNewSurface );
811 LOG_COUNTER_UPDATE_RENDER( "mDeletedSurface: %d", mDeletedSurface );
812 LOG_COUNTER_UPDATE_RENDER( "mSurfaceResized: %d", mSurfaceResized );
814 mUseElapsedTimeAfterWait = FALSE;
815 mUpdateRenderThreadCanSleep = FALSE;
816 mPendingRequestUpdate = FALSE;
818 // If we've been asked to run Update/Render cycles a finite number of times then decrement so we wait after the
819 // requested number of cycles
820 if( mUpdateRenderRunCount > 0 )
822 --mUpdateRenderRunCount;
825 // Keep the update-render thread alive if this thread is NOT to be destroyed
826 return ! mDestroyUpdateRenderThread;
829 Integration::RenderSurface* CombinedUpdateRenderController::ShouldSurfaceBeReplaced()
831 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
833 Integration::RenderSurface* newSurface = mNewSurface;
839 void CombinedUpdateRenderController::SurfaceReplaced()
841 // Just increment the semaphore
842 sem_post( &mEventThreadSemaphore );
845 Integration::RenderSurface* CombinedUpdateRenderController::ShouldSurfaceBeDeleted()
847 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
849 Integration::RenderSurface* deletedSurface = mDeletedSurface;
850 mDeletedSurface = NULL;
852 return deletedSurface;
855 void CombinedUpdateRenderController::SurfaceDeleted()
857 // Just increment the semaphore
858 sem_post( &mEventThreadSemaphore );
861 bool CombinedUpdateRenderController::ShouldSurfaceBeResized()
863 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
864 return mSurfaceResized;
867 void CombinedUpdateRenderController::SurfaceResized()
869 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
870 mSurfaceResized = FALSE;
873 ///////////////////////////////////////////////////////////////////////////////////////////////////
875 ///////////////////////////////////////////////////////////////////////////////////////////////////
877 void CombinedUpdateRenderController::NotifyThreadInitialised()
879 // Just increment the semaphore
880 sem_post( &mEventThreadSemaphore );
883 void CombinedUpdateRenderController::NotifyGraphicsInitialised()
885 sem_post( &mGraphicsInitializeSemaphore );
888 void CombinedUpdateRenderController::AddPerformanceMarker( PerformanceInterface::MarkerType type )
890 if( mPerformanceInterface )
892 mPerformanceInterface->AddMarker( type );
896 /////////////////////////////////////////////////////////////////////////////////////////////////
897 // POST RENDERING: EVENT THREAD
898 /////////////////////////////////////////////////////////////////////////////////////////////////
900 void CombinedUpdateRenderController::PostRenderComplete()
902 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
903 mPostRendering = FALSE;
904 mUpdateRenderThreadWaitCondition.Notify( lock );
907 ///////////////////////////////////////////////////////////////////////////////////////////////////
908 // POST RENDERING: RENDER THREAD
909 ///////////////////////////////////////////////////////////////////////////////////////////////////
911 void CombinedUpdateRenderController::PostRenderStarted()
913 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
914 mPostRendering = TRUE;
917 void CombinedUpdateRenderController::PostRenderWaitForCompletion()
919 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
920 while( mPostRendering &&
921 ! mNewSurface && // We should NOT wait if we're replacing the surface
922 ! mDeletedSurface && // We should NOT wait if we're deleting the surface
923 ! mDestroyUpdateRenderThread )
925 mUpdateRenderThreadWaitCondition.Wait( lock );
929 } // namespace Adaptor
931 } // namespace Internal