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 mPostRendering( FALSE ),
116 mSurfaceResized( FALSE ),
121 // Initialise frame delta/duration variables first
122 SetRenderRefreshRate( environmentOptions.GetRenderRefreshRate() );
124 // Set the thread-synchronization interface on the render-surface
125 Dali::RenderSurfaceInterface* currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
128 currentSurface->SetThreadSynchronization( *this );
131 TriggerEventFactoryInterface& triggerFactory = mAdaptorInterfaces.GetTriggerEventFactoryInterface();
132 mSleepTrigger = triggerFactory.CreateTriggerEvent( MakeCallback( this, &CombinedUpdateRenderController::ProcessSleepRequest ), TriggerEventInterface::KEEP_ALIVE_AFTER_TRIGGER );
134 sem_init( &mEventThreadSemaphore, 0, 0 ); // Initialize to 0 so that it just waits if sem_post has not been called
137 CombinedUpdateRenderController::~CombinedUpdateRenderController()
143 delete mPreRenderCallback;
144 delete mSleepTrigger;
147 void CombinedUpdateRenderController::Initialize()
151 // Ensure Update/Render Thread not already created
152 DALI_ASSERT_ALWAYS( ! mUpdateRenderThread );
154 // Create Update/Render Thread
155 mUpdateRenderThread = new pthread_t();
156 int error = pthread_create( mUpdateRenderThread, NULL, InternalUpdateRenderThreadEntryFunc, this );
157 DALI_ASSERT_ALWAYS( !error && "Return code from pthread_create() when creating UpdateRenderThread" );
159 // The Update/Render thread will now run and initialise the graphics interface etc. and will then wait for Start to be called
160 // When this function returns, the application initialisation on the event thread should occur
163 void CombinedUpdateRenderController::Start()
167 DALI_ASSERT_ALWAYS( !mRunning && mUpdateRenderThread );
169 // Wait until all threads created in Initialise are up and running
170 for( unsigned int i = 0; i < CREATED_THREAD_COUNT; ++i )
172 sem_wait( &mEventThreadSemaphore );
175 Integration::RenderSurface* currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
178 currentSurface->StartRender();
183 LOG_EVENT( "Startup Complete, starting Update/Render Thread" );
185 RunUpdateRenderThread( CONTINUOUS, false /* No animation progression */ );
187 DALI_LOG_RELEASE_INFO( "CombinedUpdateRenderController::Start\n" );
190 void CombinedUpdateRenderController::Pause()
196 PauseUpdateRenderThread();
198 AddPerformanceMarker( PerformanceInterface::PAUSED );
200 DALI_LOG_RELEASE_INFO( "CombinedUpdateRenderController::Pause\n" );
203 void CombinedUpdateRenderController::Resume()
207 if( !mRunning && IsUpdateRenderThreadPaused() )
209 LOG_EVENT( "Resuming" );
211 RunUpdateRenderThread( CONTINUOUS, true /* Animation progression required while we were paused */ );
213 AddPerformanceMarker( PerformanceInterface::RESUME );
218 DALI_LOG_RELEASE_INFO( "CombinedUpdateRenderController::Resume\n" );
222 DALI_LOG_RELEASE_INFO( "CombinedUpdateRenderController::Resume: Already resumed [%d, %d, %d]\n", mRunning, mUpdateRenderRunCount, mUpdateRenderThreadCanSleep );
226 void CombinedUpdateRenderController::Stop()
230 // Stop Rendering and the Update/Render Thread
231 Integration::RenderSurface* currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
234 currentSurface->StopRender();
237 StopUpdateRenderThread();
239 if( mUpdateRenderThread )
241 LOG_EVENT( "Destroying UpdateRenderThread" );
243 // wait for the thread to finish
244 pthread_join( *mUpdateRenderThread, NULL );
246 delete mUpdateRenderThread;
247 mUpdateRenderThread = NULL;
252 DALI_LOG_RELEASE_INFO( "CombinedUpdateRenderController::Stop\n" );
255 void CombinedUpdateRenderController::RequestUpdate()
259 // Increment the update-request count to the maximum
260 if( mUpdateRequestCount < MAXIMUM_UPDATE_REQUESTS )
262 ++mUpdateRequestCount;
265 if( mRunning && IsUpdateRenderThreadPaused() )
267 LOG_EVENT( "Processing" );
269 RunUpdateRenderThread( CONTINUOUS, false /* No animation progression */ );
272 ConditionalWait::ScopedLock updateLock( mUpdateRenderThreadWaitCondition );
273 mPendingRequestUpdate = TRUE;
276 void CombinedUpdateRenderController::RequestUpdateOnce()
278 // Increment the update-request count to the maximum
279 if( mUpdateRequestCount < MAXIMUM_UPDATE_REQUESTS )
281 ++mUpdateRequestCount;
284 if( IsUpdateRenderThreadPaused() )
288 // Run Update/Render once
289 RunUpdateRenderThread( ONCE, false /* No animation progression */ );
293 void CombinedUpdateRenderController::ReplaceSurface( Dali::RenderSurfaceInterface* newSurface )
297 // Set the ThreadSyncronizationInterface on the new surface
298 newSurface->SetThreadSynchronization( *this );
300 LOG_EVENT( "Starting to replace the surface, event-thread blocked" );
302 // Start replacing the surface.
304 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
305 mPostRendering = FALSE; // Clear the post-rendering flag as Update/Render thread will replace the surface now
306 mNewSurface = newSurface;
307 mUpdateRenderThreadWaitCondition.Notify( lock );
310 // Wait until the surface has been replaced
311 sem_wait( &mEventThreadSemaphore );
313 LOG_EVENT( "Surface replaced, event-thread continuing" );
316 void CombinedUpdateRenderController::ResizeSurface()
320 LOG_EVENT( "Resize the surface" );
323 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
324 mPostRendering = FALSE; // Clear the post-rendering flag as Update/Render thread will resize the surface now
325 mSurfaceResized = TRUE;
326 mUpdateRenderThreadWaitCondition.Notify( lock );
330 void CombinedUpdateRenderController::SetRenderRefreshRate( unsigned int numberOfFramesPerRender )
332 // Not protected by lock, but written to rarely so not worth adding a lock when reading
333 mDefaultFrameDelta = numberOfFramesPerRender * DEFAULT_FRAME_DURATION_IN_SECONDS;
334 mDefaultFrameDurationMilliseconds = uint64_t( numberOfFramesPerRender ) * DEFAULT_FRAME_DURATION_IN_MILLISECONDS;
335 mDefaultFrameDurationNanoseconds = uint64_t( numberOfFramesPerRender ) * DEFAULT_FRAME_DURATION_IN_NANOSECONDS;
336 mDefaultHalfFrameNanoseconds = mDefaultFrameDurationNanoseconds / 2u;
338 LOG_EVENT( "mDefaultFrameDelta(%.6f), mDefaultFrameDurationMilliseconds(%lld), mDefaultFrameDurationNanoseconds(%lld)", mDefaultFrameDelta, mDefaultFrameDurationMilliseconds, mDefaultFrameDurationNanoseconds );
341 void CombinedUpdateRenderController::SetPreRenderCallback( CallbackBase* callback )
344 LOG_EVENT( "Set PreRender Callback" );
346 ConditionalWait::ScopedLock updateLock( mUpdateRenderThreadWaitCondition );
347 if( mPreRenderCallback )
349 delete mPreRenderCallback;
351 mPreRenderCallback = callback;
354 ///////////////////////////////////////////////////////////////////////////////////////////////////
356 ///////////////////////////////////////////////////////////////////////////////////////////////////
358 void CombinedUpdateRenderController::RunUpdateRenderThread( int numberOfCycles, bool useElapsedTime )
360 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
361 mUpdateRenderRunCount = numberOfCycles;
362 mUpdateRenderThreadCanSleep = FALSE;
363 mUseElapsedTimeAfterWait = useElapsedTime;
364 LOG_COUNTER_EVENT( "mUpdateRenderRunCount: %d, mUseElapsedTimeAfterWait: %d", mUpdateRenderRunCount, mUseElapsedTimeAfterWait );
365 mUpdateRenderThreadWaitCondition.Notify( lock );
368 void CombinedUpdateRenderController::PauseUpdateRenderThread()
370 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
371 mUpdateRenderRunCount = 0;
374 void CombinedUpdateRenderController::StopUpdateRenderThread()
376 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
377 mDestroyUpdateRenderThread = TRUE;
378 mUpdateRenderThreadWaitCondition.Notify( lock );
381 bool CombinedUpdateRenderController::IsUpdateRenderThreadPaused()
383 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
384 return ( mUpdateRenderRunCount != CONTINUOUS ) || // Report paused if NOT continuously running
385 mUpdateRenderThreadCanSleep; // Report paused if sleeping
388 void CombinedUpdateRenderController::ProcessSleepRequest()
392 // Decrement Update request count
393 if( mUpdateRequestCount > 0 )
395 --mUpdateRequestCount;
398 // Can sleep if our update-request count is 0
399 // Update/Render thread can choose to carry on updating if it determines more update/renders are required
400 if( mUpdateRequestCount == 0 )
402 LOG_EVENT( "Going to sleep" );
404 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
405 mUpdateRenderThreadCanSleep = TRUE;
409 ///////////////////////////////////////////////////////////////////////////////////////////////////
410 // UPDATE/RENDER THREAD
411 ///////////////////////////////////////////////////////////////////////////////////////////////////
413 void CombinedUpdateRenderController::UpdateRenderThread()
415 SetThreadName("RenderThread\0");
417 // Install a function for logging
418 mEnvironmentOptions.InstallLogFunction();
420 // Install a function for tracing
421 mEnvironmentOptions.InstallTraceFunction();
423 LOG_UPDATE_RENDER( "THREAD CREATED" );
425 // Initialize EGL & OpenGL
426 Dali::DisplayConnection& displayConnection = mAdaptorInterfaces.GetDisplayConnectionInterface();
427 displayConnection.Initialize();
429 RenderSurfaceInterface* currentSurface = nullptr;
431 GraphicsInterface& graphics = mAdaptorInterfaces.GetGraphicsInterface();
432 EglGraphics* eglGraphics = static_cast<EglGraphics *>(&graphics);
434 // This will only be created once
435 EglInterface* eglInterface = &eglGraphics->GetEglInterface();
437 Internal::Adaptor::EglImplementation& eglImpl = static_cast<Internal::Adaptor::EglImplementation&>( *eglInterface );
439 // Try to use OpenGL es 3.0
440 // ChooseConfig returns false here when the device only support gles 2.0.
441 // Because eglChooseConfig with gles 3.0 setting fails when the device only support gles 2.0 and Our default setting is gles 3.0.
442 if( !eglImpl.ChooseConfig( true, COLOR_DEPTH_32 ) )
444 // Retry to use OpenGL es 2.0
445 eglGraphics->SetGlesVersion( 20 );
446 eglImpl.ChooseConfig( true, COLOR_DEPTH_32 );
450 if( !eglImpl.CreateContext() )
452 // Retry to use OpenGL es 2.0
453 eglGraphics->SetGlesVersion( 20 );
454 eglImpl.ChooseConfig( true, COLOR_DEPTH_32 );
458 // Check whether surfaceless context is supported
459 bool isSurfacelessContextSupported = eglImpl.IsSurfacelessContextSupported();
460 eglGraphics->SetIsSurfacelessContextSupported( isSurfacelessContextSupported );
462 if ( isSurfacelessContextSupported )
464 // Create a surfaceless OpenGL context for shared resources
465 if( eglImpl.GetContext() == 0 )
467 eglImpl.CreateContext();
469 eglImpl.MakeContextCurrent( EGL_NO_SURFACE, eglImpl.GetContext() );
473 currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
476 currentSurface->InitializeGraphics();
477 currentSurface->MakeContextCurrent();
481 // Tell core it has a context
482 mCore.ContextCreated();
484 NotifyThreadInitialised();
487 uint64_t lastFrameTime;
488 TimeService::GetNanoseconds( lastFrameTime );
490 LOG_UPDATE_RENDER( "THREAD INITIALISED" );
492 bool useElapsedTime = true;
493 bool updateRequired = true;
494 uint64_t timeToSleepUntil = 0;
495 int extraFramesDropped = 0;
497 const unsigned int renderToFboInterval = mEnvironmentOptions.GetRenderToFboInterval();
498 const bool renderToFboEnabled = 0u != renderToFboInterval;
499 unsigned int frameCount = 0u;
501 while( UpdateRenderReady( useElapsedTime, updateRequired, timeToSleepUntil ) )
503 LOG_UPDATE_RENDER_TRACE;
505 // Performance statistics are logged upon a VSYNC tick so use this point for a VSync marker
506 AddPerformanceMarker( PerformanceInterface::VSYNC );
508 uint64_t currentFrameStartTime = 0;
509 TimeService::GetNanoseconds( currentFrameStartTime );
511 const uint64_t timeSinceLastFrame = currentFrameStartTime - lastFrameTime;
513 // Optional FPS Tracking when continuously rendering
514 if( useElapsedTime && mFpsTracker.Enabled() )
516 float absoluteTimeSinceLastRender = timeSinceLastFrame * NANOSECONDS_TO_SECOND;
517 mFpsTracker.Track( absoluteTimeSinceLastRender );
520 lastFrameTime = currentFrameStartTime; // Store frame start time
522 //////////////////////////////
524 //////////////////////////////
526 Integration::RenderSurface* newSurface = ShouldSurfaceBeReplaced();
527 if( DALI_UNLIKELY( newSurface ) )
529 LOG_UPDATE_RENDER_TRACE_FMT( "Replacing Surface" );
531 // This is designed for replacing pixmap surfaces, but should work for window as well
532 // we need to delete the surface and renderable (pixmap / window)
533 // Then create a new pixmap/window and new surface
534 // If the new surface has a different display connection, then the context will be lost
536 mAdaptorInterfaces.GetDisplayConnectionInterface().Initialize();
537 newSurface->InitializeGraphics();
538 newSurface->ReplaceGraphicsSurface();
542 const bool isRenderingToFbo = renderToFboEnabled && ( ( 0u == frameCount ) || ( 0u != frameCount % renderToFboInterval ) );
545 //////////////////////////////
547 //////////////////////////////
549 const unsigned int currentTime = currentFrameStartTime / NANOSECONDS_PER_MILLISECOND;
550 const unsigned int nextFrameTime = currentTime + mDefaultFrameDurationMilliseconds;
552 uint64_t noOfFramesSinceLastUpdate = 1;
553 float frameDelta = 0.0f;
556 // If using the elapsed time, then calculate frameDelta as a multiple of mDefaultFrameDelta
557 noOfFramesSinceLastUpdate += extraFramesDropped;
559 frameDelta = mDefaultFrameDelta * noOfFramesSinceLastUpdate;
561 LOG_UPDATE_RENDER( "timeSinceLastFrame(%llu) noOfFramesSinceLastUpdate(%u) frameDelta(%.6f)", timeSinceLastFrame, noOfFramesSinceLastUpdate, frameDelta );
563 Integration::UpdateStatus updateStatus;
565 AddPerformanceMarker( PerformanceInterface::UPDATE_START );
566 mCore.Update( frameDelta,
572 AddPerformanceMarker( PerformanceInterface::UPDATE_END );
574 unsigned int keepUpdatingStatus = updateStatus.KeepUpdating();
576 // Tell the event-thread to wake up (if asleep) and send a notification event to Core if required
577 if( updateStatus.NeedsNotification() )
579 mNotificationTrigger.Trigger();
580 LOG_UPDATE_RENDER( "Notification Triggered" );
584 bool shouldSurfaceBeResized = ShouldSurfaceBeResized();
585 if( DALI_UNLIKELY( shouldSurfaceBeResized ) )
587 if( updateStatus.SurfaceRectChanged() )
589 LOG_UPDATE_RENDER_TRACE_FMT( "Resizing Surface" );
594 // Optional logging of update/render status
595 mUpdateStatusLogger.Log( keepUpdatingStatus );
597 //////////////////////////////
599 //////////////////////////////
601 mAdaptorInterfaces.GetDisplayConnectionInterface().ConsumeEvents();
603 if( mPreRenderCallback != NULL )
605 bool keepCallback = CallbackBase::ExecuteReturn<bool>(*mPreRenderCallback);
608 delete mPreRenderCallback;
609 mPreRenderCallback = NULL;
613 if( eglImpl.IsSurfacelessContextSupported() )
615 // Make the shared surfaceless context as current before rendering
616 eglImpl.MakeContextCurrent( EGL_NO_SURFACE, eglImpl.GetContext() );
619 Integration::RenderStatus renderStatus;
621 AddPerformanceMarker( PerformanceInterface::RENDER_START );
622 mCore.Render( renderStatus, mForceClear );
623 AddPerformanceMarker( PerformanceInterface::RENDER_END );
627 // Trigger event thread to request Update/Render thread to sleep if update not required
628 if( ( Integration::KeepUpdating::NOT_REQUESTED == keepUpdatingStatus ) && !renderStatus.NeedsUpdate() )
630 mSleepTrigger->Trigger();
631 updateRequired = false;
632 LOG_UPDATE_RENDER( "Sleep Triggered" );
636 updateRequired = true;
639 //////////////////////////////
641 //////////////////////////////
643 extraFramesDropped = 0;
645 if (timeToSleepUntil == 0)
647 // If this is the first frame after the thread is initialized or resumed, we
648 // use the actual time the current frame starts from to calculate the time to
649 // sleep until the next frame.
650 timeToSleepUntil = currentFrameStartTime + mDefaultFrameDurationNanoseconds;
654 // Otherwise, always use the sleep-until time calculated in the last frame to
655 // calculate the time to sleep until the next frame. In this way, if there is
656 // any time gap between the current frame and the next frame, or if update or
657 // rendering in the current frame takes too much time so that the specified
658 // sleep-until time has already passed, it will try to keep the frames syncing
659 // by shortening the duration of the next frame.
660 timeToSleepUntil += mDefaultFrameDurationNanoseconds;
662 // Check the current time at the end of the frame
663 uint64_t currentFrameEndTime = 0;
664 TimeService::GetNanoseconds( currentFrameEndTime );
665 while ( currentFrameEndTime > timeToSleepUntil + mDefaultFrameDurationNanoseconds )
667 // We are more than one frame behind already, so just drop the next frames
668 // until the sleep-until time is later than the current time so that we can
670 timeToSleepUntil += mDefaultFrameDurationNanoseconds;
671 extraFramesDropped++;
675 // Render to FBO is intended to measure fps above 60 so sleep is not wanted.
676 if( 0u == renderToFboInterval )
678 // Sleep until at least the the default frame duration has elapsed. This will return immediately if the specified end-time has already passed.
679 TimeService::SleepUntil( timeToSleepUntil );
683 // Inform core of context destruction & shutdown EGL
684 mCore.ContextDestroyed();
685 currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
688 currentSurface->DestroySurface();
689 currentSurface = nullptr;
692 LOG_UPDATE_RENDER( "THREAD DESTROYED" );
694 // Uninstall the logging function
695 mEnvironmentOptions.UnInstallLogFunction();
698 bool CombinedUpdateRenderController::UpdateRenderReady( bool& useElapsedTime, bool updateRequired, uint64_t& timeToSleepUntil )
700 useElapsedTime = true;
702 ConditionalWait::ScopedLock updateLock( mUpdateRenderThreadWaitCondition );
703 while( ( ! mUpdateRenderRunCount || // Should try to wait if event-thread has paused the Update/Render thread
704 ( mUpdateRenderThreadCanSleep && ! updateRequired && ! mPendingRequestUpdate ) ) && // Ensure we wait if we're supposed to be sleeping AND do not require another update
705 ! mDestroyUpdateRenderThread && // Ensure we don't wait if the update-render-thread is supposed to be destroyed
706 ! mNewSurface && // Ensure we don't wait if we need to replace the surface
707 ! mSurfaceResized ) // Ensure we don't wait if we need to resize the surface
709 LOG_UPDATE_RENDER( "WAIT: mUpdateRenderRunCount: %d", mUpdateRenderRunCount );
710 LOG_UPDATE_RENDER( " mUpdateRenderThreadCanSleep: %d, updateRequired: %d, mPendingRequestUpdate: %d", mUpdateRenderThreadCanSleep, updateRequired, mPendingRequestUpdate );
711 LOG_UPDATE_RENDER( " mDestroyUpdateRenderThread: %d", mDestroyUpdateRenderThread );
712 LOG_UPDATE_RENDER( " mNewSurface: %d", mNewSurface );
713 LOG_UPDATE_RENDER( " mSurfaceResized: %d", mSurfaceResized );
715 // Reset the time when the thread is waiting, so the sleep-until time for
716 // the first frame after resuming should be based on the actual start time
717 // of the first frame.
718 timeToSleepUntil = 0;
720 mUpdateRenderThreadWaitCondition.Wait( updateLock );
722 if( ! mUseElapsedTimeAfterWait )
724 useElapsedTime = false;
728 LOG_COUNTER_UPDATE_RENDER( "mUpdateRenderRunCount: %d", mUpdateRenderRunCount );
729 LOG_COUNTER_UPDATE_RENDER( "mUpdateRenderThreadCanSleep: %d, updateRequired: %d, mPendingRequestUpdate: %d", mUpdateRenderThreadCanSleep, updateRequired, mPendingRequestUpdate );
730 LOG_COUNTER_UPDATE_RENDER( "mDestroyUpdateRenderThread: %d", mDestroyUpdateRenderThread );
731 LOG_COUNTER_UPDATE_RENDER( "mNewSurface: %d", mNewSurface );
732 LOG_COUNTER_UPDATE_RENDER( "mSurfaceResized: %d", mSurfaceResized );
734 mUseElapsedTimeAfterWait = FALSE;
735 mUpdateRenderThreadCanSleep = FALSE;
736 mPendingRequestUpdate = FALSE;
738 // If we've been asked to run Update/Render cycles a finite number of times then decrement so we wait after the
739 // requested number of cycles
740 if( mUpdateRenderRunCount > 0 )
742 --mUpdateRenderRunCount;
745 // Keep the update-render thread alive if this thread is NOT to be destroyed
746 return ! mDestroyUpdateRenderThread;
749 Integration::RenderSurface* CombinedUpdateRenderController::ShouldSurfaceBeReplaced()
751 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
753 Integration::RenderSurface* newSurface = mNewSurface;
759 void CombinedUpdateRenderController::SurfaceReplaced()
761 // Just increment the semaphore
762 sem_post( &mEventThreadSemaphore );
765 bool CombinedUpdateRenderController::ShouldSurfaceBeResized()
767 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
768 return mSurfaceResized;
771 void CombinedUpdateRenderController::SurfaceResized()
773 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
774 mSurfaceResized = FALSE;
777 ///////////////////////////////////////////////////////////////////////////////////////////////////
779 ///////////////////////////////////////////////////////////////////////////////////////////////////
781 void CombinedUpdateRenderController::NotifyThreadInitialised()
783 // Just increment the semaphore
784 sem_post( &mEventThreadSemaphore );
787 void CombinedUpdateRenderController::AddPerformanceMarker( PerformanceInterface::MarkerType type )
789 if( mPerformanceInterface )
791 mPerformanceInterface->AddMarker( type );
795 /////////////////////////////////////////////////////////////////////////////////////////////////
796 // POST RENDERING: EVENT THREAD
797 /////////////////////////////////////////////////////////////////////////////////////////////////
799 void CombinedUpdateRenderController::PostRenderComplete()
801 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
802 mPostRendering = FALSE;
803 mUpdateRenderThreadWaitCondition.Notify( lock );
806 ///////////////////////////////////////////////////////////////////////////////////////////////////
807 // POST RENDERING: RENDER THREAD
808 ///////////////////////////////////////////////////////////////////////////////////////////////////
810 void CombinedUpdateRenderController::PostRenderStarted()
812 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
813 mPostRendering = TRUE;
816 void CombinedUpdateRenderController::PostRenderWaitForCompletion()
818 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
819 while( mPostRendering &&
820 ! mNewSurface && // We should NOT wait if we're replacing the surface
821 ! mSurfaceResized && // We should NOT wait if we're resizing the surface
822 ! mDestroyUpdateRenderThread )
824 mUpdateRenderThreadWaitCondition.Wait( lock );
828 } // namespace Adaptor
830 } // namespace Internal