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 );
219 DALI_LOG_RELEASE_INFO( "CombinedUpdateRenderController::Resume\n" );
222 void CombinedUpdateRenderController::Stop()
226 // Stop Rendering and the Update/Render Thread
227 Integration::RenderSurface* currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
230 currentSurface->StopRender();
233 StopUpdateRenderThread();
235 if( mUpdateRenderThread )
237 LOG_EVENT( "Destroying UpdateRenderThread" );
239 // wait for the thread to finish
240 pthread_join( *mUpdateRenderThread, NULL );
242 delete mUpdateRenderThread;
243 mUpdateRenderThread = NULL;
248 DALI_LOG_RELEASE_INFO( "CombinedUpdateRenderController::Stop\n" );
251 void CombinedUpdateRenderController::RequestUpdate()
255 // Increment the update-request count to the maximum
256 if( mUpdateRequestCount < MAXIMUM_UPDATE_REQUESTS )
258 ++mUpdateRequestCount;
261 if( mRunning && IsUpdateRenderThreadPaused() )
263 LOG_EVENT( "Processing" );
265 RunUpdateRenderThread( CONTINUOUS, false /* No animation progression */ );
268 ConditionalWait::ScopedLock updateLock( mUpdateRenderThreadWaitCondition );
269 mPendingRequestUpdate = TRUE;
272 void CombinedUpdateRenderController::RequestUpdateOnce()
274 // Increment the update-request count to the maximum
275 if( mUpdateRequestCount < MAXIMUM_UPDATE_REQUESTS )
277 ++mUpdateRequestCount;
280 if( IsUpdateRenderThreadPaused() )
284 // Run Update/Render once
285 RunUpdateRenderThread( ONCE, false /* No animation progression */ );
289 void CombinedUpdateRenderController::ReplaceSurface( Dali::RenderSurfaceInterface* newSurface )
293 // Set the ThreadSyncronizationInterface on the new surface
294 newSurface->SetThreadSynchronization( *this );
296 LOG_EVENT( "Starting to replace the surface, event-thread blocked" );
298 // Start replacing the surface.
300 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
301 mPostRendering = FALSE; // Clear the post-rendering flag as Update/Render thread will replace the surface now
302 mNewSurface = newSurface;
303 mUpdateRenderThreadWaitCondition.Notify( lock );
306 // Wait until the surface has been replaced
307 sem_wait( &mEventThreadSemaphore );
309 LOG_EVENT( "Surface replaced, event-thread continuing" );
312 void CombinedUpdateRenderController::ResizeSurface()
316 LOG_EVENT( "Resize the surface" );
319 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
320 mPostRendering = FALSE; // Clear the post-rendering flag as Update/Render thread will resize the surface now
321 mSurfaceResized = TRUE;
322 mUpdateRenderThreadWaitCondition.Notify( lock );
326 void CombinedUpdateRenderController::SetRenderRefreshRate( unsigned int numberOfFramesPerRender )
328 // Not protected by lock, but written to rarely so not worth adding a lock when reading
329 mDefaultFrameDelta = numberOfFramesPerRender * DEFAULT_FRAME_DURATION_IN_SECONDS;
330 mDefaultFrameDurationMilliseconds = uint64_t( numberOfFramesPerRender ) * DEFAULT_FRAME_DURATION_IN_MILLISECONDS;
331 mDefaultFrameDurationNanoseconds = uint64_t( numberOfFramesPerRender ) * DEFAULT_FRAME_DURATION_IN_NANOSECONDS;
332 mDefaultHalfFrameNanoseconds = mDefaultFrameDurationNanoseconds / 2u;
334 LOG_EVENT( "mDefaultFrameDelta(%.6f), mDefaultFrameDurationMilliseconds(%lld), mDefaultFrameDurationNanoseconds(%lld)", mDefaultFrameDelta, mDefaultFrameDurationMilliseconds, mDefaultFrameDurationNanoseconds );
337 void CombinedUpdateRenderController::SetPreRenderCallback( CallbackBase* callback )
340 LOG_EVENT( "Set PreRender Callback" );
342 ConditionalWait::ScopedLock updateLock( mUpdateRenderThreadWaitCondition );
343 if( mPreRenderCallback )
345 delete mPreRenderCallback;
347 mPreRenderCallback = callback;
350 ///////////////////////////////////////////////////////////////////////////////////////////////////
352 ///////////////////////////////////////////////////////////////////////////////////////////////////
354 void CombinedUpdateRenderController::RunUpdateRenderThread( int numberOfCycles, bool useElapsedTime )
356 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
357 mUpdateRenderRunCount = numberOfCycles;
358 mUpdateRenderThreadCanSleep = FALSE;
359 mUseElapsedTimeAfterWait = useElapsedTime;
360 LOG_COUNTER_EVENT( "mUpdateRenderRunCount: %d, mUseElapsedTimeAfterWait: %d", mUpdateRenderRunCount, mUseElapsedTimeAfterWait );
361 mUpdateRenderThreadWaitCondition.Notify( lock );
364 void CombinedUpdateRenderController::PauseUpdateRenderThread()
366 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
367 mUpdateRenderRunCount = 0;
370 void CombinedUpdateRenderController::StopUpdateRenderThread()
372 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
373 mDestroyUpdateRenderThread = TRUE;
374 mUpdateRenderThreadWaitCondition.Notify( lock );
377 bool CombinedUpdateRenderController::IsUpdateRenderThreadPaused()
379 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
380 return ( mUpdateRenderRunCount != CONTINUOUS ) || // Report paused if NOT continuously running
381 mUpdateRenderThreadCanSleep; // Report paused if sleeping
384 void CombinedUpdateRenderController::ProcessSleepRequest()
388 // Decrement Update request count
389 if( mUpdateRequestCount > 0 )
391 --mUpdateRequestCount;
394 // Can sleep if our update-request count is 0
395 // Update/Render thread can choose to carry on updating if it determines more update/renders are required
396 if( mUpdateRequestCount == 0 )
398 LOG_EVENT( "Going to sleep" );
400 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
401 mUpdateRenderThreadCanSleep = TRUE;
405 ///////////////////////////////////////////////////////////////////////////////////////////////////
406 // UPDATE/RENDER THREAD
407 ///////////////////////////////////////////////////////////////////////////////////////////////////
409 void CombinedUpdateRenderController::UpdateRenderThread()
411 SetThreadName("RenderThread\0");
413 // Install a function for logging
414 mEnvironmentOptions.InstallLogFunction();
416 // Install a function for tracing
417 mEnvironmentOptions.InstallTraceFunction();
419 LOG_UPDATE_RENDER( "THREAD CREATED" );
421 // Initialize EGL & OpenGL
422 Dali::DisplayConnection& displayConnection = mAdaptorInterfaces.GetDisplayConnectionInterface();
423 displayConnection.Initialize();
425 RenderSurfaceInterface* currentSurface = nullptr;
427 GraphicsInterface& graphics = mAdaptorInterfaces.GetGraphicsInterface();
428 EglGraphics* eglGraphics = static_cast<EglGraphics *>(&graphics);
430 // This will only be created once
431 EglInterface* eglInterface = &eglGraphics->GetEglInterface();
433 Internal::Adaptor::EglImplementation& eglImpl = static_cast<Internal::Adaptor::EglImplementation&>( *eglInterface );
435 // Try to use OpenGL es 3.0
436 // ChooseConfig returns false here when the device only support gles 2.0.
437 // Because eglChooseConfig with gles 3.0 setting fails when the device only support gles 2.0 and Our default setting is gles 3.0.
438 if( !eglImpl.ChooseConfig( true, COLOR_DEPTH_32 ) )
440 // Retry to use OpenGL es 2.0
441 eglGraphics->SetGlesVersion( 20 );
442 eglImpl.ChooseConfig( true, COLOR_DEPTH_32 );
445 // Check whether surfaceless context is supported
446 bool isSurfacelessContextSupported = eglImpl.IsSurfacelessContextSupported();
447 eglGraphics->SetIsSurfacelessContextSupported( isSurfacelessContextSupported );
449 if ( isSurfacelessContextSupported )
451 // Create a surfaceless OpenGL context for shared resources
452 eglImpl.CreateContext();
453 eglImpl.MakeContextCurrent( EGL_NO_SURFACE, eglImpl.GetContext() );
457 currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
460 currentSurface->InitializeGraphics();
461 currentSurface->MakeContextCurrent();
465 // Tell core it has a context
466 mCore.ContextCreated();
468 NotifyThreadInitialised();
471 uint64_t lastFrameTime;
472 TimeService::GetNanoseconds( lastFrameTime );
474 LOG_UPDATE_RENDER( "THREAD INITIALISED" );
476 bool useElapsedTime = true;
477 bool updateRequired = true;
478 uint64_t timeToSleepUntil = 0;
479 int extraFramesDropped = 0;
481 const unsigned int renderToFboInterval = mEnvironmentOptions.GetRenderToFboInterval();
482 const bool renderToFboEnabled = 0u != renderToFboInterval;
483 unsigned int frameCount = 0u;
485 while( UpdateRenderReady( useElapsedTime, updateRequired, timeToSleepUntil ) )
487 LOG_UPDATE_RENDER_TRACE;
489 // Performance statistics are logged upon a VSYNC tick so use this point for a VSync marker
490 AddPerformanceMarker( PerformanceInterface::VSYNC );
492 uint64_t currentFrameStartTime = 0;
493 TimeService::GetNanoseconds( currentFrameStartTime );
495 const uint64_t timeSinceLastFrame = currentFrameStartTime - lastFrameTime;
497 // Optional FPS Tracking when continuously rendering
498 if( useElapsedTime && mFpsTracker.Enabled() )
500 float absoluteTimeSinceLastRender = timeSinceLastFrame * NANOSECONDS_TO_SECOND;
501 mFpsTracker.Track( absoluteTimeSinceLastRender );
504 lastFrameTime = currentFrameStartTime; // Store frame start time
506 //////////////////////////////
508 //////////////////////////////
510 Integration::RenderSurface* newSurface = ShouldSurfaceBeReplaced();
511 if( DALI_UNLIKELY( newSurface ) )
513 LOG_UPDATE_RENDER_TRACE_FMT( "Replacing Surface" );
515 // This is designed for replacing pixmap surfaces, but should work for window as well
516 // we need to delete the surface and renderable (pixmap / window)
517 // Then create a new pixmap/window and new surface
518 // If the new surface has a different display connection, then the context will be lost
520 mAdaptorInterfaces.GetDisplayConnectionInterface().Initialize();
521 newSurface->InitializeGraphics();
522 newSurface->ReplaceGraphicsSurface();
526 const bool isRenderingToFbo = renderToFboEnabled && ( ( 0u == frameCount ) || ( 0u != frameCount % renderToFboInterval ) );
529 //////////////////////////////
531 //////////////////////////////
533 const unsigned int currentTime = currentFrameStartTime / NANOSECONDS_PER_MILLISECOND;
534 const unsigned int nextFrameTime = currentTime + mDefaultFrameDurationMilliseconds;
536 uint64_t noOfFramesSinceLastUpdate = 1;
537 float frameDelta = 0.0f;
540 // If using the elapsed time, then calculate frameDelta as a multiple of mDefaultFrameDelta
541 noOfFramesSinceLastUpdate += extraFramesDropped;
543 frameDelta = mDefaultFrameDelta * noOfFramesSinceLastUpdate;
545 LOG_UPDATE_RENDER( "timeSinceLastFrame(%llu) noOfFramesSinceLastUpdate(%u) frameDelta(%.6f)", timeSinceLastFrame, noOfFramesSinceLastUpdate, frameDelta );
547 Integration::UpdateStatus updateStatus;
549 AddPerformanceMarker( PerformanceInterface::UPDATE_START );
550 mCore.Update( frameDelta,
556 AddPerformanceMarker( PerformanceInterface::UPDATE_END );
558 unsigned int keepUpdatingStatus = updateStatus.KeepUpdating();
560 // Tell the event-thread to wake up (if asleep) and send a notification event to Core if required
561 if( updateStatus.NeedsNotification() )
563 mNotificationTrigger.Trigger();
564 LOG_UPDATE_RENDER( "Notification Triggered" );
568 bool shouldSurfaceBeResized = ShouldSurfaceBeResized();
569 if( DALI_UNLIKELY( shouldSurfaceBeResized ) )
571 if( updateStatus.SurfaceRectChanged() )
573 LOG_UPDATE_RENDER_TRACE_FMT( "Resizing Surface" );
578 // Optional logging of update/render status
579 mUpdateStatusLogger.Log( keepUpdatingStatus );
581 //////////////////////////////
583 //////////////////////////////
585 mAdaptorInterfaces.GetDisplayConnectionInterface().ConsumeEvents();
587 if( mPreRenderCallback != NULL )
589 bool keepCallback = CallbackBase::ExecuteReturn<bool>(*mPreRenderCallback);
592 delete mPreRenderCallback;
593 mPreRenderCallback = NULL;
597 if( eglImpl.IsSurfacelessContextSupported() )
599 // Make the shared surfaceless context as current before rendering
600 eglImpl.MakeContextCurrent( EGL_NO_SURFACE, eglImpl.GetContext() );
603 Integration::RenderStatus renderStatus;
605 AddPerformanceMarker( PerformanceInterface::RENDER_START );
606 mCore.Render( renderStatus, mForceClear );
607 AddPerformanceMarker( PerformanceInterface::RENDER_END );
611 // Trigger event thread to request Update/Render thread to sleep if update not required
612 if( ( Integration::KeepUpdating::NOT_REQUESTED == keepUpdatingStatus ) && !renderStatus.NeedsUpdate() )
614 mSleepTrigger->Trigger();
615 updateRequired = false;
616 LOG_UPDATE_RENDER( "Sleep Triggered" );
620 updateRequired = true;
623 //////////////////////////////
625 //////////////////////////////
627 extraFramesDropped = 0;
629 if (timeToSleepUntil == 0)
631 // If this is the first frame after the thread is initialized or resumed, we
632 // use the actual time the current frame starts from to calculate the time to
633 // sleep until the next frame.
634 timeToSleepUntil = currentFrameStartTime + mDefaultFrameDurationNanoseconds;
638 // Otherwise, always use the sleep-until time calculated in the last frame to
639 // calculate the time to sleep until the next frame. In this way, if there is
640 // any time gap between the current frame and the next frame, or if update or
641 // rendering in the current frame takes too much time so that the specified
642 // sleep-until time has already passed, it will try to keep the frames syncing
643 // by shortening the duration of the next frame.
644 timeToSleepUntil += mDefaultFrameDurationNanoseconds;
646 // Check the current time at the end of the frame
647 uint64_t currentFrameEndTime = 0;
648 TimeService::GetNanoseconds( currentFrameEndTime );
649 while ( currentFrameEndTime > timeToSleepUntil + mDefaultFrameDurationNanoseconds )
651 // We are more than one frame behind already, so just drop the next frames
652 // until the sleep-until time is later than the current time so that we can
654 timeToSleepUntil += mDefaultFrameDurationNanoseconds;
655 extraFramesDropped++;
659 // Render to FBO is intended to measure fps above 60 so sleep is not wanted.
660 if( 0u == renderToFboInterval )
662 // Sleep until at least the the default frame duration has elapsed. This will return immediately if the specified end-time has already passed.
663 TimeService::SleepUntil( timeToSleepUntil );
667 // Inform core of context destruction & shutdown EGL
668 mCore.ContextDestroyed();
669 currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
672 currentSurface->DestroySurface();
673 currentSurface = nullptr;
676 LOG_UPDATE_RENDER( "THREAD DESTROYED" );
678 // Uninstall the logging function
679 mEnvironmentOptions.UnInstallLogFunction();
682 bool CombinedUpdateRenderController::UpdateRenderReady( bool& useElapsedTime, bool updateRequired, uint64_t& timeToSleepUntil )
684 useElapsedTime = true;
686 ConditionalWait::ScopedLock updateLock( mUpdateRenderThreadWaitCondition );
687 while( ( ! mUpdateRenderRunCount || // Should try to wait if event-thread has paused the Update/Render thread
688 ( mUpdateRenderThreadCanSleep && ! updateRequired && ! mPendingRequestUpdate ) ) && // Ensure we wait if we're supposed to be sleeping AND do not require another update
689 ! mDestroyUpdateRenderThread && // Ensure we don't wait if the update-render-thread is supposed to be destroyed
690 ! mNewSurface && // Ensure we don't wait if we need to replace the surface
691 ! mSurfaceResized ) // Ensure we don't wait if we need to resize the surface
693 LOG_UPDATE_RENDER( "WAIT: mUpdateRenderRunCount: %d", mUpdateRenderRunCount );
694 LOG_UPDATE_RENDER( " mUpdateRenderThreadCanSleep: %d, updateRequired: %d, mPendingRequestUpdate: %d", mUpdateRenderThreadCanSleep, updateRequired, mPendingRequestUpdate );
695 LOG_UPDATE_RENDER( " mDestroyUpdateRenderThread: %d", mDestroyUpdateRenderThread );
696 LOG_UPDATE_RENDER( " mNewSurface: %d", mNewSurface );
697 LOG_UPDATE_RENDER( " mSurfaceResized: %d", mSurfaceResized );
699 // Reset the time when the thread is waiting, so the sleep-until time for
700 // the first frame after resuming should be based on the actual start time
701 // of the first frame.
702 timeToSleepUntil = 0;
704 mUpdateRenderThreadWaitCondition.Wait( updateLock );
706 if( ! mUseElapsedTimeAfterWait )
708 useElapsedTime = false;
712 LOG_COUNTER_UPDATE_RENDER( "mUpdateRenderRunCount: %d", mUpdateRenderRunCount );
713 LOG_COUNTER_UPDATE_RENDER( "mUpdateRenderThreadCanSleep: %d, updateRequired: %d, mPendingRequestUpdate: %d", mUpdateRenderThreadCanSleep, updateRequired, mPendingRequestUpdate );
714 LOG_COUNTER_UPDATE_RENDER( "mDestroyUpdateRenderThread: %d", mDestroyUpdateRenderThread );
715 LOG_COUNTER_UPDATE_RENDER( "mNewSurface: %d", mNewSurface );
716 LOG_COUNTER_UPDATE_RENDER( "mSurfaceResized: %d", mSurfaceResized );
718 mUseElapsedTimeAfterWait = FALSE;
719 mUpdateRenderThreadCanSleep = FALSE;
720 mPendingRequestUpdate = FALSE;
722 // If we've been asked to run Update/Render cycles a finite number of times then decrement so we wait after the
723 // requested number of cycles
724 if( mUpdateRenderRunCount > 0 )
726 --mUpdateRenderRunCount;
729 // Keep the update-render thread alive if this thread is NOT to be destroyed
730 return ! mDestroyUpdateRenderThread;
733 Integration::RenderSurface* CombinedUpdateRenderController::ShouldSurfaceBeReplaced()
735 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
737 Integration::RenderSurface* newSurface = mNewSurface;
743 void CombinedUpdateRenderController::SurfaceReplaced()
745 // Just increment the semaphore
746 sem_post( &mEventThreadSemaphore );
749 bool CombinedUpdateRenderController::ShouldSurfaceBeResized()
751 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
752 return mSurfaceResized;
755 void CombinedUpdateRenderController::SurfaceResized()
757 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
758 mSurfaceResized = FALSE;
761 ///////////////////////////////////////////////////////////////////////////////////////////////////
763 ///////////////////////////////////////////////////////////////////////////////////////////////////
765 void CombinedUpdateRenderController::NotifyThreadInitialised()
767 // Just increment the semaphore
768 sem_post( &mEventThreadSemaphore );
771 void CombinedUpdateRenderController::AddPerformanceMarker( PerformanceInterface::MarkerType type )
773 if( mPerformanceInterface )
775 mPerformanceInterface->AddMarker( type );
779 /////////////////////////////////////////////////////////////////////////////////////////////////
780 // POST RENDERING: EVENT THREAD
781 /////////////////////////////////////////////////////////////////////////////////////////////////
783 void CombinedUpdateRenderController::PostRenderComplete()
785 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
786 mPostRendering = FALSE;
787 mUpdateRenderThreadWaitCondition.Notify( lock );
790 ///////////////////////////////////////////////////////////////////////////////////////////////////
791 // POST RENDERING: RENDER THREAD
792 ///////////////////////////////////////////////////////////////////////////////////////////////////
794 void CombinedUpdateRenderController::PostRenderStarted()
796 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
797 mPostRendering = TRUE;
800 void CombinedUpdateRenderController::PostRenderWaitForCompletion()
802 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
803 while( mPostRendering &&
804 ! mNewSurface && // We should NOT wait if we're replacing the surface
805 ! mSurfaceResized && // We should NOT wait if we're resizing the surface
806 ! mDestroyUpdateRenderThread )
808 mUpdateRenderThreadWaitCondition.Wait( lock );
812 } // namespace Adaptor
814 } // namespace Internal