2 * Copyright (c) 2019 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 #include <dali/internal/adaptor/common/combined-update-render-controller.h>
23 #include <dali/integration-api/platform-abstraction.h>
26 #include <dali/integration-api/adaptor-framework/trigger-event-factory.h>
27 #include <dali/devel-api/adaptor-framework/thread-settings.h>
28 #include <dali/internal/adaptor/common/adaptor-internal-services.h>
29 #include <dali/internal/adaptor/common/combined-update-render-controller-debug.h>
30 #include <dali/internal/graphics/gles/egl-graphics.h>
31 #include <dali/internal/graphics/gles/egl-implementation.h>
32 #include <dali/internal/graphics/common/graphics-interface.h>
33 #include <dali/internal/system/common/environment-options.h>
34 #include <dali/internal/system/common/time-service.h>
48 const unsigned int CREATED_THREAD_COUNT = 1u;
50 const int CONTINUOUS = -1;
53 const unsigned int TRUE = 1u;
54 const unsigned int FALSE = 0u;
56 const unsigned int MILLISECONDS_PER_SECOND( 1e+3 );
57 const float NANOSECONDS_TO_SECOND( 1e-9f );
58 const unsigned int NANOSECONDS_PER_SECOND( 1e+9 );
59 const unsigned int NANOSECONDS_PER_MILLISECOND( 1e+6 );
61 // The following values will get calculated at compile time
62 const float DEFAULT_FRAME_DURATION_IN_SECONDS( 1.0f / 60.0f );
63 const uint64_t DEFAULT_FRAME_DURATION_IN_MILLISECONDS( DEFAULT_FRAME_DURATION_IN_SECONDS * MILLISECONDS_PER_SECOND );
64 const uint64_t DEFAULT_FRAME_DURATION_IN_NANOSECONDS( DEFAULT_FRAME_DURATION_IN_SECONDS * NANOSECONDS_PER_SECOND );
67 * Handles the use case when an update-request is received JUST before we process a sleep-request. If we did not have an update-request count then
68 * there is a danger that, on the event-thread we could have:
69 * 1) An update-request where we do nothing as Update/Render thread still running.
70 * 2) Quickly followed by a sleep-request being handled where we pause the Update/Render Thread (even though we have an update to process).
72 * Using a counter means we increment the counter on an update-request, and decrement it on a sleep-request. This handles the above scenario because:
73 * 1) MAIN THREAD: Update Request: COUNTER = 1
74 * 2) UPDATE/RENDER THREAD: Do Update/Render, then no Updates required -> Sleep Trigger
75 * 3) MAIN THREAD: Update Request: COUNTER = 2
76 * 4) MAIN THREAD: Sleep Request: COUNTER = 1 -> We do not sleep just yet
78 * Also ensures we preserve battery life by only doing ONE update when the above use case is not triggered.
79 * 1) MAIN THREAD: Update Request: COUNTER = 1
80 * 2) UPDATE/RENDER THREAD: Do Update/Render, then no Updates required -> Sleep Trigger
81 * 3) MAIN THREAD: Sleep Request: COUNTER = 0 -> Go to sleep
83 const unsigned int MAXIMUM_UPDATE_REQUESTS = 2;
84 } // unnamed namespace
86 ///////////////////////////////////////////////////////////////////////////////////////////////////
88 ///////////////////////////////////////////////////////////////////////////////////////////////////
90 CombinedUpdateRenderController::CombinedUpdateRenderController( AdaptorInternalServices& adaptorInterfaces, const EnvironmentOptions& environmentOptions )
91 : mFpsTracker( environmentOptions ),
92 mUpdateStatusLogger( environmentOptions ),
93 mEventThreadSemaphore(),
94 mUpdateRenderThreadWaitCondition(),
95 mAdaptorInterfaces( adaptorInterfaces ),
96 mPerformanceInterface( adaptorInterfaces.GetPerformanceInterface() ),
97 mCore( adaptorInterfaces.GetCore() ),
98 mEnvironmentOptions( environmentOptions ),
99 mNotificationTrigger( adaptorInterfaces.GetProcessCoreEventsTrigger() ),
100 mSleepTrigger( NULL ),
101 mPreRenderCallback( NULL ),
102 mUpdateRenderThread( NULL ),
103 mDefaultFrameDelta( 0.0f ),
104 mDefaultFrameDurationMilliseconds( 0u ),
105 mDefaultFrameDurationNanoseconds( 0u ),
106 mDefaultHalfFrameNanoseconds( 0u ),
107 mUpdateRequestCount( 0u ),
109 mUpdateRenderRunCount( 0 ),
110 mDestroyUpdateRenderThread( FALSE ),
111 mUpdateRenderThreadCanSleep( FALSE ),
112 mPendingRequestUpdate( FALSE ),
113 mUseElapsedTimeAfterWait( FALSE ),
115 mDeletedSurface( nullptr ),
116 mPostRendering( FALSE ),
117 mSurfaceResized( FALSE ),
118 mForceClear( FALSE ),
119 mUploadWithoutRendering( FALSE ),
120 mFirstFrameAfterResume( FALSE )
124 // Initialise frame delta/duration variables first
125 SetRenderRefreshRate( environmentOptions.GetRenderRefreshRate() );
127 // Set the thread-synchronization interface on the render-surface
128 Dali::RenderSurfaceInterface* currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
131 currentSurface->SetThreadSynchronization( *this );
134 TriggerEventFactoryInterface& triggerFactory = mAdaptorInterfaces.GetTriggerEventFactoryInterface();
135 mSleepTrigger = triggerFactory.CreateTriggerEvent( MakeCallback( this, &CombinedUpdateRenderController::ProcessSleepRequest ), TriggerEventInterface::KEEP_ALIVE_AFTER_TRIGGER );
137 sem_init( &mEventThreadSemaphore, 0, 0 ); // Initialize to 0 so that it just waits if sem_post has not been called
140 CombinedUpdateRenderController::~CombinedUpdateRenderController()
146 delete mPreRenderCallback;
147 delete mSleepTrigger;
150 void CombinedUpdateRenderController::Initialize()
154 // Ensure Update/Render Thread not already created
155 DALI_ASSERT_ALWAYS( ! mUpdateRenderThread );
157 // Create Update/Render Thread
158 mUpdateRenderThread = new pthread_t();
159 int error = pthread_create( mUpdateRenderThread, NULL, InternalUpdateRenderThreadEntryFunc, this );
160 DALI_ASSERT_ALWAYS( !error && "Return code from pthread_create() when creating UpdateRenderThread" );
162 // The Update/Render thread will now run and initialise the graphics interface etc. and will then wait for Start to be called
163 // When this function returns, the application initialisation on the event thread should occur
166 void CombinedUpdateRenderController::Start()
170 DALI_ASSERT_ALWAYS( !mRunning && mUpdateRenderThread );
172 // Wait until all threads created in Initialise are up and running
173 for( unsigned int i = 0; i < CREATED_THREAD_COUNT; ++i )
175 sem_wait( &mEventThreadSemaphore );
178 Integration::RenderSurface* currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
181 currentSurface->StartRender();
186 LOG_EVENT( "Startup Complete, starting Update/Render Thread" );
188 RunUpdateRenderThread( CONTINUOUS, AnimationProgression::NONE, UpdateMode::NORMAL );
190 DALI_LOG_RELEASE_INFO( "CombinedUpdateRenderController::Start\n" );
193 void CombinedUpdateRenderController::Pause()
199 PauseUpdateRenderThread();
201 AddPerformanceMarker( PerformanceInterface::PAUSED );
203 DALI_LOG_RELEASE_INFO( "CombinedUpdateRenderController::Pause\n" );
206 void CombinedUpdateRenderController::Resume()
210 if( !mRunning && IsUpdateRenderThreadPaused() )
212 LOG_EVENT( "Resuming" );
214 RunUpdateRenderThread( CONTINUOUS, AnimationProgression::USE_ELAPSED_TIME, UpdateMode::NORMAL );
216 AddPerformanceMarker( PerformanceInterface::RESUME );
220 mFirstFrameAfterResume = TRUE;
222 DALI_LOG_RELEASE_INFO( "CombinedUpdateRenderController::Resume\n" );
226 DALI_LOG_RELEASE_INFO( "CombinedUpdateRenderController::Resume: Already resumed [%d, %d, %d]\n", mRunning, mUpdateRenderRunCount, mUpdateRenderThreadCanSleep );
230 void CombinedUpdateRenderController::Stop()
234 // Stop Rendering and the Update/Render Thread
235 Integration::RenderSurface* currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
238 currentSurface->StopRender();
241 StopUpdateRenderThread();
243 if( mUpdateRenderThread )
245 LOG_EVENT( "Destroying UpdateRenderThread" );
247 // wait for the thread to finish
248 pthread_join( *mUpdateRenderThread, NULL );
250 delete mUpdateRenderThread;
251 mUpdateRenderThread = NULL;
256 DALI_LOG_RELEASE_INFO( "CombinedUpdateRenderController::Stop\n" );
259 void CombinedUpdateRenderController::RequestUpdate()
263 // Increment the update-request count to the maximum
264 if( mUpdateRequestCount < MAXIMUM_UPDATE_REQUESTS )
266 ++mUpdateRequestCount;
269 if( mRunning && IsUpdateRenderThreadPaused() )
271 LOG_EVENT( "Processing" );
273 RunUpdateRenderThread( CONTINUOUS, AnimationProgression::NONE, UpdateMode::NORMAL );
276 ConditionalWait::ScopedLock updateLock( mUpdateRenderThreadWaitCondition );
277 mPendingRequestUpdate = TRUE;
280 void CombinedUpdateRenderController::RequestUpdateOnce( UpdateMode updateMode )
282 // Increment the update-request count to the maximum
283 if( mUpdateRequestCount < MAXIMUM_UPDATE_REQUESTS )
285 ++mUpdateRequestCount;
288 if( IsUpdateRenderThreadPaused() )
292 // Run Update/Render once
293 RunUpdateRenderThread( ONCE, AnimationProgression::NONE, updateMode );
297 void CombinedUpdateRenderController::ReplaceSurface( Dali::RenderSurfaceInterface* newSurface )
301 if( mUpdateRenderThread )
303 // Set the ThreadSyncronizationInterface on the new surface
304 newSurface->SetThreadSynchronization( *this );
306 LOG_EVENT( "Starting to replace the surface, event-thread blocked" );
308 // Start replacing the surface.
310 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
311 mPostRendering = FALSE; // Clear the post-rendering flag as Update/Render thread will replace the surface now
312 mNewSurface = newSurface;
313 mUpdateRenderThreadWaitCondition.Notify( lock );
316 // Wait until the surface has been replaced
317 sem_wait( &mEventThreadSemaphore );
319 LOG_EVENT( "Surface replaced, event-thread continuing" );
323 void CombinedUpdateRenderController::DeleteSurface( Dali::RenderSurfaceInterface* surface )
327 if( mUpdateRenderThread )
329 LOG_EVENT( "Starting to delete the surface, event-thread blocked" );
331 // Start replacing the surface.
333 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
334 mPostRendering = FALSE; // Clear the post-rendering flag as Update/Render thread will delete the surface now
335 mDeletedSurface = surface;
336 mUpdateRenderThreadWaitCondition.Notify( lock );
339 // Wait until the surface has been deleted
340 sem_wait( &mEventThreadSemaphore );
342 LOG_EVENT( "Surface deleted, event-thread continuing" );
346 void CombinedUpdateRenderController::ResizeSurface()
350 LOG_EVENT( "Resize the surface" );
353 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
354 mPostRendering = FALSE; // Clear the post-rendering flag as Update/Render thread will resize the surface now
355 mSurfaceResized = TRUE;
356 mUpdateRenderThreadWaitCondition.Notify( lock );
360 void CombinedUpdateRenderController::SetRenderRefreshRate( unsigned int numberOfFramesPerRender )
362 // Not protected by lock, but written to rarely so not worth adding a lock when reading
363 mDefaultFrameDelta = numberOfFramesPerRender * DEFAULT_FRAME_DURATION_IN_SECONDS;
364 mDefaultFrameDurationMilliseconds = uint64_t( numberOfFramesPerRender ) * DEFAULT_FRAME_DURATION_IN_MILLISECONDS;
365 mDefaultFrameDurationNanoseconds = uint64_t( numberOfFramesPerRender ) * DEFAULT_FRAME_DURATION_IN_NANOSECONDS;
366 mDefaultHalfFrameNanoseconds = mDefaultFrameDurationNanoseconds / 2u;
368 LOG_EVENT( "mDefaultFrameDelta(%.6f), mDefaultFrameDurationMilliseconds(%lld), mDefaultFrameDurationNanoseconds(%lld)", mDefaultFrameDelta, mDefaultFrameDurationMilliseconds, mDefaultFrameDurationNanoseconds );
371 void CombinedUpdateRenderController::SetPreRenderCallback( CallbackBase* callback )
374 LOG_EVENT( "Set PreRender Callback" );
376 ConditionalWait::ScopedLock updateLock( mUpdateRenderThreadWaitCondition );
377 if( mPreRenderCallback )
379 delete mPreRenderCallback;
381 mPreRenderCallback = callback;
384 void CombinedUpdateRenderController::AddSurface( Dali::RenderSurfaceInterface* surface )
387 LOG_EVENT( "Surface is added" );
388 if( mUpdateRenderThread )
390 // Set the ThreadSyncronizationInterface on the added surface
391 surface->SetThreadSynchronization( *this );
395 ///////////////////////////////////////////////////////////////////////////////////////////////////
397 ///////////////////////////////////////////////////////////////////////////////////////////////////
399 void CombinedUpdateRenderController::RunUpdateRenderThread( int numberOfCycles, AnimationProgression animationProgression, UpdateMode updateMode )
401 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
402 mUpdateRenderRunCount = numberOfCycles;
403 mUpdateRenderThreadCanSleep = FALSE;
404 mUseElapsedTimeAfterWait = ( animationProgression == AnimationProgression::USE_ELAPSED_TIME );
405 mUploadWithoutRendering = ( updateMode == UpdateMode::SKIP_RENDER );
406 LOG_COUNTER_EVENT( "mUpdateRenderRunCount: %d, mUseElapsedTimeAfterWait: %d", mUpdateRenderRunCount, mUseElapsedTimeAfterWait );
407 mUpdateRenderThreadWaitCondition.Notify( lock );
410 void CombinedUpdateRenderController::PauseUpdateRenderThread()
412 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
413 mUpdateRenderRunCount = 0;
416 void CombinedUpdateRenderController::StopUpdateRenderThread()
418 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
419 mDestroyUpdateRenderThread = TRUE;
420 mUpdateRenderThreadWaitCondition.Notify( lock );
423 bool CombinedUpdateRenderController::IsUpdateRenderThreadPaused()
425 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
426 return ( mUpdateRenderRunCount != CONTINUOUS ) || // Report paused if NOT continuously running
427 mUpdateRenderThreadCanSleep; // Report paused if sleeping
430 void CombinedUpdateRenderController::ProcessSleepRequest()
434 // Decrement Update request count
435 if( mUpdateRequestCount > 0 )
437 --mUpdateRequestCount;
440 // Can sleep if our update-request count is 0
441 // Update/Render thread can choose to carry on updating if it determines more update/renders are required
442 if( mUpdateRequestCount == 0 )
444 LOG_EVENT( "Going to sleep" );
446 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
447 mUpdateRenderThreadCanSleep = TRUE;
451 ///////////////////////////////////////////////////////////////////////////////////////////////////
452 // UPDATE/RENDER THREAD
453 ///////////////////////////////////////////////////////////////////////////////////////////////////
455 void CombinedUpdateRenderController::UpdateRenderThread()
457 SetThreadName("RenderThread\0");
459 // Install a function for logging
460 mEnvironmentOptions.InstallLogFunction();
462 // Install a function for tracing
463 mEnvironmentOptions.InstallTraceFunction();
465 LOG_UPDATE_RENDER( "THREAD CREATED" );
467 // Initialize EGL & OpenGL
468 Dali::DisplayConnection& displayConnection = mAdaptorInterfaces.GetDisplayConnectionInterface();
469 displayConnection.Initialize();
471 RenderSurfaceInterface* currentSurface = nullptr;
473 GraphicsInterface& graphics = mAdaptorInterfaces.GetGraphicsInterface();
474 EglGraphics* eglGraphics = static_cast<EglGraphics *>(&graphics);
476 // This will only be created once
477 EglInterface* eglInterface = &eglGraphics->GetEglInterface();
479 Internal::Adaptor::EglImplementation& eglImpl = static_cast<Internal::Adaptor::EglImplementation&>( *eglInterface );
481 // Try to use OpenGL es 3.0
482 // ChooseConfig returns false here when the device only support gles 2.0.
483 // Because eglChooseConfig with gles 3.0 setting fails when the device only support gles 2.0 and Our default setting is gles 3.0.
484 if( !eglImpl.ChooseConfig( true, COLOR_DEPTH_32 ) )
486 // Retry to use OpenGL es 2.0
487 eglGraphics->SetGlesVersion( 20 );
488 eglImpl.ChooseConfig( true, COLOR_DEPTH_32 );
491 // Check whether surfaceless context is supported
492 bool isSurfacelessContextSupported = eglImpl.IsSurfacelessContextSupported();
493 eglGraphics->SetIsSurfacelessContextSupported( isSurfacelessContextSupported );
495 if ( isSurfacelessContextSupported )
497 // Create a surfaceless OpenGL context for shared resources
498 eglImpl.CreateContext();
499 eglImpl.MakeContextCurrent( EGL_NO_SURFACE, eglImpl.GetContext() );
503 currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
506 currentSurface->InitializeGraphics();
507 currentSurface->MakeContextCurrent();
511 eglGraphics->GetGlesInterface().ContextCreated();
513 // Tell core it has a context
514 mCore.ContextCreated();
516 NotifyThreadInitialised();
519 uint64_t lastFrameTime;
520 TimeService::GetNanoseconds( lastFrameTime );
522 LOG_UPDATE_RENDER( "THREAD INITIALISED" );
524 bool useElapsedTime = true;
525 bool updateRequired = true;
526 uint64_t timeToSleepUntil = 0;
527 int extraFramesDropped = 0;
529 const unsigned int renderToFboInterval = mEnvironmentOptions.GetRenderToFboInterval();
530 const bool renderToFboEnabled = 0u != renderToFboInterval;
531 unsigned int frameCount = 0u;
533 while( UpdateRenderReady( useElapsedTime, updateRequired, timeToSleepUntil ) )
535 LOG_UPDATE_RENDER_TRACE;
537 // Performance statistics are logged upon a VSYNC tick so use this point for a VSync marker
538 AddPerformanceMarker( PerformanceInterface::VSYNC );
540 uint64_t currentFrameStartTime = 0;
541 TimeService::GetNanoseconds( currentFrameStartTime );
543 const uint64_t timeSinceLastFrame = currentFrameStartTime - lastFrameTime;
545 // Optional FPS Tracking when continuously rendering
546 if( useElapsedTime && mFpsTracker.Enabled() )
548 float absoluteTimeSinceLastRender = timeSinceLastFrame * NANOSECONDS_TO_SECOND;
549 mFpsTracker.Track( absoluteTimeSinceLastRender );
552 lastFrameTime = currentFrameStartTime; // Store frame start time
554 //////////////////////////////
556 //////////////////////////////
558 Integration::RenderSurface* newSurface = ShouldSurfaceBeReplaced();
559 if( DALI_UNLIKELY( newSurface ) )
561 LOG_UPDATE_RENDER_TRACE_FMT( "Replacing Surface" );
562 // This is designed for replacing pixmap surfaces, but should work for window as well
563 // we need to delete the surface and renderable (pixmap / window)
564 // Then create a new pixmap/window and new surface
565 // If the new surface has a different display connection, then the context will be lost
566 mAdaptorInterfaces.GetDisplayConnectionInterface().Initialize();
567 newSurface->InitializeGraphics();
568 newSurface->MakeContextCurrent();
569 // TODO: ReplaceGraphicsSurface doesn't work, InitializeGraphics()
570 // already creates new surface window, the surface and the context.
571 // We probably don't need ReplaceGraphicsSurface at all.
572 // newSurface->ReplaceGraphicsSurface();
576 const bool isRenderingToFbo = renderToFboEnabled && ( ( 0u == frameCount ) || ( 0u != frameCount % renderToFboInterval ) );
579 //////////////////////////////
581 //////////////////////////////
583 const unsigned int currentTime = currentFrameStartTime / NANOSECONDS_PER_MILLISECOND;
584 const unsigned int nextFrameTime = currentTime + mDefaultFrameDurationMilliseconds;
586 uint64_t noOfFramesSinceLastUpdate = 1;
587 float frameDelta = 0.0f;
590 // If using the elapsed time, then calculate frameDelta as a multiple of mDefaultFrameDelta
591 noOfFramesSinceLastUpdate += extraFramesDropped;
593 frameDelta = mDefaultFrameDelta * noOfFramesSinceLastUpdate;
595 LOG_UPDATE_RENDER( "timeSinceLastFrame(%llu) noOfFramesSinceLastUpdate(%u) frameDelta(%.6f)", timeSinceLastFrame, noOfFramesSinceLastUpdate, frameDelta );
597 Integration::UpdateStatus updateStatus;
599 AddPerformanceMarker( PerformanceInterface::UPDATE_START );
600 mCore.Update( frameDelta,
606 AddPerformanceMarker( PerformanceInterface::UPDATE_END );
608 unsigned int keepUpdatingStatus = updateStatus.KeepUpdating();
610 // Tell the event-thread to wake up (if asleep) and send a notification event to Core if required
611 if( updateStatus.NeedsNotification() )
613 mNotificationTrigger.Trigger();
614 LOG_UPDATE_RENDER( "Notification Triggered" );
618 bool shouldSurfaceBeResized = ShouldSurfaceBeResized();
619 if( DALI_UNLIKELY( shouldSurfaceBeResized ) )
621 if( updateStatus.SurfaceRectChanged() )
623 LOG_UPDATE_RENDER_TRACE_FMT( "Resizing Surface" );
628 // Optional logging of update/render status
629 mUpdateStatusLogger.Log( keepUpdatingStatus );
631 //////////////////////////////
633 //////////////////////////////
635 mAdaptorInterfaces.GetDisplayConnectionInterface().ConsumeEvents();
637 if( mPreRenderCallback != NULL )
639 bool keepCallback = CallbackBase::ExecuteReturn<bool>(*mPreRenderCallback);
642 delete mPreRenderCallback;
643 mPreRenderCallback = NULL;
647 if( eglImpl.IsSurfacelessContextSupported() )
649 // Make the shared surfaceless context as current before rendering
650 eglImpl.MakeContextCurrent( EGL_NO_SURFACE, eglImpl.GetContext() );
653 if( mFirstFrameAfterResume )
655 // mFirstFrameAfterResume is set to true when the thread is resumed
656 // Let eglImplementation know the first frame after thread initialized or resumed.
657 eglImpl.SetFirstFrameAfterResume();
658 mFirstFrameAfterResume = FALSE;
661 Integration::RenderStatus renderStatus;
663 AddPerformanceMarker( PerformanceInterface::RENDER_START );
664 mCore.Render( renderStatus, mForceClear, mUploadWithoutRendering );
666 //////////////////////////////
668 //////////////////////////////
670 Integration::RenderSurface* deletedSurface = ShouldSurfaceBeDeleted();
671 if( DALI_UNLIKELY( deletedSurface ) )
673 LOG_UPDATE_RENDER_TRACE_FMT( "Deleting Surface" );
675 mCore.SurfaceDeleted( deletedSurface );
680 AddPerformanceMarker( PerformanceInterface::RENDER_END );
684 // Trigger event thread to request Update/Render thread to sleep if update not required
685 if( ( Integration::KeepUpdating::NOT_REQUESTED == keepUpdatingStatus ) && !renderStatus.NeedsUpdate() )
687 mSleepTrigger->Trigger();
688 updateRequired = false;
689 LOG_UPDATE_RENDER( "Sleep Triggered" );
693 updateRequired = true;
696 //////////////////////////////
698 //////////////////////////////
700 extraFramesDropped = 0;
702 if (timeToSleepUntil == 0)
704 // If this is the first frame after the thread is initialized or resumed, we
705 // use the actual time the current frame starts from to calculate the time to
706 // sleep until the next frame.
707 timeToSleepUntil = currentFrameStartTime + mDefaultFrameDurationNanoseconds;
711 // Otherwise, always use the sleep-until time calculated in the last frame to
712 // calculate the time to sleep until the next frame. In this way, if there is
713 // any time gap between the current frame and the next frame, or if update or
714 // rendering in the current frame takes too much time so that the specified
715 // sleep-until time has already passed, it will try to keep the frames syncing
716 // by shortening the duration of the next frame.
717 timeToSleepUntil += mDefaultFrameDurationNanoseconds;
719 // Check the current time at the end of the frame
720 uint64_t currentFrameEndTime = 0;
721 TimeService::GetNanoseconds( currentFrameEndTime );
722 while ( currentFrameEndTime > timeToSleepUntil + mDefaultFrameDurationNanoseconds )
724 // We are more than one frame behind already, so just drop the next frames
725 // until the sleep-until time is later than the current time so that we can
727 timeToSleepUntil += mDefaultFrameDurationNanoseconds;
728 extraFramesDropped++;
732 // Render to FBO is intended to measure fps above 60 so sleep is not wanted.
733 if( 0u == renderToFboInterval )
735 // Sleep until at least the the default frame duration has elapsed. This will return immediately if the specified end-time has already passed.
736 TimeService::SleepUntil( timeToSleepUntil );
740 // Inform core of context destruction & shutdown EGL
741 mCore.ContextDestroyed();
742 currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
745 currentSurface->DestroySurface();
746 currentSurface = nullptr;
749 LOG_UPDATE_RENDER( "THREAD DESTROYED" );
751 // Uninstall the logging function
752 mEnvironmentOptions.UnInstallLogFunction();
755 bool CombinedUpdateRenderController::UpdateRenderReady( bool& useElapsedTime, bool updateRequired, uint64_t& timeToSleepUntil )
757 useElapsedTime = true;
759 ConditionalWait::ScopedLock updateLock( mUpdateRenderThreadWaitCondition );
760 while( ( ! mUpdateRenderRunCount || // Should try to wait if event-thread has paused the Update/Render thread
761 ( mUpdateRenderThreadCanSleep && ! updateRequired && ! mPendingRequestUpdate ) ) && // Ensure we wait if we're supposed to be sleeping AND do not require another update
762 ! mDestroyUpdateRenderThread && // Ensure we don't wait if the update-render-thread is supposed to be destroyed
763 ! mNewSurface && // Ensure we don't wait if we need to replace the surface
764 ! mDeletedSurface && // Ensure we don't wait if we need to delete the surface
765 ! mSurfaceResized ) // Ensure we don't wait if we need to resize the surface
767 LOG_UPDATE_RENDER( "WAIT: mUpdateRenderRunCount: %d", mUpdateRenderRunCount );
768 LOG_UPDATE_RENDER( " mUpdateRenderThreadCanSleep: %d, updateRequired: %d, mPendingRequestUpdate: %d", mUpdateRenderThreadCanSleep, updateRequired, mPendingRequestUpdate );
769 LOG_UPDATE_RENDER( " mDestroyUpdateRenderThread: %d", mDestroyUpdateRenderThread );
770 LOG_UPDATE_RENDER( " mNewSurface: %d", mNewSurface );
771 LOG_UPDATE_RENDER( " mDeletedSurface: %d", mDeletedSurface );
772 LOG_UPDATE_RENDER( " mSurfaceResized: %d", mSurfaceResized );
774 // Reset the time when the thread is waiting, so the sleep-until time for
775 // the first frame after resuming should be based on the actual start time
776 // of the first frame.
777 timeToSleepUntil = 0;
779 mUpdateRenderThreadWaitCondition.Wait( updateLock );
781 if( ! mUseElapsedTimeAfterWait )
783 useElapsedTime = false;
787 LOG_COUNTER_UPDATE_RENDER( "mUpdateRenderRunCount: %d", mUpdateRenderRunCount );
788 LOG_COUNTER_UPDATE_RENDER( "mUpdateRenderThreadCanSleep: %d, updateRequired: %d, mPendingRequestUpdate: %d", mUpdateRenderThreadCanSleep, updateRequired, mPendingRequestUpdate );
789 LOG_COUNTER_UPDATE_RENDER( "mDestroyUpdateRenderThread: %d", mDestroyUpdateRenderThread );
790 LOG_COUNTER_UPDATE_RENDER( "mNewSurface: %d", mNewSurface );
791 LOG_COUNTER_UPDATE_RENDER( "mDeletedSurface: %d", mDeletedSurface );
792 LOG_COUNTER_UPDATE_RENDER( "mSurfaceResized: %d", mSurfaceResized );
794 mUseElapsedTimeAfterWait = FALSE;
795 mUpdateRenderThreadCanSleep = FALSE;
796 mPendingRequestUpdate = FALSE;
798 // If we've been asked to run Update/Render cycles a finite number of times then decrement so we wait after the
799 // requested number of cycles
800 if( mUpdateRenderRunCount > 0 )
802 --mUpdateRenderRunCount;
805 // Keep the update-render thread alive if this thread is NOT to be destroyed
806 return ! mDestroyUpdateRenderThread;
809 Integration::RenderSurface* CombinedUpdateRenderController::ShouldSurfaceBeReplaced()
811 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
813 Integration::RenderSurface* newSurface = mNewSurface;
819 void CombinedUpdateRenderController::SurfaceReplaced()
821 // Just increment the semaphore
822 sem_post( &mEventThreadSemaphore );
825 Integration::RenderSurface* CombinedUpdateRenderController::ShouldSurfaceBeDeleted()
827 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
829 Integration::RenderSurface* deletedSurface = mDeletedSurface;
830 mDeletedSurface = NULL;
832 return deletedSurface;
835 void CombinedUpdateRenderController::SurfaceDeleted()
837 // Just increment the semaphore
838 sem_post( &mEventThreadSemaphore );
841 bool CombinedUpdateRenderController::ShouldSurfaceBeResized()
843 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
844 return mSurfaceResized;
847 void CombinedUpdateRenderController::SurfaceResized()
849 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
850 mSurfaceResized = FALSE;
853 ///////////////////////////////////////////////////////////////////////////////////////////////////
855 ///////////////////////////////////////////////////////////////////////////////////////////////////
857 void CombinedUpdateRenderController::NotifyThreadInitialised()
859 // Just increment the semaphore
860 sem_post( &mEventThreadSemaphore );
863 void CombinedUpdateRenderController::AddPerformanceMarker( PerformanceInterface::MarkerType type )
865 if( mPerformanceInterface )
867 mPerformanceInterface->AddMarker( type );
871 /////////////////////////////////////////////////////////////////////////////////////////////////
872 // POST RENDERING: EVENT THREAD
873 /////////////////////////////////////////////////////////////////////////////////////////////////
875 void CombinedUpdateRenderController::PostRenderComplete()
877 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
878 mPostRendering = FALSE;
879 mUpdateRenderThreadWaitCondition.Notify( lock );
882 ///////////////////////////////////////////////////////////////////////////////////////////////////
883 // POST RENDERING: RENDER THREAD
884 ///////////////////////////////////////////////////////////////////////////////////////////////////
886 void CombinedUpdateRenderController::PostRenderStarted()
888 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
889 mPostRendering = TRUE;
892 void CombinedUpdateRenderController::PostRenderWaitForCompletion()
894 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
895 while( mPostRendering &&
896 ! mNewSurface && // We should NOT wait if we're replacing the surface
897 ! mDeletedSurface && // We should NOT wait if we're deleting the surface
898 ! mDestroyUpdateRenderThread )
900 mUpdateRenderThreadWaitCondition.Wait( lock );
904 } // namespace Adaptor
906 } // namespace Internal