2 * Copyright (c) 2018 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>
32 #include <dali/devel-api/adaptor-framework/thread-settings.h>
47 const unsigned int CREATED_THREAD_COUNT = 1u;
49 const int CONTINUOUS = -1;
52 const unsigned int TRUE = 1u;
53 const unsigned int FALSE = 0u;
55 const unsigned int MILLISECONDS_PER_SECOND( 1e+3 );
56 const float NANOSECONDS_TO_SECOND( 1e-9f );
57 const unsigned int NANOSECONDS_PER_SECOND( 1e+9 );
58 const unsigned int NANOSECONDS_PER_MILLISECOND( 1e+6 );
60 // The following values will get calculated at compile time
61 const float DEFAULT_FRAME_DURATION_IN_SECONDS( 1.0f / 60.0f );
62 const uint64_t DEFAULT_FRAME_DURATION_IN_MILLISECONDS( DEFAULT_FRAME_DURATION_IN_SECONDS * MILLISECONDS_PER_SECOND );
63 const uint64_t DEFAULT_FRAME_DURATION_IN_NANOSECONDS( DEFAULT_FRAME_DURATION_IN_SECONDS * NANOSECONDS_PER_SECOND );
66 * 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
67 * there is a danger that, on the event-thread we could have:
68 * 1) An update-request where we do nothing as Update/Render thread still running.
69 * 2) Quickly followed by a sleep-request being handled where we pause the Update/Render Thread (even though we have an update to process).
71 * 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:
72 * 1) MAIN THREAD: Update Request: COUNTER = 1
73 * 2) UPDATE/RENDER THREAD: Do Update/Render, then no Updates required -> Sleep Trigger
74 * 3) MAIN THREAD: Update Request: COUNTER = 2
75 * 4) MAIN THREAD: Sleep Request: COUNTER = 1 -> We do not sleep just yet
77 * Also ensures we preserve battery life by only doing ONE update when the above use case is not triggered.
78 * 1) MAIN THREAD: Update Request: COUNTER = 1
79 * 2) UPDATE/RENDER THREAD: Do Update/Render, then no Updates required -> Sleep Trigger
80 * 3) MAIN THREAD: Sleep Request: COUNTER = 0 -> Go to sleep
82 const unsigned int MAXIMUM_UPDATE_REQUESTS = 2;
83 } // unnamed namespace
85 ///////////////////////////////////////////////////////////////////////////////////////////////////
87 ///////////////////////////////////////////////////////////////////////////////////////////////////
89 CombinedUpdateRenderController::CombinedUpdateRenderController( AdaptorInternalServices& adaptorInterfaces, const EnvironmentOptions& environmentOptions )
90 : mFpsTracker( environmentOptions ),
91 mUpdateStatusLogger( environmentOptions ),
92 mEventThreadSemaphore(),
93 mUpdateRenderThreadWaitCondition(),
94 mAdaptorInterfaces( adaptorInterfaces ),
95 mPerformanceInterface( adaptorInterfaces.GetPerformanceInterface() ),
96 mCore( adaptorInterfaces.GetCore() ),
97 mEnvironmentOptions( environmentOptions ),
98 mNotificationTrigger( adaptorInterfaces.GetProcessCoreEventsTrigger() ),
99 mSleepTrigger( NULL ),
100 mPreRenderCallback( NULL ),
101 mUpdateRenderThread( NULL ),
102 mDefaultFrameDelta( 0.0f ),
103 mDefaultFrameDurationMilliseconds( 0u ),
104 mDefaultFrameDurationNanoseconds( 0u ),
105 mDefaultHalfFrameNanoseconds( 0u ),
106 mUpdateRequestCount( 0u ),
108 mUpdateRenderRunCount( 0 ),
109 mDestroyUpdateRenderThread( FALSE ),
110 mUpdateRenderThreadCanSleep( FALSE ),
111 mPendingRequestUpdate( FALSE ),
112 mUseElapsedTimeAfterWait( FALSE ),
114 mPostRendering( FALSE ),
115 mSurfaceResized( FALSE ),
120 // Initialise frame delta/duration variables first
121 SetRenderRefreshRate( environmentOptions.GetRenderRefreshRate() );
123 // Set the thread-synchronization interface on the render-surface
124 RenderSurface* currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
127 currentSurface->SetThreadSynchronization( *this );
130 TriggerEventFactoryInterface& triggerFactory = mAdaptorInterfaces.GetTriggerEventFactoryInterface();
131 mSleepTrigger = triggerFactory.CreateTriggerEvent( MakeCallback( this, &CombinedUpdateRenderController::ProcessSleepRequest ), TriggerEventInterface::KEEP_ALIVE_AFTER_TRIGGER );
133 sem_init( &mEventThreadSemaphore, 0, 0 ); // Initialize to 0 so that it just waits if sem_post has not been called
136 CombinedUpdateRenderController::~CombinedUpdateRenderController()
142 delete mPreRenderCallback;
143 delete mSleepTrigger;
146 void CombinedUpdateRenderController::Initialize()
150 // Ensure Update/Render Thread not already created
151 DALI_ASSERT_ALWAYS( ! mUpdateRenderThread );
153 // Create Update/Render Thread
154 mUpdateRenderThread = new pthread_t();
155 int error = pthread_create( mUpdateRenderThread, NULL, InternalUpdateRenderThreadEntryFunc, this );
156 DALI_ASSERT_ALWAYS( !error && "Return code from pthread_create() when creating UpdateRenderThread" );
158 // The Update/Render thread will now run and initialise the graphics interface etc. and will then wait for Start to be called
159 // When this function returns, the application initialisation on the event thread should occur
162 void CombinedUpdateRenderController::Start()
166 DALI_ASSERT_ALWAYS( !mRunning && mUpdateRenderThread );
168 // Wait until all threads created in Initialise are up and running
169 for( unsigned int i = 0; i < CREATED_THREAD_COUNT; ++i )
171 sem_wait( &mEventThreadSemaphore );
174 RenderSurface* currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
177 currentSurface->StartRender();
182 LOG_EVENT( "Startup Complete, starting Update/Render Thread" );
184 RunUpdateRenderThread( CONTINUOUS, false /* No animation progression */ );
187 void CombinedUpdateRenderController::Pause()
193 PauseUpdateRenderThread();
195 AddPerformanceMarker( PerformanceInterface::PAUSED );
198 void CombinedUpdateRenderController::Resume()
202 if( !mRunning && IsUpdateRenderThreadPaused() )
204 LOG_EVENT( "Resuming" );
206 RunUpdateRenderThread( CONTINUOUS, true /* Animation progression required while we were paused */ );
208 AddPerformanceMarker( PerformanceInterface::RESUME );
215 void CombinedUpdateRenderController::Stop()
219 // Stop Rendering and the Update/Render Thread
220 RenderSurface* currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
223 currentSurface->StopRender();
226 StopUpdateRenderThread();
228 if( mUpdateRenderThread )
230 LOG_EVENT( "Destroying UpdateRenderThread" );
232 // wait for the thread to finish
233 pthread_join( *mUpdateRenderThread, NULL );
235 delete mUpdateRenderThread;
236 mUpdateRenderThread = NULL;
242 void CombinedUpdateRenderController::RequestUpdate()
246 // Increment the update-request count to the maximum
247 if( mUpdateRequestCount < MAXIMUM_UPDATE_REQUESTS )
249 ++mUpdateRequestCount;
252 if( mRunning && IsUpdateRenderThreadPaused() )
254 LOG_EVENT( "Processing" );
256 RunUpdateRenderThread( CONTINUOUS, false /* No animation progression */ );
259 ConditionalWait::ScopedLock updateLock( mUpdateRenderThreadWaitCondition );
260 mPendingRequestUpdate = TRUE;
263 void CombinedUpdateRenderController::RequestUpdateOnce()
265 // Increment the update-request count to the maximum
266 if( mUpdateRequestCount < MAXIMUM_UPDATE_REQUESTS )
268 ++mUpdateRequestCount;
271 if( IsUpdateRenderThreadPaused() )
275 // Run Update/Render once
276 RunUpdateRenderThread( ONCE, false /* No animation progression */ );
280 void CombinedUpdateRenderController::ReplaceSurface( RenderSurface* newSurface )
284 // Set the ThreadSyncronizationInterface on the new surface
285 newSurface->SetThreadSynchronization( *this );
287 LOG_EVENT( "Starting to replace the surface, event-thread blocked" );
289 // Start replacing the surface.
291 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
292 mPostRendering = FALSE; // Clear the post-rendering flag as Update/Render thread will replace the surface now
293 mNewSurface = newSurface;
294 mUpdateRenderThreadWaitCondition.Notify( lock );
297 // Wait until the surface has been replaced
298 sem_wait( &mEventThreadSemaphore );
300 LOG_EVENT( "Surface replaced, event-thread continuing" );
303 void CombinedUpdateRenderController::ResizeSurface()
307 LOG_EVENT( "Resize the surface" );
310 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
311 mPostRendering = FALSE; // Clear the post-rendering flag as Update/Render thread will resize the surface now
312 mSurfaceResized = TRUE;
313 mUpdateRenderThreadWaitCondition.Notify( lock );
317 void CombinedUpdateRenderController::SetRenderRefreshRate( unsigned int numberOfFramesPerRender )
319 // Not protected by lock, but written to rarely so not worth adding a lock when reading
320 mDefaultFrameDelta = numberOfFramesPerRender * DEFAULT_FRAME_DURATION_IN_SECONDS;
321 mDefaultFrameDurationMilliseconds = uint64_t( numberOfFramesPerRender ) * DEFAULT_FRAME_DURATION_IN_MILLISECONDS;
322 mDefaultFrameDurationNanoseconds = uint64_t( numberOfFramesPerRender ) * DEFAULT_FRAME_DURATION_IN_NANOSECONDS;
323 mDefaultHalfFrameNanoseconds = mDefaultFrameDurationNanoseconds / 2u;
325 LOG_EVENT( "mDefaultFrameDelta(%.6f), mDefaultFrameDurationMilliseconds(%lld), mDefaultFrameDurationNanoseconds(%lld)", mDefaultFrameDelta, mDefaultFrameDurationMilliseconds, mDefaultFrameDurationNanoseconds );
328 void CombinedUpdateRenderController::SetPreRenderCallback( CallbackBase* callback )
331 LOG_EVENT( "Set PreRender Callback" );
333 ConditionalWait::ScopedLock updateLock( mUpdateRenderThreadWaitCondition );
334 if( mPreRenderCallback )
336 delete mPreRenderCallback;
338 mPreRenderCallback = callback;
341 ///////////////////////////////////////////////////////////////////////////////////////////////////
343 ///////////////////////////////////////////////////////////////////////////////////////////////////
345 void CombinedUpdateRenderController::RunUpdateRenderThread( int numberOfCycles, bool useElapsedTime )
347 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
348 mUpdateRenderRunCount = numberOfCycles;
349 mUpdateRenderThreadCanSleep = FALSE;
350 mUseElapsedTimeAfterWait = useElapsedTime;
351 LOG_COUNTER_EVENT( "mUpdateRenderRunCount: %d, mUseElapsedTimeAfterWait: %d", mUpdateRenderRunCount, mUseElapsedTimeAfterWait );
352 mUpdateRenderThreadWaitCondition.Notify( lock );
355 void CombinedUpdateRenderController::PauseUpdateRenderThread()
357 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
358 mUpdateRenderRunCount = 0;
361 void CombinedUpdateRenderController::StopUpdateRenderThread()
363 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
364 mDestroyUpdateRenderThread = TRUE;
365 mUpdateRenderThreadWaitCondition.Notify( lock );
368 bool CombinedUpdateRenderController::IsUpdateRenderThreadPaused()
370 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
371 return ( mUpdateRenderRunCount != CONTINUOUS ) || // Report paused if NOT continuously running
372 mUpdateRenderThreadCanSleep; // Report paused if sleeping
375 void CombinedUpdateRenderController::ProcessSleepRequest()
379 // Decrement Update request count
380 if( mUpdateRequestCount > 0 )
382 --mUpdateRequestCount;
385 // Can sleep if our update-request count is 0
386 // Update/Render thread can choose to carry on updating if it determines more update/renders are required
387 if( mUpdateRequestCount == 0 )
389 LOG_EVENT( "Going to sleep" );
391 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
392 mUpdateRenderThreadCanSleep = TRUE;
396 ///////////////////////////////////////////////////////////////////////////////////////////////////
397 // UPDATE/RENDER THREAD
398 ///////////////////////////////////////////////////////////////////////////////////////////////////
400 void CombinedUpdateRenderController::UpdateRenderThread()
402 #if defined(__GNUC__)
403 SetThreadName("RenderThread\0");
406 // Install a function for logging
407 mEnvironmentOptions.InstallLogFunction();
409 // Install a function for tracing
410 mEnvironmentOptions.InstallTraceFunction();
412 LOG_UPDATE_RENDER( "THREAD CREATED" );
414 RenderSurface* currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
417 currentSurface->InitializeGraphics( mAdaptorInterfaces.GetGraphicsInterface(), mAdaptorInterfaces.GetDisplayConnectionInterface() );
420 // Tell core it has a context
421 mCore.ContextCreated();
423 NotifyThreadInitialised();
426 uint64_t lastFrameTime;
427 TimeService::GetNanoseconds( lastFrameTime );
429 LOG_UPDATE_RENDER( "THREAD INITIALISED" );
431 bool useElapsedTime = true;
432 bool updateRequired = true;
433 uint64_t timeToSleepUntil = 0;
434 int extraFramesDropped = 0;
436 const unsigned int renderToFboInterval = mEnvironmentOptions.GetRenderToFboInterval();
437 const bool renderToFboEnabled = 0u != renderToFboInterval;
438 unsigned int frameCount = 0u;
440 while( UpdateRenderReady( useElapsedTime, updateRequired, timeToSleepUntil ) )
442 LOG_UPDATE_RENDER_TRACE;
444 // Performance statistics are logged upon a VSYNC tick so use this point for a VSync marker
445 AddPerformanceMarker( PerformanceInterface::VSYNC );
447 uint64_t currentFrameStartTime = 0;
448 TimeService::GetNanoseconds( currentFrameStartTime );
450 const uint64_t timeSinceLastFrame = currentFrameStartTime - lastFrameTime;
452 // Optional FPS Tracking when continuously rendering
453 if( useElapsedTime && mFpsTracker.Enabled() )
455 float absoluteTimeSinceLastRender = timeSinceLastFrame * NANOSECONDS_TO_SECOND;
456 mFpsTracker.Track( absoluteTimeSinceLastRender );
459 lastFrameTime = currentFrameStartTime; // Store frame start time
461 //////////////////////////////
463 //////////////////////////////
465 RenderSurface* newSurface = ShouldSurfaceBeReplaced();
466 if( DALI_UNLIKELY( newSurface ) )
468 LOG_UPDATE_RENDER_TRACE_FMT( "Replacing Surface" );
470 // This is designed for replacing pixmap surfaces, but should work for window as well
471 // we need to delete the surface and renderable (pixmap / window)
472 // Then create a new pixmap/window and new surface
473 // If the new surface has a different display connection, then the context will be lost
475 mAdaptorInterfaces.GetDisplayConnectionInterface().Initialize();
476 newSurface->InitializeGraphics( mAdaptorInterfaces.GetGraphicsInterface(), mAdaptorInterfaces.GetDisplayConnectionInterface() );
477 newSurface->ReplaceGraphicsSurface();
481 const bool isRenderingToFbo = renderToFboEnabled && ( ( 0u == frameCount ) || ( 0u != frameCount % renderToFboInterval ) );
484 //////////////////////////////
486 //////////////////////////////
488 const unsigned int currentTime = currentFrameStartTime / NANOSECONDS_PER_MILLISECOND;
489 const unsigned int nextFrameTime = currentTime + mDefaultFrameDurationMilliseconds;
491 uint64_t noOfFramesSinceLastUpdate = 1;
492 float frameDelta = 0.0f;
495 // If using the elapsed time, then calculate frameDelta as a multiple of mDefaultFrameDelta
496 noOfFramesSinceLastUpdate += extraFramesDropped;
498 frameDelta = mDefaultFrameDelta * noOfFramesSinceLastUpdate;
500 LOG_UPDATE_RENDER( "timeSinceLastFrame(%llu) noOfFramesSinceLastUpdate(%u) frameDelta(%.6f)", timeSinceLastFrame, noOfFramesSinceLastUpdate, frameDelta );
502 Integration::UpdateStatus updateStatus;
504 AddPerformanceMarker( PerformanceInterface::UPDATE_START );
505 mCore.Update( frameDelta,
511 AddPerformanceMarker( PerformanceInterface::UPDATE_END );
513 unsigned int keepUpdatingStatus = updateStatus.KeepUpdating();
515 // Tell the event-thread to wake up (if asleep) and send a notification event to Core if required
516 if( updateStatus.NeedsNotification() )
518 mNotificationTrigger.Trigger();
519 LOG_UPDATE_RENDER( "Notification Triggered" );
523 bool surfaceResized = false;
524 bool shouldSurfaceBeResized = ShouldSurfaceBeResized();
525 if( DALI_UNLIKELY( shouldSurfaceBeResized ) )
527 if( updateStatus.SurfaceRectChanged() )
529 LOG_UPDATE_RENDER_TRACE_FMT( "Resizing Surface" );
531 surfaceResized = true;
535 // Optional logging of update/render status
536 mUpdateStatusLogger.Log( keepUpdatingStatus );
538 //////////////////////////////
540 //////////////////////////////
542 mAdaptorInterfaces.GetDisplayConnectionInterface().ConsumeEvents();
544 if( mPreRenderCallback != NULL )
546 bool keepCallback = CallbackBase::ExecuteReturn<bool>(*mPreRenderCallback);
549 delete mPreRenderCallback;
550 mPreRenderCallback = NULL;
554 currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
557 currentSurface->PreRender( surfaceResized );
560 Integration::RenderStatus renderStatus;
562 AddPerformanceMarker( PerformanceInterface::RENDER_START );
563 mCore.Render( renderStatus, mForceClear );
564 AddPerformanceMarker( PerformanceInterface::RENDER_END );
568 if( renderStatus.NeedsPostRender() )
572 currentSurface->PostRender( isRenderingToFbo, ( mNewSurface != NULL ), surfaceResized );
576 // Trigger event thread to request Update/Render thread to sleep if update not required
577 if( ( Integration::KeepUpdating::NOT_REQUESTED == keepUpdatingStatus ) && !renderStatus.NeedsUpdate() )
579 mSleepTrigger->Trigger();
580 updateRequired = false;
581 LOG_UPDATE_RENDER( "Sleep Triggered" );
585 updateRequired = true;
588 //////////////////////////////
590 //////////////////////////////
592 extraFramesDropped = 0;
594 if (timeToSleepUntil == 0)
596 // If this is the first frame after the thread is initialized or resumed, we
597 // use the actual time the current frame starts from to calculate the time to
598 // sleep until the next frame.
599 timeToSleepUntil = currentFrameStartTime + mDefaultFrameDurationNanoseconds;
603 // Otherwise, always use the sleep-until time calculated in the last frame to
604 // calculate the time to sleep until the next frame. In this way, if there is
605 // any time gap between the current frame and the next frame, or if update or
606 // rendering in the current frame takes too much time so that the specified
607 // sleep-until time has already passed, it will try to keep the frames syncing
608 // by shortening the duration of the next frame.
609 timeToSleepUntil += mDefaultFrameDurationNanoseconds;
611 // Check the current time at the end of the frame
612 uint64_t currentFrameEndTime = 0;
613 TimeService::GetNanoseconds( currentFrameEndTime );
614 while ( currentFrameEndTime > timeToSleepUntil + mDefaultFrameDurationNanoseconds )
616 // We are more than one frame behind already, so just drop the next frames
617 // until the sleep-until time is later than the current time so that we can
619 timeToSleepUntil += mDefaultFrameDurationNanoseconds;
620 extraFramesDropped++;
624 // Render to FBO is intended to measure fps above 60 so sleep is not wanted.
625 if( 0u == renderToFboInterval )
627 // Sleep until at least the the default frame duration has elapsed. This will return immediately if the specified end-time has already passed.
628 TimeService::SleepUntil( timeToSleepUntil );
632 // Inform core of context destruction & shutdown EGL
633 mCore.ContextDestroyed();
634 currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
637 currentSurface->DestroySurface();
638 currentSurface = nullptr;
641 LOG_UPDATE_RENDER( "THREAD DESTROYED" );
643 // Uninstall the logging function
644 mEnvironmentOptions.UnInstallLogFunction();
647 bool CombinedUpdateRenderController::UpdateRenderReady( bool& useElapsedTime, bool updateRequired, uint64_t& timeToSleepUntil )
649 useElapsedTime = true;
651 ConditionalWait::ScopedLock updateLock( mUpdateRenderThreadWaitCondition );
652 while( ( ! mUpdateRenderRunCount || // Should try to wait if event-thread has paused the Update/Render thread
653 ( mUpdateRenderThreadCanSleep && ! updateRequired && ! mPendingRequestUpdate ) ) && // Ensure we wait if we're supposed to be sleeping AND do not require another update
654 ! mDestroyUpdateRenderThread && // Ensure we don't wait if the update-render-thread is supposed to be destroyed
655 ! mNewSurface && // Ensure we don't wait if we need to replace the surface
656 ! mSurfaceResized ) // Ensure we don't wait if we need to resize the surface
658 LOG_UPDATE_RENDER( "WAIT: mUpdateRenderRunCount: %d", mUpdateRenderRunCount );
659 LOG_UPDATE_RENDER( " mUpdateRenderThreadCanSleep: %d, updateRequired: %d, mPendingRequestUpdate: %d", mUpdateRenderThreadCanSleep, updateRequired, mPendingRequestUpdate );
660 LOG_UPDATE_RENDER( " mDestroyUpdateRenderThread: %d", mDestroyUpdateRenderThread );
661 LOG_UPDATE_RENDER( " mNewSurface: %d", mNewSurface );
662 LOG_UPDATE_RENDER( " mSurfaceResized: %d", mSurfaceResized );
664 // Reset the time when the thread is waiting, so the sleep-until time for
665 // the first frame after resuming should be based on the actual start time
666 // of the first frame.
667 timeToSleepUntil = 0;
669 mUpdateRenderThreadWaitCondition.Wait( updateLock );
671 if( ! mUseElapsedTimeAfterWait )
673 useElapsedTime = false;
677 LOG_COUNTER_UPDATE_RENDER( "mUpdateRenderRunCount: %d", mUpdateRenderRunCount );
678 LOG_COUNTER_UPDATE_RENDER( "mUpdateRenderThreadCanSleep: %d, updateRequired: %d, mPendingRequestUpdate: %d", mUpdateRenderThreadCanSleep, updateRequired, mPendingRequestUpdate );
679 LOG_COUNTER_UPDATE_RENDER( "mDestroyUpdateRenderThread: %d", mDestroyUpdateRenderThread );
680 LOG_COUNTER_UPDATE_RENDER( "mNewSurface: %d", mNewSurface );
681 LOG_COUNTER_UPDATE_RENDER( "mSurfaceResized: %d", mSurfaceResized );
683 mUseElapsedTimeAfterWait = FALSE;
684 mUpdateRenderThreadCanSleep = FALSE;
685 mPendingRequestUpdate = FALSE;
687 // If we've been asked to run Update/Render cycles a finite number of times then decrement so we wait after the
688 // requested number of cycles
689 if( mUpdateRenderRunCount > 0 )
691 --mUpdateRenderRunCount;
694 // Keep the update-render thread alive if this thread is NOT to be destroyed
695 return ! mDestroyUpdateRenderThread;
698 RenderSurface* CombinedUpdateRenderController::ShouldSurfaceBeReplaced()
700 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
702 RenderSurface* newSurface = mNewSurface;
708 void CombinedUpdateRenderController::SurfaceReplaced()
710 // Just increment the semaphore
711 sem_post( &mEventThreadSemaphore );
714 bool CombinedUpdateRenderController::ShouldSurfaceBeResized()
716 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
717 return mSurfaceResized;
720 void CombinedUpdateRenderController::SurfaceResized()
722 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
723 mSurfaceResized = FALSE;
726 ///////////////////////////////////////////////////////////////////////////////////////////////////
728 ///////////////////////////////////////////////////////////////////////////////////////////////////
730 void CombinedUpdateRenderController::NotifyThreadInitialised()
732 // Just increment the semaphore
733 sem_post( &mEventThreadSemaphore );
736 void CombinedUpdateRenderController::AddPerformanceMarker( PerformanceInterface::MarkerType type )
738 if( mPerformanceInterface )
740 mPerformanceInterface->AddMarker( type );
744 /////////////////////////////////////////////////////////////////////////////////////////////////
745 // POST RENDERING: EVENT THREAD
746 /////////////////////////////////////////////////////////////////////////////////////////////////
748 void CombinedUpdateRenderController::PostRenderComplete()
750 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
751 mPostRendering = FALSE;
752 mUpdateRenderThreadWaitCondition.Notify( lock );
755 ///////////////////////////////////////////////////////////////////////////////////////////////////
756 // POST RENDERING: RENDER THREAD
757 ///////////////////////////////////////////////////////////////////////////////////////////////////
759 void CombinedUpdateRenderController::PostRenderStarted()
761 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
762 mPostRendering = TRUE;
765 void CombinedUpdateRenderController::PostRenderWaitForCompletion()
767 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
768 while( mPostRendering &&
769 ! mNewSurface && // We should NOT wait if we're replacing the surface
770 ! mSurfaceResized && // We should NOT wait if we're resizing the surface
771 ! mDestroyUpdateRenderThread )
773 mUpdateRenderThreadWaitCondition.Wait( lock );
777 } // namespace Adaptor
779 } // namespace Internal