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 mPostRendering( FALSE ),
119 mSurfaceResized( FALSE ),
120 mForceClear( FALSE ),
121 mUploadWithoutRendering( FALSE ),
122 mFirstFrameAfterResume( FALSE ),
123 mIsRenderingWindows( 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::WaitForGraphicsInitialization()
331 if( mUpdateRenderThread )
333 LOG_EVENT( "Waiting for graphics initialisation, event-thread blocked" );
335 // Wait until the graphics has been initialised
336 sem_wait( &mGraphicsInitializeSemaphore );
338 LOG_EVENT( "graphics initialised, event-thread continuing" );
342 void CombinedUpdateRenderController::ResizeSurface()
346 LOG_EVENT( "Resize the surface" );
349 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
350 mPostRendering = FALSE; // Clear the post-rendering flag as Update/Render thread will resize the surface now
351 mSurfaceResized = TRUE;
352 mUpdateRenderThreadWaitCondition.Notify( lock );
356 void CombinedUpdateRenderController::SetRenderRefreshRate( unsigned int numberOfFramesPerRender )
358 // Not protected by lock, but written to rarely so not worth adding a lock when reading
359 mDefaultFrameDelta = numberOfFramesPerRender * DEFAULT_FRAME_DURATION_IN_SECONDS;
360 mDefaultFrameDurationMilliseconds = uint64_t( numberOfFramesPerRender ) * DEFAULT_FRAME_DURATION_IN_MILLISECONDS;
361 mDefaultFrameDurationNanoseconds = uint64_t( numberOfFramesPerRender ) * DEFAULT_FRAME_DURATION_IN_NANOSECONDS;
362 mDefaultHalfFrameNanoseconds = mDefaultFrameDurationNanoseconds / 2u;
364 LOG_EVENT( "mDefaultFrameDelta(%.6f), mDefaultFrameDurationMilliseconds(%lld), mDefaultFrameDurationNanoseconds(%lld)", mDefaultFrameDelta, mDefaultFrameDurationMilliseconds, mDefaultFrameDurationNanoseconds );
367 void CombinedUpdateRenderController::SetPreRenderCallback( CallbackBase* callback )
370 LOG_EVENT( "Set PreRender Callback" );
372 ConditionalWait::ScopedLock updateLock( mUpdateRenderThreadWaitCondition );
373 if( mPreRenderCallback )
375 delete mPreRenderCallback;
377 mPreRenderCallback = callback;
380 void CombinedUpdateRenderController::AddSurface( Dali::RenderSurfaceInterface* surface )
383 LOG_EVENT( "Surface is added" );
384 if( mUpdateRenderThread )
386 // Set the ThreadSyncronizationInterface on the added surface
387 surface->SetThreadSynchronization( *this );
391 ///////////////////////////////////////////////////////////////////////////////////////////////////
393 ///////////////////////////////////////////////////////////////////////////////////////////////////
395 void CombinedUpdateRenderController::RunUpdateRenderThread( int numberOfCycles, AnimationProgression animationProgression, UpdateMode updateMode )
397 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
398 mUpdateRenderRunCount = numberOfCycles;
399 mUpdateRenderThreadCanSleep = FALSE;
400 mUseElapsedTimeAfterWait = ( animationProgression == AnimationProgression::USE_ELAPSED_TIME );
401 mUploadWithoutRendering = ( updateMode == UpdateMode::SKIP_RENDER );
402 LOG_COUNTER_EVENT( "mUpdateRenderRunCount: %d, mUseElapsedTimeAfterWait: %d", mUpdateRenderRunCount, mUseElapsedTimeAfterWait );
403 mUpdateRenderThreadWaitCondition.Notify( lock );
406 void CombinedUpdateRenderController::PauseUpdateRenderThread()
408 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
409 mUpdateRenderRunCount = 0;
412 void CombinedUpdateRenderController::StopUpdateRenderThread()
414 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
415 mDestroyUpdateRenderThread = TRUE;
416 mUpdateRenderThreadWaitCondition.Notify( lock );
419 bool CombinedUpdateRenderController::IsUpdateRenderThreadPaused()
421 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
422 return ( mUpdateRenderRunCount != CONTINUOUS ) || // Report paused if NOT continuously running
423 mUpdateRenderThreadCanSleep; // Report paused if sleeping
426 void CombinedUpdateRenderController::ProcessSleepRequest()
430 // Decrement Update request count
431 if( mUpdateRequestCount > 0 )
433 --mUpdateRequestCount;
436 // Can sleep if our update-request count is 0
437 // Update/Render thread can choose to carry on updating if it determines more update/renders are required
438 if( mUpdateRequestCount == 0 )
440 LOG_EVENT( "Going to sleep" );
442 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
443 mUpdateRenderThreadCanSleep = TRUE;
447 ///////////////////////////////////////////////////////////////////////////////////////////////////
448 // UPDATE/RENDER THREAD
449 ///////////////////////////////////////////////////////////////////////////////////////////////////
451 void CombinedUpdateRenderController::UpdateRenderThread()
453 SetThreadName("RenderThread\0");
455 // Install a function for logging
456 mEnvironmentOptions.InstallLogFunction();
458 // Install a function for tracing
459 mEnvironmentOptions.InstallTraceFunction();
461 LOG_UPDATE_RENDER( "THREAD CREATED" );
463 // Initialize EGL & OpenGL
464 Dali::DisplayConnection& displayConnection = mAdaptorInterfaces.GetDisplayConnectionInterface();
465 displayConnection.Initialize();
467 // EGL has been initialised at this point
468 NotifyGraphicsInitialised();
470 RenderSurfaceInterface* currentSurface = nullptr;
472 GraphicsInterface& graphics = mAdaptorInterfaces.GetGraphicsInterface();
473 EglGraphics* eglGraphics = static_cast<EglGraphics *>(&graphics);
475 // This will only be created once
476 EglInterface* eglInterface = &eglGraphics->GetEglInterface();
478 Internal::Adaptor::EglImplementation& eglImpl = static_cast<Internal::Adaptor::EglImplementation&>( *eglInterface );
480 // Try to use OpenGL es 3.0
481 // ChooseConfig returns false here when the device only support gles 2.0.
482 // Because eglChooseConfig with gles 3.0 setting fails when the device only support gles 2.0 and Our default setting is gles 3.0.
483 if( !eglImpl.ChooseConfig( true, COLOR_DEPTH_32 ) )
485 // Retry to use OpenGL es 2.0
486 eglGraphics->SetGlesVersion( 20 );
487 eglImpl.ChooseConfig( true, COLOR_DEPTH_32 );
490 // Check whether surfaceless context is supported
491 bool isSurfacelessContextSupported = eglImpl.IsSurfacelessContextSupported();
492 eglGraphics->SetIsSurfacelessContextSupported( isSurfacelessContextSupported );
494 if ( isSurfacelessContextSupported )
496 // Create a surfaceless OpenGL context for shared resources
497 eglImpl.CreateContext();
498 eglImpl.MakeContextCurrent( EGL_NO_SURFACE, eglImpl.GetContext() );
502 currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
505 currentSurface->InitializeGraphics();
506 currentSurface->MakeContextCurrent();
510 eglGraphics->GetGlesInterface().ContextCreated();
512 // Tell core it has a context
513 mCore.ContextCreated();
515 NotifyThreadInitialised();
518 uint64_t lastFrameTime;
519 TimeService::GetNanoseconds( lastFrameTime );
521 LOG_UPDATE_RENDER( "THREAD INITIALISED" );
523 bool useElapsedTime = true;
524 bool updateRequired = true;
525 uint64_t timeToSleepUntil = 0;
526 int extraFramesDropped = 0;
528 const unsigned int renderToFboInterval = mEnvironmentOptions.GetRenderToFboInterval();
529 const bool renderToFboEnabled = 0u != renderToFboInterval;
530 unsigned int frameCount = 0u;
532 while( UpdateRenderReady( useElapsedTime, updateRequired, timeToSleepUntil ) )
534 LOG_UPDATE_RENDER_TRACE;
536 // Performance statistics are logged upon a VSYNC tick so use this point for a VSync marker
537 AddPerformanceMarker( PerformanceInterface::VSYNC );
539 uint64_t currentFrameStartTime = 0;
540 TimeService::GetNanoseconds( currentFrameStartTime );
542 const uint64_t timeSinceLastFrame = currentFrameStartTime - lastFrameTime;
544 // Optional FPS Tracking when continuously rendering
545 if( useElapsedTime && mFpsTracker.Enabled() )
547 float absoluteTimeSinceLastRender = timeSinceLastFrame * NANOSECONDS_TO_SECOND;
548 mFpsTracker.Track( absoluteTimeSinceLastRender );
551 lastFrameTime = currentFrameStartTime; // Store frame start time
553 //////////////////////////////
555 //////////////////////////////
557 Dali::RenderSurfaceInterface* newSurface = ShouldSurfaceBeReplaced();
558 if( DALI_UNLIKELY( newSurface ) )
560 LOG_UPDATE_RENDER_TRACE_FMT( "Replacing Surface" );
561 // This is designed for replacing pixmap surfaces, but should work for window as well
562 // we need to delete the surface and renderable (pixmap / window)
563 // Then create a new pixmap/window and new surface
564 // If the new surface has a different display connection, then the context will be lost
565 mAdaptorInterfaces.GetDisplayConnectionInterface().Initialize();
566 newSurface->InitializeGraphics();
567 newSurface->MakeContextCurrent();
568 // TODO: ReplaceGraphicsSurface doesn't work, InitializeGraphics()
569 // already creates new surface window, the surface and the context.
570 // We probably don't need ReplaceGraphicsSurface at all.
571 // newSurface->ReplaceGraphicsSurface();
575 const bool isRenderingToFbo = renderToFboEnabled && ( ( 0u == frameCount ) || ( 0u != frameCount % renderToFboInterval ) );
578 //////////////////////////////
580 //////////////////////////////
582 const unsigned int currentTime = currentFrameStartTime / NANOSECONDS_PER_MILLISECOND;
583 const unsigned int nextFrameTime = currentTime + mDefaultFrameDurationMilliseconds;
585 uint64_t noOfFramesSinceLastUpdate = 1;
586 float frameDelta = 0.0f;
589 // If using the elapsed time, then calculate frameDelta as a multiple of mDefaultFrameDelta
590 noOfFramesSinceLastUpdate += extraFramesDropped;
592 frameDelta = mDefaultFrameDelta * noOfFramesSinceLastUpdate;
594 LOG_UPDATE_RENDER( "timeSinceLastFrame(%llu) noOfFramesSinceLastUpdate(%u) frameDelta(%.6f)", timeSinceLastFrame, noOfFramesSinceLastUpdate, frameDelta );
596 Integration::UpdateStatus updateStatus;
598 AddPerformanceMarker( PerformanceInterface::UPDATE_START );
599 mCore.Update( frameDelta,
605 AddPerformanceMarker( PerformanceInterface::UPDATE_END );
607 unsigned int keepUpdatingStatus = updateStatus.KeepUpdating();
609 // Tell the event-thread to wake up (if asleep) and send a notification event to Core if required
610 if( updateStatus.NeedsNotification() )
612 mNotificationTrigger.Trigger();
613 LOG_UPDATE_RENDER( "Notification Triggered" );
617 bool surfaceResized = false;
618 bool shouldSurfaceBeResized = ShouldSurfaceBeResized();
619 if( DALI_UNLIKELY( shouldSurfaceBeResized ) )
621 if( updateStatus.SurfaceRectChanged() )
623 LOG_UPDATE_RENDER_TRACE_FMT( "Resizing Surface" );
625 surfaceResized = true;
629 // Optional logging of update/render status
630 mUpdateStatusLogger.Log( keepUpdatingStatus );
632 //////////////////////////////
634 //////////////////////////////
636 mAdaptorInterfaces.GetDisplayConnectionInterface().ConsumeEvents();
638 if( mPreRenderCallback != NULL )
640 bool keepCallback = CallbackBase::ExecuteReturn<bool>(*mPreRenderCallback);
643 delete mPreRenderCallback;
644 mPreRenderCallback = NULL;
648 if( eglImpl.IsSurfacelessContextSupported() )
650 // Make the shared surfaceless context as current before rendering
651 eglImpl.MakeContextCurrent( EGL_NO_SURFACE, eglImpl.GetContext() );
654 if( mFirstFrameAfterResume )
656 // mFirstFrameAfterResume is set to true when the thread is resumed
657 // Let eglImplementation know the first frame after thread initialized or resumed.
658 eglImpl.SetFirstFrameAfterResume();
659 mFirstFrameAfterResume = FALSE;
662 Integration::RenderStatus renderStatus;
664 AddPerformanceMarker( PerformanceInterface::RENDER_START );
666 mIsRenderingWindows = true;
668 // Upload shared resources
669 mCore.PreRender( renderStatus, mForceClear, mUploadWithoutRendering );
671 if ( !mUploadWithoutRendering )
673 // Go through each window
674 WindowContainer windows;
675 mAdaptorInterfaces.GetWindowContainerInterface( windows );
677 for( auto&& window : windows )
679 if ( window && !window->IsBeingDeleted() )
681 Dali::Integration::Scene scene = window->GetScene();
682 Dali::RenderSurfaceInterface* windowSurface = window->GetSurface();
684 if ( scene && windowSurface )
686 windowSurface->InitializeGraphics();
688 // Render off-screen frame buffers first if any
689 mCore.RenderScene( scene, true );
691 // Switch to the EGL context of the surface
692 windowSurface->PreRender( surfaceResized ); // Switch GL context
694 // Render the surface
695 mCore.RenderScene( scene, false );
697 windowSurface->PostRender( false, false, surfaceResized ); // Swap Buffer
703 mCore.PostRender( mUploadWithoutRendering );
705 mIsRenderingWindows = false;
707 AddPerformanceMarker( PerformanceInterface::RENDER_END );
711 // Trigger event thread to request Update/Render thread to sleep if update not required
712 if( ( Integration::KeepUpdating::NOT_REQUESTED == keepUpdatingStatus ) && !renderStatus.NeedsUpdate() )
714 mSleepTrigger->Trigger();
715 updateRequired = false;
716 LOG_UPDATE_RENDER( "Sleep Triggered" );
720 updateRequired = true;
723 //////////////////////////////
725 //////////////////////////////
727 extraFramesDropped = 0;
729 if (timeToSleepUntil == 0)
731 // If this is the first frame after the thread is initialized or resumed, we
732 // use the actual time the current frame starts from to calculate the time to
733 // sleep until the next frame.
734 timeToSleepUntil = currentFrameStartTime + mDefaultFrameDurationNanoseconds;
738 // Otherwise, always use the sleep-until time calculated in the last frame to
739 // calculate the time to sleep until the next frame. In this way, if there is
740 // any time gap between the current frame and the next frame, or if update or
741 // rendering in the current frame takes too much time so that the specified
742 // sleep-until time has already passed, it will try to keep the frames syncing
743 // by shortening the duration of the next frame.
744 timeToSleepUntil += mDefaultFrameDurationNanoseconds;
746 // Check the current time at the end of the frame
747 uint64_t currentFrameEndTime = 0;
748 TimeService::GetNanoseconds( currentFrameEndTime );
749 while ( currentFrameEndTime > timeToSleepUntil + mDefaultFrameDurationNanoseconds )
751 // We are more than one frame behind already, so just drop the next frames
752 // until the sleep-until time is later than the current time so that we can
754 timeToSleepUntil += mDefaultFrameDurationNanoseconds;
755 extraFramesDropped++;
759 // Render to FBO is intended to measure fps above 60 so sleep is not wanted.
760 if( 0u == renderToFboInterval )
762 // Sleep until at least the the default frame duration has elapsed. This will return immediately if the specified end-time has already passed.
763 TimeService::SleepUntil( timeToSleepUntil );
767 // Inform core of context destruction & shutdown EGL
768 mCore.ContextDestroyed();
769 currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
772 currentSurface->DestroySurface();
773 currentSurface = nullptr;
776 LOG_UPDATE_RENDER( "THREAD DESTROYED" );
778 // Uninstall the logging function
779 mEnvironmentOptions.UnInstallLogFunction();
782 bool CombinedUpdateRenderController::UpdateRenderReady( bool& useElapsedTime, bool updateRequired, uint64_t& timeToSleepUntil )
784 useElapsedTime = true;
786 ConditionalWait::ScopedLock updateLock( mUpdateRenderThreadWaitCondition );
787 while( ( ! mUpdateRenderRunCount || // Should try to wait if event-thread has paused the Update/Render thread
788 ( mUpdateRenderThreadCanSleep && ! updateRequired && ! mPendingRequestUpdate ) ) && // Ensure we wait if we're supposed to be sleeping AND do not require another update
789 ! mDestroyUpdateRenderThread && // Ensure we don't wait if the update-render-thread is supposed to be destroyed
790 ! mNewSurface && // Ensure we don't wait if we need to replace the surface
791 ! mSurfaceResized ) // Ensure we don't wait if we need to resize the surface
793 LOG_UPDATE_RENDER( "WAIT: mUpdateRenderRunCount: %d", mUpdateRenderRunCount );
794 LOG_UPDATE_RENDER( " mUpdateRenderThreadCanSleep: %d, updateRequired: %d, mPendingRequestUpdate: %d", mUpdateRenderThreadCanSleep, updateRequired, mPendingRequestUpdate );
795 LOG_UPDATE_RENDER( " mDestroyUpdateRenderThread: %d", mDestroyUpdateRenderThread );
796 LOG_UPDATE_RENDER( " mNewSurface: %d", mNewSurface );
797 LOG_UPDATE_RENDER( " mSurfaceResized: %d", mSurfaceResized );
799 // Reset the time when the thread is waiting, so the sleep-until time for
800 // the first frame after resuming should be based on the actual start time
801 // of the first frame.
802 timeToSleepUntil = 0;
804 mUpdateRenderThreadWaitCondition.Wait( updateLock );
806 if( ! mUseElapsedTimeAfterWait )
808 useElapsedTime = false;
812 LOG_COUNTER_UPDATE_RENDER( "mUpdateRenderRunCount: %d", mUpdateRenderRunCount );
813 LOG_COUNTER_UPDATE_RENDER( "mUpdateRenderThreadCanSleep: %d, updateRequired: %d, mPendingRequestUpdate: %d", mUpdateRenderThreadCanSleep, updateRequired, mPendingRequestUpdate );
814 LOG_COUNTER_UPDATE_RENDER( "mDestroyUpdateRenderThread: %d", mDestroyUpdateRenderThread );
815 LOG_COUNTER_UPDATE_RENDER( "mNewSurface: %d", mNewSurface );
816 LOG_COUNTER_UPDATE_RENDER( "mSurfaceResized: %d", mSurfaceResized );
818 mUseElapsedTimeAfterWait = FALSE;
819 mUpdateRenderThreadCanSleep = FALSE;
820 mPendingRequestUpdate = FALSE;
822 // If we've been asked to run Update/Render cycles a finite number of times then decrement so we wait after the
823 // requested number of cycles
824 if( mUpdateRenderRunCount > 0 )
826 --mUpdateRenderRunCount;
829 // Keep the update-render thread alive if this thread is NOT to be destroyed
830 return ! mDestroyUpdateRenderThread;
833 Dali::RenderSurfaceInterface* CombinedUpdateRenderController::ShouldSurfaceBeReplaced()
835 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
837 Dali::RenderSurfaceInterface* newSurface = mNewSurface;
843 void CombinedUpdateRenderController::SurfaceReplaced()
845 // Just increment the semaphore
846 sem_post( &mEventThreadSemaphore );
849 bool CombinedUpdateRenderController::ShouldSurfaceBeResized()
851 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
852 return mSurfaceResized;
855 void CombinedUpdateRenderController::SurfaceResized()
857 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
858 mSurfaceResized = FALSE;
861 ///////////////////////////////////////////////////////////////////////////////////////////////////
863 ///////////////////////////////////////////////////////////////////////////////////////////////////
865 void CombinedUpdateRenderController::NotifyThreadInitialised()
867 // Just increment the semaphore
868 sem_post( &mEventThreadSemaphore );
871 void CombinedUpdateRenderController::NotifyGraphicsInitialised()
873 sem_post( &mGraphicsInitializeSemaphore );
876 void CombinedUpdateRenderController::AddPerformanceMarker( PerformanceInterface::MarkerType type )
878 if( mPerformanceInterface )
880 mPerformanceInterface->AddMarker( type );
884 /////////////////////////////////////////////////////////////////////////////////////////////////
885 // POST RENDERING: EVENT THREAD
886 /////////////////////////////////////////////////////////////////////////////////////////////////
888 void CombinedUpdateRenderController::PostRenderComplete()
890 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
891 mPostRendering = FALSE;
892 mUpdateRenderThreadWaitCondition.Notify( lock );
895 ///////////////////////////////////////////////////////////////////////////////////////////////////
896 // POST RENDERING: RENDER THREAD
897 ///////////////////////////////////////////////////////////////////////////////////////////////////
899 void CombinedUpdateRenderController::PostRenderStarted()
901 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
902 mPostRendering = TRUE;
905 void CombinedUpdateRenderController::PostRenderWaitForCompletion()
907 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
908 while( mPostRendering &&
909 ! mNewSurface && // We should NOT wait if we're replacing the surface
910 ! mDestroyUpdateRenderThread )
912 mUpdateRenderThreadWaitCondition.Wait( lock );
916 } // namespace Adaptor
918 } // namespace Internal