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>
25 #include "dali/public-api/common/dali-common.h"
28 #include <dali/integration-api/adaptor-framework/trigger-event-factory.h>
29 #include <dali/devel-api/adaptor-framework/thread-settings.h>
30 #include <dali/internal/adaptor/common/adaptor-internal-services.h>
31 #include <dali/internal/adaptor/common/combined-update-render-controller-debug.h>
32 #include <dali/internal/graphics/gles/egl-graphics.h>
33 #include <dali/internal/graphics/gles/egl-implementation.h>
34 #include <dali/internal/graphics/common/graphics-interface.h>
35 #include <dali/internal/system/common/environment-options.h>
36 #include <dali/internal/system/common/time-service.h>
37 #include <dali/internal/window-system/common/window-impl.h>
51 const unsigned int CREATED_THREAD_COUNT = 1u;
53 const int CONTINUOUS = -1;
56 const unsigned int TRUE = 1u;
57 const unsigned int FALSE = 0u;
59 const unsigned int MILLISECONDS_PER_SECOND( 1e+3 );
60 const float NANOSECONDS_TO_SECOND( 1e-9f );
61 const unsigned int NANOSECONDS_PER_SECOND( 1e+9 );
62 const unsigned int NANOSECONDS_PER_MILLISECOND( 1e+6 );
64 // The following values will get calculated at compile time
65 const float DEFAULT_FRAME_DURATION_IN_SECONDS( 1.0f / 60.0f );
66 const uint64_t DEFAULT_FRAME_DURATION_IN_MILLISECONDS( DEFAULT_FRAME_DURATION_IN_SECONDS * MILLISECONDS_PER_SECOND );
67 const uint64_t DEFAULT_FRAME_DURATION_IN_NANOSECONDS( DEFAULT_FRAME_DURATION_IN_SECONDS * NANOSECONDS_PER_SECOND );
70 * 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
71 * there is a danger that, on the event-thread we could have:
72 * 1) An update-request where we do nothing as Update/Render thread still running.
73 * 2) Quickly followed by a sleep-request being handled where we pause the Update/Render Thread (even though we have an update to process).
75 * 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:
76 * 1) MAIN THREAD: Update Request: COUNTER = 1
77 * 2) UPDATE/RENDER THREAD: Do Update/Render, then no Updates required -> Sleep Trigger
78 * 3) MAIN THREAD: Update Request: COUNTER = 2
79 * 4) MAIN THREAD: Sleep Request: COUNTER = 1 -> We do not sleep just yet
81 * Also ensures we preserve battery life by only doing ONE update when the above use case is not triggered.
82 * 1) MAIN THREAD: Update Request: COUNTER = 1
83 * 2) UPDATE/RENDER THREAD: Do Update/Render, then no Updates required -> Sleep Trigger
84 * 3) MAIN THREAD: Sleep Request: COUNTER = 0 -> Go to sleep
86 const unsigned int MAXIMUM_UPDATE_REQUESTS = 2;
87 } // unnamed namespace
89 ///////////////////////////////////////////////////////////////////////////////////////////////////
91 ///////////////////////////////////////////////////////////////////////////////////////////////////
93 CombinedUpdateRenderController::CombinedUpdateRenderController( AdaptorInternalServices& adaptorInterfaces, const EnvironmentOptions& environmentOptions, ThreadMode threadMode )
94 : mFpsTracker( environmentOptions ),
95 mUpdateStatusLogger( environmentOptions ),
96 mEventThreadSemaphore(0),
98 mUpdateRenderThreadWaitCondition(),
99 mAdaptorInterfaces( adaptorInterfaces ),
100 mPerformanceInterface( adaptorInterfaces.GetPerformanceInterface() ),
101 mCore( adaptorInterfaces.GetCore() ),
102 mEnvironmentOptions( environmentOptions ),
103 mNotificationTrigger( adaptorInterfaces.GetProcessCoreEventsTrigger() ),
104 mSleepTrigger( NULL ),
105 mPreRenderCallback( NULL ),
106 mUpdateRenderThread( NULL ),
107 mDefaultFrameDelta( 0.0f ),
108 mDefaultFrameDurationMilliseconds( 0u ),
109 mDefaultFrameDurationNanoseconds( 0u ),
110 mDefaultHalfFrameNanoseconds( 0u ),
111 mUpdateRequestCount( 0u ),
113 mThreadMode( threadMode ),
114 mUpdateRenderRunCount( 0 ),
115 mDestroyUpdateRenderThread( FALSE ),
116 mUpdateRenderThreadCanSleep( FALSE ),
117 mPendingRequestUpdate( FALSE ),
118 mUseElapsedTimeAfterWait( FALSE ),
120 mDeletedSurface( nullptr ),
121 mPostRendering( FALSE ),
122 mSurfaceResized( FALSE ),
123 mForceClear( FALSE ),
124 mUploadWithoutRendering( FALSE ),
125 mFirstFrameAfterResume( FALSE )
129 // Initialise frame delta/duration variables first
130 SetRenderRefreshRate( environmentOptions.GetRenderRefreshRate() );
132 // Set the thread-synchronization interface on the render-surface
133 Dali::RenderSurfaceInterface* currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
136 currentSurface->SetThreadSynchronization( *this );
139 mSleepTrigger = TriggerEventFactory::CreateTriggerEvent( MakeCallback( this, &CombinedUpdateRenderController::ProcessSleepRequest ), TriggerEventInterface::KEEP_ALIVE_AFTER_TRIGGER );
142 CombinedUpdateRenderController::~CombinedUpdateRenderController()
148 delete mPreRenderCallback;
149 delete mSleepTrigger;
152 void CombinedUpdateRenderController::Initialize()
156 // Ensure Update/Render Thread not already created
157 DALI_ASSERT_ALWAYS( ! mUpdateRenderThread );
159 // Create Update/Render Thread
160 ConditionalWait::ScopedLock lock(mGraphicsInitializeWait);
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 mEventThreadSemaphore.Acquire();
181 Dali::RenderSurfaceInterface* 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 Dali::RenderSurfaceInterface* 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() || updateMode == UpdateMode::FORCE_RENDER )
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 mSurfaceSemaphore.Acquire();
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 mSurfaceSemaphore.Acquire();
345 LOG_EVENT( "Surface deleted, event-thread continuing" );
349 void CombinedUpdateRenderController::WaitForGraphicsInitialization()
351 ConditionalWait::ScopedLock lk(mGraphicsInitializeWait);
354 if( mUpdateRenderThread )
356 LOG_EVENT( "Waiting for graphics initialisation, event-thread blocked" );
358 // Wait until the graphics has been initialised
359 mGraphicsInitializeWait.Wait(lk);
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 );
422 switch( mThreadMode )
424 case ThreadMode::NORMAL:
426 mUpdateRenderRunCount = numberOfCycles;
427 mUseElapsedTimeAfterWait = ( animationProgression == AnimationProgression::USE_ELAPSED_TIME );
430 case ThreadMode::RUN_IF_REQUESTED:
432 if( updateMode != UpdateMode::FORCE_RENDER )
434 // Render only if the update mode is FORCE_RENDER which means the application requests it.
435 // We don't want to awake the update thread.
439 mUpdateRenderRunCount++; // Increase the update request count
440 mUseElapsedTimeAfterWait = TRUE; // The elapsed time should be used. We want animations to proceed.
445 mUpdateRenderThreadCanSleep = FALSE;
446 mUploadWithoutRendering = ( updateMode == UpdateMode::SKIP_RENDER );
447 LOG_COUNTER_EVENT( "mUpdateRenderRunCount: %d, mUseElapsedTimeAfterWait: %d", mUpdateRenderRunCount, mUseElapsedTimeAfterWait );
448 mUpdateRenderThreadWaitCondition.Notify( lock );
451 void CombinedUpdateRenderController::PauseUpdateRenderThread()
453 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
454 mUpdateRenderRunCount = 0;
457 void CombinedUpdateRenderController::StopUpdateRenderThread()
459 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
460 mDestroyUpdateRenderThread = TRUE;
461 mUpdateRenderThreadWaitCondition.Notify( lock );
464 bool CombinedUpdateRenderController::IsUpdateRenderThreadPaused()
466 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
468 if( mThreadMode == ThreadMode::RUN_IF_REQUESTED )
470 return !mRunning || mUpdateRenderThreadCanSleep;
473 return ( mUpdateRenderRunCount != CONTINUOUS ) || // Report paused if NOT continuously running
474 mUpdateRenderThreadCanSleep; // Report paused if sleeping
477 void CombinedUpdateRenderController::ProcessSleepRequest()
481 // Decrement Update request count
482 if( mUpdateRequestCount > 0 )
484 --mUpdateRequestCount;
487 // Can sleep if our update-request count is 0
488 // Update/Render thread can choose to carry on updating if it determines more update/renders are required
489 if( mUpdateRequestCount == 0 )
491 LOG_EVENT( "Going to sleep" );
493 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
494 mUpdateRenderThreadCanSleep = TRUE;
498 ///////////////////////////////////////////////////////////////////////////////////////////////////
499 // UPDATE/RENDER THREAD
500 ///////////////////////////////////////////////////////////////////////////////////////////////////
502 void CombinedUpdateRenderController::UpdateRenderThread()
504 SetThreadName("RenderThread\0");
506 // Install a function for logging
507 mEnvironmentOptions.InstallLogFunction();
509 // Install a function for tracing
510 mEnvironmentOptions.InstallTraceFunction();
512 LOG_UPDATE_RENDER( "THREAD CREATED" );
514 // Initialize EGL & OpenGL
515 Dali::DisplayConnection& displayConnection = mAdaptorInterfaces.GetDisplayConnectionInterface();
516 displayConnection.Initialize();
518 // EGL has been initialised at this point
519 NotifyGraphicsInitialised();
521 RenderSurfaceInterface* currentSurface = nullptr;
523 GraphicsInterface& graphics = mAdaptorInterfaces.GetGraphicsInterface();
524 EglGraphics* eglGraphics = static_cast<EglGraphics *>(&graphics);
526 // This will only be created once
527 EglInterface* eglInterface = &eglGraphics->GetEglInterface();
529 Internal::Adaptor::EglImplementation& eglImpl = static_cast<Internal::Adaptor::EglImplementation&>( *eglInterface );
531 // Try to use OpenGL es 3.0
532 // ChooseConfig returns false here when the device only support gles 2.0.
533 // Because eglChooseConfig with gles 3.0 setting fails when the device only support gles 2.0 and Our default setting is gles 3.0.
534 if( !eglImpl.ChooseConfig( true, COLOR_DEPTH_32 ) )
536 // Retry to use OpenGL es 2.0
537 eglGraphics->SetGlesVersion( 20 );
538 eglImpl.ChooseConfig( true, COLOR_DEPTH_32 );
541 // Check whether surfaceless context is supported
542 bool isSurfacelessContextSupported = eglImpl.IsSurfacelessContextSupported();
543 eglGraphics->SetIsSurfacelessContextSupported( isSurfacelessContextSupported );
545 if ( isSurfacelessContextSupported )
547 // Create a surfaceless OpenGL context for shared resources
548 eglImpl.CreateContext();
549 eglImpl.MakeContextCurrent( EGL_NO_SURFACE, eglImpl.GetContext() );
553 currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
556 currentSurface->InitializeGraphics();
557 currentSurface->MakeContextCurrent();
561 GlImplementation& gles = eglGraphics->GetGlesInterface();
562 gles.ContextCreated();
563 eglGraphics->SetGlesVersion( gles.GetGlesVersion() );
565 // Tell core it has a context
566 mCore.ContextCreated();
568 NotifyThreadInitialised();
571 uint64_t lastFrameTime;
572 TimeService::GetNanoseconds( lastFrameTime );
574 LOG_UPDATE_RENDER( "THREAD INITIALISED" );
576 bool useElapsedTime = true;
577 bool updateRequired = true;
578 uint64_t timeToSleepUntil = 0;
579 int extraFramesDropped = 0;
581 const unsigned int renderToFboInterval = mEnvironmentOptions.GetRenderToFboInterval();
582 const bool renderToFboEnabled = 0u != renderToFboInterval;
583 unsigned int frameCount = 0u;
585 while( UpdateRenderReady( useElapsedTime, updateRequired, timeToSleepUntil ) )
587 LOG_UPDATE_RENDER_TRACE;
589 // Performance statistics are logged upon a VSYNC tick so use this point for a VSync marker
590 AddPerformanceMarker( PerformanceInterface::VSYNC );
592 uint64_t currentFrameStartTime = 0;
593 TimeService::GetNanoseconds( currentFrameStartTime );
595 uint64_t timeSinceLastFrame = currentFrameStartTime - lastFrameTime;
597 // Optional FPS Tracking when continuously rendering
598 if( useElapsedTime && mFpsTracker.Enabled() )
600 float absoluteTimeSinceLastRender = timeSinceLastFrame * NANOSECONDS_TO_SECOND;
601 mFpsTracker.Track( absoluteTimeSinceLastRender );
604 lastFrameTime = currentFrameStartTime; // Store frame start time
606 //////////////////////////////
608 //////////////////////////////
610 Dali::RenderSurfaceInterface* newSurface = ShouldSurfaceBeReplaced();
611 if( DALI_UNLIKELY( newSurface ) )
613 LOG_UPDATE_RENDER_TRACE_FMT( "Replacing Surface" );
614 // This is designed for replacing pixmap surfaces, but should work for window as well
615 // we need to delete the surface and renderable (pixmap / window)
616 // Then create a new pixmap/window and new surface
617 // If the new surface has a different display connection, then the context will be lost
618 mAdaptorInterfaces.GetDisplayConnectionInterface().Initialize();
619 newSurface->InitializeGraphics();
620 newSurface->MakeContextCurrent();
621 // TODO: ReplaceGraphicsSurface doesn't work, InitializeGraphics()
622 // already creates new surface window, the surface and the context.
623 // We probably don't need ReplaceGraphicsSurface at all.
624 // newSurface->ReplaceGraphicsSurface();
628 const bool isRenderingToFbo = renderToFboEnabled && ( ( 0u == frameCount ) || ( 0u != frameCount % renderToFboInterval ) );
631 //////////////////////////////
633 //////////////////////////////
635 const unsigned int currentTime = currentFrameStartTime / NANOSECONDS_PER_MILLISECOND;
636 const unsigned int nextFrameTime = currentTime + mDefaultFrameDurationMilliseconds;
638 uint64_t noOfFramesSinceLastUpdate = 1;
639 float frameDelta = 0.0f;
642 if( mThreadMode == ThreadMode::RUN_IF_REQUESTED )
644 extraFramesDropped = 0;
645 while( timeSinceLastFrame >= mDefaultFrameDurationNanoseconds )
647 timeSinceLastFrame -= mDefaultFrameDurationNanoseconds;
648 extraFramesDropped++;
652 // If using the elapsed time, then calculate frameDelta as a multiple of mDefaultFrameDelta
653 noOfFramesSinceLastUpdate += extraFramesDropped;
655 frameDelta = mDefaultFrameDelta * noOfFramesSinceLastUpdate;
657 LOG_UPDATE_RENDER( "timeSinceLastFrame(%llu) noOfFramesSinceLastUpdate(%u) frameDelta(%.6f)", timeSinceLastFrame, noOfFramesSinceLastUpdate, frameDelta );
659 Integration::UpdateStatus updateStatus;
661 AddPerformanceMarker( PerformanceInterface::UPDATE_START );
662 mCore.Update( frameDelta,
668 AddPerformanceMarker( PerformanceInterface::UPDATE_END );
670 unsigned int keepUpdatingStatus = updateStatus.KeepUpdating();
672 // Tell the event-thread to wake up (if asleep) and send a notification event to Core if required
673 if( updateStatus.NeedsNotification() )
675 mNotificationTrigger.Trigger();
676 LOG_UPDATE_RENDER( "Notification Triggered" );
680 bool surfaceResized = false;
681 bool shouldSurfaceBeResized = ShouldSurfaceBeResized();
682 if( DALI_UNLIKELY( shouldSurfaceBeResized ) )
684 if( updateStatus.SurfaceRectChanged() )
686 LOG_UPDATE_RENDER_TRACE_FMT( "Resizing Surface" );
688 surfaceResized = true;
692 // Optional logging of update/render status
693 mUpdateStatusLogger.Log( keepUpdatingStatus );
695 //////////////////////////////
697 //////////////////////////////
699 mAdaptorInterfaces.GetDisplayConnectionInterface().ConsumeEvents();
701 if( mPreRenderCallback != NULL )
703 bool keepCallback = CallbackBase::ExecuteReturn<bool>(*mPreRenderCallback);
706 delete mPreRenderCallback;
707 mPreRenderCallback = NULL;
711 if( eglImpl.IsSurfacelessContextSupported() )
713 // Make the shared surfaceless context as current before rendering
714 eglImpl.MakeContextCurrent( EGL_NO_SURFACE, eglImpl.GetContext() );
717 if( mFirstFrameAfterResume )
719 // mFirstFrameAfterResume is set to true when the thread is resumed
720 // Let eglImplementation know the first frame after thread initialized or resumed.
721 eglImpl.SetFirstFrameAfterResume();
722 mFirstFrameAfterResume = FALSE;
725 Integration::RenderStatus renderStatus;
727 AddPerformanceMarker( PerformanceInterface::RENDER_START );
729 // Upload shared resources
730 mCore.PreRender( renderStatus, mForceClear, mUploadWithoutRendering );
732 if ( !mUploadWithoutRendering )
734 // Go through each window
735 WindowContainer windows;
736 mAdaptorInterfaces.GetWindowContainerInterface( windows );
738 for( auto&& window : windows )
740 Dali::Integration::Scene scene = window->GetScene();
741 Dali::RenderSurfaceInterface* windowSurface = window->GetSurface();
743 if ( scene && windowSurface )
745 Integration::RenderStatus windowRenderStatus;
747 windowSurface->InitializeGraphics();
749 // clear previous frame damaged render items rects, buffer history is tracked on surface level
750 mDamagedRects.clear();
752 // Collect damage rects
753 mCore.PreRender( scene, mDamagedRects );
755 // Render off-screen frame buffers first if any
756 mCore.RenderScene( windowRenderStatus, scene, true );
758 Rect<int> clippingRect; // Empty for fbo rendering
760 // Switch to the EGL context of the surface, merge damaged areas for previous frames
761 windowSurface->PreRender( surfaceResized, mDamagedRects, clippingRect ); // Switch GL context
763 if (clippingRect.IsEmpty())
765 mDamagedRects.clear();
768 // Render the surface
769 mCore.RenderScene( windowRenderStatus, scene, false, clippingRect );
771 if( windowRenderStatus.NeedsPostRender() )
773 windowSurface->PostRender( false, false, surfaceResized, mDamagedRects ); // Swap Buffer with damage
779 mCore.PostRender( mUploadWithoutRendering );
781 //////////////////////////////
783 //////////////////////////////
785 Dali::RenderSurfaceInterface* deletedSurface = ShouldSurfaceBeDeleted();
786 if( DALI_UNLIKELY( deletedSurface ) )
788 LOG_UPDATE_RENDER_TRACE_FMT( "Deleting Surface" );
790 deletedSurface->DestroySurface();
795 AddPerformanceMarker( PerformanceInterface::RENDER_END );
799 // Trigger event thread to request Update/Render thread to sleep if update not required
800 if( ( Integration::KeepUpdating::NOT_REQUESTED == keepUpdatingStatus ) && !renderStatus.NeedsUpdate() )
802 mSleepTrigger->Trigger();
803 updateRequired = false;
804 LOG_UPDATE_RENDER( "Sleep Triggered" );
808 updateRequired = true;
811 //////////////////////////////
813 //////////////////////////////
815 extraFramesDropped = 0;
817 if (timeToSleepUntil == 0)
819 // If this is the first frame after the thread is initialized or resumed, we
820 // use the actual time the current frame starts from to calculate the time to
821 // sleep until the next frame.
822 timeToSleepUntil = currentFrameStartTime + mDefaultFrameDurationNanoseconds;
826 // Otherwise, always use the sleep-until time calculated in the last frame to
827 // calculate the time to sleep until the next frame. In this way, if there is
828 // any time gap between the current frame and the next frame, or if update or
829 // rendering in the current frame takes too much time so that the specified
830 // sleep-until time has already passed, it will try to keep the frames syncing
831 // by shortening the duration of the next frame.
832 timeToSleepUntil += mDefaultFrameDurationNanoseconds;
834 // Check the current time at the end of the frame
835 uint64_t currentFrameEndTime = 0;
836 TimeService::GetNanoseconds( currentFrameEndTime );
837 while ( currentFrameEndTime > timeToSleepUntil + mDefaultFrameDurationNanoseconds )
839 // We are more than one frame behind already, so just drop the next frames
840 // until the sleep-until time is later than the current time so that we can
842 timeToSleepUntil += mDefaultFrameDurationNanoseconds;
843 extraFramesDropped++;
847 // Render to FBO is intended to measure fps above 60 so sleep is not wanted.
848 if( 0u == renderToFboInterval )
850 // Sleep until at least the the default frame duration has elapsed. This will return immediately if the specified end-time has already passed.
851 TimeService::SleepUntil( timeToSleepUntil );
855 // Inform core of context destruction
856 mCore.ContextDestroyed();
858 WindowContainer windows;
859 mAdaptorInterfaces.GetWindowContainerInterface( windows );
862 for( auto&& window : windows )
864 Dali::RenderSurfaceInterface* surface = window->GetSurface();
865 surface->DestroySurface();
869 eglInterface->TerminateGles();
871 LOG_UPDATE_RENDER( "THREAD DESTROYED" );
873 // Uninstall the logging function
874 mEnvironmentOptions.UnInstallLogFunction();
877 bool CombinedUpdateRenderController::UpdateRenderReady( bool& useElapsedTime, bool updateRequired, uint64_t& timeToSleepUntil )
879 useElapsedTime = true;
881 ConditionalWait::ScopedLock updateLock( mUpdateRenderThreadWaitCondition );
882 while( ( ! mUpdateRenderRunCount || // Should try to wait if event-thread has paused the Update/Render thread
883 ( mUpdateRenderThreadCanSleep && ! updateRequired && ! mPendingRequestUpdate ) ) && // Ensure we wait if we're supposed to be sleeping AND do not require another update
884 ! mDestroyUpdateRenderThread && // Ensure we don't wait if the update-render-thread is supposed to be destroyed
885 ! mNewSurface && // Ensure we don't wait if we need to replace the surface
886 ! mDeletedSurface && // Ensure we don't wait if we need to delete the surface
887 ! mSurfaceResized ) // Ensure we don't wait if we need to resize the surface
889 LOG_UPDATE_RENDER( "WAIT: mUpdateRenderRunCount: %d", mUpdateRenderRunCount );
890 LOG_UPDATE_RENDER( " mUpdateRenderThreadCanSleep: %d, updateRequired: %d, mPendingRequestUpdate: %d", mUpdateRenderThreadCanSleep, updateRequired, mPendingRequestUpdate );
891 LOG_UPDATE_RENDER( " mDestroyUpdateRenderThread: %d", mDestroyUpdateRenderThread );
892 LOG_UPDATE_RENDER( " mNewSurface: %d", mNewSurface );
893 LOG_UPDATE_RENDER( " mDeletedSurface: %d", mDeletedSurface );
894 LOG_UPDATE_RENDER( " mSurfaceResized: %d", mSurfaceResized );
896 // Reset the time when the thread is waiting, so the sleep-until time for
897 // the first frame after resuming should be based on the actual start time
898 // of the first frame.
899 timeToSleepUntil = 0;
901 mUpdateRenderThreadWaitCondition.Wait( updateLock );
903 if( ! mUseElapsedTimeAfterWait )
905 useElapsedTime = false;
909 LOG_COUNTER_UPDATE_RENDER( "mUpdateRenderRunCount: %d", mUpdateRenderRunCount );
910 LOG_COUNTER_UPDATE_RENDER( "mUpdateRenderThreadCanSleep: %d, updateRequired: %d, mPendingRequestUpdate: %d", mUpdateRenderThreadCanSleep, updateRequired, mPendingRequestUpdate );
911 LOG_COUNTER_UPDATE_RENDER( "mDestroyUpdateRenderThread: %d", mDestroyUpdateRenderThread );
912 LOG_COUNTER_UPDATE_RENDER( "mNewSurface: %d", mNewSurface );
913 LOG_COUNTER_UPDATE_RENDER( "mDeletedSurface: %d", mDeletedSurface );
914 LOG_COUNTER_UPDATE_RENDER( "mSurfaceResized: %d", mSurfaceResized );
916 mUseElapsedTimeAfterWait = FALSE;
917 mUpdateRenderThreadCanSleep = FALSE;
918 mPendingRequestUpdate = FALSE;
920 // If we've been asked to run Update/Render cycles a finite number of times then decrement so we wait after the
921 // requested number of cycles
922 if( mUpdateRenderRunCount > 0 )
924 --mUpdateRenderRunCount;
927 // Keep the update-render thread alive if this thread is NOT to be destroyed
928 return ! mDestroyUpdateRenderThread;
931 Dali::RenderSurfaceInterface* CombinedUpdateRenderController::ShouldSurfaceBeReplaced()
933 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
935 Dali::RenderSurfaceInterface* newSurface = mNewSurface;
941 void CombinedUpdateRenderController::SurfaceReplaced()
943 // Just increment the semaphore
944 mSurfaceSemaphore.Release(1);
947 Dali::RenderSurfaceInterface* CombinedUpdateRenderController::ShouldSurfaceBeDeleted()
949 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
951 Dali::RenderSurfaceInterface* deletedSurface = mDeletedSurface;
952 mDeletedSurface = NULL;
954 return deletedSurface;
957 void CombinedUpdateRenderController::SurfaceDeleted()
959 // Just increment the semaphore
960 mSurfaceSemaphore.Release(1);
963 bool CombinedUpdateRenderController::ShouldSurfaceBeResized()
965 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
966 return mSurfaceResized;
969 void CombinedUpdateRenderController::SurfaceResized()
971 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
972 mSurfaceResized = FALSE;
975 ///////////////////////////////////////////////////////////////////////////////////////////////////
977 ///////////////////////////////////////////////////////////////////////////////////////////////////
979 void CombinedUpdateRenderController::NotifyThreadInitialised()
981 // Just increment the semaphore
982 mEventThreadSemaphore.Release(1);
985 void CombinedUpdateRenderController::NotifyGraphicsInitialised()
987 mGraphicsInitializeWait.Notify();
990 void CombinedUpdateRenderController::AddPerformanceMarker( PerformanceInterface::MarkerType type )
992 if( mPerformanceInterface )
994 mPerformanceInterface->AddMarker( type );
998 /////////////////////////////////////////////////////////////////////////////////////////////////
999 // POST RENDERING: EVENT THREAD
1000 /////////////////////////////////////////////////////////////////////////////////////////////////
1002 void CombinedUpdateRenderController::PostRenderComplete()
1004 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
1005 mPostRendering = FALSE;
1006 mUpdateRenderThreadWaitCondition.Notify( lock );
1009 ///////////////////////////////////////////////////////////////////////////////////////////////////
1010 // POST RENDERING: RENDER THREAD
1011 ///////////////////////////////////////////////////////////////////////////////////////////////////
1013 void CombinedUpdateRenderController::PostRenderStarted()
1015 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
1016 mPostRendering = TRUE;
1019 void CombinedUpdateRenderController::PostRenderWaitForCompletion()
1021 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
1022 while( mPostRendering &&
1023 ! mNewSurface && // We should NOT wait if we're replacing the surface
1024 ! mDeletedSurface && // We should NOT wait if we're deleting the surface
1025 ! mDestroyUpdateRenderThread )
1027 mUpdateRenderThreadWaitCondition.Wait( lock );
1031 } // namespace Adaptor
1033 } // namespace Internal