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 ),
122 // Initialise frame delta/duration variables first
123 SetRenderRefreshRate( environmentOptions.GetRenderRefreshRate() );
125 // Set the thread-synchronization interface on the render-surface
126 Dali::RenderSurfaceInterface* currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
129 currentSurface->SetThreadSynchronization( *this );
132 TriggerEventFactoryInterface& triggerFactory = mAdaptorInterfaces.GetTriggerEventFactoryInterface();
133 mSleepTrigger = triggerFactory.CreateTriggerEvent( MakeCallback( this, &CombinedUpdateRenderController::ProcessSleepRequest ), TriggerEventInterface::KEEP_ALIVE_AFTER_TRIGGER );
135 sem_init( &mEventThreadSemaphore, 0, 0 ); // Initialize to 0 so that it just waits if sem_post has not been called
138 CombinedUpdateRenderController::~CombinedUpdateRenderController()
144 delete mPreRenderCallback;
145 delete mSleepTrigger;
148 void CombinedUpdateRenderController::Initialize()
152 // Ensure Update/Render Thread not already created
153 DALI_ASSERT_ALWAYS( ! mUpdateRenderThread );
155 // Create Update/Render Thread
156 mUpdateRenderThread = new pthread_t();
157 int error = pthread_create( mUpdateRenderThread, NULL, InternalUpdateRenderThreadEntryFunc, this );
158 DALI_ASSERT_ALWAYS( !error && "Return code from pthread_create() when creating UpdateRenderThread" );
160 // The Update/Render thread will now run and initialise the graphics interface etc. and will then wait for Start to be called
161 // When this function returns, the application initialisation on the event thread should occur
164 void CombinedUpdateRenderController::Start()
168 DALI_ASSERT_ALWAYS( !mRunning && mUpdateRenderThread );
170 // Wait until all threads created in Initialise are up and running
171 for( unsigned int i = 0; i < CREATED_THREAD_COUNT; ++i )
173 sem_wait( &mEventThreadSemaphore );
176 Integration::RenderSurface* currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
179 currentSurface->StartRender();
184 LOG_EVENT( "Startup Complete, starting Update/Render Thread" );
186 RunUpdateRenderThread( CONTINUOUS, false /* No animation progression */ );
188 DALI_LOG_RELEASE_INFO( "CombinedUpdateRenderController::Start\n" );
191 void CombinedUpdateRenderController::Pause()
197 PauseUpdateRenderThread();
199 AddPerformanceMarker( PerformanceInterface::PAUSED );
201 DALI_LOG_RELEASE_INFO( "CombinedUpdateRenderController::Pause\n" );
204 void CombinedUpdateRenderController::Resume()
208 if( !mRunning && IsUpdateRenderThreadPaused() )
210 LOG_EVENT( "Resuming" );
212 RunUpdateRenderThread( CONTINUOUS, true /* Animation progression required while we were paused */ );
214 AddPerformanceMarker( PerformanceInterface::RESUME );
219 DALI_LOG_RELEASE_INFO( "CombinedUpdateRenderController::Resume\n" );
223 DALI_LOG_RELEASE_INFO( "CombinedUpdateRenderController::Resume: Already resumed [%d, %d, %d]\n", mRunning, mUpdateRenderRunCount, mUpdateRenderThreadCanSleep );
227 void CombinedUpdateRenderController::Stop()
231 // Stop Rendering and the Update/Render Thread
232 Integration::RenderSurface* currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
235 currentSurface->StopRender();
238 StopUpdateRenderThread();
240 if( mUpdateRenderThread )
242 LOG_EVENT( "Destroying UpdateRenderThread" );
244 // wait for the thread to finish
245 pthread_join( *mUpdateRenderThread, NULL );
247 delete mUpdateRenderThread;
248 mUpdateRenderThread = NULL;
253 DALI_LOG_RELEASE_INFO( "CombinedUpdateRenderController::Stop\n" );
256 void CombinedUpdateRenderController::RequestUpdate()
260 // Increment the update-request count to the maximum
261 if( mUpdateRequestCount < MAXIMUM_UPDATE_REQUESTS )
263 ++mUpdateRequestCount;
266 if( mRunning && IsUpdateRenderThreadPaused() )
268 LOG_EVENT( "Processing" );
270 RunUpdateRenderThread( CONTINUOUS, false /* No animation progression */ );
273 ConditionalWait::ScopedLock updateLock( mUpdateRenderThreadWaitCondition );
274 mPendingRequestUpdate = TRUE;
277 void CombinedUpdateRenderController::RequestUpdateOnce()
279 // Increment the update-request count to the maximum
280 if( mUpdateRequestCount < MAXIMUM_UPDATE_REQUESTS )
282 ++mUpdateRequestCount;
285 if( IsUpdateRenderThreadPaused() )
289 // Run Update/Render once
290 RunUpdateRenderThread( ONCE, false /* No animation progression */ );
294 void CombinedUpdateRenderController::ReplaceSurface( Dali::RenderSurfaceInterface* newSurface )
298 if( mUpdateRenderThread )
300 // Set the ThreadSyncronizationInterface on the new surface
301 newSurface->SetThreadSynchronization( *this );
303 LOG_EVENT( "Starting to replace the surface, event-thread blocked" );
305 // Start replacing the surface.
307 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
308 mPostRendering = FALSE; // Clear the post-rendering flag as Update/Render thread will replace the surface now
309 mNewSurface = newSurface;
310 mUpdateRenderThreadWaitCondition.Notify( lock );
313 // Wait until the surface has been replaced
314 sem_wait( &mEventThreadSemaphore );
316 LOG_EVENT( "Surface replaced, event-thread continuing" );
320 void CombinedUpdateRenderController::DeleteSurface( Dali::RenderSurfaceInterface* surface )
324 if( mUpdateRenderThread )
326 LOG_EVENT( "Starting to delete the surface, event-thread blocked" );
328 // Start replacing the surface.
330 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
331 mPostRendering = FALSE; // Clear the post-rendering flag as Update/Render thread will delete the surface now
332 mDeletedSurface = surface;
333 mUpdateRenderThreadWaitCondition.Notify( lock );
336 // Wait until the surface has been deleted
337 sem_wait( &mEventThreadSemaphore );
339 LOG_EVENT( "Surface deleted, event-thread continuing" );
343 void CombinedUpdateRenderController::ResizeSurface()
347 LOG_EVENT( "Resize the surface" );
350 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
351 mPostRendering = FALSE; // Clear the post-rendering flag as Update/Render thread will resize the surface now
352 mSurfaceResized = TRUE;
353 mUpdateRenderThreadWaitCondition.Notify( lock );
357 void CombinedUpdateRenderController::SetRenderRefreshRate( unsigned int numberOfFramesPerRender )
359 // Not protected by lock, but written to rarely so not worth adding a lock when reading
360 mDefaultFrameDelta = numberOfFramesPerRender * DEFAULT_FRAME_DURATION_IN_SECONDS;
361 mDefaultFrameDurationMilliseconds = uint64_t( numberOfFramesPerRender ) * DEFAULT_FRAME_DURATION_IN_MILLISECONDS;
362 mDefaultFrameDurationNanoseconds = uint64_t( numberOfFramesPerRender ) * DEFAULT_FRAME_DURATION_IN_NANOSECONDS;
363 mDefaultHalfFrameNanoseconds = mDefaultFrameDurationNanoseconds / 2u;
365 LOG_EVENT( "mDefaultFrameDelta(%.6f), mDefaultFrameDurationMilliseconds(%lld), mDefaultFrameDurationNanoseconds(%lld)", mDefaultFrameDelta, mDefaultFrameDurationMilliseconds, mDefaultFrameDurationNanoseconds );
368 void CombinedUpdateRenderController::SetPreRenderCallback( CallbackBase* callback )
371 LOG_EVENT( "Set PreRender Callback" );
373 ConditionalWait::ScopedLock updateLock( mUpdateRenderThreadWaitCondition );
374 if( mPreRenderCallback )
376 delete mPreRenderCallback;
378 mPreRenderCallback = callback;
381 ///////////////////////////////////////////////////////////////////////////////////////////////////
383 ///////////////////////////////////////////////////////////////////////////////////////////////////
385 void CombinedUpdateRenderController::RunUpdateRenderThread( int numberOfCycles, bool useElapsedTime )
387 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
388 mUpdateRenderRunCount = numberOfCycles;
389 mUpdateRenderThreadCanSleep = FALSE;
390 mUseElapsedTimeAfterWait = useElapsedTime;
391 LOG_COUNTER_EVENT( "mUpdateRenderRunCount: %d, mUseElapsedTimeAfterWait: %d", mUpdateRenderRunCount, mUseElapsedTimeAfterWait );
392 mUpdateRenderThreadWaitCondition.Notify( lock );
395 void CombinedUpdateRenderController::PauseUpdateRenderThread()
397 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
398 mUpdateRenderRunCount = 0;
401 void CombinedUpdateRenderController::StopUpdateRenderThread()
403 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
404 mDestroyUpdateRenderThread = TRUE;
405 mUpdateRenderThreadWaitCondition.Notify( lock );
408 bool CombinedUpdateRenderController::IsUpdateRenderThreadPaused()
410 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
411 return ( mUpdateRenderRunCount != CONTINUOUS ) || // Report paused if NOT continuously running
412 mUpdateRenderThreadCanSleep; // Report paused if sleeping
415 void CombinedUpdateRenderController::ProcessSleepRequest()
419 // Decrement Update request count
420 if( mUpdateRequestCount > 0 )
422 --mUpdateRequestCount;
425 // Can sleep if our update-request count is 0
426 // Update/Render thread can choose to carry on updating if it determines more update/renders are required
427 if( mUpdateRequestCount == 0 )
429 LOG_EVENT( "Going to sleep" );
431 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
432 mUpdateRenderThreadCanSleep = TRUE;
436 ///////////////////////////////////////////////////////////////////////////////////////////////////
437 // UPDATE/RENDER THREAD
438 ///////////////////////////////////////////////////////////////////////////////////////////////////
440 void CombinedUpdateRenderController::UpdateRenderThread()
442 SetThreadName("RenderThread\0");
444 // Install a function for logging
445 mEnvironmentOptions.InstallLogFunction();
447 // Install a function for tracing
448 mEnvironmentOptions.InstallTraceFunction();
450 LOG_UPDATE_RENDER( "THREAD CREATED" );
452 // Initialize EGL & OpenGL
453 Dali::DisplayConnection& displayConnection = mAdaptorInterfaces.GetDisplayConnectionInterface();
454 displayConnection.Initialize();
456 RenderSurfaceInterface* currentSurface = nullptr;
458 GraphicsInterface& graphics = mAdaptorInterfaces.GetGraphicsInterface();
459 EglGraphics* eglGraphics = static_cast<EglGraphics *>(&graphics);
461 // This will only be created once
462 EglInterface* eglInterface = &eglGraphics->GetEglInterface();
464 Internal::Adaptor::EglImplementation& eglImpl = static_cast<Internal::Adaptor::EglImplementation&>( *eglInterface );
466 // Try to use OpenGL es 3.0
467 // ChooseConfig returns false here when the device only support gles 2.0.
468 // Because eglChooseConfig with gles 3.0 setting fails when the device only support gles 2.0 and Our default setting is gles 3.0.
469 if( !eglImpl.ChooseConfig( true, COLOR_DEPTH_32 ) )
471 // Retry to use OpenGL es 2.0
472 eglGraphics->SetGlesVersion( 20 );
473 eglImpl.ChooseConfig( true, COLOR_DEPTH_32 );
476 // Check whether surfaceless context is supported
477 bool isSurfacelessContextSupported = eglImpl.IsSurfacelessContextSupported();
478 eglGraphics->SetIsSurfacelessContextSupported( isSurfacelessContextSupported );
480 if ( isSurfacelessContextSupported )
482 // Create a surfaceless OpenGL context for shared resources
483 eglImpl.CreateContext();
484 eglImpl.MakeContextCurrent( EGL_NO_SURFACE, eglImpl.GetContext() );
488 currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
491 currentSurface->InitializeGraphics();
492 currentSurface->MakeContextCurrent();
496 eglGraphics->GetGlesInterface().ContextCreated();
498 // Tell core it has a context
499 mCore.ContextCreated();
501 NotifyThreadInitialised();
504 uint64_t lastFrameTime;
505 TimeService::GetNanoseconds( lastFrameTime );
507 LOG_UPDATE_RENDER( "THREAD INITIALISED" );
509 bool useElapsedTime = true;
510 bool updateRequired = true;
511 uint64_t timeToSleepUntil = 0;
512 int extraFramesDropped = 0;
514 const unsigned int renderToFboInterval = mEnvironmentOptions.GetRenderToFboInterval();
515 const bool renderToFboEnabled = 0u != renderToFboInterval;
516 unsigned int frameCount = 0u;
518 while( UpdateRenderReady( useElapsedTime, updateRequired, timeToSleepUntil ) )
520 LOG_UPDATE_RENDER_TRACE;
522 // Performance statistics are logged upon a VSYNC tick so use this point for a VSync marker
523 AddPerformanceMarker( PerformanceInterface::VSYNC );
525 uint64_t currentFrameStartTime = 0;
526 TimeService::GetNanoseconds( currentFrameStartTime );
528 const uint64_t timeSinceLastFrame = currentFrameStartTime - lastFrameTime;
530 // Optional FPS Tracking when continuously rendering
531 if( useElapsedTime && mFpsTracker.Enabled() )
533 float absoluteTimeSinceLastRender = timeSinceLastFrame * NANOSECONDS_TO_SECOND;
534 mFpsTracker.Track( absoluteTimeSinceLastRender );
537 lastFrameTime = currentFrameStartTime; // Store frame start time
539 //////////////////////////////
541 //////////////////////////////
543 Integration::RenderSurface* newSurface = ShouldSurfaceBeReplaced();
544 if( DALI_UNLIKELY( newSurface ) )
546 LOG_UPDATE_RENDER_TRACE_FMT( "Replacing Surface" );
548 // This is designed for replacing pixmap surfaces, but should work for window as well
549 // we need to delete the surface and renderable (pixmap / window)
550 // Then create a new pixmap/window and new surface
551 // If the new surface has a different display connection, then the context will be lost
553 mAdaptorInterfaces.GetDisplayConnectionInterface().Initialize();
554 newSurface->InitializeGraphics();
555 newSurface->ReplaceGraphicsSurface();
559 const bool isRenderingToFbo = renderToFboEnabled && ( ( 0u == frameCount ) || ( 0u != frameCount % renderToFboInterval ) );
562 //////////////////////////////
564 //////////////////////////////
566 const unsigned int currentTime = currentFrameStartTime / NANOSECONDS_PER_MILLISECOND;
567 const unsigned int nextFrameTime = currentTime + mDefaultFrameDurationMilliseconds;
569 uint64_t noOfFramesSinceLastUpdate = 1;
570 float frameDelta = 0.0f;
573 // If using the elapsed time, then calculate frameDelta as a multiple of mDefaultFrameDelta
574 noOfFramesSinceLastUpdate += extraFramesDropped;
576 frameDelta = mDefaultFrameDelta * noOfFramesSinceLastUpdate;
578 LOG_UPDATE_RENDER( "timeSinceLastFrame(%llu) noOfFramesSinceLastUpdate(%u) frameDelta(%.6f)", timeSinceLastFrame, noOfFramesSinceLastUpdate, frameDelta );
580 Integration::UpdateStatus updateStatus;
582 AddPerformanceMarker( PerformanceInterface::UPDATE_START );
583 mCore.Update( frameDelta,
589 AddPerformanceMarker( PerformanceInterface::UPDATE_END );
591 unsigned int keepUpdatingStatus = updateStatus.KeepUpdating();
593 // Tell the event-thread to wake up (if asleep) and send a notification event to Core if required
594 if( updateStatus.NeedsNotification() )
596 mNotificationTrigger.Trigger();
597 LOG_UPDATE_RENDER( "Notification Triggered" );
601 bool shouldSurfaceBeResized = ShouldSurfaceBeResized();
602 if( DALI_UNLIKELY( shouldSurfaceBeResized ) )
604 if( updateStatus.SurfaceRectChanged() )
606 LOG_UPDATE_RENDER_TRACE_FMT( "Resizing Surface" );
611 // Optional logging of update/render status
612 mUpdateStatusLogger.Log( keepUpdatingStatus );
614 //////////////////////////////
616 //////////////////////////////
618 mAdaptorInterfaces.GetDisplayConnectionInterface().ConsumeEvents();
620 if( mPreRenderCallback != NULL )
622 bool keepCallback = CallbackBase::ExecuteReturn<bool>(*mPreRenderCallback);
625 delete mPreRenderCallback;
626 mPreRenderCallback = NULL;
630 if( eglImpl.IsSurfacelessContextSupported() )
632 // Make the shared surfaceless context as current before rendering
633 eglImpl.MakeContextCurrent( EGL_NO_SURFACE, eglImpl.GetContext() );
636 if( timeToSleepUntil == 0 )
638 eglImpl.SetFirstFrameAfterResume();
641 Integration::RenderStatus renderStatus;
643 AddPerformanceMarker( PerformanceInterface::RENDER_START );
644 mCore.Render( renderStatus, mForceClear );
646 //////////////////////////////
648 //////////////////////////////
650 Integration::RenderSurface* deletedSurface = ShouldSurfaceBeDeleted();
651 if( DALI_UNLIKELY( deletedSurface ) )
653 LOG_UPDATE_RENDER_TRACE_FMT( "Deleting Surface" );
655 mCore.SurfaceDeleted( deletedSurface );
660 AddPerformanceMarker( PerformanceInterface::RENDER_END );
664 // Trigger event thread to request Update/Render thread to sleep if update not required
665 if( ( Integration::KeepUpdating::NOT_REQUESTED == keepUpdatingStatus ) && !renderStatus.NeedsUpdate() )
667 mSleepTrigger->Trigger();
668 updateRequired = false;
669 LOG_UPDATE_RENDER( "Sleep Triggered" );
673 updateRequired = true;
676 //////////////////////////////
678 //////////////////////////////
680 extraFramesDropped = 0;
682 if (timeToSleepUntil == 0)
684 // If this is the first frame after the thread is initialized or resumed, we
685 // use the actual time the current frame starts from to calculate the time to
686 // sleep until the next frame.
687 timeToSleepUntil = currentFrameStartTime + mDefaultFrameDurationNanoseconds;
691 // Otherwise, always use the sleep-until time calculated in the last frame to
692 // calculate the time to sleep until the next frame. In this way, if there is
693 // any time gap between the current frame and the next frame, or if update or
694 // rendering in the current frame takes too much time so that the specified
695 // sleep-until time has already passed, it will try to keep the frames syncing
696 // by shortening the duration of the next frame.
697 timeToSleepUntil += mDefaultFrameDurationNanoseconds;
699 // Check the current time at the end of the frame
700 uint64_t currentFrameEndTime = 0;
701 TimeService::GetNanoseconds( currentFrameEndTime );
702 while ( currentFrameEndTime > timeToSleepUntil + mDefaultFrameDurationNanoseconds )
704 // We are more than one frame behind already, so just drop the next frames
705 // until the sleep-until time is later than the current time so that we can
707 timeToSleepUntil += mDefaultFrameDurationNanoseconds;
708 extraFramesDropped++;
712 // Render to FBO is intended to measure fps above 60 so sleep is not wanted.
713 if( 0u == renderToFboInterval )
715 // Sleep until at least the the default frame duration has elapsed. This will return immediately if the specified end-time has already passed.
716 TimeService::SleepUntil( timeToSleepUntil );
720 // Inform core of context destruction & shutdown EGL
721 mCore.ContextDestroyed();
722 currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
725 currentSurface->DestroySurface();
726 currentSurface = nullptr;
729 LOG_UPDATE_RENDER( "THREAD DESTROYED" );
731 // Uninstall the logging function
732 mEnvironmentOptions.UnInstallLogFunction();
735 bool CombinedUpdateRenderController::UpdateRenderReady( bool& useElapsedTime, bool updateRequired, uint64_t& timeToSleepUntil )
737 useElapsedTime = true;
739 ConditionalWait::ScopedLock updateLock( mUpdateRenderThreadWaitCondition );
740 while( ( ! mUpdateRenderRunCount || // Should try to wait if event-thread has paused the Update/Render thread
741 ( mUpdateRenderThreadCanSleep && ! updateRequired && ! mPendingRequestUpdate ) ) && // Ensure we wait if we're supposed to be sleeping AND do not require another update
742 ! mDestroyUpdateRenderThread && // Ensure we don't wait if the update-render-thread is supposed to be destroyed
743 ! mNewSurface && // Ensure we don't wait if we need to replace the surface
744 ! mDeletedSurface && // Ensure we don't wait if we need to delete the surface
745 ! mSurfaceResized ) // Ensure we don't wait if we need to resize the surface
747 LOG_UPDATE_RENDER( "WAIT: mUpdateRenderRunCount: %d", mUpdateRenderRunCount );
748 LOG_UPDATE_RENDER( " mUpdateRenderThreadCanSleep: %d, updateRequired: %d, mPendingRequestUpdate: %d", mUpdateRenderThreadCanSleep, updateRequired, mPendingRequestUpdate );
749 LOG_UPDATE_RENDER( " mDestroyUpdateRenderThread: %d", mDestroyUpdateRenderThread );
750 LOG_UPDATE_RENDER( " mNewSurface: %d", mNewSurface );
751 LOG_UPDATE_RENDER( " mDeletedSurface: %d", mDeletedSurface );
752 LOG_UPDATE_RENDER( " mSurfaceResized: %d", mSurfaceResized );
754 // Reset the time when the thread is waiting, so the sleep-until time for
755 // the first frame after resuming should be based on the actual start time
756 // of the first frame.
757 timeToSleepUntil = 0;
759 mUpdateRenderThreadWaitCondition.Wait( updateLock );
761 if( ! mUseElapsedTimeAfterWait )
763 useElapsedTime = false;
767 LOG_COUNTER_UPDATE_RENDER( "mUpdateRenderRunCount: %d", mUpdateRenderRunCount );
768 LOG_COUNTER_UPDATE_RENDER( "mUpdateRenderThreadCanSleep: %d, updateRequired: %d, mPendingRequestUpdate: %d", mUpdateRenderThreadCanSleep, updateRequired, mPendingRequestUpdate );
769 LOG_COUNTER_UPDATE_RENDER( "mDestroyUpdateRenderThread: %d", mDestroyUpdateRenderThread );
770 LOG_COUNTER_UPDATE_RENDER( "mNewSurface: %d", mNewSurface );
771 LOG_COUNTER_UPDATE_RENDER( "mDeletedSurface: %d", mDeletedSurface );
772 LOG_COUNTER_UPDATE_RENDER( "mSurfaceResized: %d", mSurfaceResized );
774 mUseElapsedTimeAfterWait = FALSE;
775 mUpdateRenderThreadCanSleep = FALSE;
776 mPendingRequestUpdate = FALSE;
778 // If we've been asked to run Update/Render cycles a finite number of times then decrement so we wait after the
779 // requested number of cycles
780 if( mUpdateRenderRunCount > 0 )
782 --mUpdateRenderRunCount;
785 // Keep the update-render thread alive if this thread is NOT to be destroyed
786 return ! mDestroyUpdateRenderThread;
789 Integration::RenderSurface* CombinedUpdateRenderController::ShouldSurfaceBeReplaced()
791 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
793 Integration::RenderSurface* newSurface = mNewSurface;
799 void CombinedUpdateRenderController::SurfaceReplaced()
801 // Just increment the semaphore
802 sem_post( &mEventThreadSemaphore );
805 Integration::RenderSurface* CombinedUpdateRenderController::ShouldSurfaceBeDeleted()
807 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
809 Integration::RenderSurface* deletedSurface = mDeletedSurface;
810 mDeletedSurface = NULL;
812 return deletedSurface;
815 void CombinedUpdateRenderController::SurfaceDeleted()
817 // Just increment the semaphore
818 sem_post( &mEventThreadSemaphore );
821 bool CombinedUpdateRenderController::ShouldSurfaceBeResized()
823 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
824 return mSurfaceResized;
827 void CombinedUpdateRenderController::SurfaceResized()
829 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
830 mSurfaceResized = FALSE;
833 ///////////////////////////////////////////////////////////////////////////////////////////////////
835 ///////////////////////////////////////////////////////////////////////////////////////////////////
837 void CombinedUpdateRenderController::NotifyThreadInitialised()
839 // Just increment the semaphore
840 sem_post( &mEventThreadSemaphore );
843 void CombinedUpdateRenderController::AddPerformanceMarker( PerformanceInterface::MarkerType type )
845 if( mPerformanceInterface )
847 mPerformanceInterface->AddMarker( type );
851 /////////////////////////////////////////////////////////////////////////////////////////////////
852 // POST RENDERING: EVENT THREAD
853 /////////////////////////////////////////////////////////////////////////////////////////////////
855 void CombinedUpdateRenderController::PostRenderComplete()
857 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
858 mPostRendering = FALSE;
859 mUpdateRenderThreadWaitCondition.Notify( lock );
862 ///////////////////////////////////////////////////////////////////////////////////////////////////
863 // POST RENDERING: RENDER THREAD
864 ///////////////////////////////////////////////////////////////////////////////////////////////////
866 void CombinedUpdateRenderController::PostRenderStarted()
868 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
869 mPostRendering = TRUE;
872 void CombinedUpdateRenderController::PostRenderWaitForCompletion()
874 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
875 while( mPostRendering &&
876 ! mNewSurface && // We should NOT wait if we're replacing the surface
877 ! mDeletedSurface && // We should NOT wait if we're deleting the surface
878 ! mSurfaceResized && // We should NOT wait if we're resizing the surface
879 ! mDestroyUpdateRenderThread )
881 mUpdateRenderThreadWaitCondition.Wait( lock );
885 } // namespace Adaptor
887 } // namespace Internal