2 * Copyright (c) 2015 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 "thread-synchronization.h"
22 #include <base/interfaces/adaptor-internal-services.h>
23 #include <base/thread-synchronization-debug.h>
36 const unsigned int TIME_PER_FRAME_IN_MICROSECONDS = 16667;
37 const int TOTAL_THREAD_COUNT = 3;
38 const unsigned int TRUE = 1u;
39 const unsigned int FALSE = 0u;
40 } // unnamed namespace
42 ThreadSynchronization::ThreadSynchronization( AdaptorInternalServices& adaptorInterfaces, unsigned int numberOfVSyncsPerRender)
43 : mFrameTime( adaptorInterfaces.GetPlatformAbstractionInterface() ),
44 mNotificationTrigger( adaptorInterfaces.GetProcessCoreEventsTrigger() ),
45 mPerformanceInterface( adaptorInterfaces.GetPerformanceInterface() ),
46 mReplaceSurfaceRequest(),
47 mUpdateThreadWaitCondition(),
48 mRenderThreadWaitCondition(),
49 mVSyncThreadWaitCondition(),
50 mEventThreadWaitCondition(),
51 mMaximumUpdateCount( adaptorInterfaces.GetCore().GetMaximumUpdateCount()),
52 mNumberOfVSyncsPerRender( numberOfVSyncsPerRender ),
53 mTryToSleepCount( 0u ),
54 mState( State::STOPPED ),
55 mVSyncAheadOfUpdate( 0u ),
56 mUpdateAheadOfRender( 0u ),
57 mNumberOfThreadsStarted( 0u ),
58 mUpdateThreadResuming( FALSE ),
59 mVSyncThreadRunning( FALSE ),
60 mVSyncThreadStop( FALSE ),
61 mRenderThreadStop( FALSE ),
62 mRenderThreadReplacingSurface( FALSE ),
63 mRenderThreadPostRendering( FALSE ),
64 mEventThreadSurfaceReplaced( FALSE ),
65 mVSyncThreadInitialised( FALSE ),
66 mRenderThreadInitialised( FALSE ),
67 mRenderThreadSurfaceReplaced( FALSE )
71 ThreadSynchronization::~ThreadSynchronization()
75 ///////////////////////////////////////////////////////////////////////////////////////////////////
77 ///////////////////////////////////////////////////////////////////////////////////////////////////
79 void ThreadSynchronization::Initialise()
83 ConditionalWait::ScopedLock lock( mUpdateThreadWaitCondition );
84 if( mState == State::STOPPED )
86 LOG_EVENT( "INITIALISING" );
87 mState = State::INITIALISING;
91 void ThreadSynchronization::Start()
97 ConditionalWait::ScopedLock lock( mUpdateThreadWaitCondition );
98 if( mState == State::INITIALISING )
104 // Not atomic, but does not matter here as we just want to ensure we only start from State::INITIALISING
107 LOG_EVENT( "STARTING" );
108 mFrameTime.SetMinimumFrameTimeInterval( mNumberOfVSyncsPerRender * TIME_PER_FRAME_IN_MICROSECONDS );
111 ConditionalWait::ScopedLock lock( mEventThreadWaitCondition );
112 while( mNumberOfThreadsStarted < TOTAL_THREAD_COUNT )
114 mEventThreadWaitCondition.Wait( lock );
119 ConditionalWait::ScopedLock lock( mUpdateThreadWaitCondition );
120 mState = State::RUNNING;
122 mUpdateThreadWaitCondition.Notify();
126 void ThreadSynchronization::Stop()
132 ConditionalWait::ScopedLock lock( mUpdateThreadWaitCondition );
133 if( mState != State::STOPPED )
136 mState = State::STOPPED;
140 // Not atomic, but does not matter here as we just want to ensure we do not stop more than once
143 LOG_EVENT( "STOPPING" );
145 // Notify update-thread so that it continues and sets up the other threads to stop as well
146 mUpdateThreadWaitCondition.Notify();
148 mFrameTime.Suspend();
152 void ThreadSynchronization::Pause()
156 bool addPerformanceMarker = false;
158 // Only pause if we're RUNNING or SLEEPING
159 ConditionalWait::ScopedLock lock( mUpdateThreadWaitCondition );
160 if( ( mState == State::RUNNING ) ||
161 ( mState == State::SLEEPING ) )
163 LOG_EVENT( "PAUSING" );
165 mState = State::PAUSED;
167 mUpdateThreadResuming = FALSE;
169 mFrameTime.Suspend();
171 addPerformanceMarker = true;
175 if( addPerformanceMarker )
177 // Can lock so we do not want to have a lock when calling this to avoid deadlocks
178 AddPerformanceMarker( PerformanceInterface::PAUSED );
182 void ThreadSynchronization::Resume()
186 // Only resume if we're PAUSED
189 ConditionalWait::ScopedLock lock( mUpdateThreadWaitCondition );
190 if( mState == State::PAUSED )
193 mState = State::RUNNING;
194 mUpdateThreadResuming = TRUE;
198 // Not atomic, but does not matter here as we just want to ensure we only resume if we're paused
201 LOG_EVENT( "RESUMING" );
205 // Start up Update thread again
206 mUpdateThreadWaitCondition.Notify();
208 // Can lock so we do not want to have a lock when calling this to avoid deadlocks
209 AddPerformanceMarker( PerformanceInterface::RESUME);
213 void ThreadSynchronization::UpdateRequest()
219 ConditionalWait::ScopedLock lock( mUpdateThreadWaitCondition );
220 if( mState == State::SLEEPING )
222 mState = State::RUNNING;
225 mTryToSleepCount = 0;
230 LOG_EVENT( "UPDATE REQUEST" );
231 mUpdateThreadWaitCondition.Notify();
235 void ThreadSynchronization::UpdateOnce()
238 LOG_EVENT( "UPDATE ONCE" );
240 // If we're sleeping then change state to running as this will also wake up the v-sync-thread
242 ConditionalWait::ScopedLock lock( mUpdateThreadWaitCondition );
243 if( mState == State::SLEEPING )
245 mState = State::RUNNING;
249 mUpdateThreadWaitCondition.Notify();
252 void ThreadSynchronization::ReplaceSurface( RenderSurface* newSurface )
256 State::Type previousState( State::STOPPED );
258 ConditionalWait::ScopedLock lock( mUpdateThreadWaitCondition );
259 previousState = mState;
260 mState = State::REPLACING_SURFACE;
264 ConditionalWait::ScopedLock lock( mEventThreadWaitCondition );
265 mEventThreadSurfaceReplaced = FALSE;
269 ConditionalWait::ScopedLock lock( mRenderThreadWaitCondition );
270 mReplaceSurfaceRequest.SetSurface( newSurface );
271 mRenderThreadReplacingSurface = TRUE;
274 // Notify the RenderThread in case it's waiting
275 mRenderThreadWaitCondition.Notify();
278 ConditionalWait::ScopedLock lock( mEventThreadWaitCondition );
280 // Wait for RenderThread to replace the surface
281 while( ! mEventThreadSurfaceReplaced )
283 LOG_EVENT( "Waiting for Surface to be Replaced" );
285 mEventThreadWaitCondition.Wait( lock );
290 ConditionalWait::ScopedLock lock( mUpdateThreadWaitCondition );
291 mState = previousState;
293 mUpdateThreadWaitCondition.Notify();
296 void ThreadSynchronization::SetRenderRefreshRate( unsigned int numberOfVSyncsPerRender )
299 LOG_EVENT( "SET RENDER REFRESH RATE" );
301 mNumberOfVSyncsPerRender = numberOfVSyncsPerRender;
304 ///////////////////////////////////////////////////////////////////////////////////////////////////
306 ///////////////////////////////////////////////////////////////////////////////////////////////////
308 bool ThreadSynchronization::UpdateReady( bool notifyEvent, bool runUpdate, float& lastFrameDeltaSeconds, unsigned int& lastSyncTimeMilliseconds, unsigned int& nextSyncTimeMilliseconds )
312 State::Type state = State::STOPPED;
314 ConditionalWait::ScopedLock updateLock( mUpdateThreadWaitCondition );
323 return false; // Stop update-thread
326 case State::INITIALISING:
328 UpdateInitialising();
334 LOG_UPDATE_TRACE_FMT( "PAUSED" );
336 // Just pause the VSyncThread, locks so we shouldn't have a scoped-lock when calling this
339 // No break, fall through
343 LOG_UPDATE_TRACE_FMT( "RUNNING" );
345 if( IsUpdateThreadResuming() )
347 LOG_UPDATE( "Restarting VSyncThread" );
350 ConditionalWait::ScopedLock updateLock( mUpdateThreadWaitCondition );
351 mUpdateThreadResuming = FALSE;
354 // Restart the VSyncThread, locks so we shouldn't have a scoped-lock when calling this
360 LOG_UPDATE( "Notify Event Thread" );
362 // Do the notifications first so the event thread can start processing them
363 // Tell the event-thread to wake up (if asleep) and send a notification event to Core
364 mNotificationTrigger.Trigger();
367 // Inform render thread
369 ConditionalWait::ScopedLock lock( mRenderThreadWaitCondition );
370 ++mUpdateAheadOfRender;
371 DALI_ASSERT_ALWAYS( mUpdateAheadOfRender >= 0 );
372 DALI_ASSERT_ALWAYS( mUpdateAheadOfRender <= mMaximumUpdateCount );
373 LOG_UPDATE_COUNTER_UPDATE( "updateAheadOfRender(%d)", mUpdateAheadOfRender );
375 mRenderThreadWaitCondition.Notify();
377 // Wait if we've reached the maximum-ahead-of-render count.
378 while( MaximumUpdateAheadOfRenderReached() )
380 LOG_UPDATE( "Maximum Update Ahead of Render: WAIT" );
382 mRenderThreadWaitCondition.Notify(); // Notify the render thread in case it was waiting
385 // Ensure we did not stop while we were waiting previously.
386 ConditionalWait::ScopedLock updateLock( mUpdateThreadWaitCondition );
387 if( mState == State::STOPPED )
389 break; // Break out of while loop
391 mUpdateThreadWaitCondition.Wait( updateLock );
395 // Ensure we have had at least 1 V-Sync before we continue
396 // Ensure we didn't stop while we were previously waiting
398 ConditionalWait::ScopedLock updateLock( mUpdateThreadWaitCondition );
399 if( ( mState != State::STOPPED ) &&
400 ( mVSyncAheadOfUpdate == 0 ) &&
401 ( !mUpdateThreadResuming ) ) // Ensure we don't wait if the update-thread is JUST resuming
403 LOG_VSYNC_COUNTER_UPDATE( " vSyncAheadOfUpdate(%d) WAIT", mVSyncAheadOfUpdate );
404 mUpdateThreadWaitCondition.Wait( updateLock );
408 LOG_VSYNC_COUNTER_UPDATE( " vSyncAheadOfUpdate(%d)", mVSyncAheadOfUpdate );
410 mVSyncAheadOfUpdate = 0;
413 // Try to sleep if we do not require any more updates
414 UpdateTryToSleep( runUpdate );
419 case State::SLEEPING:
420 case State::REPLACING_SURFACE:
426 // Ensure we didn't stop while we were waiting
427 if( IsUpdateThreadStopping() )
429 // Locks so we shouldn't have a scoped-lock when calling this
431 return false; // Stop update-thread
434 // Just wait if we're replacing the surface as the render-thread is busy
435 UpdateWaitIfReplacingSurface();
437 mFrameTime.PredictNextSyncTime( lastFrameDeltaSeconds, lastSyncTimeMilliseconds, nextSyncTimeMilliseconds );
439 return true; // Keep update-thread running
442 ///////////////////////////////////////////////////////////////////////////////////////////////////
444 ///////////////////////////////////////////////////////////////////////////////////////////////////
446 bool ThreadSynchronization::RenderReady( RenderRequest*& requestPtr )
450 if( ! IsRenderThreadReplacingSurface() ) // Call to this function locks so should not be called if we have a scoped-lock
452 if( ! mRenderThreadInitialised )
454 LOG_RENDER( "Initialised" );
456 mRenderThreadInitialised = TRUE;
458 // Notify event thread that this thread is up and running, this locks so we should have a scoped-lock
459 NotifyThreadInitialised();
463 if( mRenderThreadSurfaceReplaced )
465 mRenderThreadSurfaceReplaced = FALSE;
469 // Check if we've had an update, if we haven't then we just wait
470 // Ensure we do not wait if we're supposed to stop
472 ConditionalWait::ScopedLock renderLock( mRenderThreadWaitCondition );
473 if( mUpdateAheadOfRender <= 0 && ! mRenderThreadStop )
477 LOG_UPDATE_COUNTER_RENDER( "updateAheadOfRender(%d) WAIT", mUpdateAheadOfRender );
478 mRenderThreadWaitCondition.Wait( renderLock );
479 } while( mUpdateAheadOfRender <= 0 && ! mRenderThreadStop && ! mRenderThreadReplacingSurface );
483 LOG_UPDATE_COUNTER_RENDER( "updateAheadOfRender(%d)", mUpdateAheadOfRender );
488 // We may have been asked to replace the surface while we were waiting so check again here
489 if( IsRenderThreadReplacingSurface() )
492 LOG_RENDER( "REPLACE SURFACE" );
494 ConditionalWait::ScopedLock renderLock( mRenderThreadWaitCondition );
495 requestPtr = &mReplaceSurfaceRequest;
496 mRenderThreadReplacingSurface = FALSE;
497 mRenderThreadSurfaceReplaced = FALSE;
500 return IsRenderThreadRunning(); // Call to this function locks so should not be called if we have a scoped-lock
503 void ThreadSynchronization::RenderFinished()
505 // A frame has been rendered; decrement counter
506 ConditionalWait::ScopedLock renderLock( mRenderThreadWaitCondition );
507 --mUpdateAheadOfRender;
509 LOG_RENDER( "mUpdateAheadOfRender %d\n", mUpdateAheadOfRender );
510 DALI_ASSERT_ALWAYS( mUpdateAheadOfRender < mMaximumUpdateCount );
511 DALI_ASSERT_ALWAYS( mUpdateAheadOfRender >= 0 );
514 void ThreadSynchronization::RenderInformSurfaceReplaced()
518 mRenderThreadSurfaceReplaced = TRUE;
520 ConditionalWait::ScopedLock lock( mEventThreadWaitCondition );
521 mEventThreadSurfaceReplaced = TRUE;
523 mEventThreadWaitCondition.Notify();
526 ///////////////////////////////////////////////////////////////////////////////////////////////////
528 ///////////////////////////////////////////////////////////////////////////////////////////////////
530 bool ThreadSynchronization::VSyncReady( bool validSync, unsigned int frameNumber, unsigned int seconds, unsigned int microseconds, unsigned int& numberOfVSyncsPerRender )
534 // Ensure we do not process an invalid v-sync
537 bool minimumFrameTimeIntervalChanged = false;
539 ConditionalWait::ScopedLock vSyncLock( mVSyncThreadWaitCondition );
540 if( numberOfVSyncsPerRender != mNumberOfVSyncsPerRender )
542 numberOfVSyncsPerRender = mNumberOfVSyncsPerRender; // save it back
543 minimumFrameTimeIntervalChanged = true;
547 if( minimumFrameTimeIntervalChanged )
549 mFrameTime.SetMinimumFrameTimeInterval( mNumberOfVSyncsPerRender * TIME_PER_FRAME_IN_MICROSECONDS );
552 mFrameTime.SetSyncTime( frameNumber );
554 if( ! mVSyncThreadInitialised )
556 LOG_VSYNC( "Initialised" );
558 mVSyncThreadInitialised = TRUE;
560 // Notify event thread that this thread is up and running, this locks so we should have a scoped-lock
561 NotifyThreadInitialised();
565 // Increment v-sync-ahead-of-update count and inform update-thread
567 ConditionalWait::ScopedLock lock( mUpdateThreadWaitCondition );
568 ++mVSyncAheadOfUpdate;
569 LOG_VSYNC_COUNTER_VSYNC( " vSyncAheadOfUpdate(%d)", mVSyncAheadOfUpdate );
571 mUpdateThreadWaitCondition.Notify();
574 // Ensure update-thread has set us to run before continuing
575 // Ensure we do not wait if we're supposed to stop
577 ConditionalWait::ScopedLock vSyncLock( mVSyncThreadWaitCondition );
578 while( ! mVSyncThreadRunning && ! mVSyncThreadStop )
581 mVSyncThreadWaitCondition.Wait( vSyncLock );
587 LOG_VSYNC( "INVALID SYNC" );
589 // Later we still check if the v-sync thread is supposed to keep running so we can still stop the thread if we are supposed to
592 return IsVSyncThreadRunning(); // Call to this function locks so should not be called if we have a scoped-lock
595 /////////////////////////////////////////////////////////////////////////////////////////////////
596 // POST RENDERING: EVENT THREAD
597 /////////////////////////////////////////////////////////////////////////////////////////////////
599 void ThreadSynchronization::PostRenderComplete()
604 ConditionalWait::ScopedLock lock( mRenderThreadWaitCondition );
605 mRenderThreadPostRendering = FALSE;
607 mRenderThreadWaitCondition.Notify();
610 ///////////////////////////////////////////////////////////////////////////////////////////////////
611 // POST RENDERING: RENDER THREAD
612 ///////////////////////////////////////////////////////////////////////////////////////////////////
614 void ThreadSynchronization::PostRenderStarted()
618 ConditionalWait::ScopedLock lock( mRenderThreadWaitCondition );
619 mRenderThreadPostRendering = TRUE;
622 void ThreadSynchronization::PostRenderWaitForCompletion()
626 ConditionalWait::ScopedLock lock( mRenderThreadWaitCondition );
627 while( mRenderThreadPostRendering &&
628 ! mRenderThreadReplacingSurface ) // We should NOT wait if we're replacing the surface
630 LOG_RENDER( "WAIT" );
631 mRenderThreadWaitCondition.Wait( lock );
635 ///////////////////////////////////////////////////////////////////////////////////////////////////
636 // ALL THREADS: Performance Marker
637 ///////////////////////////////////////////////////////////////////////////////////////////////////
639 void ThreadSynchronization::AddPerformanceMarker( PerformanceInterface::MarkerType type )
641 if( mPerformanceInterface )
643 mPerformanceInterface->AddMarker( type );
647 ///////////////////////////////////////////////////////////////////////////////////////////////////
651 ///////////////////////////////////////////////////////////////////////////////////////////////////
653 ///////////////////////////////////////////////////////////////////////////////////////////////////
654 // Called by ALL Threads
655 ///////////////////////////////////////////////////////////////////////////////////////////////////
657 void ThreadSynchronization::NotifyThreadInitialised()
660 ConditionalWait::ScopedLock lock( mEventThreadWaitCondition );
661 ++mNumberOfThreadsStarted;
663 mEventThreadWaitCondition.Notify();
666 ///////////////////////////////////////////////////////////////////////////////////////////////////
667 // Called by Update Thread
668 ///////////////////////////////////////////////////////////////////////////////////////////////////
670 void ThreadSynchronization::UpdateInitialising()
674 // Notify event thread that this thread is up and running, locks so we shouldn't have a scoped-lock when calling this
675 NotifyThreadInitialised();
677 // Wait for first thread-sync point
679 ConditionalWait::ScopedLock updateLock( mUpdateThreadWaitCondition );
681 while( mState == State::INITIALISING )
683 mUpdateThreadWaitCondition.Wait( updateLock );
687 // Locks so we shouldn't have a scoped-lock when calling this
691 void ThreadSynchronization::UpdateTryToSleep( bool runUpdate )
696 ! IsUpdateThreadResuming() ) // Locks so we shouldn't have a lock, we shouldn't try to sleep if we're JUST resuming
698 LOG_UPDATE( "TryToSleep" );
700 if( ++mTryToSleepCount >= 3 )
702 LOG_UPDATE( "Going to sleep" );
704 // Locks so we shouldn't have a scoped-lock when calling this
707 // Render thread will automatically wait as it relies on update-ahead-of-render count
711 ConditionalWait::ScopedLock updateLock( mUpdateThreadWaitCondition );
713 // Ensure we weren't stopped while we have been processing
714 if( mState != State::STOPPED )
716 mState = State::SLEEPING;
720 // Inform FrameTime that we're going to sleep
723 // Wait while we're SLEEPING
725 ConditionalWait::ScopedLock updateLock( mUpdateThreadWaitCondition );
726 while( mState == State::SLEEPING )
728 mUpdateThreadWaitCondition.Wait( updateLock );
732 ////////////////////////
734 ////////////////////////
736 LOG_UPDATE( "Waking Up" );
738 // Clear V-Sync-ahead-of-update-count
740 ConditionalWait::ScopedLock updateLock( mUpdateThreadWaitCondition );
741 mVSyncAheadOfUpdate = 0;
744 // Restart the v-sync-thread, locks so we shouldn't have a scoped-lock
747 // Reset try-to-sleep count
748 mTryToSleepCount = 0;
750 // Inform frame timer that we've woken up
756 mTryToSleepCount = 0;
760 void ThreadSynchronization::UpdateWaitIfReplacingSurface()
762 bool replacingSurface = false;
764 ConditionalWait::ScopedLock updateLock( mUpdateThreadWaitCondition );
765 replacingSurface = ( mState == State::REPLACING_SURFACE );
767 while( replacingSurface )
769 LOG_UPDATE_TRACE_FMT( "REPLACING SURFACE" );
771 // Locks so should not be called while we have a scoped-lock
774 // One last check before we actually wait in case the state has changed since we checked earlier
776 ConditionalWait::ScopedLock updateLock( mUpdateThreadWaitCondition );
777 replacingSurface = ( mState == State::REPLACING_SURFACE );
778 if( replacingSurface )
780 mUpdateThreadWaitCondition.Wait( updateLock );
785 ConditionalWait::ScopedLock updateLock( mUpdateThreadWaitCondition );
786 mVSyncAheadOfUpdate = 0;
789 // Locks so should not be called while we have a scoped-lock
794 bool ThreadSynchronization::IsUpdateThreadResuming()
796 ConditionalWait::ScopedLock updateLock( mUpdateThreadWaitCondition );
797 return mUpdateThreadResuming;
800 bool ThreadSynchronization::IsUpdateThreadStopping()
802 ConditionalWait::ScopedLock updateLock( mUpdateThreadWaitCondition );
803 return ( mState == State::STOPPED );
806 bool ThreadSynchronization::MaximumUpdateAheadOfRenderReached()
808 ConditionalWait::ScopedLock lock( mRenderThreadWaitCondition );
809 return mUpdateAheadOfRender >= mMaximumUpdateCount;
812 void ThreadSynchronization::StopAllThreads()
816 // Lock so we shouldn't have a scoped-lock when calling these methods
821 void ThreadSynchronization::RunVSyncThread()
824 ConditionalWait::ScopedLock lock( mVSyncThreadWaitCondition );
825 mVSyncThreadRunning = TRUE;
827 mVSyncThreadWaitCondition.Notify();
830 void ThreadSynchronization::PauseVSyncThread()
832 ConditionalWait::ScopedLock lock( mVSyncThreadWaitCondition );
833 mVSyncThreadRunning = FALSE;
836 void ThreadSynchronization::StopVSyncThread()
839 ConditionalWait::ScopedLock lock( mVSyncThreadWaitCondition );
840 mVSyncThreadStop = TRUE;
842 mVSyncThreadWaitCondition.Notify();
845 void ThreadSynchronization::StopRenderThread()
848 ConditionalWait::ScopedLock lock( mRenderThreadWaitCondition );
849 mRenderThreadStop = TRUE;
851 mRenderThreadWaitCondition.Notify();
854 ///////////////////////////////////////////////////////////////////////////////////////////////////
855 // Called by V-Sync Thread
856 ///////////////////////////////////////////////////////////////////////////////////////////////////
858 bool ThreadSynchronization::IsVSyncThreadRunning()
860 ConditionalWait::ScopedLock lock( mVSyncThreadWaitCondition );
861 return ! mVSyncThreadStop;
864 ///////////////////////////////////////////////////////////////////////////////////////////////////
865 // Called by Render Thread
866 ///////////////////////////////////////////////////////////////////////////////////////////////////
868 bool ThreadSynchronization::IsRenderThreadRunning()
870 ConditionalWait::ScopedLock lock( mRenderThreadWaitCondition );
871 return ! mRenderThreadStop;
874 bool ThreadSynchronization::IsRenderThreadReplacingSurface()
876 ConditionalWait::ScopedLock lock( mRenderThreadWaitCondition );
877 return mRenderThreadReplacingSurface;
880 } // namespace Adaptor
882 } // namespace Internal