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>
44 const unsigned int CREATED_THREAD_COUNT = 1u;
46 const int CONTINUOUS = -1;
49 const unsigned int TRUE = 1u;
50 const unsigned int FALSE = 0u;
52 const unsigned int MILLISECONDS_PER_SECOND( 1e+3 );
53 const float NANOSECONDS_TO_SECOND( 1e-9f );
54 const unsigned int NANOSECONDS_PER_SECOND( 1e+9 );
55 const unsigned int NANOSECONDS_PER_MILLISECOND( 1e+6 );
57 // The following values will get calculated at compile time
58 const float DEFAULT_FRAME_DURATION_IN_SECONDS( 1.0f / 60.0f );
59 const uint64_t DEFAULT_FRAME_DURATION_IN_MILLISECONDS( DEFAULT_FRAME_DURATION_IN_SECONDS * MILLISECONDS_PER_SECOND );
60 const uint64_t DEFAULT_FRAME_DURATION_IN_NANOSECONDS( DEFAULT_FRAME_DURATION_IN_SECONDS * NANOSECONDS_PER_SECOND );
63 * 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
64 * there is a danger that, on the event-thread we could have:
65 * 1) An update-request where we do nothing as Update/Render thread still running.
66 * 2) Quickly followed by a sleep-request being handled where we pause the Update/Render Thread (even though we have an update to process).
68 * 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:
69 * 1) MAIN THREAD: Update Request: COUNTER = 1
70 * 2) UPDATE/RENDER THREAD: Do Update/Render, then no Updates required -> Sleep Trigger
71 * 3) MAIN THREAD: Update Request: COUNTER = 2
72 * 4) MAIN THREAD: Sleep Request: COUNTER = 1 -> We do not sleep just yet
74 * Also ensures we preserve battery life by only doing ONE update when the above use case is not triggered.
75 * 1) MAIN THREAD: Update Request: COUNTER = 1
76 * 2) UPDATE/RENDER THREAD: Do Update/Render, then no Updates required -> Sleep Trigger
77 * 3) MAIN THREAD: Sleep Request: COUNTER = 0 -> Go to sleep
79 const unsigned int MAXIMUM_UPDATE_REQUESTS = 2;
80 } // unnamed namespace
82 ///////////////////////////////////////////////////////////////////////////////////////////////////
84 ///////////////////////////////////////////////////////////////////////////////////////////////////
86 CombinedUpdateRenderController::CombinedUpdateRenderController( AdaptorInternalServices& adaptorInterfaces, const EnvironmentOptions& environmentOptions )
87 : mFpsTracker( environmentOptions ),
88 mUpdateStatusLogger( environmentOptions ),
89 mRenderHelper( adaptorInterfaces ),
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 mUpdateRenderThread( NULL ),
99 mDefaultFrameDelta( 0.0f ),
100 mDefaultFrameDurationMilliseconds( 0u ),
101 mDefaultFrameDurationNanoseconds( 0u ),
102 mDefaultHalfFrameNanoseconds( 0u ),
103 mUpdateRequestCount( 0u ),
105 mUpdateRenderRunCount( 0 ),
106 mDestroyUpdateRenderThread( FALSE ),
107 mUpdateRenderThreadCanSleep( FALSE ),
108 mPendingRequestUpdate( FALSE ),
109 mUseElapsedTimeAfterWait( FALSE ),
111 mPostRendering( FALSE ),
112 mSurfaceResized( FALSE ),
117 // Initialise frame delta/duration variables first
118 SetRenderRefreshRate( environmentOptions.GetRenderRefreshRate() );
120 // Set the thread-synchronization interface on the render-surface
121 RenderSurface* currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
124 currentSurface->SetThreadSynchronization( *this );
127 TriggerEventFactoryInterface& triggerFactory = mAdaptorInterfaces.GetTriggerEventFactoryInterface();
128 mSleepTrigger = triggerFactory.CreateTriggerEvent( MakeCallback( this, &CombinedUpdateRenderController::ProcessSleepRequest ), TriggerEventInterface::KEEP_ALIVE_AFTER_TRIGGER );
130 sem_init( &mEventThreadSemaphore, 0, 0 ); // Initialize to 0 so that it just waits if sem_post has not been called
133 CombinedUpdateRenderController::~CombinedUpdateRenderController()
139 delete mSleepTrigger;
142 void CombinedUpdateRenderController::Initialize()
146 // Ensure Update/Render Thread not already created
147 DALI_ASSERT_ALWAYS( ! mUpdateRenderThread );
149 // Create Update/Render Thread
150 mUpdateRenderThread = new pthread_t();
151 int error = pthread_create( mUpdateRenderThread, NULL, InternalUpdateRenderThreadEntryFunc, this );
152 DALI_ASSERT_ALWAYS( !error && "Return code from pthread_create() when creating UpdateRenderThread" );
154 // The Update/Render thread will now run and initialise EGL etc. and will then wait for Start to be called
155 // When this function returns, the application initialisation on the event thread should occur
158 void CombinedUpdateRenderController::Start()
162 DALI_ASSERT_ALWAYS( !mRunning && mUpdateRenderThread );
164 // Wait until all threads created in Initialise are up and running
165 for( unsigned int i = 0; i < CREATED_THREAD_COUNT; ++i )
167 sem_wait( &mEventThreadSemaphore );
170 mRenderHelper.Start();
174 LOG_EVENT( "Startup Complete, starting Update/Render Thread" );
176 RunUpdateRenderThread( CONTINUOUS, false /* No animation progression */ );
179 void CombinedUpdateRenderController::Pause()
185 PauseUpdateRenderThread();
187 AddPerformanceMarker( PerformanceInterface::PAUSED );
190 void CombinedUpdateRenderController::Resume()
194 if( !mRunning && IsUpdateRenderThreadPaused() )
196 LOG_EVENT( "Resuming" );
198 RunUpdateRenderThread( CONTINUOUS, true /* Animation progression required while we were paused */ );
200 AddPerformanceMarker( PerformanceInterface::RESUME );
207 void CombinedUpdateRenderController::Stop()
211 // Stop Rendering and the Update/Render Thread
212 mRenderHelper.Stop();
214 StopUpdateRenderThread();
216 if( mUpdateRenderThread )
218 LOG_EVENT( "Destroying UpdateRenderThread" );
220 // wait for the thread to finish
221 pthread_join( *mUpdateRenderThread, NULL );
223 delete mUpdateRenderThread;
224 mUpdateRenderThread = NULL;
230 void CombinedUpdateRenderController::RequestUpdate()
234 // Increment the update-request count to the maximum
235 if( mUpdateRequestCount < MAXIMUM_UPDATE_REQUESTS )
237 ++mUpdateRequestCount;
240 if( mRunning && IsUpdateRenderThreadPaused() )
242 LOG_EVENT( "Processing" );
244 RunUpdateRenderThread( CONTINUOUS, false /* No animation progression */ );
247 ConditionalWait::ScopedLock updateLock( mUpdateRenderThreadWaitCondition );
248 mPendingRequestUpdate = TRUE;
251 void CombinedUpdateRenderController::RequestUpdateOnce()
253 // Increment the update-request count to the maximum
254 if( mUpdateRequestCount < MAXIMUM_UPDATE_REQUESTS )
256 ++mUpdateRequestCount;
259 if( IsUpdateRenderThreadPaused() )
263 // Run Update/Render once
264 RunUpdateRenderThread( ONCE, false /* No animation progression */ );
268 void CombinedUpdateRenderController::ReplaceSurface( RenderSurface* newSurface )
272 // Set the ThreadSyncronizationInterface on the new surface
273 newSurface->SetThreadSynchronization( *this );
275 LOG_EVENT( "Starting to replace the surface, event-thread blocked" );
277 // Start replacing the surface.
279 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
280 mPostRendering = FALSE; // Clear the post-rendering flag as Update/Render thread will replace the surface now
281 mNewSurface = newSurface;
282 mUpdateRenderThreadWaitCondition.Notify( lock );
285 // Wait until the surface has been replaced
286 sem_wait( &mEventThreadSemaphore );
288 LOG_EVENT( "Surface replaced, event-thread continuing" );
291 void CombinedUpdateRenderController::ResizeSurface()
295 LOG_EVENT( "Starting to resize the surface, event-thread blocked" );
297 // Start resizing the surface.
299 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
300 mPostRendering = FALSE; // Clear the post-rendering flag as Update/Render thread will resize the surface now
301 mSurfaceResized = TRUE;
302 mUpdateRenderThreadWaitCondition.Notify( lock );
305 // Wait until the surface has been resized
306 sem_wait( &mEventThreadSemaphore );
308 LOG_EVENT( "Surface resized, event-thread continuing" );
311 void CombinedUpdateRenderController::SetRenderRefreshRate( unsigned int numberOfFramesPerRender )
313 // Not protected by lock, but written to rarely so not worth adding a lock when reading
314 mDefaultFrameDelta = numberOfFramesPerRender * DEFAULT_FRAME_DURATION_IN_SECONDS;
315 mDefaultFrameDurationMilliseconds = uint64_t( numberOfFramesPerRender ) * DEFAULT_FRAME_DURATION_IN_MILLISECONDS;
316 mDefaultFrameDurationNanoseconds = uint64_t( numberOfFramesPerRender ) * DEFAULT_FRAME_DURATION_IN_NANOSECONDS;
317 mDefaultHalfFrameNanoseconds = mDefaultFrameDurationNanoseconds / 2u;
319 LOG_EVENT( "mDefaultFrameDelta(%.6f), mDefaultFrameDurationMilliseconds(%lld), mDefaultFrameDurationNanoseconds(%lld)", mDefaultFrameDelta, mDefaultFrameDurationMilliseconds, mDefaultFrameDurationNanoseconds );
322 ///////////////////////////////////////////////////////////////////////////////////////////////////
324 ///////////////////////////////////////////////////////////////////////////////////////////////////
326 void CombinedUpdateRenderController::RunUpdateRenderThread( int numberOfCycles, bool useElapsedTime )
328 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
329 mUpdateRenderRunCount = numberOfCycles;
330 mUpdateRenderThreadCanSleep = FALSE;
331 mUseElapsedTimeAfterWait = useElapsedTime;
332 LOG_COUNTER_EVENT( "mUpdateRenderRunCount: %d, mUseElapsedTimeAfterWait: %d", mUpdateRenderRunCount, mUseElapsedTimeAfterWait );
333 mUpdateRenderThreadWaitCondition.Notify( lock );
336 void CombinedUpdateRenderController::PauseUpdateRenderThread()
338 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
339 mUpdateRenderRunCount = 0;
342 void CombinedUpdateRenderController::StopUpdateRenderThread()
344 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
345 mDestroyUpdateRenderThread = TRUE;
346 mUpdateRenderThreadWaitCondition.Notify( lock );
349 bool CombinedUpdateRenderController::IsUpdateRenderThreadPaused()
351 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
352 return ( mUpdateRenderRunCount != CONTINUOUS ) || // Report paused if NOT continuously running
353 mUpdateRenderThreadCanSleep; // Report paused if sleeping
356 void CombinedUpdateRenderController::ProcessSleepRequest()
360 // Decrement Update request count
361 if( mUpdateRequestCount > 0 )
363 --mUpdateRequestCount;
366 // Can sleep if our update-request count is 0
367 // Update/Render thread can choose to carry on updating if it determines more update/renders are required
368 if( mUpdateRequestCount == 0 )
370 LOG_EVENT( "Going to sleep" );
372 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
373 mUpdateRenderThreadCanSleep = TRUE;
377 ///////////////////////////////////////////////////////////////////////////////////////////////////
378 // UPDATE/RENDER THREAD
379 ///////////////////////////////////////////////////////////////////////////////////////////////////
381 void CombinedUpdateRenderController::UpdateRenderThread()
383 // Install a function for logging
384 mEnvironmentOptions.InstallLogFunction();
386 // Install a function for tracing
387 mEnvironmentOptions.InstallTraceFunction();
389 LOG_UPDATE_RENDER( "THREAD CREATED" );
391 mRenderHelper.InitializeEgl();
393 // tell core it has a context
394 mCore.ContextCreated();
396 NotifyThreadInitialised();
399 uint64_t lastFrameTime;
400 TimeService::GetNanoseconds( lastFrameTime );
402 LOG_UPDATE_RENDER( "THREAD INITIALISED" );
404 bool useElapsedTime = true;
405 bool updateRequired = true;
406 uint64_t timeToSleepUntil = 0;
407 int extraFramesDropped = 0;
409 const unsigned int renderToFboInterval = mEnvironmentOptions.GetRenderToFboInterval();
410 const bool renderToFboEnabled = 0u != renderToFboInterval;
411 unsigned int frameCount = 0u;
413 while( UpdateRenderReady( useElapsedTime, updateRequired, timeToSleepUntil ) )
415 LOG_UPDATE_RENDER_TRACE;
417 // Performance statistics are logged upon a VSYNC tick so use this point for a VSync marker
418 AddPerformanceMarker( PerformanceInterface::VSYNC );
420 uint64_t currentFrameStartTime = 0;
421 TimeService::GetNanoseconds( currentFrameStartTime );
423 const uint64_t timeSinceLastFrame = currentFrameStartTime - lastFrameTime;
425 // Optional FPS Tracking when continuously rendering
426 if( useElapsedTime && mFpsTracker.Enabled() )
428 float absoluteTimeSinceLastRender = timeSinceLastFrame * NANOSECONDS_TO_SECOND;
429 mFpsTracker.Track( absoluteTimeSinceLastRender );
432 lastFrameTime = currentFrameStartTime; // Store frame start time
434 //////////////////////////////
436 //////////////////////////////
438 RenderSurface* newSurface = ShouldSurfaceBeReplaced();
439 if( DALI_UNLIKELY( newSurface ) )
441 LOG_UPDATE_RENDER_TRACE_FMT( "Replacing Surface" );
442 mRenderHelper.ReplaceSurface( newSurface );
446 //////////////////////////////
448 //////////////////////////////
450 const bool isRenderingToFbo = renderToFboEnabled && ( ( 0u == frameCount ) || ( 0u != frameCount % renderToFboInterval ) );
453 // The resizing will be applied in the next loop
454 bool surfaceResized = ShouldSurfaceBeResized();
455 if( DALI_UNLIKELY( surfaceResized ) )
457 LOG_UPDATE_RENDER_TRACE_FMT( "Resizing Surface" );
458 mRenderHelper.ResizeSurface();
462 //////////////////////////////
464 //////////////////////////////
466 const unsigned int currentTime = currentFrameStartTime / NANOSECONDS_PER_MILLISECOND;
467 const unsigned int nextFrameTime = currentTime + mDefaultFrameDurationMilliseconds;
469 uint64_t noOfFramesSinceLastUpdate = 1;
470 float frameDelta = 0.0f;
473 // If using the elapsed time, then calculate frameDelta as a multiple of mDefaultFrameDelta
474 noOfFramesSinceLastUpdate += extraFramesDropped;
476 frameDelta = mDefaultFrameDelta * noOfFramesSinceLastUpdate;
478 LOG_UPDATE_RENDER( "timeSinceLastFrame(%llu) noOfFramesSinceLastUpdate(%u) frameDelta(%.6f)", timeSinceLastFrame, noOfFramesSinceLastUpdate, frameDelta );
480 Integration::UpdateStatus updateStatus;
482 AddPerformanceMarker( PerformanceInterface::UPDATE_START );
483 mCore.Update( frameDelta,
489 AddPerformanceMarker( PerformanceInterface::UPDATE_END );
491 unsigned int keepUpdatingStatus = updateStatus.KeepUpdating();
493 // Tell the event-thread to wake up (if asleep) and send a notification event to Core if required
494 if( updateStatus.NeedsNotification() )
496 mNotificationTrigger.Trigger();
497 LOG_UPDATE_RENDER( "Notification Triggered" );
500 // Optional logging of update/render status
501 mUpdateStatusLogger.Log( keepUpdatingStatus );
503 //////////////////////////////
505 //////////////////////////////
507 mRenderHelper.ConsumeEvents();
508 mRenderHelper.PreRender();
510 Integration::RenderStatus renderStatus;
512 AddPerformanceMarker( PerformanceInterface::RENDER_START );
513 mCore.Render( renderStatus, mForceClear );
514 AddPerformanceMarker( PerformanceInterface::RENDER_END );
518 if( renderStatus.NeedsPostRender() )
520 mRenderHelper.PostRender( isRenderingToFbo );
523 // Trigger event thread to request Update/Render thread to sleep if update not required
524 if( ( Integration::KeepUpdating::NOT_REQUESTED == keepUpdatingStatus ) &&
525 ! renderStatus.NeedsUpdate() )
527 mSleepTrigger->Trigger();
528 updateRequired = false;
529 LOG_UPDATE_RENDER( "Sleep Triggered" );
533 updateRequired = true;
536 //////////////////////////////
538 //////////////////////////////
540 extraFramesDropped = 0;
542 if (timeToSleepUntil == 0)
544 // If this is the first frame after the thread is initialized or resumed, we
545 // use the actual time the current frame starts from to calculate the time to
546 // sleep until the next frame.
547 timeToSleepUntil = currentFrameStartTime + mDefaultFrameDurationNanoseconds;
551 // Otherwise, always use the sleep-until time calculated in the last frame to
552 // calculate the time to sleep until the next frame. In this way, if there is
553 // any time gap between the current frame and the next frame, or if update or
554 // rendering in the current frame takes too much time so that the specified
555 // sleep-until time has already passed, it will try to keep the frames syncing
556 // by shortening the duration of the next frame.
557 timeToSleepUntil += mDefaultFrameDurationNanoseconds;
559 // Check the current time at the end of the frame
560 uint64_t currentFrameEndTime = 0;
561 TimeService::GetNanoseconds( currentFrameEndTime );
562 while ( currentFrameEndTime > timeToSleepUntil + mDefaultFrameDurationNanoseconds )
564 // We are more than one frame behind already, so just drop the next frames
565 // until the sleep-until time is later than the current time so that we can
567 timeToSleepUntil += mDefaultFrameDurationNanoseconds;
568 extraFramesDropped++;
572 // Render to FBO is intended to measure fps above 60 so sleep is not wanted.
573 if( 0u == renderToFboInterval )
575 // Sleep until at least the the default frame duration has elapsed. This will return immediately if the specified end-time has already passed.
576 TimeService::SleepUntil( timeToSleepUntil );
580 // Inform core of context destruction & shutdown EGL
581 mCore.ContextDestroyed();
582 mRenderHelper.ShutdownEgl();
584 LOG_UPDATE_RENDER( "THREAD DESTROYED" );
586 // Uninstall the logging function
587 mEnvironmentOptions.UnInstallLogFunction();
590 bool CombinedUpdateRenderController::UpdateRenderReady( bool& useElapsedTime, bool updateRequired, uint64_t& timeToSleepUntil )
592 useElapsedTime = true;
594 ConditionalWait::ScopedLock updateLock( mUpdateRenderThreadWaitCondition );
595 while( ( ! mUpdateRenderRunCount || // Should try to wait if event-thread has paused the Update/Render thread
596 ( mUpdateRenderThreadCanSleep && ! updateRequired && ! mPendingRequestUpdate ) ) && // Ensure we wait if we're supposed to be sleeping AND do not require another update
597 ! mDestroyUpdateRenderThread && // Ensure we don't wait if the update-render-thread is supposed to be destroyed
598 ! mNewSurface && // Ensure we don't wait if we need to replace the surface
599 ! mSurfaceResized ) // Ensure we don't wait if we need to resize the surface
601 LOG_UPDATE_RENDER( "WAIT: mUpdateRenderRunCount: %d", mUpdateRenderRunCount );
602 LOG_UPDATE_RENDER( " mUpdateRenderThreadCanSleep: %d, updateRequired: %d, mPendingRequestUpdate: %d", mUpdateRenderThreadCanSleep, updateRequired, mPendingRequestUpdate );
603 LOG_UPDATE_RENDER( " mDestroyUpdateRenderThread: %d", mDestroyUpdateRenderThread );
604 LOG_UPDATE_RENDER( " mNewSurface: %d", mNewSurface );
605 LOG_UPDATE_RENDER( " mSurfaceResized: %d", mSurfaceResized );
607 // Reset the time when the thread is waiting, so the sleep-until time for
608 // the first frame after resuming should be based on the actual start time
609 // of the first frame.
610 timeToSleepUntil = 0;
612 mUpdateRenderThreadWaitCondition.Wait( updateLock );
614 if( ! mUseElapsedTimeAfterWait )
616 useElapsedTime = false;
620 LOG_COUNTER_UPDATE_RENDER( "mUpdateRenderRunCount: %d", mUpdateRenderRunCount );
621 LOG_COUNTER_UPDATE_RENDER( "mUpdateRenderThreadCanSleep: %d, updateRequired: %d, mPendingRequestUpdate: %d", mUpdateRenderThreadCanSleep, updateRequired, mPendingRequestUpdate );
622 LOG_COUNTER_UPDATE_RENDER( "mDestroyUpdateRenderThread: %d", mDestroyUpdateRenderThread );
623 LOG_COUNTER_UPDATE_RENDER( "mNewSurface: %d", mNewSurface );
624 LOG_COUNTER_UPDATE_RENDER( "mSurfaceResized: %d", mSurfaceResized );
626 mUseElapsedTimeAfterWait = FALSE;
627 mUpdateRenderThreadCanSleep = FALSE;
628 mPendingRequestUpdate = FALSE;
630 // If we've been asked to run Update/Render cycles a finite number of times then decrement so we wait after the
631 // requested number of cycles
632 if( mUpdateRenderRunCount > 0 )
634 --mUpdateRenderRunCount;
637 // Keep the update-render thread alive if this thread is NOT to be destroyed
638 return ! mDestroyUpdateRenderThread;
641 RenderSurface* CombinedUpdateRenderController::ShouldSurfaceBeReplaced()
643 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
645 RenderSurface* newSurface = mNewSurface;
651 void CombinedUpdateRenderController::SurfaceReplaced()
653 // Just increment the semaphore
654 sem_post( &mEventThreadSemaphore );
657 bool CombinedUpdateRenderController::ShouldSurfaceBeResized()
659 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
661 bool surfaceSized = mSurfaceResized;
662 mSurfaceResized = FALSE;
667 void CombinedUpdateRenderController::SurfaceResized()
669 // Just increment the semaphore
670 sem_post( &mEventThreadSemaphore );
673 ///////////////////////////////////////////////////////////////////////////////////////////////////
675 ///////////////////////////////////////////////////////////////////////////////////////////////////
677 void CombinedUpdateRenderController::NotifyThreadInitialised()
679 // Just increment the semaphore
680 sem_post( &mEventThreadSemaphore );
683 void CombinedUpdateRenderController::AddPerformanceMarker( PerformanceInterface::MarkerType type )
685 if( mPerformanceInterface )
687 mPerformanceInterface->AddMarker( type );
691 /////////////////////////////////////////////////////////////////////////////////////////////////
692 // POST RENDERING: EVENT THREAD
693 /////////////////////////////////////////////////////////////////////////////////////////////////
695 void CombinedUpdateRenderController::PostRenderComplete()
697 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
698 mPostRendering = FALSE;
699 mUpdateRenderThreadWaitCondition.Notify( lock );
702 ///////////////////////////////////////////////////////////////////////////////////////////////////
703 // POST RENDERING: RENDER THREAD
704 ///////////////////////////////////////////////////////////////////////////////////////////////////
706 void CombinedUpdateRenderController::PostRenderStarted()
708 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
709 mPostRendering = TRUE;
712 void CombinedUpdateRenderController::PostRenderWaitForCompletion()
714 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
715 while( mPostRendering &&
716 ! mNewSurface && // We should NOT wait if we're replacing the surface
717 ! mSurfaceResized && // We should NOT wait if we're resizing the surface
718 ! mDestroyUpdateRenderThread )
720 mUpdateRenderThreadWaitCondition.Wait( lock );
724 } // namespace Adaptor
726 } // namespace Internal