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 TriggerEventFactoryInterface& triggerFactory = mAdaptorInterfaces.GetTriggerEventFactoryInterface();
136 mSleepTrigger = triggerFactory.CreateTriggerEvent( MakeCallback( this, &CombinedUpdateRenderController::ProcessSleepRequest ), TriggerEventInterface::KEEP_ALIVE_AFTER_TRIGGER );
138 // Initialize to 0 so that it just waits if sem_post has not been called
139 sem_init( &mEventThreadSemaphore, 0, 0 );
140 sem_init( &mGraphicsInitializeSemaphore, 0, 0 );
143 CombinedUpdateRenderController::~CombinedUpdateRenderController()
149 delete mPreRenderCallback;
150 delete mSleepTrigger;
153 void CombinedUpdateRenderController::Initialize()
157 // Ensure Update/Render Thread not already created
158 DALI_ASSERT_ALWAYS( ! mUpdateRenderThread );
160 // Create Update/Render Thread
161 mUpdateRenderThread = new pthread_t();
162 int error = pthread_create( mUpdateRenderThread, NULL, InternalUpdateRenderThreadEntryFunc, this );
163 DALI_ASSERT_ALWAYS( !error && "Return code from pthread_create() when creating UpdateRenderThread" );
165 // The Update/Render thread will now run and initialise the graphics interface etc. and will then wait for Start to be called
166 // When this function returns, the application initialisation on the event thread should occur
169 void CombinedUpdateRenderController::Start()
173 DALI_ASSERT_ALWAYS( !mRunning && mUpdateRenderThread );
175 // Wait until all threads created in Initialise are up and running
176 for( unsigned int i = 0; i < CREATED_THREAD_COUNT; ++i )
178 sem_wait( &mEventThreadSemaphore );
181 Integration::RenderSurface* currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
184 currentSurface->StartRender();
189 LOG_EVENT( "Startup Complete, starting Update/Render Thread" );
191 RunUpdateRenderThread( CONTINUOUS, AnimationProgression::NONE, UpdateMode::NORMAL );
193 DALI_LOG_RELEASE_INFO( "CombinedUpdateRenderController::Start\n" );
196 void CombinedUpdateRenderController::Pause()
202 PauseUpdateRenderThread();
204 AddPerformanceMarker( PerformanceInterface::PAUSED );
206 DALI_LOG_RELEASE_INFO( "CombinedUpdateRenderController::Pause\n" );
209 void CombinedUpdateRenderController::Resume()
213 if( !mRunning && IsUpdateRenderThreadPaused() )
215 LOG_EVENT( "Resuming" );
217 RunUpdateRenderThread( CONTINUOUS, AnimationProgression::USE_ELAPSED_TIME, UpdateMode::NORMAL );
219 AddPerformanceMarker( PerformanceInterface::RESUME );
223 mFirstFrameAfterResume = TRUE;
225 DALI_LOG_RELEASE_INFO( "CombinedUpdateRenderController::Resume\n" );
229 DALI_LOG_RELEASE_INFO( "CombinedUpdateRenderController::Resume: Already resumed [%d, %d, %d]\n", mRunning, mUpdateRenderRunCount, mUpdateRenderThreadCanSleep );
233 void CombinedUpdateRenderController::Stop()
237 // Stop Rendering and the Update/Render Thread
238 Integration::RenderSurface* currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
241 currentSurface->StopRender();
244 StopUpdateRenderThread();
246 if( mUpdateRenderThread )
248 LOG_EVENT( "Destroying UpdateRenderThread" );
250 // wait for the thread to finish
251 pthread_join( *mUpdateRenderThread, NULL );
253 delete mUpdateRenderThread;
254 mUpdateRenderThread = NULL;
259 DALI_LOG_RELEASE_INFO( "CombinedUpdateRenderController::Stop\n" );
262 void CombinedUpdateRenderController::RequestUpdate()
266 // Increment the update-request count to the maximum
267 if( mUpdateRequestCount < MAXIMUM_UPDATE_REQUESTS )
269 ++mUpdateRequestCount;
272 if( mRunning && IsUpdateRenderThreadPaused() )
274 LOG_EVENT( "Processing" );
276 RunUpdateRenderThread( CONTINUOUS, AnimationProgression::NONE, UpdateMode::NORMAL );
279 ConditionalWait::ScopedLock updateLock( mUpdateRenderThreadWaitCondition );
280 mPendingRequestUpdate = TRUE;
283 void CombinedUpdateRenderController::RequestUpdateOnce( UpdateMode updateMode )
285 // Increment the update-request count to the maximum
286 if( mUpdateRequestCount < MAXIMUM_UPDATE_REQUESTS )
288 ++mUpdateRequestCount;
291 if( IsUpdateRenderThreadPaused() )
295 // Run Update/Render once
296 RunUpdateRenderThread( ONCE, AnimationProgression::NONE, updateMode );
300 void CombinedUpdateRenderController::ReplaceSurface( Dali::RenderSurfaceInterface* newSurface )
304 if( mUpdateRenderThread )
306 // Set the ThreadSyncronizationInterface on the new surface
307 newSurface->SetThreadSynchronization( *this );
309 LOG_EVENT( "Starting to replace the surface, event-thread blocked" );
311 // Start replacing the surface.
313 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
314 mPostRendering = FALSE; // Clear the post-rendering flag as Update/Render thread will replace the surface now
315 mNewSurface = newSurface;
316 mUpdateRenderThreadWaitCondition.Notify( lock );
319 // Wait until the surface has been replaced
320 sem_wait( &mEventThreadSemaphore );
322 LOG_EVENT( "Surface replaced, event-thread continuing" );
326 void CombinedUpdateRenderController::DeleteSurface( Dali::RenderSurfaceInterface* surface )
330 if( mUpdateRenderThread )
332 LOG_EVENT( "Starting to delete the surface, event-thread blocked" );
334 // Start replacing the surface.
336 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
337 mPostRendering = FALSE; // Clear the post-rendering flag as Update/Render thread will delete the surface now
338 mDeletedSurface = surface;
339 mUpdateRenderThreadWaitCondition.Notify( lock );
342 // Wait until the surface has been deleted
343 sem_wait( &mEventThreadSemaphore );
345 LOG_EVENT( "Surface deleted, event-thread continuing" );
349 void CombinedUpdateRenderController::WaitForGraphicsInitialization()
353 if( mUpdateRenderThread )
355 LOG_EVENT( "Waiting for graphics initialisation, event-thread blocked" );
357 // Wait until the graphics has been initialised
358 sem_wait( &mGraphicsInitializeSemaphore );
360 LOG_EVENT( "graphics initialised, event-thread continuing" );
364 void CombinedUpdateRenderController::ResizeSurface()
368 LOG_EVENT( "Resize the surface" );
371 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
372 mPostRendering = FALSE; // Clear the post-rendering flag as Update/Render thread will resize the surface now
373 mSurfaceResized = TRUE;
374 mUpdateRenderThreadWaitCondition.Notify( lock );
378 void CombinedUpdateRenderController::SetRenderRefreshRate( unsigned int numberOfFramesPerRender )
380 // Not protected by lock, but written to rarely so not worth adding a lock when reading
381 mDefaultFrameDelta = numberOfFramesPerRender * DEFAULT_FRAME_DURATION_IN_SECONDS;
382 mDefaultFrameDurationMilliseconds = uint64_t( numberOfFramesPerRender ) * DEFAULT_FRAME_DURATION_IN_MILLISECONDS;
383 mDefaultFrameDurationNanoseconds = uint64_t( numberOfFramesPerRender ) * DEFAULT_FRAME_DURATION_IN_NANOSECONDS;
384 mDefaultHalfFrameNanoseconds = mDefaultFrameDurationNanoseconds / 2u;
386 LOG_EVENT( "mDefaultFrameDelta(%.6f), mDefaultFrameDurationMilliseconds(%lld), mDefaultFrameDurationNanoseconds(%lld)", mDefaultFrameDelta, mDefaultFrameDurationMilliseconds, mDefaultFrameDurationNanoseconds );
389 void CombinedUpdateRenderController::SetPreRenderCallback( CallbackBase* callback )
392 LOG_EVENT( "Set PreRender Callback" );
394 ConditionalWait::ScopedLock updateLock( mUpdateRenderThreadWaitCondition );
395 if( mPreRenderCallback )
397 delete mPreRenderCallback;
399 mPreRenderCallback = callback;
402 void CombinedUpdateRenderController::AddSurface( Dali::RenderSurfaceInterface* surface )
405 LOG_EVENT( "Surface is added" );
406 if( mUpdateRenderThread )
408 // Set the ThreadSyncronizationInterface on the added surface
409 surface->SetThreadSynchronization( *this );
413 ///////////////////////////////////////////////////////////////////////////////////////////////////
415 ///////////////////////////////////////////////////////////////////////////////////////////////////
417 void CombinedUpdateRenderController::RunUpdateRenderThread( int numberOfCycles, AnimationProgression animationProgression, UpdateMode updateMode )
419 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
420 mUpdateRenderRunCount = numberOfCycles;
421 mUpdateRenderThreadCanSleep = FALSE;
422 mUseElapsedTimeAfterWait = ( animationProgression == AnimationProgression::USE_ELAPSED_TIME );
423 mUploadWithoutRendering = ( updateMode == UpdateMode::SKIP_RENDER );
424 LOG_COUNTER_EVENT( "mUpdateRenderRunCount: %d, mUseElapsedTimeAfterWait: %d", mUpdateRenderRunCount, mUseElapsedTimeAfterWait );
425 mUpdateRenderThreadWaitCondition.Notify( lock );
428 void CombinedUpdateRenderController::PauseUpdateRenderThread()
430 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
431 mUpdateRenderRunCount = 0;
434 void CombinedUpdateRenderController::StopUpdateRenderThread()
436 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
437 mDestroyUpdateRenderThread = TRUE;
438 mUpdateRenderThreadWaitCondition.Notify( lock );
441 bool CombinedUpdateRenderController::IsUpdateRenderThreadPaused()
443 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
444 return ( mUpdateRenderRunCount != CONTINUOUS ) || // Report paused if NOT continuously running
445 mUpdateRenderThreadCanSleep; // Report paused if sleeping
448 void CombinedUpdateRenderController::ProcessSleepRequest()
452 // Decrement Update request count
453 if( mUpdateRequestCount > 0 )
455 --mUpdateRequestCount;
458 // Can sleep if our update-request count is 0
459 // Update/Render thread can choose to carry on updating if it determines more update/renders are required
460 if( mUpdateRequestCount == 0 )
462 LOG_EVENT( "Going to sleep" );
464 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
465 mUpdateRenderThreadCanSleep = TRUE;
469 ///////////////////////////////////////////////////////////////////////////////////////////////////
470 // UPDATE/RENDER THREAD
471 ///////////////////////////////////////////////////////////////////////////////////////////////////
473 void CombinedUpdateRenderController::UpdateRenderThread()
475 SetThreadName("RenderThread\0");
477 // Install a function for logging
478 mEnvironmentOptions.InstallLogFunction();
480 // Install a function for tracing
481 mEnvironmentOptions.InstallTraceFunction();
483 LOG_UPDATE_RENDER( "THREAD CREATED" );
485 // Initialize EGL & OpenGL
486 Dali::DisplayConnection& displayConnection = mAdaptorInterfaces.GetDisplayConnectionInterface();
487 displayConnection.Initialize();
489 // EGL has been initialised at this point
490 NotifyGraphicsInitialised();
492 RenderSurfaceInterface* currentSurface = nullptr;
494 GraphicsInterface& graphics = mAdaptorInterfaces.GetGraphicsInterface();
495 EglGraphics* eglGraphics = static_cast<EglGraphics *>(&graphics);
497 // This will only be created once
498 EglInterface* eglInterface = &eglGraphics->GetEglInterface();
500 Internal::Adaptor::EglImplementation& eglImpl = static_cast<Internal::Adaptor::EglImplementation&>( *eglInterface );
502 // Try to use OpenGL es 3.0
503 // ChooseConfig returns false here when the device only support gles 2.0.
504 // Because eglChooseConfig with gles 3.0 setting fails when the device only support gles 2.0 and Our default setting is gles 3.0.
505 if( !eglImpl.ChooseConfig( true, COLOR_DEPTH_32 ) )
507 // Retry to use OpenGL es 2.0
508 eglGraphics->SetGlesVersion( 20 );
509 eglImpl.ChooseConfig( true, COLOR_DEPTH_32 );
512 // Check whether surfaceless context is supported
513 bool isSurfacelessContextSupported = eglImpl.IsSurfacelessContextSupported();
514 eglGraphics->SetIsSurfacelessContextSupported( isSurfacelessContextSupported );
516 if ( isSurfacelessContextSupported )
518 // Create a surfaceless OpenGL context for shared resources
519 eglImpl.CreateContext();
520 eglImpl.MakeContextCurrent( EGL_NO_SURFACE, eglImpl.GetContext() );
524 currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
527 currentSurface->InitializeGraphics();
528 currentSurface->MakeContextCurrent();
532 eglGraphics->GetGlesInterface().ContextCreated();
534 // Tell core it has a context
535 mCore.ContextCreated();
537 NotifyThreadInitialised();
540 uint64_t lastFrameTime;
541 TimeService::GetNanoseconds( lastFrameTime );
543 LOG_UPDATE_RENDER( "THREAD INITIALISED" );
545 bool useElapsedTime = true;
546 bool updateRequired = true;
547 uint64_t timeToSleepUntil = 0;
548 int extraFramesDropped = 0;
550 const unsigned int renderToFboInterval = mEnvironmentOptions.GetRenderToFboInterval();
551 const bool renderToFboEnabled = 0u != renderToFboInterval;
552 unsigned int frameCount = 0u;
554 while( UpdateRenderReady( useElapsedTime, updateRequired, timeToSleepUntil ) )
556 LOG_UPDATE_RENDER_TRACE;
558 // Performance statistics are logged upon a VSYNC tick so use this point for a VSync marker
559 AddPerformanceMarker( PerformanceInterface::VSYNC );
561 uint64_t currentFrameStartTime = 0;
562 TimeService::GetNanoseconds( currentFrameStartTime );
564 const uint64_t timeSinceLastFrame = currentFrameStartTime - lastFrameTime;
566 // Optional FPS Tracking when continuously rendering
567 if( useElapsedTime && mFpsTracker.Enabled() )
569 float absoluteTimeSinceLastRender = timeSinceLastFrame * NANOSECONDS_TO_SECOND;
570 mFpsTracker.Track( absoluteTimeSinceLastRender );
573 lastFrameTime = currentFrameStartTime; // Store frame start time
575 //////////////////////////////
577 //////////////////////////////
579 Integration::RenderSurface* newSurface = ShouldSurfaceBeReplaced();
580 if( DALI_UNLIKELY( newSurface ) )
582 LOG_UPDATE_RENDER_TRACE_FMT( "Replacing Surface" );
583 // This is designed for replacing pixmap surfaces, but should work for window as well
584 // we need to delete the surface and renderable (pixmap / window)
585 // Then create a new pixmap/window and new surface
586 // If the new surface has a different display connection, then the context will be lost
587 mAdaptorInterfaces.GetDisplayConnectionInterface().Initialize();
588 newSurface->InitializeGraphics();
589 newSurface->MakeContextCurrent();
590 // TODO: ReplaceGraphicsSurface doesn't work, InitializeGraphics()
591 // already creates new surface window, the surface and the context.
592 // We probably don't need ReplaceGraphicsSurface at all.
593 // newSurface->ReplaceGraphicsSurface();
597 const bool isRenderingToFbo = renderToFboEnabled && ( ( 0u == frameCount ) || ( 0u != frameCount % renderToFboInterval ) );
600 //////////////////////////////
602 //////////////////////////////
604 const unsigned int currentTime = currentFrameStartTime / NANOSECONDS_PER_MILLISECOND;
605 const unsigned int nextFrameTime = currentTime + mDefaultFrameDurationMilliseconds;
607 uint64_t noOfFramesSinceLastUpdate = 1;
608 float frameDelta = 0.0f;
611 // If using the elapsed time, then calculate frameDelta as a multiple of mDefaultFrameDelta
612 noOfFramesSinceLastUpdate += extraFramesDropped;
614 frameDelta = mDefaultFrameDelta * noOfFramesSinceLastUpdate;
616 LOG_UPDATE_RENDER( "timeSinceLastFrame(%llu) noOfFramesSinceLastUpdate(%u) frameDelta(%.6f)", timeSinceLastFrame, noOfFramesSinceLastUpdate, frameDelta );
618 Integration::UpdateStatus updateStatus;
620 AddPerformanceMarker( PerformanceInterface::UPDATE_START );
621 mCore.Update( frameDelta,
627 AddPerformanceMarker( PerformanceInterface::UPDATE_END );
629 unsigned int keepUpdatingStatus = updateStatus.KeepUpdating();
631 // Tell the event-thread to wake up (if asleep) and send a notification event to Core if required
632 if( updateStatus.NeedsNotification() )
634 mNotificationTrigger.Trigger();
635 LOG_UPDATE_RENDER( "Notification Triggered" );
639 bool shouldSurfaceBeResized = ShouldSurfaceBeResized();
640 if( DALI_UNLIKELY( shouldSurfaceBeResized ) )
642 if( updateStatus.SurfaceRectChanged() )
644 LOG_UPDATE_RENDER_TRACE_FMT( "Resizing Surface" );
649 // Optional logging of update/render status
650 mUpdateStatusLogger.Log( keepUpdatingStatus );
652 //////////////////////////////
654 //////////////////////////////
656 mAdaptorInterfaces.GetDisplayConnectionInterface().ConsumeEvents();
658 if( mPreRenderCallback != NULL )
660 bool keepCallback = CallbackBase::ExecuteReturn<bool>(*mPreRenderCallback);
663 delete mPreRenderCallback;
664 mPreRenderCallback = NULL;
668 if( eglImpl.IsSurfacelessContextSupported() )
670 // Make the shared surfaceless context as current before rendering
671 eglImpl.MakeContextCurrent( EGL_NO_SURFACE, eglImpl.GetContext() );
674 if( mFirstFrameAfterResume )
676 // mFirstFrameAfterResume is set to true when the thread is resumed
677 // Let eglImplementation know the first frame after thread initialized or resumed.
678 eglImpl.SetFirstFrameAfterResume();
679 mFirstFrameAfterResume = FALSE;
682 Integration::RenderStatus renderStatus;
684 AddPerformanceMarker( PerformanceInterface::RENDER_START );
685 mCore.Render( renderStatus, mForceClear, mUploadWithoutRendering );
687 //////////////////////////////
689 //////////////////////////////
691 Integration::RenderSurface* deletedSurface = ShouldSurfaceBeDeleted();
692 if( DALI_UNLIKELY( deletedSurface ) )
694 LOG_UPDATE_RENDER_TRACE_FMT( "Deleting Surface" );
696 mCore.SurfaceDeleted( deletedSurface );
701 AddPerformanceMarker( PerformanceInterface::RENDER_END );
705 // Trigger event thread to request Update/Render thread to sleep if update not required
706 if( ( Integration::KeepUpdating::NOT_REQUESTED == keepUpdatingStatus ) && !renderStatus.NeedsUpdate() )
708 mSleepTrigger->Trigger();
709 updateRequired = false;
710 LOG_UPDATE_RENDER( "Sleep Triggered" );
714 updateRequired = true;
717 //////////////////////////////
719 //////////////////////////////
721 extraFramesDropped = 0;
723 if (timeToSleepUntil == 0)
725 // If this is the first frame after the thread is initialized or resumed, we
726 // use the actual time the current frame starts from to calculate the time to
727 // sleep until the next frame.
728 timeToSleepUntil = currentFrameStartTime + mDefaultFrameDurationNanoseconds;
732 // Otherwise, always use the sleep-until time calculated in the last frame to
733 // calculate the time to sleep until the next frame. In this way, if there is
734 // any time gap between the current frame and the next frame, or if update or
735 // rendering in the current frame takes too much time so that the specified
736 // sleep-until time has already passed, it will try to keep the frames syncing
737 // by shortening the duration of the next frame.
738 timeToSleepUntil += mDefaultFrameDurationNanoseconds;
740 // Check the current time at the end of the frame
741 uint64_t currentFrameEndTime = 0;
742 TimeService::GetNanoseconds( currentFrameEndTime );
743 while ( currentFrameEndTime > timeToSleepUntil + mDefaultFrameDurationNanoseconds )
745 // We are more than one frame behind already, so just drop the next frames
746 // until the sleep-until time is later than the current time so that we can
748 timeToSleepUntil += mDefaultFrameDurationNanoseconds;
749 extraFramesDropped++;
753 // Render to FBO is intended to measure fps above 60 so sleep is not wanted.
754 if( 0u == renderToFboInterval )
756 // Sleep until at least the the default frame duration has elapsed. This will return immediately if the specified end-time has already passed.
757 TimeService::SleepUntil( timeToSleepUntil );
761 // Inform core of context destruction & shutdown EGL
762 mCore.ContextDestroyed();
763 currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
766 currentSurface->DestroySurface();
767 currentSurface = nullptr;
770 LOG_UPDATE_RENDER( "THREAD DESTROYED" );
772 // Uninstall the logging function
773 mEnvironmentOptions.UnInstallLogFunction();
776 bool CombinedUpdateRenderController::UpdateRenderReady( bool& useElapsedTime, bool updateRequired, uint64_t& timeToSleepUntil )
778 useElapsedTime = true;
780 ConditionalWait::ScopedLock updateLock( mUpdateRenderThreadWaitCondition );
781 while( ( ! mUpdateRenderRunCount || // Should try to wait if event-thread has paused the Update/Render thread
782 ( mUpdateRenderThreadCanSleep && ! updateRequired && ! mPendingRequestUpdate ) ) && // Ensure we wait if we're supposed to be sleeping AND do not require another update
783 ! mDestroyUpdateRenderThread && // Ensure we don't wait if the update-render-thread is supposed to be destroyed
784 ! mNewSurface && // Ensure we don't wait if we need to replace the surface
785 ! mDeletedSurface && // Ensure we don't wait if we need to delete the surface
786 ! mSurfaceResized ) // Ensure we don't wait if we need to resize the surface
788 LOG_UPDATE_RENDER( "WAIT: mUpdateRenderRunCount: %d", mUpdateRenderRunCount );
789 LOG_UPDATE_RENDER( " mUpdateRenderThreadCanSleep: %d, updateRequired: %d, mPendingRequestUpdate: %d", mUpdateRenderThreadCanSleep, updateRequired, mPendingRequestUpdate );
790 LOG_UPDATE_RENDER( " mDestroyUpdateRenderThread: %d", mDestroyUpdateRenderThread );
791 LOG_UPDATE_RENDER( " mNewSurface: %d", mNewSurface );
792 LOG_UPDATE_RENDER( " mDeletedSurface: %d", mDeletedSurface );
793 LOG_UPDATE_RENDER( " mSurfaceResized: %d", mSurfaceResized );
795 // Reset the time when the thread is waiting, so the sleep-until time for
796 // the first frame after resuming should be based on the actual start time
797 // of the first frame.
798 timeToSleepUntil = 0;
800 mUpdateRenderThreadWaitCondition.Wait( updateLock );
802 if( ! mUseElapsedTimeAfterWait )
804 useElapsedTime = false;
808 LOG_COUNTER_UPDATE_RENDER( "mUpdateRenderRunCount: %d", mUpdateRenderRunCount );
809 LOG_COUNTER_UPDATE_RENDER( "mUpdateRenderThreadCanSleep: %d, updateRequired: %d, mPendingRequestUpdate: %d", mUpdateRenderThreadCanSleep, updateRequired, mPendingRequestUpdate );
810 LOG_COUNTER_UPDATE_RENDER( "mDestroyUpdateRenderThread: %d", mDestroyUpdateRenderThread );
811 LOG_COUNTER_UPDATE_RENDER( "mNewSurface: %d", mNewSurface );
812 LOG_COUNTER_UPDATE_RENDER( "mDeletedSurface: %d", mDeletedSurface );
813 LOG_COUNTER_UPDATE_RENDER( "mSurfaceResized: %d", mSurfaceResized );
815 mUseElapsedTimeAfterWait = FALSE;
816 mUpdateRenderThreadCanSleep = FALSE;
817 mPendingRequestUpdate = FALSE;
819 // If we've been asked to run Update/Render cycles a finite number of times then decrement so we wait after the
820 // requested number of cycles
821 if( mUpdateRenderRunCount > 0 )
823 --mUpdateRenderRunCount;
826 // Keep the update-render thread alive if this thread is NOT to be destroyed
827 return ! mDestroyUpdateRenderThread;
830 Integration::RenderSurface* CombinedUpdateRenderController::ShouldSurfaceBeReplaced()
832 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
834 Integration::RenderSurface* newSurface = mNewSurface;
840 void CombinedUpdateRenderController::SurfaceReplaced()
842 // Just increment the semaphore
843 sem_post( &mEventThreadSemaphore );
846 Integration::RenderSurface* CombinedUpdateRenderController::ShouldSurfaceBeDeleted()
848 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
850 Integration::RenderSurface* deletedSurface = mDeletedSurface;
851 mDeletedSurface = NULL;
853 return deletedSurface;
856 void CombinedUpdateRenderController::SurfaceDeleted()
858 // Just increment the semaphore
859 sem_post( &mEventThreadSemaphore );
862 bool CombinedUpdateRenderController::ShouldSurfaceBeResized()
864 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
865 return mSurfaceResized;
868 void CombinedUpdateRenderController::SurfaceResized()
870 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
871 mSurfaceResized = FALSE;
874 ///////////////////////////////////////////////////////////////////////////////////////////////////
876 ///////////////////////////////////////////////////////////////////////////////////////////////////
878 void CombinedUpdateRenderController::NotifyThreadInitialised()
880 // Just increment the semaphore
881 sem_post( &mEventThreadSemaphore );
884 void CombinedUpdateRenderController::NotifyGraphicsInitialised()
886 sem_post( &mGraphicsInitializeSemaphore );
889 void CombinedUpdateRenderController::AddPerformanceMarker( PerformanceInterface::MarkerType type )
891 if( mPerformanceInterface )
893 mPerformanceInterface->AddMarker( type );
897 /////////////////////////////////////////////////////////////////////////////////////////////////
898 // POST RENDERING: EVENT THREAD
899 /////////////////////////////////////////////////////////////////////////////////////////////////
901 void CombinedUpdateRenderController::PostRenderComplete()
903 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
904 mPostRendering = FALSE;
905 mUpdateRenderThreadWaitCondition.Notify( lock );
908 ///////////////////////////////////////////////////////////////////////////////////////////////////
909 // POST RENDERING: RENDER THREAD
910 ///////////////////////////////////////////////////////////////////////////////////////////////////
912 void CombinedUpdateRenderController::PostRenderStarted()
914 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
915 mPostRendering = TRUE;
918 void CombinedUpdateRenderController::PostRenderWaitForCompletion()
920 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
921 while( mPostRendering &&
922 ! mNewSurface && // We should NOT wait if we're replacing the surface
923 ! mDeletedSurface && // We should NOT wait if we're deleting the surface
924 ! mDestroyUpdateRenderThread )
926 mUpdateRenderThreadWaitCondition.Wait( lock );
930 } // namespace Adaptor
932 } // namespace Internal