2 * Copyright (c) 2020 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 #include <dali/internal/adaptor/common/combined-update-render-controller.h>
23 #include <dali/integration-api/platform-abstraction.h>
27 #include <dali/integration-api/adaptor-framework/trigger-event-factory.h>
28 #include <dali/devel-api/adaptor-framework/thread-settings.h>
29 #include <dali/internal/adaptor/common/adaptor-internal-services.h>
30 #include <dali/internal/adaptor/common/combined-update-render-controller-debug.h>
31 #include <dali/internal/graphics/gles/egl-graphics.h>
32 #include <dali/internal/graphics/gles/egl-implementation.h>
33 #include <dali/internal/graphics/common/graphics-interface.h>
34 #include <dali/internal/system/common/environment-options.h>
35 #include <dali/internal/system/common/time-service.h>
36 #include <dali/internal/window-system/common/window-impl.h>
50 const unsigned int CREATED_THREAD_COUNT = 1u;
52 const int CONTINUOUS = -1;
55 const unsigned int TRUE = 1u;
56 const unsigned int FALSE = 0u;
58 const unsigned int MILLISECONDS_PER_SECOND( 1e+3 );
59 const float NANOSECONDS_TO_SECOND( 1e-9f );
60 const unsigned int NANOSECONDS_PER_SECOND( 1e+9 );
61 const unsigned int NANOSECONDS_PER_MILLISECOND( 1e+6 );
63 // The following values will get calculated at compile time
64 const float DEFAULT_FRAME_DURATION_IN_SECONDS( 1.0f / 60.0f );
65 const uint64_t DEFAULT_FRAME_DURATION_IN_MILLISECONDS( DEFAULT_FRAME_DURATION_IN_SECONDS * MILLISECONDS_PER_SECOND );
66 const uint64_t DEFAULT_FRAME_DURATION_IN_NANOSECONDS( DEFAULT_FRAME_DURATION_IN_SECONDS * NANOSECONDS_PER_SECOND );
69 * Handles the use case when an update-request is received JUST before we process a sleep-request. If we did not have an update-request count then
70 * there is a danger that, on the event-thread we could have:
71 * 1) An update-request where we do nothing as Update/Render thread still running.
72 * 2) Quickly followed by a sleep-request being handled where we pause the Update/Render Thread (even though we have an update to process).
74 * Using a counter means we increment the counter on an update-request, and decrement it on a sleep-request. This handles the above scenario because:
75 * 1) MAIN THREAD: Update Request: COUNTER = 1
76 * 2) UPDATE/RENDER THREAD: Do Update/Render, then no Updates required -> Sleep Trigger
77 * 3) MAIN THREAD: Update Request: COUNTER = 2
78 * 4) MAIN THREAD: Sleep Request: COUNTER = 1 -> We do not sleep just yet
80 * Also ensures we preserve battery life by only doing ONE update when the above use case is not triggered.
81 * 1) MAIN THREAD: Update Request: COUNTER = 1
82 * 2) UPDATE/RENDER THREAD: Do Update/Render, then no Updates required -> Sleep Trigger
83 * 3) MAIN THREAD: Sleep Request: COUNTER = 0 -> Go to sleep
85 const unsigned int MAXIMUM_UPDATE_REQUESTS = 2;
86 } // unnamed namespace
88 ///////////////////////////////////////////////////////////////////////////////////////////////////
90 ///////////////////////////////////////////////////////////////////////////////////////////////////
92 CombinedUpdateRenderController::CombinedUpdateRenderController( AdaptorInternalServices& adaptorInterfaces, const EnvironmentOptions& environmentOptions )
93 : mFpsTracker( environmentOptions ),
94 mUpdateStatusLogger( environmentOptions ),
95 mEventThreadSemaphore(),
96 mGraphicsInitializeSemaphore(),
97 mUpdateRenderThreadWaitCondition(),
98 mAdaptorInterfaces( adaptorInterfaces ),
99 mPerformanceInterface( adaptorInterfaces.GetPerformanceInterface() ),
100 mCore( adaptorInterfaces.GetCore() ),
101 mEnvironmentOptions( environmentOptions ),
102 mNotificationTrigger( adaptorInterfaces.GetProcessCoreEventsTrigger() ),
103 mSleepTrigger( NULL ),
104 mPreRenderCallback( NULL ),
105 mUpdateRenderThread( NULL ),
106 mDefaultFrameDelta( 0.0f ),
107 mDefaultFrameDurationMilliseconds( 0u ),
108 mDefaultFrameDurationNanoseconds( 0u ),
109 mDefaultHalfFrameNanoseconds( 0u ),
110 mUpdateRequestCount( 0u ),
112 mUpdateRenderRunCount( 0 ),
113 mDestroyUpdateRenderThread( FALSE ),
114 mUpdateRenderThreadCanSleep( FALSE ),
115 mPendingRequestUpdate( FALSE ),
116 mUseElapsedTimeAfterWait( FALSE ),
118 mDeletedSurface( nullptr ),
119 mPostRendering( FALSE ),
120 mSurfaceResized( FALSE ),
121 mForceClear( FALSE ),
122 mUploadWithoutRendering( FALSE ),
123 mFirstFrameAfterResume( FALSE )
127 // Initialise frame delta/duration variables first
128 SetRenderRefreshRate( environmentOptions.GetRenderRefreshRate() );
130 // Set the thread-synchronization interface on the render-surface
131 Dali::RenderSurfaceInterface* currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
134 currentSurface->SetThreadSynchronization( *this );
137 mSleepTrigger = TriggerEventFactory::CreateTriggerEvent( MakeCallback( this, &CombinedUpdateRenderController::ProcessSleepRequest ), TriggerEventInterface::KEEP_ALIVE_AFTER_TRIGGER );
139 // Initialize to 0 so that it just waits if sem_post has not been called
140 sem_init( &mEventThreadSemaphore, 0, 0 );
141 sem_init( &mGraphicsInitializeSemaphore, 0, 0 );
144 CombinedUpdateRenderController::~CombinedUpdateRenderController()
150 delete mPreRenderCallback;
151 delete mSleepTrigger;
154 void CombinedUpdateRenderController::Initialize()
158 // Ensure Update/Render Thread not already created
159 DALI_ASSERT_ALWAYS( ! mUpdateRenderThread );
161 // Create Update/Render Thread
162 mUpdateRenderThread = new pthread_t();
163 int error = pthread_create( mUpdateRenderThread, NULL, InternalUpdateRenderThreadEntryFunc, this );
164 DALI_ASSERT_ALWAYS( !error && "Return code from pthread_create() when creating UpdateRenderThread" );
166 // The Update/Render thread will now run and initialise the graphics interface etc. and will then wait for Start to be called
167 // When this function returns, the application initialisation on the event thread should occur
170 void CombinedUpdateRenderController::Start()
174 DALI_ASSERT_ALWAYS( !mRunning && mUpdateRenderThread );
176 // Wait until all threads created in Initialise are up and running
177 for( unsigned int i = 0; i < CREATED_THREAD_COUNT; ++i )
179 sem_wait( &mEventThreadSemaphore );
182 Dali::RenderSurfaceInterface* currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
185 currentSurface->StartRender();
190 LOG_EVENT( "Startup Complete, starting Update/Render Thread" );
192 RunUpdateRenderThread( CONTINUOUS, AnimationProgression::NONE, UpdateMode::NORMAL );
194 DALI_LOG_RELEASE_INFO( "CombinedUpdateRenderController::Start\n" );
197 void CombinedUpdateRenderController::Pause()
203 PauseUpdateRenderThread();
205 AddPerformanceMarker( PerformanceInterface::PAUSED );
207 DALI_LOG_RELEASE_INFO( "CombinedUpdateRenderController::Pause\n" );
210 void CombinedUpdateRenderController::Resume()
214 if( !mRunning && IsUpdateRenderThreadPaused() )
216 LOG_EVENT( "Resuming" );
218 RunUpdateRenderThread( CONTINUOUS, AnimationProgression::USE_ELAPSED_TIME, UpdateMode::NORMAL );
220 AddPerformanceMarker( PerformanceInterface::RESUME );
224 mFirstFrameAfterResume = TRUE;
226 DALI_LOG_RELEASE_INFO( "CombinedUpdateRenderController::Resume\n" );
230 DALI_LOG_RELEASE_INFO( "CombinedUpdateRenderController::Resume: Already resumed [%d, %d, %d]\n", mRunning, mUpdateRenderRunCount, mUpdateRenderThreadCanSleep );
234 void CombinedUpdateRenderController::Stop()
238 // Stop Rendering and the Update/Render Thread
239 Dali::RenderSurfaceInterface* currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
242 currentSurface->StopRender();
245 StopUpdateRenderThread();
247 if( mUpdateRenderThread )
249 LOG_EVENT( "Destroying UpdateRenderThread" );
251 // wait for the thread to finish
252 pthread_join( *mUpdateRenderThread, NULL );
254 delete mUpdateRenderThread;
255 mUpdateRenderThread = NULL;
260 DALI_LOG_RELEASE_INFO( "CombinedUpdateRenderController::Stop\n" );
263 void CombinedUpdateRenderController::RequestUpdate()
267 // Increment the update-request count to the maximum
268 if( mUpdateRequestCount < MAXIMUM_UPDATE_REQUESTS )
270 ++mUpdateRequestCount;
273 if( mRunning && IsUpdateRenderThreadPaused() )
275 LOG_EVENT( "Processing" );
277 RunUpdateRenderThread( CONTINUOUS, AnimationProgression::NONE, UpdateMode::NORMAL );
280 ConditionalWait::ScopedLock updateLock( mUpdateRenderThreadWaitCondition );
281 mPendingRequestUpdate = TRUE;
284 void CombinedUpdateRenderController::RequestUpdateOnce( UpdateMode updateMode )
286 // Increment the update-request count to the maximum
287 if( mUpdateRequestCount < MAXIMUM_UPDATE_REQUESTS )
289 ++mUpdateRequestCount;
292 if( IsUpdateRenderThreadPaused() )
296 // Run Update/Render once
297 RunUpdateRenderThread( ONCE, AnimationProgression::NONE, updateMode );
301 void CombinedUpdateRenderController::ReplaceSurface( Dali::RenderSurfaceInterface* newSurface )
305 if( mUpdateRenderThread )
307 // Set the ThreadSyncronizationInterface on the new surface
308 newSurface->SetThreadSynchronization( *this );
310 LOG_EVENT( "Starting to replace the surface, event-thread blocked" );
312 // Start replacing the surface.
314 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
315 mPostRendering = FALSE; // Clear the post-rendering flag as Update/Render thread will replace the surface now
316 mNewSurface = newSurface;
317 mUpdateRenderThreadWaitCondition.Notify( lock );
320 // Wait until the surface has been replaced
321 sem_wait( &mEventThreadSemaphore );
323 LOG_EVENT( "Surface replaced, event-thread continuing" );
327 void CombinedUpdateRenderController::DeleteSurface( Dali::RenderSurfaceInterface* surface )
331 if( mUpdateRenderThread )
333 LOG_EVENT( "Starting to delete the surface, event-thread blocked" );
335 // Start replacing the surface.
337 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
338 mPostRendering = FALSE; // Clear the post-rendering flag as Update/Render thread will delete the surface now
339 mDeletedSurface = surface;
340 mUpdateRenderThreadWaitCondition.Notify( lock );
343 // Wait until the surface has been deleted
344 sem_wait( &mEventThreadSemaphore );
346 LOG_EVENT( "Surface deleted, event-thread continuing" );
350 void CombinedUpdateRenderController::WaitForGraphicsInitialization()
354 if( mUpdateRenderThread )
356 LOG_EVENT( "Waiting for graphics initialisation, event-thread blocked" );
358 // Wait until the graphics has been initialised
359 sem_wait( &mGraphicsInitializeSemaphore );
361 LOG_EVENT( "graphics initialised, event-thread continuing" );
365 void CombinedUpdateRenderController::ResizeSurface()
369 LOG_EVENT( "Resize the surface" );
372 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
373 mPostRendering = FALSE; // Clear the post-rendering flag as Update/Render thread will resize the surface now
374 mSurfaceResized = TRUE;
375 mUpdateRenderThreadWaitCondition.Notify( lock );
379 void CombinedUpdateRenderController::SetRenderRefreshRate( unsigned int numberOfFramesPerRender )
381 // Not protected by lock, but written to rarely so not worth adding a lock when reading
382 mDefaultFrameDelta = numberOfFramesPerRender * DEFAULT_FRAME_DURATION_IN_SECONDS;
383 mDefaultFrameDurationMilliseconds = uint64_t( numberOfFramesPerRender ) * DEFAULT_FRAME_DURATION_IN_MILLISECONDS;
384 mDefaultFrameDurationNanoseconds = uint64_t( numberOfFramesPerRender ) * DEFAULT_FRAME_DURATION_IN_NANOSECONDS;
385 mDefaultHalfFrameNanoseconds = mDefaultFrameDurationNanoseconds / 2u;
387 LOG_EVENT( "mDefaultFrameDelta(%.6f), mDefaultFrameDurationMilliseconds(%lld), mDefaultFrameDurationNanoseconds(%lld)", mDefaultFrameDelta, mDefaultFrameDurationMilliseconds, mDefaultFrameDurationNanoseconds );
390 void CombinedUpdateRenderController::SetPreRenderCallback( CallbackBase* callback )
393 LOG_EVENT( "Set PreRender Callback" );
395 ConditionalWait::ScopedLock updateLock( mUpdateRenderThreadWaitCondition );
396 if( mPreRenderCallback )
398 delete mPreRenderCallback;
400 mPreRenderCallback = callback;
403 void CombinedUpdateRenderController::AddSurface( Dali::RenderSurfaceInterface* surface )
406 LOG_EVENT( "Surface is added" );
407 if( mUpdateRenderThread )
409 // Set the ThreadSyncronizationInterface on the added surface
410 surface->SetThreadSynchronization( *this );
414 ///////////////////////////////////////////////////////////////////////////////////////////////////
416 ///////////////////////////////////////////////////////////////////////////////////////////////////
418 void CombinedUpdateRenderController::RunUpdateRenderThread( int numberOfCycles, AnimationProgression animationProgression, UpdateMode updateMode )
420 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
421 mUpdateRenderRunCount = numberOfCycles;
422 mUpdateRenderThreadCanSleep = FALSE;
423 mUseElapsedTimeAfterWait = ( animationProgression == AnimationProgression::USE_ELAPSED_TIME );
424 mUploadWithoutRendering = ( updateMode == UpdateMode::SKIP_RENDER );
425 LOG_COUNTER_EVENT( "mUpdateRenderRunCount: %d, mUseElapsedTimeAfterWait: %d", mUpdateRenderRunCount, mUseElapsedTimeAfterWait );
426 mUpdateRenderThreadWaitCondition.Notify( lock );
429 void CombinedUpdateRenderController::PauseUpdateRenderThread()
431 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
432 mUpdateRenderRunCount = 0;
435 void CombinedUpdateRenderController::StopUpdateRenderThread()
437 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
438 mDestroyUpdateRenderThread = TRUE;
439 mUpdateRenderThreadWaitCondition.Notify( lock );
442 bool CombinedUpdateRenderController::IsUpdateRenderThreadPaused()
444 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
445 return ( mUpdateRenderRunCount != CONTINUOUS ) || // Report paused if NOT continuously running
446 mUpdateRenderThreadCanSleep; // Report paused if sleeping
449 void CombinedUpdateRenderController::ProcessSleepRequest()
453 // Decrement Update request count
454 if( mUpdateRequestCount > 0 )
456 --mUpdateRequestCount;
459 // Can sleep if our update-request count is 0
460 // Update/Render thread can choose to carry on updating if it determines more update/renders are required
461 if( mUpdateRequestCount == 0 )
463 LOG_EVENT( "Going to sleep" );
465 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
466 mUpdateRenderThreadCanSleep = TRUE;
470 ///////////////////////////////////////////////////////////////////////////////////////////////////
471 // UPDATE/RENDER THREAD
472 ///////////////////////////////////////////////////////////////////////////////////////////////////
474 void CombinedUpdateRenderController::UpdateRenderThread()
476 SetThreadName("RenderThread\0");
478 // Install a function for logging
479 mEnvironmentOptions.InstallLogFunction();
481 // Install a function for tracing
482 mEnvironmentOptions.InstallTraceFunction();
484 LOG_UPDATE_RENDER( "THREAD CREATED" );
486 // Initialize EGL & OpenGL
487 Dali::DisplayConnection& displayConnection = mAdaptorInterfaces.GetDisplayConnectionInterface();
488 displayConnection.Initialize();
490 // EGL has been initialised at this point
491 NotifyGraphicsInitialised();
493 RenderSurfaceInterface* currentSurface = nullptr;
495 GraphicsInterface& graphics = mAdaptorInterfaces.GetGraphicsInterface();
496 EglGraphics* eglGraphics = static_cast<EglGraphics *>(&graphics);
498 // This will only be created once
499 EglInterface* eglInterface = &eglGraphics->GetEglInterface();
501 Internal::Adaptor::EglImplementation& eglImpl = static_cast<Internal::Adaptor::EglImplementation&>( *eglInterface );
503 // Try to use OpenGL es 3.0
504 // ChooseConfig returns false here when the device only support gles 2.0.
505 // Because eglChooseConfig with gles 3.0 setting fails when the device only support gles 2.0 and Our default setting is gles 3.0.
506 if( !eglImpl.ChooseConfig( true, COLOR_DEPTH_32 ) )
508 // Retry to use OpenGL es 2.0
509 eglGraphics->SetGlesVersion( 20 );
510 eglImpl.ChooseConfig( true, COLOR_DEPTH_32 );
513 // Check whether surfaceless context is supported
514 bool isSurfacelessContextSupported = eglImpl.IsSurfacelessContextSupported();
515 eglGraphics->SetIsSurfacelessContextSupported( isSurfacelessContextSupported );
517 if ( isSurfacelessContextSupported )
519 // Create a surfaceless OpenGL context for shared resources
520 eglImpl.CreateContext();
521 eglImpl.MakeContextCurrent( EGL_NO_SURFACE, eglImpl.GetContext() );
525 currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
528 currentSurface->InitializeGraphics();
529 currentSurface->MakeContextCurrent();
533 eglGraphics->GetGlesInterface().ContextCreated();
535 // Tell core it has a context
536 mCore.ContextCreated();
538 NotifyThreadInitialised();
541 uint64_t lastFrameTime;
542 TimeService::GetNanoseconds( lastFrameTime );
544 LOG_UPDATE_RENDER( "THREAD INITIALISED" );
546 bool useElapsedTime = true;
547 bool updateRequired = true;
548 uint64_t timeToSleepUntil = 0;
549 int extraFramesDropped = 0;
551 const unsigned int renderToFboInterval = mEnvironmentOptions.GetRenderToFboInterval();
552 const bool renderToFboEnabled = 0u != renderToFboInterval;
553 unsigned int frameCount = 0u;
555 while( UpdateRenderReady( useElapsedTime, updateRequired, timeToSleepUntil ) )
557 LOG_UPDATE_RENDER_TRACE;
559 // Performance statistics are logged upon a VSYNC tick so use this point for a VSync marker
560 AddPerformanceMarker( PerformanceInterface::VSYNC );
562 uint64_t currentFrameStartTime = 0;
563 TimeService::GetNanoseconds( currentFrameStartTime );
565 const uint64_t timeSinceLastFrame = currentFrameStartTime - lastFrameTime;
567 // Optional FPS Tracking when continuously rendering
568 if( useElapsedTime && mFpsTracker.Enabled() )
570 float absoluteTimeSinceLastRender = timeSinceLastFrame * NANOSECONDS_TO_SECOND;
571 mFpsTracker.Track( absoluteTimeSinceLastRender );
574 lastFrameTime = currentFrameStartTime; // Store frame start time
576 //////////////////////////////
578 //////////////////////////////
580 Dali::RenderSurfaceInterface* newSurface = ShouldSurfaceBeReplaced();
581 if( DALI_UNLIKELY( newSurface ) )
583 LOG_UPDATE_RENDER_TRACE_FMT( "Replacing Surface" );
584 // This is designed for replacing pixmap surfaces, but should work for window as well
585 // we need to delete the surface and renderable (pixmap / window)
586 // Then create a new pixmap/window and new surface
587 // If the new surface has a different display connection, then the context will be lost
588 mAdaptorInterfaces.GetDisplayConnectionInterface().Initialize();
589 newSurface->InitializeGraphics();
590 newSurface->MakeContextCurrent();
591 // TODO: ReplaceGraphicsSurface doesn't work, InitializeGraphics()
592 // already creates new surface window, the surface and the context.
593 // We probably don't need ReplaceGraphicsSurface at all.
594 // newSurface->ReplaceGraphicsSurface();
598 const bool isRenderingToFbo = renderToFboEnabled && ( ( 0u == frameCount ) || ( 0u != frameCount % renderToFboInterval ) );
601 //////////////////////////////
603 //////////////////////////////
605 const unsigned int currentTime = currentFrameStartTime / NANOSECONDS_PER_MILLISECOND;
606 const unsigned int nextFrameTime = currentTime + mDefaultFrameDurationMilliseconds;
608 uint64_t noOfFramesSinceLastUpdate = 1;
609 float frameDelta = 0.0f;
612 // If using the elapsed time, then calculate frameDelta as a multiple of mDefaultFrameDelta
613 noOfFramesSinceLastUpdate += extraFramesDropped;
615 frameDelta = mDefaultFrameDelta * noOfFramesSinceLastUpdate;
617 LOG_UPDATE_RENDER( "timeSinceLastFrame(%llu) noOfFramesSinceLastUpdate(%u) frameDelta(%.6f)", timeSinceLastFrame, noOfFramesSinceLastUpdate, frameDelta );
619 Integration::UpdateStatus updateStatus;
621 AddPerformanceMarker( PerformanceInterface::UPDATE_START );
622 mCore.Update( frameDelta,
628 AddPerformanceMarker( PerformanceInterface::UPDATE_END );
630 unsigned int keepUpdatingStatus = updateStatus.KeepUpdating();
632 // Tell the event-thread to wake up (if asleep) and send a notification event to Core if required
633 if( updateStatus.NeedsNotification() )
635 mNotificationTrigger.Trigger();
636 LOG_UPDATE_RENDER( "Notification Triggered" );
640 bool surfaceResized = false;
641 bool shouldSurfaceBeResized = ShouldSurfaceBeResized();
642 if( DALI_UNLIKELY( shouldSurfaceBeResized ) )
644 if( updateStatus.SurfaceRectChanged() )
646 LOG_UPDATE_RENDER_TRACE_FMT( "Resizing Surface" );
648 surfaceResized = true;
652 // Optional logging of update/render status
653 mUpdateStatusLogger.Log( keepUpdatingStatus );
655 //////////////////////////////
657 //////////////////////////////
659 mAdaptorInterfaces.GetDisplayConnectionInterface().ConsumeEvents();
661 if( mPreRenderCallback != NULL )
663 bool keepCallback = CallbackBase::ExecuteReturn<bool>(*mPreRenderCallback);
666 delete mPreRenderCallback;
667 mPreRenderCallback = NULL;
671 if( eglImpl.IsSurfacelessContextSupported() )
673 // Make the shared surfaceless context as current before rendering
674 eglImpl.MakeContextCurrent( EGL_NO_SURFACE, eglImpl.GetContext() );
677 if( mFirstFrameAfterResume )
679 // mFirstFrameAfterResume is set to true when the thread is resumed
680 // Let eglImplementation know the first frame after thread initialized or resumed.
681 eglImpl.SetFirstFrameAfterResume();
682 mFirstFrameAfterResume = FALSE;
685 Integration::RenderStatus renderStatus;
687 AddPerformanceMarker( PerformanceInterface::RENDER_START );
689 // Upload shared resources
690 mCore.PreRender( renderStatus, mForceClear, mUploadWithoutRendering );
692 if ( !mUploadWithoutRendering )
694 // Go through each window
695 WindowContainer windows;
696 mAdaptorInterfaces.GetWindowContainerInterface( windows );
698 for( auto&& window : windows )
700 Dali::Integration::Scene scene = window->GetScene();
701 Dali::RenderSurfaceInterface* windowSurface = window->GetSurface();
703 if ( scene && windowSurface )
705 windowSurface->InitializeGraphics();
707 // Render off-screen frame buffers first if any
708 mCore.RenderScene( scene, true );
710 // Switch to the EGL context of the surface
711 windowSurface->PreRender( surfaceResized ); // Switch GL context
713 // Render the surface
714 mCore.RenderScene( scene, false );
716 windowSurface->PostRender( false, false, surfaceResized ); // Swap Buffer
721 mCore.PostRender( mUploadWithoutRendering );
723 //////////////////////////////
725 //////////////////////////////
727 Dali::RenderSurfaceInterface* deletedSurface = ShouldSurfaceBeDeleted();
728 if( DALI_UNLIKELY( deletedSurface ) )
730 LOG_UPDATE_RENDER_TRACE_FMT( "Deleting Surface" );
732 deletedSurface->DestroySurface();
737 AddPerformanceMarker( PerformanceInterface::RENDER_END );
741 // Trigger event thread to request Update/Render thread to sleep if update not required
742 if( ( Integration::KeepUpdating::NOT_REQUESTED == keepUpdatingStatus ) && !renderStatus.NeedsUpdate() )
744 mSleepTrigger->Trigger();
745 updateRequired = false;
746 LOG_UPDATE_RENDER( "Sleep Triggered" );
750 updateRequired = true;
753 //////////////////////////////
755 //////////////////////////////
757 extraFramesDropped = 0;
759 if (timeToSleepUntil == 0)
761 // If this is the first frame after the thread is initialized or resumed, we
762 // use the actual time the current frame starts from to calculate the time to
763 // sleep until the next frame.
764 timeToSleepUntil = currentFrameStartTime + mDefaultFrameDurationNanoseconds;
768 // Otherwise, always use the sleep-until time calculated in the last frame to
769 // calculate the time to sleep until the next frame. In this way, if there is
770 // any time gap between the current frame and the next frame, or if update or
771 // rendering in the current frame takes too much time so that the specified
772 // sleep-until time has already passed, it will try to keep the frames syncing
773 // by shortening the duration of the next frame.
774 timeToSleepUntil += mDefaultFrameDurationNanoseconds;
776 // Check the current time at the end of the frame
777 uint64_t currentFrameEndTime = 0;
778 TimeService::GetNanoseconds( currentFrameEndTime );
779 while ( currentFrameEndTime > timeToSleepUntil + mDefaultFrameDurationNanoseconds )
781 // We are more than one frame behind already, so just drop the next frames
782 // until the sleep-until time is later than the current time so that we can
784 timeToSleepUntil += mDefaultFrameDurationNanoseconds;
785 extraFramesDropped++;
789 // Render to FBO is intended to measure fps above 60 so sleep is not wanted.
790 if( 0u == renderToFboInterval )
792 // Sleep until at least the the default frame duration has elapsed. This will return immediately if the specified end-time has already passed.
793 TimeService::SleepUntil( timeToSleepUntil );
797 // Inform core of context destruction & shutdown EGL
798 mCore.ContextDestroyed();
799 currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
802 currentSurface->DestroySurface();
803 currentSurface = nullptr;
806 LOG_UPDATE_RENDER( "THREAD DESTROYED" );
808 // Uninstall the logging function
809 mEnvironmentOptions.UnInstallLogFunction();
812 bool CombinedUpdateRenderController::UpdateRenderReady( bool& useElapsedTime, bool updateRequired, uint64_t& timeToSleepUntil )
814 useElapsedTime = true;
816 ConditionalWait::ScopedLock updateLock( mUpdateRenderThreadWaitCondition );
817 while( ( ! mUpdateRenderRunCount || // Should try to wait if event-thread has paused the Update/Render thread
818 ( mUpdateRenderThreadCanSleep && ! updateRequired && ! mPendingRequestUpdate ) ) && // Ensure we wait if we're supposed to be sleeping AND do not require another update
819 ! mDestroyUpdateRenderThread && // Ensure we don't wait if the update-render-thread is supposed to be destroyed
820 ! mNewSurface && // Ensure we don't wait if we need to replace the surface
821 ! mDeletedSurface && // Ensure we don't wait if we need to delete the surface
822 ! mSurfaceResized ) // Ensure we don't wait if we need to resize the surface
824 LOG_UPDATE_RENDER( "WAIT: mUpdateRenderRunCount: %d", mUpdateRenderRunCount );
825 LOG_UPDATE_RENDER( " mUpdateRenderThreadCanSleep: %d, updateRequired: %d, mPendingRequestUpdate: %d", mUpdateRenderThreadCanSleep, updateRequired, mPendingRequestUpdate );
826 LOG_UPDATE_RENDER( " mDestroyUpdateRenderThread: %d", mDestroyUpdateRenderThread );
827 LOG_UPDATE_RENDER( " mNewSurface: %d", mNewSurface );
828 LOG_UPDATE_RENDER( " mDeletedSurface: %d", mDeletedSurface );
829 LOG_UPDATE_RENDER( " mSurfaceResized: %d", mSurfaceResized );
831 // Reset the time when the thread is waiting, so the sleep-until time for
832 // the first frame after resuming should be based on the actual start time
833 // of the first frame.
834 timeToSleepUntil = 0;
836 mUpdateRenderThreadWaitCondition.Wait( updateLock );
838 if( ! mUseElapsedTimeAfterWait )
840 useElapsedTime = false;
844 LOG_COUNTER_UPDATE_RENDER( "mUpdateRenderRunCount: %d", mUpdateRenderRunCount );
845 LOG_COUNTER_UPDATE_RENDER( "mUpdateRenderThreadCanSleep: %d, updateRequired: %d, mPendingRequestUpdate: %d", mUpdateRenderThreadCanSleep, updateRequired, mPendingRequestUpdate );
846 LOG_COUNTER_UPDATE_RENDER( "mDestroyUpdateRenderThread: %d", mDestroyUpdateRenderThread );
847 LOG_COUNTER_UPDATE_RENDER( "mNewSurface: %d", mNewSurface );
848 LOG_COUNTER_UPDATE_RENDER( "mDeletedSurface: %d", mDeletedSurface );
849 LOG_COUNTER_UPDATE_RENDER( "mSurfaceResized: %d", mSurfaceResized );
851 mUseElapsedTimeAfterWait = FALSE;
852 mUpdateRenderThreadCanSleep = FALSE;
853 mPendingRequestUpdate = FALSE;
855 // If we've been asked to run Update/Render cycles a finite number of times then decrement so we wait after the
856 // requested number of cycles
857 if( mUpdateRenderRunCount > 0 )
859 --mUpdateRenderRunCount;
862 // Keep the update-render thread alive if this thread is NOT to be destroyed
863 return ! mDestroyUpdateRenderThread;
866 Dali::RenderSurfaceInterface* CombinedUpdateRenderController::ShouldSurfaceBeReplaced()
868 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
870 Dali::RenderSurfaceInterface* newSurface = mNewSurface;
876 void CombinedUpdateRenderController::SurfaceReplaced()
878 // Just increment the semaphore
879 sem_post( &mEventThreadSemaphore );
882 Dali::RenderSurfaceInterface* CombinedUpdateRenderController::ShouldSurfaceBeDeleted()
884 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
886 Dali::RenderSurfaceInterface* deletedSurface = mDeletedSurface;
887 mDeletedSurface = NULL;
889 return deletedSurface;
892 void CombinedUpdateRenderController::SurfaceDeleted()
894 // Just increment the semaphore
895 sem_post( &mEventThreadSemaphore );
898 bool CombinedUpdateRenderController::ShouldSurfaceBeResized()
900 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
901 return mSurfaceResized;
904 void CombinedUpdateRenderController::SurfaceResized()
906 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
907 mSurfaceResized = FALSE;
910 ///////////////////////////////////////////////////////////////////////////////////////////////////
912 ///////////////////////////////////////////////////////////////////////////////////////////////////
914 void CombinedUpdateRenderController::NotifyThreadInitialised()
916 // Just increment the semaphore
917 sem_post( &mEventThreadSemaphore );
920 void CombinedUpdateRenderController::NotifyGraphicsInitialised()
922 sem_post( &mGraphicsInitializeSemaphore );
925 void CombinedUpdateRenderController::AddPerformanceMarker( PerformanceInterface::MarkerType type )
927 if( mPerformanceInterface )
929 mPerformanceInterface->AddMarker( type );
933 /////////////////////////////////////////////////////////////////////////////////////////////////
934 // POST RENDERING: EVENT THREAD
935 /////////////////////////////////////////////////////////////////////////////////////////////////
937 void CombinedUpdateRenderController::PostRenderComplete()
939 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
940 mPostRendering = FALSE;
941 mUpdateRenderThreadWaitCondition.Notify( lock );
944 ///////////////////////////////////////////////////////////////////////////////////////////////////
945 // POST RENDERING: RENDER THREAD
946 ///////////////////////////////////////////////////////////////////////////////////////////////////
948 void CombinedUpdateRenderController::PostRenderStarted()
950 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
951 mPostRendering = TRUE;
954 void CombinedUpdateRenderController::PostRenderWaitForCompletion()
956 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
957 while( mPostRendering &&
958 ! mNewSurface && // We should NOT wait if we're replacing the surface
959 ! mDeletedSurface && // We should NOT wait if we're deleting the surface
960 ! mDestroyUpdateRenderThread )
962 mUpdateRenderThreadWaitCondition.Wait( lock );
966 } // namespace Adaptor
968 } // namespace Internal