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/trigger-event-factory.h>
27 #include <dali/internal/adaptor/common/combined-update-render-controller-debug.h>
28 #include <dali/internal/system/common/environment-options.h>
29 #include <dali/internal/system/common/time-service.h>
30 #include <dali/internal/adaptor/common/adaptor-internal-services.h>
31 #include <dali/devel-api/adaptor-framework/thread-settings.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>
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 )
123 // Initialise frame delta/duration variables first
124 SetRenderRefreshRate( environmentOptions.GetRenderRefreshRate() );
126 // Set the thread-synchronization interface on the render-surface
127 Dali::RenderSurfaceInterface* currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
130 currentSurface->SetThreadSynchronization( *this );
133 TriggerEventFactoryInterface& triggerFactory = mAdaptorInterfaces.GetTriggerEventFactoryInterface();
134 mSleepTrigger = triggerFactory.CreateTriggerEvent( MakeCallback( this, &CombinedUpdateRenderController::ProcessSleepRequest ), TriggerEventInterface::KEEP_ALIVE_AFTER_TRIGGER );
136 sem_init( &mEventThreadSemaphore, 0, 0 ); // Initialize to 0 so that it just waits if sem_post has not been called
139 CombinedUpdateRenderController::~CombinedUpdateRenderController()
145 delete mPreRenderCallback;
146 delete mSleepTrigger;
149 void CombinedUpdateRenderController::Initialize()
153 // Ensure Update/Render Thread not already created
154 DALI_ASSERT_ALWAYS( ! mUpdateRenderThread );
156 // Create Update/Render Thread
157 mUpdateRenderThread = new pthread_t();
158 int error = pthread_create( mUpdateRenderThread, NULL, InternalUpdateRenderThreadEntryFunc, this );
159 DALI_ASSERT_ALWAYS( !error && "Return code from pthread_create() when creating UpdateRenderThread" );
161 // The Update/Render thread will now run and initialise the graphics interface etc. and will then wait for Start to be called
162 // When this function returns, the application initialisation on the event thread should occur
165 void CombinedUpdateRenderController::Start()
169 DALI_ASSERT_ALWAYS( !mRunning && mUpdateRenderThread );
171 // Wait until all threads created in Initialise are up and running
172 for( unsigned int i = 0; i < CREATED_THREAD_COUNT; ++i )
174 sem_wait( &mEventThreadSemaphore );
177 Integration::RenderSurface* currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
180 currentSurface->StartRender();
185 LOG_EVENT( "Startup Complete, starting Update/Render Thread" );
187 RunUpdateRenderThread( CONTINUOUS, AnimationProgression::NONE, UpdateMode::NORMAL );
189 DALI_LOG_RELEASE_INFO( "CombinedUpdateRenderController::Start\n" );
192 void CombinedUpdateRenderController::Pause()
198 PauseUpdateRenderThread();
200 AddPerformanceMarker( PerformanceInterface::PAUSED );
202 DALI_LOG_RELEASE_INFO( "CombinedUpdateRenderController::Pause\n" );
205 void CombinedUpdateRenderController::Resume()
209 if( !mRunning && IsUpdateRenderThreadPaused() )
211 LOG_EVENT( "Resuming" );
213 RunUpdateRenderThread( CONTINUOUS, AnimationProgression::USE_ELAPSED_TIME, UpdateMode::NORMAL );
215 AddPerformanceMarker( PerformanceInterface::RESUME );
220 DALI_LOG_RELEASE_INFO( "CombinedUpdateRenderController::Resume\n" );
224 DALI_LOG_RELEASE_INFO( "CombinedUpdateRenderController::Resume: Already resumed [%d, %d, %d]\n", mRunning, mUpdateRenderRunCount, mUpdateRenderThreadCanSleep );
228 void CombinedUpdateRenderController::Stop()
232 // Stop Rendering and the Update/Render Thread
233 Integration::RenderSurface* currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
236 currentSurface->StopRender();
239 StopUpdateRenderThread();
241 if( mUpdateRenderThread )
243 LOG_EVENT( "Destroying UpdateRenderThread" );
245 // wait for the thread to finish
246 pthread_join( *mUpdateRenderThread, NULL );
248 delete mUpdateRenderThread;
249 mUpdateRenderThread = NULL;
254 DALI_LOG_RELEASE_INFO( "CombinedUpdateRenderController::Stop\n" );
257 void CombinedUpdateRenderController::RequestUpdate()
261 // Increment the update-request count to the maximum
262 if( mUpdateRequestCount < MAXIMUM_UPDATE_REQUESTS )
264 ++mUpdateRequestCount;
267 if( mRunning && IsUpdateRenderThreadPaused() )
269 LOG_EVENT( "Processing" );
271 RunUpdateRenderThread( CONTINUOUS, AnimationProgression::NONE, UpdateMode::NORMAL );
274 ConditionalWait::ScopedLock updateLock( mUpdateRenderThreadWaitCondition );
275 mPendingRequestUpdate = TRUE;
278 void CombinedUpdateRenderController::RequestUpdateOnce( UpdateMode updateMode )
280 // Increment the update-request count to the maximum
281 if( mUpdateRequestCount < MAXIMUM_UPDATE_REQUESTS )
283 ++mUpdateRequestCount;
286 if( IsUpdateRenderThreadPaused() )
290 // Run Update/Render once
291 RunUpdateRenderThread( ONCE, AnimationProgression::NONE, updateMode );
295 void CombinedUpdateRenderController::ReplaceSurface( Dali::RenderSurfaceInterface* newSurface )
299 if( mUpdateRenderThread )
301 // Set the ThreadSyncronizationInterface on the new surface
302 newSurface->SetThreadSynchronization( *this );
304 LOG_EVENT( "Starting to replace the surface, event-thread blocked" );
306 // Start replacing the surface.
308 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
309 mPostRendering = FALSE; // Clear the post-rendering flag as Update/Render thread will replace the surface now
310 mNewSurface = newSurface;
311 mUpdateRenderThreadWaitCondition.Notify( lock );
314 // Wait until the surface has been replaced
315 sem_wait( &mEventThreadSemaphore );
317 LOG_EVENT( "Surface replaced, event-thread continuing" );
321 void CombinedUpdateRenderController::DeleteSurface( Dali::RenderSurfaceInterface* surface )
325 if( mUpdateRenderThread )
327 LOG_EVENT( "Starting to delete the surface, event-thread blocked" );
329 // Start replacing the surface.
331 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
332 mPostRendering = FALSE; // Clear the post-rendering flag as Update/Render thread will delete the surface now
333 mDeletedSurface = surface;
334 mUpdateRenderThreadWaitCondition.Notify( lock );
337 // Wait until the surface has been deleted
338 sem_wait( &mEventThreadSemaphore );
340 LOG_EVENT( "Surface deleted, event-thread continuing" );
344 void CombinedUpdateRenderController::ResizeSurface()
348 LOG_EVENT( "Resize the surface" );
351 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
352 mPostRendering = FALSE; // Clear the post-rendering flag as Update/Render thread will resize the surface now
353 mSurfaceResized = TRUE;
354 mUpdateRenderThreadWaitCondition.Notify( lock );
358 void CombinedUpdateRenderController::SetRenderRefreshRate( unsigned int numberOfFramesPerRender )
360 // Not protected by lock, but written to rarely so not worth adding a lock when reading
361 mDefaultFrameDelta = numberOfFramesPerRender * DEFAULT_FRAME_DURATION_IN_SECONDS;
362 mDefaultFrameDurationMilliseconds = uint64_t( numberOfFramesPerRender ) * DEFAULT_FRAME_DURATION_IN_MILLISECONDS;
363 mDefaultFrameDurationNanoseconds = uint64_t( numberOfFramesPerRender ) * DEFAULT_FRAME_DURATION_IN_NANOSECONDS;
364 mDefaultHalfFrameNanoseconds = mDefaultFrameDurationNanoseconds / 2u;
366 LOG_EVENT( "mDefaultFrameDelta(%.6f), mDefaultFrameDurationMilliseconds(%lld), mDefaultFrameDurationNanoseconds(%lld)", mDefaultFrameDelta, mDefaultFrameDurationMilliseconds, mDefaultFrameDurationNanoseconds );
369 void CombinedUpdateRenderController::SetPreRenderCallback( CallbackBase* callback )
372 LOG_EVENT( "Set PreRender Callback" );
374 ConditionalWait::ScopedLock updateLock( mUpdateRenderThreadWaitCondition );
375 if( mPreRenderCallback )
377 delete mPreRenderCallback;
379 mPreRenderCallback = callback;
382 ///////////////////////////////////////////////////////////////////////////////////////////////////
384 ///////////////////////////////////////////////////////////////////////////////////////////////////
386 void CombinedUpdateRenderController::RunUpdateRenderThread( int numberOfCycles, AnimationProgression animationProgression, UpdateMode updateMode )
388 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
389 mUpdateRenderRunCount = numberOfCycles;
390 mUpdateRenderThreadCanSleep = FALSE;
391 mUseElapsedTimeAfterWait = ( animationProgression == AnimationProgression::USE_ELAPSED_TIME );
392 mUploadWithoutRendering = ( updateMode == UpdateMode::SKIP_RENDER );
393 LOG_COUNTER_EVENT( "mUpdateRenderRunCount: %d, mUseElapsedTimeAfterWait: %d", mUpdateRenderRunCount, mUseElapsedTimeAfterWait );
394 mUpdateRenderThreadWaitCondition.Notify( lock );
397 void CombinedUpdateRenderController::PauseUpdateRenderThread()
399 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
400 mUpdateRenderRunCount = 0;
403 void CombinedUpdateRenderController::StopUpdateRenderThread()
405 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
406 mDestroyUpdateRenderThread = TRUE;
407 mUpdateRenderThreadWaitCondition.Notify( lock );
410 bool CombinedUpdateRenderController::IsUpdateRenderThreadPaused()
412 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
413 return ( mUpdateRenderRunCount != CONTINUOUS ) || // Report paused if NOT continuously running
414 mUpdateRenderThreadCanSleep; // Report paused if sleeping
417 void CombinedUpdateRenderController::ProcessSleepRequest()
421 // Decrement Update request count
422 if( mUpdateRequestCount > 0 )
424 --mUpdateRequestCount;
427 // Can sleep if our update-request count is 0
428 // Update/Render thread can choose to carry on updating if it determines more update/renders are required
429 if( mUpdateRequestCount == 0 )
431 LOG_EVENT( "Going to sleep" );
433 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
434 mUpdateRenderThreadCanSleep = TRUE;
438 ///////////////////////////////////////////////////////////////////////////////////////////////////
439 // UPDATE/RENDER THREAD
440 ///////////////////////////////////////////////////////////////////////////////////////////////////
442 void CombinedUpdateRenderController::UpdateRenderThread()
444 SetThreadName("RenderThread\0");
446 // Install a function for logging
447 mEnvironmentOptions.InstallLogFunction();
449 // Install a function for tracing
450 mEnvironmentOptions.InstallTraceFunction();
452 LOG_UPDATE_RENDER( "THREAD CREATED" );
454 // Initialize EGL & OpenGL
455 Dali::DisplayConnection& displayConnection = mAdaptorInterfaces.GetDisplayConnectionInterface();
456 displayConnection.Initialize();
458 RenderSurfaceInterface* currentSurface = nullptr;
460 GraphicsInterface& graphics = mAdaptorInterfaces.GetGraphicsInterface();
461 EglGraphics* eglGraphics = static_cast<EglGraphics *>(&graphics);
463 // This will only be created once
464 EglInterface* eglInterface = &eglGraphics->GetEglInterface();
466 Internal::Adaptor::EglImplementation& eglImpl = static_cast<Internal::Adaptor::EglImplementation&>( *eglInterface );
468 // Try to use OpenGL es 3.0
469 // ChooseConfig returns false here when the device only support gles 2.0.
470 // Because eglChooseConfig with gles 3.0 setting fails when the device only support gles 2.0 and Our default setting is gles 3.0.
471 if( !eglImpl.ChooseConfig( true, COLOR_DEPTH_32 ) )
473 // Retry to use OpenGL es 2.0
474 eglGraphics->SetGlesVersion( 20 );
475 eglImpl.ChooseConfig( true, COLOR_DEPTH_32 );
478 // Check whether surfaceless context is supported
479 bool isSurfacelessContextSupported = eglImpl.IsSurfacelessContextSupported();
480 eglGraphics->SetIsSurfacelessContextSupported( isSurfacelessContextSupported );
482 if ( isSurfacelessContextSupported )
484 // Create a surfaceless OpenGL context for shared resources
485 eglImpl.CreateContext();
486 eglImpl.MakeContextCurrent( EGL_NO_SURFACE, eglImpl.GetContext() );
490 currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
493 currentSurface->InitializeGraphics();
494 currentSurface->MakeContextCurrent();
498 eglGraphics->GetGlesInterface().ContextCreated();
500 // Tell core it has a context
501 mCore.ContextCreated();
503 NotifyThreadInitialised();
506 uint64_t lastFrameTime;
507 TimeService::GetNanoseconds( lastFrameTime );
509 LOG_UPDATE_RENDER( "THREAD INITIALISED" );
511 bool useElapsedTime = true;
512 bool updateRequired = true;
513 uint64_t timeToSleepUntil = 0;
514 int extraFramesDropped = 0;
516 const unsigned int renderToFboInterval = mEnvironmentOptions.GetRenderToFboInterval();
517 const bool renderToFboEnabled = 0u != renderToFboInterval;
518 unsigned int frameCount = 0u;
520 while( UpdateRenderReady( useElapsedTime, updateRequired, timeToSleepUntil ) )
522 LOG_UPDATE_RENDER_TRACE;
524 // Performance statistics are logged upon a VSYNC tick so use this point for a VSync marker
525 AddPerformanceMarker( PerformanceInterface::VSYNC );
527 uint64_t currentFrameStartTime = 0;
528 TimeService::GetNanoseconds( currentFrameStartTime );
530 const uint64_t timeSinceLastFrame = currentFrameStartTime - lastFrameTime;
532 // Optional FPS Tracking when continuously rendering
533 if( useElapsedTime && mFpsTracker.Enabled() )
535 float absoluteTimeSinceLastRender = timeSinceLastFrame * NANOSECONDS_TO_SECOND;
536 mFpsTracker.Track( absoluteTimeSinceLastRender );
539 lastFrameTime = currentFrameStartTime; // Store frame start time
541 //////////////////////////////
543 //////////////////////////////
545 Integration::RenderSurface* newSurface = ShouldSurfaceBeReplaced();
546 if( DALI_UNLIKELY( newSurface ) )
548 LOG_UPDATE_RENDER_TRACE_FMT( "Replacing Surface" );
550 // This is designed for replacing pixmap surfaces, but should work for window as well
551 // we need to delete the surface and renderable (pixmap / window)
552 // Then create a new pixmap/window and new surface
553 // If the new surface has a different display connection, then the context will be lost
555 mAdaptorInterfaces.GetDisplayConnectionInterface().Initialize();
556 newSurface->InitializeGraphics();
557 newSurface->ReplaceGraphicsSurface();
561 const bool isRenderingToFbo = renderToFboEnabled && ( ( 0u == frameCount ) || ( 0u != frameCount % renderToFboInterval ) );
564 //////////////////////////////
566 //////////////////////////////
568 const unsigned int currentTime = currentFrameStartTime / NANOSECONDS_PER_MILLISECOND;
569 const unsigned int nextFrameTime = currentTime + mDefaultFrameDurationMilliseconds;
571 uint64_t noOfFramesSinceLastUpdate = 1;
572 float frameDelta = 0.0f;
575 // If using the elapsed time, then calculate frameDelta as a multiple of mDefaultFrameDelta
576 noOfFramesSinceLastUpdate += extraFramesDropped;
578 frameDelta = mDefaultFrameDelta * noOfFramesSinceLastUpdate;
580 LOG_UPDATE_RENDER( "timeSinceLastFrame(%llu) noOfFramesSinceLastUpdate(%u) frameDelta(%.6f)", timeSinceLastFrame, noOfFramesSinceLastUpdate, frameDelta );
582 Integration::UpdateStatus updateStatus;
584 AddPerformanceMarker( PerformanceInterface::UPDATE_START );
585 mCore.Update( frameDelta,
591 AddPerformanceMarker( PerformanceInterface::UPDATE_END );
593 unsigned int keepUpdatingStatus = updateStatus.KeepUpdating();
595 // Tell the event-thread to wake up (if asleep) and send a notification event to Core if required
596 if( updateStatus.NeedsNotification() )
598 mNotificationTrigger.Trigger();
599 LOG_UPDATE_RENDER( "Notification Triggered" );
603 bool shouldSurfaceBeResized = ShouldSurfaceBeResized();
604 if( DALI_UNLIKELY( shouldSurfaceBeResized ) )
606 if( updateStatus.SurfaceRectChanged() )
608 LOG_UPDATE_RENDER_TRACE_FMT( "Resizing Surface" );
613 // Optional logging of update/render status
614 mUpdateStatusLogger.Log( keepUpdatingStatus );
616 //////////////////////////////
618 //////////////////////////////
620 mAdaptorInterfaces.GetDisplayConnectionInterface().ConsumeEvents();
622 if( mPreRenderCallback != NULL )
624 bool keepCallback = CallbackBase::ExecuteReturn<bool>(*mPreRenderCallback);
627 delete mPreRenderCallback;
628 mPreRenderCallback = NULL;
632 if( eglImpl.IsSurfacelessContextSupported() )
634 // Make the shared surfaceless context as current before rendering
635 eglImpl.MakeContextCurrent( EGL_NO_SURFACE, eglImpl.GetContext() );
638 if( timeToSleepUntil == 0 )
640 // timeToSleepUntil is set to 0 when the thread is initalized or resumed
641 // Let eglImplementation know the first frame after thread initialized or resumed.
642 eglImpl.SetFirstFrameAfterResume();
645 Integration::RenderStatus renderStatus;
647 AddPerformanceMarker( PerformanceInterface::RENDER_START );
648 mCore.Render( renderStatus, mForceClear, mUploadWithoutRendering );
650 //////////////////////////////
652 //////////////////////////////
654 Integration::RenderSurface* deletedSurface = ShouldSurfaceBeDeleted();
655 if( DALI_UNLIKELY( deletedSurface ) )
657 LOG_UPDATE_RENDER_TRACE_FMT( "Deleting Surface" );
659 mCore.SurfaceDeleted( deletedSurface );
664 AddPerformanceMarker( PerformanceInterface::RENDER_END );
668 // Trigger event thread to request Update/Render thread to sleep if update not required
669 if( ( Integration::KeepUpdating::NOT_REQUESTED == keepUpdatingStatus ) && !renderStatus.NeedsUpdate() )
671 mSleepTrigger->Trigger();
672 updateRequired = false;
673 LOG_UPDATE_RENDER( "Sleep Triggered" );
677 updateRequired = true;
680 //////////////////////////////
682 //////////////////////////////
684 extraFramesDropped = 0;
686 if (timeToSleepUntil == 0)
688 // If this is the first frame after the thread is initialized or resumed, we
689 // use the actual time the current frame starts from to calculate the time to
690 // sleep until the next frame.
691 timeToSleepUntil = currentFrameStartTime + mDefaultFrameDurationNanoseconds;
695 // Otherwise, always use the sleep-until time calculated in the last frame to
696 // calculate the time to sleep until the next frame. In this way, if there is
697 // any time gap between the current frame and the next frame, or if update or
698 // rendering in the current frame takes too much time so that the specified
699 // sleep-until time has already passed, it will try to keep the frames syncing
700 // by shortening the duration of the next frame.
701 timeToSleepUntil += mDefaultFrameDurationNanoseconds;
703 // Check the current time at the end of the frame
704 uint64_t currentFrameEndTime = 0;
705 TimeService::GetNanoseconds( currentFrameEndTime );
706 while ( currentFrameEndTime > timeToSleepUntil + mDefaultFrameDurationNanoseconds )
708 // We are more than one frame behind already, so just drop the next frames
709 // until the sleep-until time is later than the current time so that we can
711 timeToSleepUntil += mDefaultFrameDurationNanoseconds;
712 extraFramesDropped++;
716 // Render to FBO is intended to measure fps above 60 so sleep is not wanted.
717 if( 0u == renderToFboInterval )
719 // Sleep until at least the the default frame duration has elapsed. This will return immediately if the specified end-time has already passed.
720 TimeService::SleepUntil( timeToSleepUntil );
724 // Inform core of context destruction & shutdown EGL
725 mCore.ContextDestroyed();
726 currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
729 currentSurface->DestroySurface();
730 currentSurface = nullptr;
733 LOG_UPDATE_RENDER( "THREAD DESTROYED" );
735 // Uninstall the logging function
736 mEnvironmentOptions.UnInstallLogFunction();
739 bool CombinedUpdateRenderController::UpdateRenderReady( bool& useElapsedTime, bool updateRequired, uint64_t& timeToSleepUntil )
741 useElapsedTime = true;
743 ConditionalWait::ScopedLock updateLock( mUpdateRenderThreadWaitCondition );
744 while( ( ! mUpdateRenderRunCount || // Should try to wait if event-thread has paused the Update/Render thread
745 ( mUpdateRenderThreadCanSleep && ! updateRequired && ! mPendingRequestUpdate ) ) && // Ensure we wait if we're supposed to be sleeping AND do not require another update
746 ! mDestroyUpdateRenderThread && // Ensure we don't wait if the update-render-thread is supposed to be destroyed
747 ! mNewSurface && // Ensure we don't wait if we need to replace the surface
748 ! mDeletedSurface && // Ensure we don't wait if we need to delete the surface
749 ! mSurfaceResized ) // Ensure we don't wait if we need to resize the surface
751 LOG_UPDATE_RENDER( "WAIT: mUpdateRenderRunCount: %d", mUpdateRenderRunCount );
752 LOG_UPDATE_RENDER( " mUpdateRenderThreadCanSleep: %d, updateRequired: %d, mPendingRequestUpdate: %d", mUpdateRenderThreadCanSleep, updateRequired, mPendingRequestUpdate );
753 LOG_UPDATE_RENDER( " mDestroyUpdateRenderThread: %d", mDestroyUpdateRenderThread );
754 LOG_UPDATE_RENDER( " mNewSurface: %d", mNewSurface );
755 LOG_UPDATE_RENDER( " mDeletedSurface: %d", mDeletedSurface );
756 LOG_UPDATE_RENDER( " mSurfaceResized: %d", mSurfaceResized );
758 // Reset the time when the thread is waiting, so the sleep-until time for
759 // the first frame after resuming should be based on the actual start time
760 // of the first frame.
761 timeToSleepUntil = 0;
763 mUpdateRenderThreadWaitCondition.Wait( updateLock );
765 if( ! mUseElapsedTimeAfterWait )
767 useElapsedTime = false;
771 LOG_COUNTER_UPDATE_RENDER( "mUpdateRenderRunCount: %d", mUpdateRenderRunCount );
772 LOG_COUNTER_UPDATE_RENDER( "mUpdateRenderThreadCanSleep: %d, updateRequired: %d, mPendingRequestUpdate: %d", mUpdateRenderThreadCanSleep, updateRequired, mPendingRequestUpdate );
773 LOG_COUNTER_UPDATE_RENDER( "mDestroyUpdateRenderThread: %d", mDestroyUpdateRenderThread );
774 LOG_COUNTER_UPDATE_RENDER( "mNewSurface: %d", mNewSurface );
775 LOG_COUNTER_UPDATE_RENDER( "mDeletedSurface: %d", mDeletedSurface );
776 LOG_COUNTER_UPDATE_RENDER( "mSurfaceResized: %d", mSurfaceResized );
778 mUseElapsedTimeAfterWait = FALSE;
779 mUpdateRenderThreadCanSleep = FALSE;
780 mPendingRequestUpdate = FALSE;
782 // If we've been asked to run Update/Render cycles a finite number of times then decrement so we wait after the
783 // requested number of cycles
784 if( mUpdateRenderRunCount > 0 )
786 --mUpdateRenderRunCount;
789 // Keep the update-render thread alive if this thread is NOT to be destroyed
790 return ! mDestroyUpdateRenderThread;
793 Integration::RenderSurface* CombinedUpdateRenderController::ShouldSurfaceBeReplaced()
795 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
797 Integration::RenderSurface* newSurface = mNewSurface;
803 void CombinedUpdateRenderController::SurfaceReplaced()
805 // Just increment the semaphore
806 sem_post( &mEventThreadSemaphore );
809 Integration::RenderSurface* CombinedUpdateRenderController::ShouldSurfaceBeDeleted()
811 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
813 Integration::RenderSurface* deletedSurface = mDeletedSurface;
814 mDeletedSurface = NULL;
816 return deletedSurface;
819 void CombinedUpdateRenderController::SurfaceDeleted()
821 // Just increment the semaphore
822 sem_post( &mEventThreadSemaphore );
825 bool CombinedUpdateRenderController::ShouldSurfaceBeResized()
827 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
828 return mSurfaceResized;
831 void CombinedUpdateRenderController::SurfaceResized()
833 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
834 mSurfaceResized = FALSE;
837 ///////////////////////////////////////////////////////////////////////////////////////////////////
839 ///////////////////////////////////////////////////////////////////////////////////////////////////
841 void CombinedUpdateRenderController::NotifyThreadInitialised()
843 // Just increment the semaphore
844 sem_post( &mEventThreadSemaphore );
847 void CombinedUpdateRenderController::AddPerformanceMarker( PerformanceInterface::MarkerType type )
849 if( mPerformanceInterface )
851 mPerformanceInterface->AddMarker( type );
855 /////////////////////////////////////////////////////////////////////////////////////////////////
856 // POST RENDERING: EVENT THREAD
857 /////////////////////////////////////////////////////////////////////////////////////////////////
859 void CombinedUpdateRenderController::PostRenderComplete()
861 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
862 mPostRendering = FALSE;
863 mUpdateRenderThreadWaitCondition.Notify( lock );
866 ///////////////////////////////////////////////////////////////////////////////////////////////////
867 // POST RENDERING: RENDER THREAD
868 ///////////////////////////////////////////////////////////////////////////////////////////////////
870 void CombinedUpdateRenderController::PostRenderStarted()
872 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
873 mPostRendering = TRUE;
876 void CombinedUpdateRenderController::PostRenderWaitForCompletion()
878 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
879 while( mPostRendering &&
880 ! mNewSurface && // We should NOT wait if we're replacing the surface
881 ! mDeletedSurface && // We should NOT wait if we're deleting the surface
882 ! mSurfaceResized && // We should NOT wait if we're resizing the surface
883 ! mDestroyUpdateRenderThread )
885 mUpdateRenderThreadWaitCondition.Wait( lock );
889 } // namespace Adaptor
891 } // namespace Internal