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 */ );
188 void CombinedUpdateRenderController::Pause()
194 PauseUpdateRenderThread();
196 AddPerformanceMarker( PerformanceInterface::PAUSED );
199 void CombinedUpdateRenderController::Resume()
203 if( !mRunning && IsUpdateRenderThreadPaused() )
205 LOG_EVENT( "Resuming" );
207 RunUpdateRenderThread( CONTINUOUS, true /* Animation progression required while we were paused */ );
209 AddPerformanceMarker( PerformanceInterface::RESUME );
216 void CombinedUpdateRenderController::Stop()
220 // Stop Rendering and the Update/Render Thread
221 Integration::RenderSurface* currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
224 currentSurface->StopRender();
227 StopUpdateRenderThread();
229 if( mUpdateRenderThread )
231 LOG_EVENT( "Destroying UpdateRenderThread" );
233 // wait for the thread to finish
234 pthread_join( *mUpdateRenderThread, NULL );
236 delete mUpdateRenderThread;
237 mUpdateRenderThread = NULL;
243 void CombinedUpdateRenderController::RequestUpdate()
247 // Increment the update-request count to the maximum
248 if( mUpdateRequestCount < MAXIMUM_UPDATE_REQUESTS )
250 ++mUpdateRequestCount;
253 if( mRunning && IsUpdateRenderThreadPaused() )
255 LOG_EVENT( "Processing" );
257 RunUpdateRenderThread( CONTINUOUS, false /* No animation progression */ );
260 ConditionalWait::ScopedLock updateLock( mUpdateRenderThreadWaitCondition );
261 mPendingRequestUpdate = TRUE;
264 void CombinedUpdateRenderController::RequestUpdateOnce()
266 // Increment the update-request count to the maximum
267 if( mUpdateRequestCount < MAXIMUM_UPDATE_REQUESTS )
269 ++mUpdateRequestCount;
272 if( IsUpdateRenderThreadPaused() )
276 // Run Update/Render once
277 RunUpdateRenderThread( ONCE, false /* No animation progression */ );
281 void CombinedUpdateRenderController::ReplaceSurface( Dali::RenderSurfaceInterface* newSurface )
285 // Set the ThreadSyncronizationInterface on the new surface
286 newSurface->SetThreadSynchronization( *this );
288 LOG_EVENT( "Starting to replace the surface, event-thread blocked" );
290 // Start replacing the surface.
292 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
293 mPostRendering = FALSE; // Clear the post-rendering flag as Update/Render thread will replace the surface now
294 mNewSurface = newSurface;
295 mUpdateRenderThreadWaitCondition.Notify( lock );
298 // Wait until the surface has been replaced
299 sem_wait( &mEventThreadSemaphore );
301 LOG_EVENT( "Surface replaced, event-thread continuing" );
304 void CombinedUpdateRenderController::ResizeSurface()
308 LOG_EVENT( "Resize the surface" );
311 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
312 mPostRendering = FALSE; // Clear the post-rendering flag as Update/Render thread will resize the surface now
313 mSurfaceResized = TRUE;
314 mUpdateRenderThreadWaitCondition.Notify( lock );
318 void CombinedUpdateRenderController::SetRenderRefreshRate( unsigned int numberOfFramesPerRender )
320 // Not protected by lock, but written to rarely so not worth adding a lock when reading
321 mDefaultFrameDelta = numberOfFramesPerRender * DEFAULT_FRAME_DURATION_IN_SECONDS;
322 mDefaultFrameDurationMilliseconds = uint64_t( numberOfFramesPerRender ) * DEFAULT_FRAME_DURATION_IN_MILLISECONDS;
323 mDefaultFrameDurationNanoseconds = uint64_t( numberOfFramesPerRender ) * DEFAULT_FRAME_DURATION_IN_NANOSECONDS;
324 mDefaultHalfFrameNanoseconds = mDefaultFrameDurationNanoseconds / 2u;
326 LOG_EVENT( "mDefaultFrameDelta(%.6f), mDefaultFrameDurationMilliseconds(%lld), mDefaultFrameDurationNanoseconds(%lld)", mDefaultFrameDelta, mDefaultFrameDurationMilliseconds, mDefaultFrameDurationNanoseconds );
329 void CombinedUpdateRenderController::SetPreRenderCallback( CallbackBase* callback )
332 LOG_EVENT( "Set PreRender Callback" );
334 ConditionalWait::ScopedLock updateLock( mUpdateRenderThreadWaitCondition );
335 if( mPreRenderCallback )
337 delete mPreRenderCallback;
339 mPreRenderCallback = callback;
342 ///////////////////////////////////////////////////////////////////////////////////////////////////
344 ///////////////////////////////////////////////////////////////////////////////////////////////////
346 void CombinedUpdateRenderController::RunUpdateRenderThread( int numberOfCycles, bool useElapsedTime )
348 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
349 mUpdateRenderRunCount = numberOfCycles;
350 mUpdateRenderThreadCanSleep = FALSE;
351 mUseElapsedTimeAfterWait = useElapsedTime;
352 LOG_COUNTER_EVENT( "mUpdateRenderRunCount: %d, mUseElapsedTimeAfterWait: %d", mUpdateRenderRunCount, mUseElapsedTimeAfterWait );
353 mUpdateRenderThreadWaitCondition.Notify( lock );
356 void CombinedUpdateRenderController::PauseUpdateRenderThread()
358 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
359 mUpdateRenderRunCount = 0;
362 void CombinedUpdateRenderController::StopUpdateRenderThread()
364 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
365 mDestroyUpdateRenderThread = TRUE;
366 mUpdateRenderThreadWaitCondition.Notify( lock );
369 bool CombinedUpdateRenderController::IsUpdateRenderThreadPaused()
371 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
372 return ( mUpdateRenderRunCount != CONTINUOUS ) || // Report paused if NOT continuously running
373 mUpdateRenderThreadCanSleep; // Report paused if sleeping
376 void CombinedUpdateRenderController::ProcessSleepRequest()
380 // Decrement Update request count
381 if( mUpdateRequestCount > 0 )
383 --mUpdateRequestCount;
386 // Can sleep if our update-request count is 0
387 // Update/Render thread can choose to carry on updating if it determines more update/renders are required
388 if( mUpdateRequestCount == 0 )
390 LOG_EVENT( "Going to sleep" );
392 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
393 mUpdateRenderThreadCanSleep = TRUE;
397 ///////////////////////////////////////////////////////////////////////////////////////////////////
398 // UPDATE/RENDER THREAD
399 ///////////////////////////////////////////////////////////////////////////////////////////////////
401 void CombinedUpdateRenderController::UpdateRenderThread()
403 SetThreadName("RenderThread\0");
405 // Install a function for logging
406 mEnvironmentOptions.InstallLogFunction();
408 // Install a function for tracing
409 mEnvironmentOptions.InstallTraceFunction();
411 LOG_UPDATE_RENDER( "THREAD CREATED" );
413 // Initialize EGL & OpenGL
414 Dali::DisplayConnection& displayConnection = mAdaptorInterfaces.GetDisplayConnectionInterface();
415 displayConnection.Initialize();
417 RenderSurfaceInterface* currentSurface = nullptr;
419 #if DALI_GLES_VERSION >= 30
421 GraphicsInterface& graphics = mAdaptorInterfaces.GetGraphicsInterface();
422 EglGraphics* eglGraphics = static_cast<EglGraphics *>(&graphics);
424 // This will only be created once
425 EglInterface* eglInterface = &eglGraphics->GetEglInterface();
427 Internal::Adaptor::EglImplementation& eglImpl = static_cast<Internal::Adaptor::EglImplementation&>( *eglInterface );
428 eglImpl.ChooseConfig( true, COLOR_DEPTH_32 ); // Always use this for shared context???
430 // Create a surfaceless OpenGL context for shared resources
431 eglImpl.CreateContext();
432 eglImpl.MakeContextCurrent( EGL_NO_SURFACE, eglImpl.GetContext() );
434 #else // DALI_GLES_VERSION >= 30
436 currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
439 currentSurface->InitializeGraphics();
440 currentSurface->MakeContextCurrent();
445 // Tell core it has a context
446 mCore.ContextCreated();
448 NotifyThreadInitialised();
451 uint64_t lastFrameTime;
452 TimeService::GetNanoseconds( lastFrameTime );
454 LOG_UPDATE_RENDER( "THREAD INITIALISED" );
456 bool useElapsedTime = true;
457 bool updateRequired = true;
458 uint64_t timeToSleepUntil = 0;
459 int extraFramesDropped = 0;
461 const unsigned int renderToFboInterval = mEnvironmentOptions.GetRenderToFboInterval();
462 const bool renderToFboEnabled = 0u != renderToFboInterval;
463 unsigned int frameCount = 0u;
465 while( UpdateRenderReady( useElapsedTime, updateRequired, timeToSleepUntil ) )
467 LOG_UPDATE_RENDER_TRACE;
469 // Performance statistics are logged upon a VSYNC tick so use this point for a VSync marker
470 AddPerformanceMarker( PerformanceInterface::VSYNC );
472 uint64_t currentFrameStartTime = 0;
473 TimeService::GetNanoseconds( currentFrameStartTime );
475 const uint64_t timeSinceLastFrame = currentFrameStartTime - lastFrameTime;
477 // Optional FPS Tracking when continuously rendering
478 if( useElapsedTime && mFpsTracker.Enabled() )
480 float absoluteTimeSinceLastRender = timeSinceLastFrame * NANOSECONDS_TO_SECOND;
481 mFpsTracker.Track( absoluteTimeSinceLastRender );
484 lastFrameTime = currentFrameStartTime; // Store frame start time
486 //////////////////////////////
488 //////////////////////////////
490 Integration::RenderSurface* newSurface = ShouldSurfaceBeReplaced();
491 if( DALI_UNLIKELY( newSurface ) )
493 LOG_UPDATE_RENDER_TRACE_FMT( "Replacing Surface" );
495 // This is designed for replacing pixmap surfaces, but should work for window as well
496 // we need to delete the surface and renderable (pixmap / window)
497 // Then create a new pixmap/window and new surface
498 // If the new surface has a different display connection, then the context will be lost
500 mAdaptorInterfaces.GetDisplayConnectionInterface().Initialize();
501 newSurface->InitializeGraphics();
502 newSurface->ReplaceGraphicsSurface();
506 const bool isRenderingToFbo = renderToFboEnabled && ( ( 0u == frameCount ) || ( 0u != frameCount % renderToFboInterval ) );
509 //////////////////////////////
511 //////////////////////////////
513 const unsigned int currentTime = currentFrameStartTime / NANOSECONDS_PER_MILLISECOND;
514 const unsigned int nextFrameTime = currentTime + mDefaultFrameDurationMilliseconds;
516 uint64_t noOfFramesSinceLastUpdate = 1;
517 float frameDelta = 0.0f;
520 // If using the elapsed time, then calculate frameDelta as a multiple of mDefaultFrameDelta
521 noOfFramesSinceLastUpdate += extraFramesDropped;
523 frameDelta = mDefaultFrameDelta * noOfFramesSinceLastUpdate;
525 LOG_UPDATE_RENDER( "timeSinceLastFrame(%llu) noOfFramesSinceLastUpdate(%u) frameDelta(%.6f)", timeSinceLastFrame, noOfFramesSinceLastUpdate, frameDelta );
527 Integration::UpdateStatus updateStatus;
529 AddPerformanceMarker( PerformanceInterface::UPDATE_START );
530 mCore.Update( frameDelta,
536 AddPerformanceMarker( PerformanceInterface::UPDATE_END );
538 unsigned int keepUpdatingStatus = updateStatus.KeepUpdating();
540 // Tell the event-thread to wake up (if asleep) and send a notification event to Core if required
541 if( updateStatus.NeedsNotification() )
543 mNotificationTrigger.Trigger();
544 LOG_UPDATE_RENDER( "Notification Triggered" );
548 bool shouldSurfaceBeResized = ShouldSurfaceBeResized();
549 if( DALI_UNLIKELY( shouldSurfaceBeResized ) )
551 if( updateStatus.SurfaceRectChanged() )
553 LOG_UPDATE_RENDER_TRACE_FMT( "Resizing Surface" );
558 // Optional logging of update/render status
559 mUpdateStatusLogger.Log( keepUpdatingStatus );
561 //////////////////////////////
563 //////////////////////////////
565 mAdaptorInterfaces.GetDisplayConnectionInterface().ConsumeEvents();
567 if( mPreRenderCallback != NULL )
569 bool keepCallback = CallbackBase::ExecuteReturn<bool>(*mPreRenderCallback);
572 delete mPreRenderCallback;
573 mPreRenderCallback = NULL;
577 #if DALI_GLES_VERSION >= 30
578 // Make the shared surfaceless context as current before rendering
579 eglImpl.MakeContextCurrent( EGL_NO_SURFACE, eglImpl.GetContext() );
582 Integration::RenderStatus renderStatus;
584 AddPerformanceMarker( PerformanceInterface::RENDER_START );
585 mCore.Render( renderStatus, mForceClear );
586 AddPerformanceMarker( PerformanceInterface::RENDER_END );
590 // Trigger event thread to request Update/Render thread to sleep if update not required
591 if( ( Integration::KeepUpdating::NOT_REQUESTED == keepUpdatingStatus ) && !renderStatus.NeedsUpdate() )
593 mSleepTrigger->Trigger();
594 updateRequired = false;
595 LOG_UPDATE_RENDER( "Sleep Triggered" );
599 updateRequired = true;
602 //////////////////////////////
604 //////////////////////////////
606 extraFramesDropped = 0;
608 if (timeToSleepUntil == 0)
610 // If this is the first frame after the thread is initialized or resumed, we
611 // use the actual time the current frame starts from to calculate the time to
612 // sleep until the next frame.
613 timeToSleepUntil = currentFrameStartTime + mDefaultFrameDurationNanoseconds;
617 // Otherwise, always use the sleep-until time calculated in the last frame to
618 // calculate the time to sleep until the next frame. In this way, if there is
619 // any time gap between the current frame and the next frame, or if update or
620 // rendering in the current frame takes too much time so that the specified
621 // sleep-until time has already passed, it will try to keep the frames syncing
622 // by shortening the duration of the next frame.
623 timeToSleepUntil += mDefaultFrameDurationNanoseconds;
625 // Check the current time at the end of the frame
626 uint64_t currentFrameEndTime = 0;
627 TimeService::GetNanoseconds( currentFrameEndTime );
628 while ( currentFrameEndTime > timeToSleepUntil + mDefaultFrameDurationNanoseconds )
630 // We are more than one frame behind already, so just drop the next frames
631 // until the sleep-until time is later than the current time so that we can
633 timeToSleepUntil += mDefaultFrameDurationNanoseconds;
634 extraFramesDropped++;
638 // Render to FBO is intended to measure fps above 60 so sleep is not wanted.
639 if( 0u == renderToFboInterval )
641 // Sleep until at least the the default frame duration has elapsed. This will return immediately if the specified end-time has already passed.
642 TimeService::SleepUntil( timeToSleepUntil );
646 // Inform core of context destruction & shutdown EGL
647 mCore.ContextDestroyed();
648 currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
651 currentSurface->DestroySurface();
652 currentSurface = nullptr;
655 LOG_UPDATE_RENDER( "THREAD DESTROYED" );
657 // Uninstall the logging function
658 mEnvironmentOptions.UnInstallLogFunction();
661 bool CombinedUpdateRenderController::UpdateRenderReady( bool& useElapsedTime, bool updateRequired, uint64_t& timeToSleepUntil )
663 useElapsedTime = true;
665 ConditionalWait::ScopedLock updateLock( mUpdateRenderThreadWaitCondition );
666 while( ( ! mUpdateRenderRunCount || // Should try to wait if event-thread has paused the Update/Render thread
667 ( mUpdateRenderThreadCanSleep && ! updateRequired && ! mPendingRequestUpdate ) ) && // Ensure we wait if we're supposed to be sleeping AND do not require another update
668 ! mDestroyUpdateRenderThread && // Ensure we don't wait if the update-render-thread is supposed to be destroyed
669 ! mNewSurface && // Ensure we don't wait if we need to replace the surface
670 ! mSurfaceResized ) // Ensure we don't wait if we need to resize the surface
672 LOG_UPDATE_RENDER( "WAIT: mUpdateRenderRunCount: %d", mUpdateRenderRunCount );
673 LOG_UPDATE_RENDER( " mUpdateRenderThreadCanSleep: %d, updateRequired: %d, mPendingRequestUpdate: %d", mUpdateRenderThreadCanSleep, updateRequired, mPendingRequestUpdate );
674 LOG_UPDATE_RENDER( " mDestroyUpdateRenderThread: %d", mDestroyUpdateRenderThread );
675 LOG_UPDATE_RENDER( " mNewSurface: %d", mNewSurface );
676 LOG_UPDATE_RENDER( " mSurfaceResized: %d", mSurfaceResized );
678 // Reset the time when the thread is waiting, so the sleep-until time for
679 // the first frame after resuming should be based on the actual start time
680 // of the first frame.
681 timeToSleepUntil = 0;
683 mUpdateRenderThreadWaitCondition.Wait( updateLock );
685 if( ! mUseElapsedTimeAfterWait )
687 useElapsedTime = false;
691 LOG_COUNTER_UPDATE_RENDER( "mUpdateRenderRunCount: %d", mUpdateRenderRunCount );
692 LOG_COUNTER_UPDATE_RENDER( "mUpdateRenderThreadCanSleep: %d, updateRequired: %d, mPendingRequestUpdate: %d", mUpdateRenderThreadCanSleep, updateRequired, mPendingRequestUpdate );
693 LOG_COUNTER_UPDATE_RENDER( "mDestroyUpdateRenderThread: %d", mDestroyUpdateRenderThread );
694 LOG_COUNTER_UPDATE_RENDER( "mNewSurface: %d", mNewSurface );
695 LOG_COUNTER_UPDATE_RENDER( "mSurfaceResized: %d", mSurfaceResized );
697 mUseElapsedTimeAfterWait = FALSE;
698 mUpdateRenderThreadCanSleep = FALSE;
699 mPendingRequestUpdate = FALSE;
701 // If we've been asked to run Update/Render cycles a finite number of times then decrement so we wait after the
702 // requested number of cycles
703 if( mUpdateRenderRunCount > 0 )
705 --mUpdateRenderRunCount;
708 // Keep the update-render thread alive if this thread is NOT to be destroyed
709 return ! mDestroyUpdateRenderThread;
712 Integration::RenderSurface* CombinedUpdateRenderController::ShouldSurfaceBeReplaced()
714 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
716 Integration::RenderSurface* newSurface = mNewSurface;
722 void CombinedUpdateRenderController::SurfaceReplaced()
724 // Just increment the semaphore
725 sem_post( &mEventThreadSemaphore );
728 bool CombinedUpdateRenderController::ShouldSurfaceBeResized()
730 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
731 return mSurfaceResized;
734 void CombinedUpdateRenderController::SurfaceResized()
736 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
737 mSurfaceResized = FALSE;
740 ///////////////////////////////////////////////////////////////////////////////////////////////////
742 ///////////////////////////////////////////////////////////////////////////////////////////////////
744 void CombinedUpdateRenderController::NotifyThreadInitialised()
746 // Just increment the semaphore
747 sem_post( &mEventThreadSemaphore );
750 void CombinedUpdateRenderController::AddPerformanceMarker( PerformanceInterface::MarkerType type )
752 if( mPerformanceInterface )
754 mPerformanceInterface->AddMarker( type );
758 /////////////////////////////////////////////////////////////////////////////////////////////////
759 // POST RENDERING: EVENT THREAD
760 /////////////////////////////////////////////////////////////////////////////////////////////////
762 void CombinedUpdateRenderController::PostRenderComplete()
764 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
765 mPostRendering = FALSE;
766 mUpdateRenderThreadWaitCondition.Notify( lock );
769 ///////////////////////////////////////////////////////////////////////////////////////////////////
770 // POST RENDERING: RENDER THREAD
771 ///////////////////////////////////////////////////////////////////////////////////////////////////
773 void CombinedUpdateRenderController::PostRenderStarted()
775 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
776 mPostRendering = TRUE;
779 void CombinedUpdateRenderController::PostRenderWaitForCompletion()
781 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
782 while( mPostRendering &&
783 ! mNewSurface && // We should NOT wait if we're replacing the surface
784 ! mSurfaceResized && // We should NOT wait if we're resizing the surface
785 ! mDestroyUpdateRenderThread )
787 mUpdateRenderThreadWaitCondition.Wait( lock );
791 } // namespace Adaptor
793 } // namespace Internal