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 ///////////////////////////////////////////////////////////////////////////////////////////////////
404 ///////////////////////////////////////////////////////////////////////////////////////////////////
406 void CombinedUpdateRenderController::RunUpdateRenderThread( int numberOfCycles, AnimationProgression animationProgression, UpdateMode updateMode )
408 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
409 mUpdateRenderRunCount = numberOfCycles;
410 mUpdateRenderThreadCanSleep = FALSE;
411 mUseElapsedTimeAfterWait = ( animationProgression == AnimationProgression::USE_ELAPSED_TIME );
412 mUploadWithoutRendering = ( updateMode == UpdateMode::SKIP_RENDER );
413 LOG_COUNTER_EVENT( "mUpdateRenderRunCount: %d, mUseElapsedTimeAfterWait: %d", mUpdateRenderRunCount, mUseElapsedTimeAfterWait );
414 mUpdateRenderThreadWaitCondition.Notify( lock );
417 void CombinedUpdateRenderController::PauseUpdateRenderThread()
419 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
420 mUpdateRenderRunCount = 0;
423 void CombinedUpdateRenderController::StopUpdateRenderThread()
425 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
426 mDestroyUpdateRenderThread = TRUE;
427 mUpdateRenderThreadWaitCondition.Notify( lock );
430 bool CombinedUpdateRenderController::IsUpdateRenderThreadPaused()
432 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
433 return ( mUpdateRenderRunCount != CONTINUOUS ) || // Report paused if NOT continuously running
434 mUpdateRenderThreadCanSleep; // Report paused if sleeping
437 void CombinedUpdateRenderController::ProcessSleepRequest()
441 // Decrement Update request count
442 if( mUpdateRequestCount > 0 )
444 --mUpdateRequestCount;
447 // Can sleep if our update-request count is 0
448 // Update/Render thread can choose to carry on updating if it determines more update/renders are required
449 if( mUpdateRequestCount == 0 )
451 LOG_EVENT( "Going to sleep" );
453 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
454 mUpdateRenderThreadCanSleep = TRUE;
458 ///////////////////////////////////////////////////////////////////////////////////////////////////
459 // UPDATE/RENDER THREAD
460 ///////////////////////////////////////////////////////////////////////////////////////////////////
462 void CombinedUpdateRenderController::UpdateRenderThread()
464 SetThreadName("RenderThread\0");
466 // Install a function for logging
467 mEnvironmentOptions.InstallLogFunction();
469 // Install a function for tracing
470 mEnvironmentOptions.InstallTraceFunction();
472 LOG_UPDATE_RENDER( "THREAD CREATED" );
474 // Initialize EGL & OpenGL
475 Dali::DisplayConnection& displayConnection = mAdaptorInterfaces.GetDisplayConnectionInterface();
476 displayConnection.Initialize();
478 // EGL has been initialised at this point
479 NotifyGraphicsInitialised();
481 RenderSurfaceInterface* currentSurface = nullptr;
483 GraphicsInterface& graphics = mAdaptorInterfaces.GetGraphicsInterface();
484 EglGraphics* eglGraphics = static_cast<EglGraphics *>(&graphics);
486 // This will only be created once
487 EglInterface* eglInterface = &eglGraphics->GetEglInterface();
489 Internal::Adaptor::EglImplementation& eglImpl = static_cast<Internal::Adaptor::EglImplementation&>( *eglInterface );
491 // Try to use OpenGL es 3.0
492 // ChooseConfig returns false here when the device only support gles 2.0.
493 // Because eglChooseConfig with gles 3.0 setting fails when the device only support gles 2.0 and Our default setting is gles 3.0.
494 if( !eglImpl.ChooseConfig( true, COLOR_DEPTH_32 ) )
496 // Retry to use OpenGL es 2.0
497 eglGraphics->SetGlesVersion( 20 );
498 eglImpl.ChooseConfig( true, COLOR_DEPTH_32 );
501 // Check whether surfaceless context is supported
502 bool isSurfacelessContextSupported = eglImpl.IsSurfacelessContextSupported();
503 eglGraphics->SetIsSurfacelessContextSupported( isSurfacelessContextSupported );
505 if ( isSurfacelessContextSupported )
507 // Create a surfaceless OpenGL context for shared resources
508 eglImpl.CreateContext();
509 eglImpl.MakeContextCurrent( EGL_NO_SURFACE, eglImpl.GetContext() );
513 currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
516 currentSurface->InitializeGraphics();
517 currentSurface->MakeContextCurrent();
521 eglGraphics->GetGlesInterface().ContextCreated();
523 // Tell core it has a context
524 mCore.ContextCreated();
526 NotifyThreadInitialised();
529 uint64_t lastFrameTime;
530 TimeService::GetNanoseconds( lastFrameTime );
532 LOG_UPDATE_RENDER( "THREAD INITIALISED" );
534 bool useElapsedTime = true;
535 bool updateRequired = true;
536 uint64_t timeToSleepUntil = 0;
537 int extraFramesDropped = 0;
539 const unsigned int renderToFboInterval = mEnvironmentOptions.GetRenderToFboInterval();
540 const bool renderToFboEnabled = 0u != renderToFboInterval;
541 unsigned int frameCount = 0u;
543 while( UpdateRenderReady( useElapsedTime, updateRequired, timeToSleepUntil ) )
545 LOG_UPDATE_RENDER_TRACE;
547 // Performance statistics are logged upon a VSYNC tick so use this point for a VSync marker
548 AddPerformanceMarker( PerformanceInterface::VSYNC );
550 uint64_t currentFrameStartTime = 0;
551 TimeService::GetNanoseconds( currentFrameStartTime );
553 const uint64_t timeSinceLastFrame = currentFrameStartTime - lastFrameTime;
555 // Optional FPS Tracking when continuously rendering
556 if( useElapsedTime && mFpsTracker.Enabled() )
558 float absoluteTimeSinceLastRender = timeSinceLastFrame * NANOSECONDS_TO_SECOND;
559 mFpsTracker.Track( absoluteTimeSinceLastRender );
562 lastFrameTime = currentFrameStartTime; // Store frame start time
564 //////////////////////////////
566 //////////////////////////////
568 Integration::RenderSurface* newSurface = ShouldSurfaceBeReplaced();
569 if( DALI_UNLIKELY( newSurface ) )
571 LOG_UPDATE_RENDER_TRACE_FMT( "Replacing Surface" );
572 // This is designed for replacing pixmap surfaces, but should work for window as well
573 // we need to delete the surface and renderable (pixmap / window)
574 // Then create a new pixmap/window and new surface
575 // If the new surface has a different display connection, then the context will be lost
576 mAdaptorInterfaces.GetDisplayConnectionInterface().Initialize();
577 newSurface->InitializeGraphics();
578 newSurface->MakeContextCurrent();
579 // TODO: ReplaceGraphicsSurface doesn't work, InitializeGraphics()
580 // already creates new surface window, the surface and the context.
581 // We probably don't need ReplaceGraphicsSurface at all.
582 // newSurface->ReplaceGraphicsSurface();
586 const bool isRenderingToFbo = renderToFboEnabled && ( ( 0u == frameCount ) || ( 0u != frameCount % renderToFboInterval ) );
589 //////////////////////////////
591 //////////////////////////////
593 const unsigned int currentTime = currentFrameStartTime / NANOSECONDS_PER_MILLISECOND;
594 const unsigned int nextFrameTime = currentTime + mDefaultFrameDurationMilliseconds;
596 uint64_t noOfFramesSinceLastUpdate = 1;
597 float frameDelta = 0.0f;
600 // If using the elapsed time, then calculate frameDelta as a multiple of mDefaultFrameDelta
601 noOfFramesSinceLastUpdate += extraFramesDropped;
603 frameDelta = mDefaultFrameDelta * noOfFramesSinceLastUpdate;
605 LOG_UPDATE_RENDER( "timeSinceLastFrame(%llu) noOfFramesSinceLastUpdate(%u) frameDelta(%.6f)", timeSinceLastFrame, noOfFramesSinceLastUpdate, frameDelta );
607 Integration::UpdateStatus updateStatus;
609 AddPerformanceMarker( PerformanceInterface::UPDATE_START );
610 mCore.Update( frameDelta,
616 AddPerformanceMarker( PerformanceInterface::UPDATE_END );
618 unsigned int keepUpdatingStatus = updateStatus.KeepUpdating();
620 // Tell the event-thread to wake up (if asleep) and send a notification event to Core if required
621 if( updateStatus.NeedsNotification() )
623 mNotificationTrigger.Trigger();
624 LOG_UPDATE_RENDER( "Notification Triggered" );
628 bool shouldSurfaceBeResized = ShouldSurfaceBeResized();
629 if( DALI_UNLIKELY( shouldSurfaceBeResized ) )
631 if( updateStatus.SurfaceRectChanged() )
633 LOG_UPDATE_RENDER_TRACE_FMT( "Resizing Surface" );
638 // Optional logging of update/render status
639 mUpdateStatusLogger.Log( keepUpdatingStatus );
641 //////////////////////////////
643 //////////////////////////////
645 mAdaptorInterfaces.GetDisplayConnectionInterface().ConsumeEvents();
647 if( mPreRenderCallback != NULL )
649 bool keepCallback = CallbackBase::ExecuteReturn<bool>(*mPreRenderCallback);
652 delete mPreRenderCallback;
653 mPreRenderCallback = NULL;
657 if( eglImpl.IsSurfacelessContextSupported() )
659 // Make the shared surfaceless context as current before rendering
660 eglImpl.MakeContextCurrent( EGL_NO_SURFACE, eglImpl.GetContext() );
663 if( mFirstFrameAfterResume )
665 // mFirstFrameAfterResume is set to true when the thread is resumed
666 // Let eglImplementation know the first frame after thread initialized or resumed.
667 eglImpl.SetFirstFrameAfterResume();
668 mFirstFrameAfterResume = FALSE;
671 Integration::RenderStatus renderStatus;
673 AddPerformanceMarker( PerformanceInterface::RENDER_START );
674 mCore.Render( renderStatus, mForceClear, mUploadWithoutRendering );
676 //////////////////////////////
678 //////////////////////////////
680 Integration::RenderSurface* deletedSurface = ShouldSurfaceBeDeleted();
681 if( DALI_UNLIKELY( deletedSurface ) )
683 LOG_UPDATE_RENDER_TRACE_FMT( "Deleting Surface" );
685 mCore.SurfaceDeleted( deletedSurface );
690 AddPerformanceMarker( PerformanceInterface::RENDER_END );
694 // Trigger event thread to request Update/Render thread to sleep if update not required
695 if( ( Integration::KeepUpdating::NOT_REQUESTED == keepUpdatingStatus ) && !renderStatus.NeedsUpdate() )
697 mSleepTrigger->Trigger();
698 updateRequired = false;
699 LOG_UPDATE_RENDER( "Sleep Triggered" );
703 updateRequired = true;
706 //////////////////////////////
708 //////////////////////////////
710 extraFramesDropped = 0;
712 if (timeToSleepUntil == 0)
714 // If this is the first frame after the thread is initialized or resumed, we
715 // use the actual time the current frame starts from to calculate the time to
716 // sleep until the next frame.
717 timeToSleepUntil = currentFrameStartTime + mDefaultFrameDurationNanoseconds;
721 // Otherwise, always use the sleep-until time calculated in the last frame to
722 // calculate the time to sleep until the next frame. In this way, if there is
723 // any time gap between the current frame and the next frame, or if update or
724 // rendering in the current frame takes too much time so that the specified
725 // sleep-until time has already passed, it will try to keep the frames syncing
726 // by shortening the duration of the next frame.
727 timeToSleepUntil += mDefaultFrameDurationNanoseconds;
729 // Check the current time at the end of the frame
730 uint64_t currentFrameEndTime = 0;
731 TimeService::GetNanoseconds( currentFrameEndTime );
732 while ( currentFrameEndTime > timeToSleepUntil + mDefaultFrameDurationNanoseconds )
734 // We are more than one frame behind already, so just drop the next frames
735 // until the sleep-until time is later than the current time so that we can
737 timeToSleepUntil += mDefaultFrameDurationNanoseconds;
738 extraFramesDropped++;
742 // Render to FBO is intended to measure fps above 60 so sleep is not wanted.
743 if( 0u == renderToFboInterval )
745 // Sleep until at least the the default frame duration has elapsed. This will return immediately if the specified end-time has already passed.
746 TimeService::SleepUntil( timeToSleepUntil );
750 // Inform core of context destruction & shutdown EGL
751 mCore.ContextDestroyed();
752 currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
755 currentSurface->DestroySurface();
756 currentSurface = nullptr;
759 LOG_UPDATE_RENDER( "THREAD DESTROYED" );
761 // Uninstall the logging function
762 mEnvironmentOptions.UnInstallLogFunction();
765 bool CombinedUpdateRenderController::UpdateRenderReady( bool& useElapsedTime, bool updateRequired, uint64_t& timeToSleepUntil )
767 useElapsedTime = true;
769 ConditionalWait::ScopedLock updateLock( mUpdateRenderThreadWaitCondition );
770 while( ( ! mUpdateRenderRunCount || // Should try to wait if event-thread has paused the Update/Render thread
771 ( mUpdateRenderThreadCanSleep && ! updateRequired && ! mPendingRequestUpdate ) ) && // Ensure we wait if we're supposed to be sleeping AND do not require another update
772 ! mDestroyUpdateRenderThread && // Ensure we don't wait if the update-render-thread is supposed to be destroyed
773 ! mNewSurface && // Ensure we don't wait if we need to replace the surface
774 ! mDeletedSurface && // Ensure we don't wait if we need to delete the surface
775 ! mSurfaceResized ) // Ensure we don't wait if we need to resize the surface
777 LOG_UPDATE_RENDER( "WAIT: mUpdateRenderRunCount: %d", mUpdateRenderRunCount );
778 LOG_UPDATE_RENDER( " mUpdateRenderThreadCanSleep: %d, updateRequired: %d, mPendingRequestUpdate: %d", mUpdateRenderThreadCanSleep, updateRequired, mPendingRequestUpdate );
779 LOG_UPDATE_RENDER( " mDestroyUpdateRenderThread: %d", mDestroyUpdateRenderThread );
780 LOG_UPDATE_RENDER( " mNewSurface: %d", mNewSurface );
781 LOG_UPDATE_RENDER( " mDeletedSurface: %d", mDeletedSurface );
782 LOG_UPDATE_RENDER( " mSurfaceResized: %d", mSurfaceResized );
784 // Reset the time when the thread is waiting, so the sleep-until time for
785 // the first frame after resuming should be based on the actual start time
786 // of the first frame.
787 timeToSleepUntil = 0;
789 mUpdateRenderThreadWaitCondition.Wait( updateLock );
791 if( ! mUseElapsedTimeAfterWait )
793 useElapsedTime = false;
797 LOG_COUNTER_UPDATE_RENDER( "mUpdateRenderRunCount: %d", mUpdateRenderRunCount );
798 LOG_COUNTER_UPDATE_RENDER( "mUpdateRenderThreadCanSleep: %d, updateRequired: %d, mPendingRequestUpdate: %d", mUpdateRenderThreadCanSleep, updateRequired, mPendingRequestUpdate );
799 LOG_COUNTER_UPDATE_RENDER( "mDestroyUpdateRenderThread: %d", mDestroyUpdateRenderThread );
800 LOG_COUNTER_UPDATE_RENDER( "mNewSurface: %d", mNewSurface );
801 LOG_COUNTER_UPDATE_RENDER( "mDeletedSurface: %d", mDeletedSurface );
802 LOG_COUNTER_UPDATE_RENDER( "mSurfaceResized: %d", mSurfaceResized );
804 mUseElapsedTimeAfterWait = FALSE;
805 mUpdateRenderThreadCanSleep = FALSE;
806 mPendingRequestUpdate = FALSE;
808 // If we've been asked to run Update/Render cycles a finite number of times then decrement so we wait after the
809 // requested number of cycles
810 if( mUpdateRenderRunCount > 0 )
812 --mUpdateRenderRunCount;
815 // Keep the update-render thread alive if this thread is NOT to be destroyed
816 return ! mDestroyUpdateRenderThread;
819 Integration::RenderSurface* CombinedUpdateRenderController::ShouldSurfaceBeReplaced()
821 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
823 Integration::RenderSurface* newSurface = mNewSurface;
829 void CombinedUpdateRenderController::SurfaceReplaced()
831 // Just increment the semaphore
832 sem_post( &mEventThreadSemaphore );
835 Integration::RenderSurface* CombinedUpdateRenderController::ShouldSurfaceBeDeleted()
837 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
839 Integration::RenderSurface* deletedSurface = mDeletedSurface;
840 mDeletedSurface = NULL;
842 return deletedSurface;
845 void CombinedUpdateRenderController::SurfaceDeleted()
847 // Just increment the semaphore
848 sem_post( &mEventThreadSemaphore );
851 bool CombinedUpdateRenderController::ShouldSurfaceBeResized()
853 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
854 return mSurfaceResized;
857 void CombinedUpdateRenderController::SurfaceResized()
859 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
860 mSurfaceResized = FALSE;
863 ///////////////////////////////////////////////////////////////////////////////////////////////////
865 ///////////////////////////////////////////////////////////////////////////////////////////////////
867 void CombinedUpdateRenderController::NotifyThreadInitialised()
869 // Just increment the semaphore
870 sem_post( &mEventThreadSemaphore );
873 void CombinedUpdateRenderController::NotifyGraphicsInitialised()
875 sem_post( &mGraphicsInitializeSemaphore );
878 void CombinedUpdateRenderController::AddPerformanceMarker( PerformanceInterface::MarkerType type )
880 if( mPerformanceInterface )
882 mPerformanceInterface->AddMarker( type );
886 /////////////////////////////////////////////////////////////////////////////////////////////////
887 // POST RENDERING: EVENT THREAD
888 /////////////////////////////////////////////////////////////////////////////////////////////////
890 void CombinedUpdateRenderController::PostRenderComplete()
892 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
893 mPostRendering = FALSE;
894 mUpdateRenderThreadWaitCondition.Notify( lock );
897 ///////////////////////////////////////////////////////////////////////////////////////////////////
898 // POST RENDERING: RENDER THREAD
899 ///////////////////////////////////////////////////////////////////////////////////////////////////
901 void CombinedUpdateRenderController::PostRenderStarted()
903 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
904 mPostRendering = TRUE;
907 void CombinedUpdateRenderController::PostRenderWaitForCompletion()
909 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
910 while( mPostRendering &&
911 ! mNewSurface && // We should NOT wait if we're replacing the surface
912 ! mDeletedSurface && // We should NOT wait if we're deleting the surface
913 ! mSurfaceResized && // We should NOT wait if we're resizing the surface
914 ! mDestroyUpdateRenderThread )
916 mUpdateRenderThreadWaitCondition.Wait( lock );
920 } // namespace Adaptor
922 } // namespace Internal