2 * Copyright (c) 2014 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 "update-render-synchronization.h"
22 #include <dali/integration-api/debug.h>
25 #include <base/interfaces/adaptor-internal-services.h>
38 const unsigned int TIME_PER_FRAME_IN_MICROSECONDS = 16667;
39 const unsigned int MICROSECONDS_PER_SECOND( 1000000 );
40 const unsigned int INPUT_EVENT_UPDATE_PERIOD( MICROSECONDS_PER_SECOND / 90 ); // period between ecore x event updates
42 } // unnamed namespace
44 UpdateRenderSynchronization::UpdateRenderSynchronization( AdaptorInternalServices& adaptorInterfaces,
45 unsigned int numberOfVSyncsPerRender)
46 : mMaximumUpdateCount( adaptorInterfaces.GetCore().GetMaximumUpdateCount()),
47 mNumberOfVSyncsPerRender( numberOfVSyncsPerRender ),
48 mUpdateReadyCount( 0u ),
50 mUpdateRequired( false ),
52 mUpdateRequested( false ),
53 mAllowUpdateWhilePaused( false ),
55 mSyncFrameNumber( 0u ),
57 mSyncMicroseconds( 0u ),
58 mFrameTime( adaptorInterfaces.GetPlatformAbstractionInterface() ),
59 mNotificationTrigger( adaptorInterfaces.GetTriggerEventInterface() ),
60 mPerformanceInterface( adaptorInterfaces.GetPerformanceInterface() ),
61 mReplaceSurfaceRequest(),
62 mReplaceSurfaceRequested( false )
66 UpdateRenderSynchronization::~UpdateRenderSynchronization()
70 void UpdateRenderSynchronization::Start()
72 mFrameTime.SetMinimumFrameTimeInterval( mNumberOfVSyncsPerRender * TIME_PER_FRAME_IN_MICROSECONDS );
76 void UpdateRenderSynchronization::Stop()
83 // we may be paused so need to resume
86 // Notify all condition variables, so if threads are waiting
87 // they can break out, and check the running status.
88 mUpdateFinishedCondition.notify_one();
89 mRenderFinishedCondition.notify_one();
90 mVSyncSleepCondition.notify_one();
91 mVSyncReceivedCondition.notify_one();
92 mRenderRequestSleepCondition.notify_one();
97 void UpdateRenderSynchronization::Pause()
101 AddPerformanceMarker( PerformanceInterface::PAUSED );
102 mFrameTime.Suspend();
105 void UpdateRenderSynchronization::ResumeFrameTime()
110 void UpdateRenderSynchronization::Resume()
115 mPausedCondition.notify_one();
116 mVSyncSleepCondition.notify_one();
118 AddPerformanceMarker( PerformanceInterface::RESUME);
121 void UpdateRenderSynchronization::UpdateRequested()
123 mUpdateRequested = true;
125 // Wake update thread if sleeping
126 mUpdateSleepCondition.notify_one();
129 void UpdateRenderSynchronization::UpdateWhilePaused()
132 boost::unique_lock< boost::mutex > lock( mMutex );
134 mAllowUpdateWhilePaused = true;
137 // wake vsync if sleeping
138 mVSyncSleepCondition.notify_one();
139 // Wake update if sleeping
140 mUpdateSleepCondition.notify_one();
141 // stay paused but notify the pause condition
142 mPausedCondition.notify_one();
145 bool UpdateRenderSynchronization::ReplaceSurface( RenderSurface* newSurface )
152 boost::unique_lock< boost::mutex > lock( mMutex );
154 mReplaceSurfaceRequest.SetSurface(newSurface);
155 mReplaceSurfaceRequested = true;
157 mRenderRequestFinishedCondition.wait(lock); // wait unlocks the mutex on entry, and locks again on exit.
159 mReplaceSurfaceRequested = false;
160 result = mReplaceSurfaceRequest.GetReplaceCompleted();
166 bool UpdateRenderSynchronization::NewSurface( RenderSurface* newSurface )
173 boost::unique_lock< boost::mutex > lock( mMutex );
175 mReplaceSurfaceRequest.SetSurface(newSurface);
176 mReplaceSurfaceRequested = true;
178 // Unlock the render thread sleeping on requests
179 mRenderRequestSleepCondition.notify_one();
181 // Lock event thread until request has been processed
182 mRenderRequestFinishedCondition.wait(lock);// wait unlocks the mutex on entry, and locks again on exit.
184 mReplaceSurfaceRequested = false;
185 result = mReplaceSurfaceRequest.GetReplaceCompleted();
192 void UpdateRenderSynchronization::UpdateReadyToRun()
194 bool wokenFromPause( false );
196 // atomic check first to avoid mutex lock in 99.99% of cases
199 boost::unique_lock< boost::mutex > lock( mMutex );
202 while( mPaused && !mAllowUpdateWhilePaused )
204 // this will automatically unlock mMutex
205 mPausedCondition.wait( lock );
207 wokenFromPause = true;
211 if ( !wokenFromPause )
213 // Wait for the next Sync
217 AddPerformanceMarker( PerformanceInterface::UPDATE_START );
220 bool UpdateRenderSynchronization::UpdateSyncWithRender( bool notifyEvent, bool& renderNeedsUpdate )
222 AddPerformanceMarker( PerformanceInterface::UPDATE_END );
224 // Do the notifications first so the event thread can start processing them
225 if( notifyEvent && mRunning )
227 // Tell the event-thread to wake up (if asleep) and send a notification event to Core
228 mNotificationTrigger.Trigger();
231 boost::unique_lock< boost::mutex > lock( mMutex );
233 // Another frame was prepared for rendering; increment counter
235 DALI_ASSERT_DEBUG( mUpdateReadyCount <= mMaximumUpdateCount );
237 // Notify the render-thread that an update has completed
238 mUpdateFinishedCondition.notify_one();
240 // The update-thread must wait until a frame has been rendered, when mMaximumUpdateCount is reached
241 while( mRunning && ( mMaximumUpdateCount == mUpdateReadyCount ) )
243 // Wait will atomically add the thread to the set of threads waiting on
244 // the condition variable mRenderFinishedCondition and unlock the mutex.
245 mRenderFinishedCondition.wait( lock );
248 renderNeedsUpdate = mUpdateRequired;
250 // Flag is used to during UpdateThread::Stop() to exit the update/render loops
254 void UpdateRenderSynchronization::UpdateWaitForAllRenderingToFinish()
256 boost::unique_lock< boost::mutex > lock( mMutex );
258 // Wait for all of the prepared frames to be rendered
259 while ( mRunning && ( 0u != mUpdateReadyCount ) && !mUpdateRequested )
261 // Wait will atomically add the thread to the set of threads waiting on
262 // the condition variable mRenderFinishedCondition and unlock the mutex.
263 mRenderFinishedCondition.wait( lock );
267 bool UpdateRenderSynchronization::UpdateTryToSleep()
269 if ( !mUpdateRequired && !mUpdateRequested )
271 // there's nothing to update in the scene, so wait for render to finish processing
272 UpdateWaitForAllRenderingToFinish();
275 boost::mutex sleepMutex;
276 boost::unique_lock< boost::mutex > lock( sleepMutex );
278 while( mRunning && !mUpdateRequired && !mUpdateRequested )
284 // 1. put VSync thread to sleep.
287 // 2. inform frame time
290 // 3. block thread and wait for wakeup event
291 mUpdateSleepCondition.wait( lock );
297 // 1. inform frame timer
300 // 2. wake VSync thread.
302 mVSyncSleepCondition.notify_one();
305 mUpdateRequested = false;
310 bool UpdateRenderSynchronization::RenderSyncWithRequest(RenderRequest*& requestPtr)
312 boost::unique_lock< boost::mutex > lock( mMutex );
314 // Wait for a replace surface request
315 mRenderRequestSleepCondition.wait(lock);
317 // write any new requests
318 if( mReplaceSurfaceRequested )
320 requestPtr = &mReplaceSurfaceRequest;
322 mReplaceSurfaceRequested = false;
326 bool UpdateRenderSynchronization::RenderSyncWithUpdate(RenderRequest*& requestPtr)
328 boost::unique_lock< boost::mutex > lock( mMutex );
330 // Wait for update to produce a buffer, or for the mRunning state to change
331 while ( mRunning && ( 0u == mUpdateReadyCount ) )
333 // Wait will atomically add the thread to the set of threads waiting on
334 // the condition variable mUpdateFinishedCondition and unlock the mutex.
335 mUpdateFinishedCondition.wait( lock );
340 AddPerformanceMarker( PerformanceInterface::RENDER_START );
343 // write any new requests
344 if( mReplaceSurfaceRequested )
346 requestPtr = &mReplaceSurfaceRequest;
348 mReplaceSurfaceRequested = false;
350 // Flag is used to during UpdateThread::Stop() to exit the update/render loops
354 void UpdateRenderSynchronization::RenderFinished( bool updateRequired, bool requestProcessed )
357 boost::unique_lock< boost::mutex > lock( mMutex );
359 // Set the flag to say if update needs to run again.
360 mUpdateRequired = updateRequired;
362 // A frame has been rendered; decrement counter
364 DALI_ASSERT_DEBUG( mUpdateReadyCount < mMaximumUpdateCount );
367 // Notify the update-thread that a render has completed
368 mRenderFinishedCondition.notify_one();
370 if( requestProcessed )
372 // Notify the event thread that a request has completed
373 mRenderRequestFinishedCondition.notify_one();
376 AddPerformanceMarker( PerformanceInterface::RENDER_END );
379 void UpdateRenderSynchronization::WaitSync()
381 // Block until the start of a new sync.
382 // If we're experiencing slowdown and are behind by more than a frame
383 // then we should wait for the next frame
385 unsigned int updateFrameNumber = mSyncFrameNumber;
387 boost::unique_lock< boost::mutex > lock( mMutex );
389 while ( mRunning && ( updateFrameNumber == mSyncFrameNumber ) )
391 // Wait will atomically add the thread to the set of threads waiting on
392 // the condition variable mVSyncReceivedCondition and unlock the mutex.
393 mVSyncReceivedCondition.wait( lock );
396 // reset update while paused flag
397 mAllowUpdateWhilePaused = false;
400 bool UpdateRenderSynchronization::VSyncNotifierSyncWithUpdateAndRender( bool validSync, unsigned int frameNumber, unsigned int seconds, unsigned int microseconds, unsigned int& numberOfVSyncsPerRender )
402 // This may have changed since the last sync. Update VSyncNotifier's copy here if so.
403 if( numberOfVSyncsPerRender != mNumberOfVSyncsPerRender )
405 numberOfVSyncsPerRender = mNumberOfVSyncsPerRender; // save it back
406 mFrameTime.SetMinimumFrameTimeInterval( mNumberOfVSyncsPerRender * TIME_PER_FRAME_IN_MICROSECONDS );
411 mFrameTime.SetSyncTime( frameNumber );
414 boost::unique_lock< boost::mutex > lock( mMutex );
416 mSyncFrameNumber = frameNumber;
417 mSyncSeconds = seconds;
418 mSyncMicroseconds = microseconds;
420 mVSyncReceivedCondition.notify_all();
422 AddPerformanceMarker( PerformanceInterface::VSYNC );
424 while( mRunning && // sleep on condition variable WHILE still running
425 !mAllowUpdateWhilePaused && // AND NOT allowing updates while paused
426 ( mVSyncSleep || mPaused ) ) // AND sleeping OR paused
428 // Wait will atomically add the thread to the set of threads waiting on
429 // the condition variable mVSyncSleepCondition and unlock the mutex.
430 mVSyncSleepCondition.wait( lock );
436 unsigned int UpdateRenderSynchronization::GetFrameNumber() const
438 return mSyncFrameNumber;
441 uint64_t UpdateRenderSynchronization::GetTimeMicroseconds()
443 uint64_t currentTime(0);
446 boost::unique_lock< boost::mutex > lock( mMutex );
448 currentTime = mSyncSeconds;
449 currentTime *= MICROSECONDS_PER_SECOND;
450 currentTime += mSyncMicroseconds;
456 void UpdateRenderSynchronization::SetRenderRefreshRate( unsigned int numberOfVSyncsPerRender )
458 mNumberOfVSyncsPerRender = numberOfVSyncsPerRender;
461 inline void UpdateRenderSynchronization::AddPerformanceMarker( PerformanceInterface::MarkerType type )
463 if( mPerformanceInterface )
465 mPerformanceInterface->AddMarker( type );
469 void UpdateRenderSynchronization::PredictNextSyncTime(
470 float& lastFrameDeltaSeconds,
471 unsigned int& lastSyncTimeMilliseconds,
472 unsigned int& nextSyncTimeMilliseconds )
474 mFrameTime.PredictNextSyncTime( lastFrameDeltaSeconds, lastSyncTimeMilliseconds, nextSyncTimeMilliseconds );
477 } // namespace Adaptor
479 } // namespace Internal