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>
31 #include <dali/devel-api/adaptor-framework/thread-settings.h>
45 const unsigned int CREATED_THREAD_COUNT = 1u;
47 const int CONTINUOUS = -1;
50 const unsigned int TRUE = 1u;
51 const unsigned int FALSE = 0u;
53 const unsigned int MILLISECONDS_PER_SECOND( 1e+3 );
54 const float NANOSECONDS_TO_SECOND( 1e-9f );
55 const unsigned int NANOSECONDS_PER_SECOND( 1e+9 );
56 const unsigned int NANOSECONDS_PER_MILLISECOND( 1e+6 );
58 // The following values will get calculated at compile time
59 const float DEFAULT_FRAME_DURATION_IN_SECONDS( 1.0f / 60.0f );
60 const uint64_t DEFAULT_FRAME_DURATION_IN_MILLISECONDS( DEFAULT_FRAME_DURATION_IN_SECONDS * MILLISECONDS_PER_SECOND );
61 const uint64_t DEFAULT_FRAME_DURATION_IN_NANOSECONDS( DEFAULT_FRAME_DURATION_IN_SECONDS * NANOSECONDS_PER_SECOND );
64 * 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
65 * there is a danger that, on the event-thread we could have:
66 * 1) An update-request where we do nothing as Update/Render thread still running.
67 * 2) Quickly followed by a sleep-request being handled where we pause the Update/Render Thread (even though we have an update to process).
69 * 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:
70 * 1) MAIN THREAD: Update Request: COUNTER = 1
71 * 2) UPDATE/RENDER THREAD: Do Update/Render, then no Updates required -> Sleep Trigger
72 * 3) MAIN THREAD: Update Request: COUNTER = 2
73 * 4) MAIN THREAD: Sleep Request: COUNTER = 1 -> We do not sleep just yet
75 * Also ensures we preserve battery life by only doing ONE update when the above use case is not triggered.
76 * 1) MAIN THREAD: Update Request: COUNTER = 1
77 * 2) UPDATE/RENDER THREAD: Do Update/Render, then no Updates required -> Sleep Trigger
78 * 3) MAIN THREAD: Sleep Request: COUNTER = 0 -> Go to sleep
80 const unsigned int MAXIMUM_UPDATE_REQUESTS = 2;
81 } // unnamed namespace
83 ///////////////////////////////////////////////////////////////////////////////////////////////////
85 ///////////////////////////////////////////////////////////////////////////////////////////////////
87 CombinedUpdateRenderController::CombinedUpdateRenderController( AdaptorInternalServices& adaptorInterfaces, const EnvironmentOptions& environmentOptions )
88 : mFpsTracker( environmentOptions ),
89 mUpdateStatusLogger( environmentOptions ),
90 mEventThreadSemaphore(),
91 mUpdateRenderThreadWaitCondition(),
92 mAdaptorInterfaces( adaptorInterfaces ),
93 mPerformanceInterface( adaptorInterfaces.GetPerformanceInterface() ),
94 mCore( adaptorInterfaces.GetCore() ),
95 mEnvironmentOptions( environmentOptions ),
96 mNotificationTrigger( adaptorInterfaces.GetProcessCoreEventsTrigger() ),
97 mSleepTrigger( NULL ),
98 mPreRenderCallback( NULL ),
99 mUpdateRenderThread( NULL ),
100 mDefaultFrameDelta( 0.0f ),
101 mDefaultFrameDurationMilliseconds( 0u ),
102 mDefaultFrameDurationNanoseconds( 0u ),
103 mDefaultHalfFrameNanoseconds( 0u ),
104 mUpdateRequestCount( 0u ),
106 mUpdateRenderRunCount( 0 ),
107 mDestroyUpdateRenderThread( FALSE ),
108 mUpdateRenderThreadCanSleep( FALSE ),
109 mPendingRequestUpdate( FALSE ),
110 mUseElapsedTimeAfterWait( FALSE ),
112 mPostRendering( FALSE ),
113 mSurfaceResized( FALSE ),
118 // Initialise frame delta/duration variables first
119 SetRenderRefreshRate( environmentOptions.GetRenderRefreshRate() );
121 // Set the thread-synchronization interface on the render-surface
122 RenderSurface* currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
125 currentSurface->SetThreadSynchronization( *this );
128 TriggerEventFactoryInterface& triggerFactory = mAdaptorInterfaces.GetTriggerEventFactoryInterface();
129 mSleepTrigger = triggerFactory.CreateTriggerEvent( MakeCallback( this, &CombinedUpdateRenderController::ProcessSleepRequest ), TriggerEventInterface::KEEP_ALIVE_AFTER_TRIGGER );
131 sem_init( &mEventThreadSemaphore, 0, 0 ); // Initialize to 0 so that it just waits if sem_post has not been called
134 CombinedUpdateRenderController::~CombinedUpdateRenderController()
140 delete mPreRenderCallback;
141 delete mSleepTrigger;
144 void CombinedUpdateRenderController::Initialize()
148 // Ensure Update/Render Thread not already created
149 DALI_ASSERT_ALWAYS( ! mUpdateRenderThread );
151 // Create Update/Render Thread
152 mUpdateRenderThread = new pthread_t();
153 int error = pthread_create( mUpdateRenderThread, NULL, InternalUpdateRenderThreadEntryFunc, this );
154 DALI_ASSERT_ALWAYS( !error && "Return code from pthread_create() when creating UpdateRenderThread" );
156 // The Update/Render thread will now run and initialise the graphics interface etc. and will then wait for Start to be called
157 // When this function returns, the application initialisation on the event thread should occur
160 void CombinedUpdateRenderController::Start()
164 DALI_ASSERT_ALWAYS( !mRunning && mUpdateRenderThread );
166 // Wait until all threads created in Initialise are up and running
167 for( unsigned int i = 0; i < CREATED_THREAD_COUNT; ++i )
169 sem_wait( &mEventThreadSemaphore );
172 RenderSurface* currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
175 currentSurface->StartRender();
180 LOG_EVENT( "Startup Complete, starting Update/Render Thread" );
182 RunUpdateRenderThread( CONTINUOUS, false /* No animation progression */ );
185 void CombinedUpdateRenderController::Pause()
191 PauseUpdateRenderThread();
193 AddPerformanceMarker( PerformanceInterface::PAUSED );
196 void CombinedUpdateRenderController::Resume()
200 if( !mRunning && IsUpdateRenderThreadPaused() )
202 LOG_EVENT( "Resuming" );
204 RunUpdateRenderThread( CONTINUOUS, true /* Animation progression required while we were paused */ );
206 AddPerformanceMarker( PerformanceInterface::RESUME );
213 void CombinedUpdateRenderController::Stop()
217 // Stop Rendering and the Update/Render Thread
218 RenderSurface* currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
221 currentSurface->StopRender();
224 StopUpdateRenderThread();
226 if( mUpdateRenderThread )
228 LOG_EVENT( "Destroying UpdateRenderThread" );
230 // wait for the thread to finish
231 pthread_join( *mUpdateRenderThread, NULL );
233 delete mUpdateRenderThread;
234 mUpdateRenderThread = NULL;
240 void CombinedUpdateRenderController::RequestUpdate()
244 // Increment the update-request count to the maximum
245 if( mUpdateRequestCount < MAXIMUM_UPDATE_REQUESTS )
247 ++mUpdateRequestCount;
250 if( mRunning && IsUpdateRenderThreadPaused() )
252 LOG_EVENT( "Processing" );
254 RunUpdateRenderThread( CONTINUOUS, false /* No animation progression */ );
257 ConditionalWait::ScopedLock updateLock( mUpdateRenderThreadWaitCondition );
258 mPendingRequestUpdate = TRUE;
261 void CombinedUpdateRenderController::RequestUpdateOnce()
263 // Increment the update-request count to the maximum
264 if( mUpdateRequestCount < MAXIMUM_UPDATE_REQUESTS )
266 ++mUpdateRequestCount;
269 if( IsUpdateRenderThreadPaused() )
273 // Run Update/Render once
274 RunUpdateRenderThread( ONCE, false /* No animation progression */ );
278 void CombinedUpdateRenderController::ReplaceSurface( RenderSurface* newSurface )
282 // Set the ThreadSyncronizationInterface on the new surface
283 newSurface->SetThreadSynchronization( *this );
285 LOG_EVENT( "Starting to replace the surface, event-thread blocked" );
287 // Start replacing the surface.
289 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
290 mPostRendering = FALSE; // Clear the post-rendering flag as Update/Render thread will replace the surface now
291 mNewSurface = newSurface;
292 mUpdateRenderThreadWaitCondition.Notify( lock );
295 // Wait until the surface has been replaced
296 sem_wait( &mEventThreadSemaphore );
298 LOG_EVENT( "Surface replaced, event-thread continuing" );
301 void CombinedUpdateRenderController::ResizeSurface()
305 LOG_EVENT( "Resize the surface" );
308 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
309 mPostRendering = FALSE; // Clear the post-rendering flag as Update/Render thread will resize the surface now
310 mSurfaceResized = TRUE;
311 mUpdateRenderThreadWaitCondition.Notify( lock );
315 void CombinedUpdateRenderController::SetRenderRefreshRate( unsigned int numberOfFramesPerRender )
317 // Not protected by lock, but written to rarely so not worth adding a lock when reading
318 mDefaultFrameDelta = numberOfFramesPerRender * DEFAULT_FRAME_DURATION_IN_SECONDS;
319 mDefaultFrameDurationMilliseconds = uint64_t( numberOfFramesPerRender ) * DEFAULT_FRAME_DURATION_IN_MILLISECONDS;
320 mDefaultFrameDurationNanoseconds = uint64_t( numberOfFramesPerRender ) * DEFAULT_FRAME_DURATION_IN_NANOSECONDS;
321 mDefaultHalfFrameNanoseconds = mDefaultFrameDurationNanoseconds / 2u;
323 LOG_EVENT( "mDefaultFrameDelta(%.6f), mDefaultFrameDurationMilliseconds(%lld), mDefaultFrameDurationNanoseconds(%lld)", mDefaultFrameDelta, mDefaultFrameDurationMilliseconds, mDefaultFrameDurationNanoseconds );
326 void CombinedUpdateRenderController::SetPreRenderCallback( CallbackBase* callback )
329 LOG_EVENT( "Set PreRender Callback" );
331 ConditionalWait::ScopedLock updateLock( mUpdateRenderThreadWaitCondition );
332 if( mPreRenderCallback )
334 delete mPreRenderCallback;
336 mPreRenderCallback = callback;
339 ///////////////////////////////////////////////////////////////////////////////////////////////////
341 ///////////////////////////////////////////////////////////////////////////////////////////////////
343 void CombinedUpdateRenderController::RunUpdateRenderThread( int numberOfCycles, bool useElapsedTime )
345 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
346 mUpdateRenderRunCount = numberOfCycles;
347 mUpdateRenderThreadCanSleep = FALSE;
348 mUseElapsedTimeAfterWait = useElapsedTime;
349 LOG_COUNTER_EVENT( "mUpdateRenderRunCount: %d, mUseElapsedTimeAfterWait: %d", mUpdateRenderRunCount, mUseElapsedTimeAfterWait );
350 mUpdateRenderThreadWaitCondition.Notify( lock );
353 void CombinedUpdateRenderController::PauseUpdateRenderThread()
355 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
356 mUpdateRenderRunCount = 0;
359 void CombinedUpdateRenderController::StopUpdateRenderThread()
361 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
362 mDestroyUpdateRenderThread = TRUE;
363 mUpdateRenderThreadWaitCondition.Notify( lock );
366 bool CombinedUpdateRenderController::IsUpdateRenderThreadPaused()
368 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
369 return ( mUpdateRenderRunCount != CONTINUOUS ) || // Report paused if NOT continuously running
370 mUpdateRenderThreadCanSleep; // Report paused if sleeping
373 void CombinedUpdateRenderController::ProcessSleepRequest()
377 // Decrement Update request count
378 if( mUpdateRequestCount > 0 )
380 --mUpdateRequestCount;
383 // Can sleep if our update-request count is 0
384 // Update/Render thread can choose to carry on updating if it determines more update/renders are required
385 if( mUpdateRequestCount == 0 )
387 LOG_EVENT( "Going to sleep" );
389 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
390 mUpdateRenderThreadCanSleep = TRUE;
394 ///////////////////////////////////////////////////////////////////////////////////////////////////
395 // UPDATE/RENDER THREAD
396 ///////////////////////////////////////////////////////////////////////////////////////////////////
398 void CombinedUpdateRenderController::UpdateRenderThread()
400 SetThreadName("RenderThread\0");
402 // Install a function for logging
403 mEnvironmentOptions.InstallLogFunction();
405 // Install a function for tracing
406 mEnvironmentOptions.InstallTraceFunction();
408 LOG_UPDATE_RENDER( "THREAD CREATED" );
410 RenderSurface* currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
413 currentSurface->InitializeGraphics( mAdaptorInterfaces.GetGraphicsInterface(), mAdaptorInterfaces.GetDisplayConnectionInterface() );
416 // Tell core it has a context
417 mCore.ContextCreated();
419 NotifyThreadInitialised();
422 uint64_t lastFrameTime;
423 TimeService::GetNanoseconds( lastFrameTime );
425 LOG_UPDATE_RENDER( "THREAD INITIALISED" );
427 bool useElapsedTime = true;
428 bool updateRequired = true;
429 uint64_t timeToSleepUntil = 0;
430 int extraFramesDropped = 0;
432 const unsigned int renderToFboInterval = mEnvironmentOptions.GetRenderToFboInterval();
433 const bool renderToFboEnabled = 0u != renderToFboInterval;
434 unsigned int frameCount = 0u;
436 while( UpdateRenderReady( useElapsedTime, updateRequired, timeToSleepUntil ) )
438 LOG_UPDATE_RENDER_TRACE;
440 // Performance statistics are logged upon a VSYNC tick so use this point for a VSync marker
441 AddPerformanceMarker( PerformanceInterface::VSYNC );
443 uint64_t currentFrameStartTime = 0;
444 TimeService::GetNanoseconds( currentFrameStartTime );
446 const uint64_t timeSinceLastFrame = currentFrameStartTime - lastFrameTime;
448 // Optional FPS Tracking when continuously rendering
449 if( useElapsedTime && mFpsTracker.Enabled() )
451 float absoluteTimeSinceLastRender = timeSinceLastFrame * NANOSECONDS_TO_SECOND;
452 mFpsTracker.Track( absoluteTimeSinceLastRender );
455 lastFrameTime = currentFrameStartTime; // Store frame start time
457 //////////////////////////////
459 //////////////////////////////
461 RenderSurface* newSurface = ShouldSurfaceBeReplaced();
462 if( DALI_UNLIKELY( newSurface ) )
464 LOG_UPDATE_RENDER_TRACE_FMT( "Replacing Surface" );
466 // This is designed for replacing pixmap surfaces, but should work for window as well
467 // we need to delete the surface and renderable (pixmap / window)
468 // Then create a new pixmap/window and new surface
469 // If the new surface has a different display connection, then the context will be lost
471 mAdaptorInterfaces.GetDisplayConnectionInterface().Initialize();
472 newSurface->ReplaceGraphicsSurface();
476 const bool isRenderingToFbo = renderToFboEnabled && ( ( 0u == frameCount ) || ( 0u != frameCount % renderToFboInterval ) );
479 //////////////////////////////
481 //////////////////////////////
483 const unsigned int currentTime = currentFrameStartTime / NANOSECONDS_PER_MILLISECOND;
484 const unsigned int nextFrameTime = currentTime + mDefaultFrameDurationMilliseconds;
486 uint64_t noOfFramesSinceLastUpdate = 1;
487 float frameDelta = 0.0f;
490 // If using the elapsed time, then calculate frameDelta as a multiple of mDefaultFrameDelta
491 noOfFramesSinceLastUpdate += extraFramesDropped;
493 frameDelta = mDefaultFrameDelta * noOfFramesSinceLastUpdate;
495 LOG_UPDATE_RENDER( "timeSinceLastFrame(%llu) noOfFramesSinceLastUpdate(%u) frameDelta(%.6f)", timeSinceLastFrame, noOfFramesSinceLastUpdate, frameDelta );
497 Integration::UpdateStatus updateStatus;
499 AddPerformanceMarker( PerformanceInterface::UPDATE_START );
500 mCore.Update( frameDelta,
506 AddPerformanceMarker( PerformanceInterface::UPDATE_END );
508 unsigned int keepUpdatingStatus = updateStatus.KeepUpdating();
510 // Tell the event-thread to wake up (if asleep) and send a notification event to Core if required
511 if( updateStatus.NeedsNotification() )
513 mNotificationTrigger.Trigger();
514 LOG_UPDATE_RENDER( "Notification Triggered" );
518 bool surfaceResized = false;
519 bool shouldSurfaceBeResized = ShouldSurfaceBeResized();
520 if( DALI_UNLIKELY( shouldSurfaceBeResized ) )
522 if( updateStatus.SurfaceRectChanged() )
524 LOG_UPDATE_RENDER_TRACE_FMT( "Resizing Surface" );
526 surfaceResized = true;
530 // Optional logging of update/render status
531 mUpdateStatusLogger.Log( keepUpdatingStatus );
533 //////////////////////////////
535 //////////////////////////////
537 mAdaptorInterfaces.GetDisplayConnectionInterface().ConsumeEvents();
539 if( mPreRenderCallback != NULL )
541 bool keepCallback = CallbackBase::ExecuteReturn<bool>(*mPreRenderCallback);
544 delete mPreRenderCallback;
545 mPreRenderCallback = NULL;
549 RenderSurface* currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
552 currentSurface->PreRender( surfaceResized );
555 Integration::RenderStatus renderStatus;
557 AddPerformanceMarker( PerformanceInterface::RENDER_START );
558 mCore.Render( renderStatus, mForceClear );
559 AddPerformanceMarker( PerformanceInterface::RENDER_END );
563 if( renderStatus.NeedsPostRender() )
567 currentSurface->PostRender( isRenderingToFbo, ( mNewSurface != NULL ), surfaceResized );
571 // Trigger event thread to request Update/Render thread to sleep if update not required
572 if( ( Integration::KeepUpdating::NOT_REQUESTED == keepUpdatingStatus ) && !renderStatus.NeedsUpdate() )
574 mSleepTrigger->Trigger();
575 updateRequired = false;
576 LOG_UPDATE_RENDER( "Sleep Triggered" );
580 updateRequired = true;
583 //////////////////////////////
585 //////////////////////////////
587 extraFramesDropped = 0;
589 if (timeToSleepUntil == 0)
591 // If this is the first frame after the thread is initialized or resumed, we
592 // use the actual time the current frame starts from to calculate the time to
593 // sleep until the next frame.
594 timeToSleepUntil = currentFrameStartTime + mDefaultFrameDurationNanoseconds;
598 // Otherwise, always use the sleep-until time calculated in the last frame to
599 // calculate the time to sleep until the next frame. In this way, if there is
600 // any time gap between the current frame and the next frame, or if update or
601 // rendering in the current frame takes too much time so that the specified
602 // sleep-until time has already passed, it will try to keep the frames syncing
603 // by shortening the duration of the next frame.
604 timeToSleepUntil += mDefaultFrameDurationNanoseconds;
606 // Check the current time at the end of the frame
607 uint64_t currentFrameEndTime = 0;
608 TimeService::GetNanoseconds( currentFrameEndTime );
609 while ( currentFrameEndTime > timeToSleepUntil + mDefaultFrameDurationNanoseconds )
611 // We are more than one frame behind already, so just drop the next frames
612 // until the sleep-until time is later than the current time so that we can
614 timeToSleepUntil += mDefaultFrameDurationNanoseconds;
615 extraFramesDropped++;
619 // Render to FBO is intended to measure fps above 60 so sleep is not wanted.
620 if( 0u == renderToFboInterval )
622 // Sleep until at least the the default frame duration has elapsed. This will return immediately if the specified end-time has already passed.
623 TimeService::SleepUntil( timeToSleepUntil );
627 // Inform core of context destruction & shutdown EGL
628 mCore.ContextDestroyed();
631 currentSurface->DestroySurface();
632 currentSurface = nullptr;
635 LOG_UPDATE_RENDER( "THREAD DESTROYED" );
637 // Uninstall the logging function
638 mEnvironmentOptions.UnInstallLogFunction();
641 bool CombinedUpdateRenderController::UpdateRenderReady( bool& useElapsedTime, bool updateRequired, uint64_t& timeToSleepUntil )
643 useElapsedTime = true;
645 ConditionalWait::ScopedLock updateLock( mUpdateRenderThreadWaitCondition );
646 while( ( ! mUpdateRenderRunCount || // Should try to wait if event-thread has paused the Update/Render thread
647 ( mUpdateRenderThreadCanSleep && ! updateRequired && ! mPendingRequestUpdate ) ) && // Ensure we wait if we're supposed to be sleeping AND do not require another update
648 ! mDestroyUpdateRenderThread && // Ensure we don't wait if the update-render-thread is supposed to be destroyed
649 ! mNewSurface && // Ensure we don't wait if we need to replace the surface
650 ! mSurfaceResized ) // Ensure we don't wait if we need to resize the surface
652 LOG_UPDATE_RENDER( "WAIT: mUpdateRenderRunCount: %d", mUpdateRenderRunCount );
653 LOG_UPDATE_RENDER( " mUpdateRenderThreadCanSleep: %d, updateRequired: %d, mPendingRequestUpdate: %d", mUpdateRenderThreadCanSleep, updateRequired, mPendingRequestUpdate );
654 LOG_UPDATE_RENDER( " mDestroyUpdateRenderThread: %d", mDestroyUpdateRenderThread );
655 LOG_UPDATE_RENDER( " mNewSurface: %d", mNewSurface );
656 LOG_UPDATE_RENDER( " mSurfaceResized: %d", mSurfaceResized );
658 // Reset the time when the thread is waiting, so the sleep-until time for
659 // the first frame after resuming should be based on the actual start time
660 // of the first frame.
661 timeToSleepUntil = 0;
663 mUpdateRenderThreadWaitCondition.Wait( updateLock );
665 if( ! mUseElapsedTimeAfterWait )
667 useElapsedTime = false;
671 LOG_COUNTER_UPDATE_RENDER( "mUpdateRenderRunCount: %d", mUpdateRenderRunCount );
672 LOG_COUNTER_UPDATE_RENDER( "mUpdateRenderThreadCanSleep: %d, updateRequired: %d, mPendingRequestUpdate: %d", mUpdateRenderThreadCanSleep, updateRequired, mPendingRequestUpdate );
673 LOG_COUNTER_UPDATE_RENDER( "mDestroyUpdateRenderThread: %d", mDestroyUpdateRenderThread );
674 LOG_COUNTER_UPDATE_RENDER( "mNewSurface: %d", mNewSurface );
675 LOG_COUNTER_UPDATE_RENDER( "mSurfaceResized: %d", mSurfaceResized );
677 mUseElapsedTimeAfterWait = FALSE;
678 mUpdateRenderThreadCanSleep = FALSE;
679 mPendingRequestUpdate = FALSE;
681 // If we've been asked to run Update/Render cycles a finite number of times then decrement so we wait after the
682 // requested number of cycles
683 if( mUpdateRenderRunCount > 0 )
685 --mUpdateRenderRunCount;
688 // Keep the update-render thread alive if this thread is NOT to be destroyed
689 return ! mDestroyUpdateRenderThread;
692 RenderSurface* CombinedUpdateRenderController::ShouldSurfaceBeReplaced()
694 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
696 RenderSurface* newSurface = mNewSurface;
702 void CombinedUpdateRenderController::SurfaceReplaced()
704 // Just increment the semaphore
705 sem_post( &mEventThreadSemaphore );
708 bool CombinedUpdateRenderController::ShouldSurfaceBeResized()
710 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
711 return mSurfaceResized;
714 void CombinedUpdateRenderController::SurfaceResized()
716 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
717 mSurfaceResized = FALSE;
720 ///////////////////////////////////////////////////////////////////////////////////////////////////
722 ///////////////////////////////////////////////////////////////////////////////////////////////////
724 void CombinedUpdateRenderController::NotifyThreadInitialised()
726 // Just increment the semaphore
727 sem_post( &mEventThreadSemaphore );
730 void CombinedUpdateRenderController::AddPerformanceMarker( PerformanceInterface::MarkerType type )
732 if( mPerformanceInterface )
734 mPerformanceInterface->AddMarker( type );
738 /////////////////////////////////////////////////////////////////////////////////////////////////
739 // POST RENDERING: EVENT THREAD
740 /////////////////////////////////////////////////////////////////////////////////////////////////
742 void CombinedUpdateRenderController::PostRenderComplete()
744 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
745 mPostRendering = FALSE;
746 mUpdateRenderThreadWaitCondition.Notify( lock );
749 ///////////////////////////////////////////////////////////////////////////////////////////////////
750 // POST RENDERING: RENDER THREAD
751 ///////////////////////////////////////////////////////////////////////////////////////////////////
753 void CombinedUpdateRenderController::PostRenderStarted()
755 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
756 mPostRendering = TRUE;
759 void CombinedUpdateRenderController::PostRenderWaitForCompletion()
761 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
762 while( mPostRendering &&
763 ! mNewSurface && // We should NOT wait if we're replacing the surface
764 ! mSurfaceResized && // We should NOT wait if we're resizing the surface
765 ! mDestroyUpdateRenderThread )
767 mUpdateRenderThreadWaitCondition.Wait( lock );
771 } // namespace Adaptor
773 } // namespace Internal