3c52e0aca1d2a7399686b556102c849061c4dd38
[platform/core/uifw/dali-adaptor.git] / adaptors / base / update-render-synchronization.cpp
1 //
2 // Copyright (c) 2014 Samsung Electronics Co., Ltd.
3 //
4 // Licensed under the Flora License, Version 1.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
7 //
8 //     http://floralicense.org/license/
9 //
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.
15 //
16
17 // CLASS HEADER
18 #include "update-render-synchronization.h"
19
20 // INTERNAL INCLUDES
21 #include <dali/integration-api/debug.h>
22 #include <base/interfaces/adaptor-internal-services.h>
23
24 namespace Dali
25 {
26
27 namespace Internal
28 {
29
30 namespace Adaptor
31 {
32
33 namespace
34 {
35
36 const unsigned int MICROSECONDS_PER_SECOND( 1000000 );
37 const unsigned int INPUT_EVENT_UPDATE_PERIOD( MICROSECONDS_PER_SECOND / 90 ); // period between ecore x event updates
38
39 } // unnamed namespace
40
41 UpdateRenderSynchronization::UpdateRenderSynchronization( AdaptorInternalServices& adaptorInterfaces )
42 : mMaximumUpdateCount( adaptorInterfaces.GetCore().GetMaximumUpdateCount()),
43   mUpdateReadyCount( 0u ),
44   mRunning( false ),
45   mUpdateRequired( false ),
46   mPaused( false ),
47   mUpdateRequested( false ),
48   mAllowUpdateWhilePaused( false ),
49   mVSyncSleep( false ),
50   mVSyncFrameNumber( 0u ),
51   mVSyncSeconds( 0u ),
52   mVSyncMicroseconds( 0u ),
53   mCore( adaptorInterfaces.GetCore() ),
54   mPerformanceInterface( adaptorInterfaces.GetPerformanceInterface() ),
55   mSkipNextVSync( false )
56 {
57 }
58
59 UpdateRenderSynchronization::~UpdateRenderSynchronization()
60 {
61 }
62
63 void UpdateRenderSynchronization::Start()
64 {
65   mRunning = true;
66 }
67
68 void UpdateRenderSynchronization::Stop()
69 {
70   mRunning = false;
71
72   // Wake if sleeping
73   UpdateRequested();
74
75   // we may be paused so need to resume
76   Resume();
77
78   // Notify all condition variables, so if threads are waiting
79   // they can break out, and check the running status.
80   mUpdateFinishedCondition.notify_one();
81   mRenderFinishedCondition.notify_one();
82   mVSyncSleepCondition.notify_one();
83   mVSyncReceivedCondition.notify_one();
84 }
85
86 void UpdateRenderSynchronization::Pause()
87 {
88   mPaused = true;
89
90   AddPerformanceMarker( PerformanceMarker::PAUSED );
91
92 }
93
94 void UpdateRenderSynchronization::Resume()
95 {
96   mPaused = false;
97   mVSyncSleep = false;
98
99   mPausedCondition.notify_one();
100   mVSyncSleepCondition.notify_one();
101
102   AddPerformanceMarker( PerformanceMarker::RESUME);
103 }
104
105 void UpdateRenderSynchronization::UpdateRequested()
106 {
107   mUpdateRequested = true;
108
109   // Wake update thread if sleeping
110   mUpdateSleepCondition.notify_one();
111 }
112
113 void UpdateRenderSynchronization::UpdateWhilePaused()
114 {
115   {
116     boost::unique_lock< boost::mutex > lock( mMutex );
117
118     mAllowUpdateWhilePaused = true;
119   }
120
121   // wake vsync if sleeping
122   mVSyncSleepCondition.notify_one();
123   // Wake update if sleeping
124   mUpdateSleepCondition.notify_one();
125   // stay paused but notify the pause condition
126   mPausedCondition.notify_one();
127 }
128
129 void UpdateRenderSynchronization::UpdateReadyToRun()
130 {
131   bool wokenFromPause( false );
132
133   // atomic check first to avoid mutex lock in 99.99% of cases
134   if( mPaused )
135   {
136     boost::unique_lock< boost::mutex > lock( mMutex );
137
138     // wait while paused
139     while( mPaused && !mAllowUpdateWhilePaused )
140     {
141       // this will automatically unlock mMutex
142       mPausedCondition.wait( lock );
143
144       wokenFromPause = true;
145     }
146   }
147
148   if ( !wokenFromPause && !mSkipNextVSync)
149   {
150     // Wait for the next VSync
151     WaitVSync();
152   }
153
154   mSkipNextVSync = false;
155
156   AddPerformanceMarker( PerformanceMarker::UPDATE_START );
157 }
158
159 bool UpdateRenderSynchronization::UpdateSyncWithRender( bool& renderNeedsUpdate )
160 {
161
162   AddPerformanceMarker( PerformanceMarker::UPDATE_END );
163
164   boost::unique_lock< boost::mutex > lock( mMutex );
165
166   // Another frame was prepared for rendering; increment counter
167   ++mUpdateReadyCount;
168   DALI_ASSERT_DEBUG( mUpdateReadyCount <= mMaximumUpdateCount );
169
170   // Notify the render-thread that an update has completed
171   mUpdateFinishedCondition.notify_one();
172
173   // The update-thread must wait until a frame has been rendered, when mMaximumUpdateCount is reached
174   while( mRunning && ( mMaximumUpdateCount == mUpdateReadyCount ) )
175   {
176     // Wait will atomically add the thread to the set of threads waiting on
177     // the condition variable mRenderFinishedCondition and unlock the mutex.
178     mRenderFinishedCondition.wait( lock );
179   }
180
181   renderNeedsUpdate = mUpdateRequired;
182
183   // Flag is used to during UpdateThread::Stop() to exit the update/render loops
184   return mRunning;
185 }
186
187 void UpdateRenderSynchronization::UpdateWaitForAllRenderingToFinish()
188 {
189   boost::unique_lock< boost::mutex > lock( mMutex );
190
191   // Wait for all of the prepared frames to be rendered
192   while ( mRunning && ( 0u != mUpdateReadyCount ) && !mUpdateRequested )
193   {
194     // Wait will atomically add the thread to the set of threads waiting on
195     // the condition variable mRenderFinishedCondition and unlock the mutex.
196     mRenderFinishedCondition.wait( lock );
197   }
198 }
199
200 bool UpdateRenderSynchronization::UpdateTryToSleep()
201 {
202   if ( !mUpdateRequired && !mUpdateRequested )
203   {
204     // there's nothing to update in the scene, so wait for render to finish processing
205     UpdateWaitForAllRenderingToFinish();
206   }
207
208   boost::mutex sleepMutex;
209   boost::unique_lock< boost::mutex > lock( sleepMutex );
210
211   while( mRunning && !mUpdateRequired && !mUpdateRequested )
212   {
213     //
214     // Going to sleep
215     //
216
217     // 1. put VSync thread to sleep.
218     mVSyncSleep = true;
219
220     // 2. inform Core
221     mCore.Sleep();
222
223     // 3. block thread and wait for wakeup event
224     mUpdateSleepCondition.wait( lock );
225
226     //
227     // Woken up
228     //
229
230     // 1. inform Core
231     mCore.WakeUp();
232
233     // 2. wake VSync thread.
234     mVSyncSleep = false;
235     mVSyncSleepCondition.notify_one();
236
237     // 3. Update shouldn't wait for next VSync
238     mSkipNextVSync = true;
239   }
240
241   mUpdateRequested = false;
242
243   return mRunning;
244 }
245
246 void UpdateRenderSynchronization::RenderFinished( bool updateRequired )
247 {
248   {
249     boost::unique_lock< boost::mutex > lock( mMutex );
250
251     // Set the flag to say if update needs to run again.
252     mUpdateRequired = updateRequired;
253
254     // A frame has been rendered; decrement counter
255     --mUpdateReadyCount;
256     DALI_ASSERT_DEBUG( mUpdateReadyCount < mMaximumUpdateCount );
257   }
258
259   // Notify the update-thread that a render has completed
260   mRenderFinishedCondition.notify_one();
261
262   AddPerformanceMarker( PerformanceMarker::RENDER_END );
263 }
264
265 bool UpdateRenderSynchronization::RenderSyncWithUpdate()
266 {
267   boost::unique_lock< boost::mutex > lock( mMutex );
268
269   // Wait for update to produce a buffer, or for the mRunning state to change
270   while ( mRunning && ( 0u == mUpdateReadyCount ) )
271   {
272     // Wait will atomically add the thread to the set of threads waiting on
273     // the condition variable mUpdateFinishedCondition and unlock the mutex.
274     mUpdateFinishedCondition.wait( lock );
275   }
276
277   if( mRunning )
278   {
279     AddPerformanceMarker( PerformanceMarker::RENDER_START );
280   }
281   // Flag is used to during UpdateThread::Stop() to exit the update/render loops
282   return mRunning;
283 }
284
285 void UpdateRenderSynchronization::WaitVSync()
286 {
287   // Block until the start of a new vsync.
288   // If we're experiencing slowdown and are behind by more than a frame
289   // then we should wait for the next frame as the Video output will also
290   // do this (lock-step to 60Hz)
291
292   unsigned int updateFrameNumber = mVSyncFrameNumber;
293
294   boost::unique_lock< boost::mutex > lock( mMutex );
295
296   while ( mRunning && ( updateFrameNumber == mVSyncFrameNumber ) )
297   {
298     // Wait will atomically add the thread to the set of threads waiting on
299     // the condition variable mVSyncReceivedCondition and unlock the mutex.
300     mVSyncReceivedCondition.wait( lock );
301   }
302
303   // reset update while paused flag
304   mAllowUpdateWhilePaused = false;
305 }
306
307 bool UpdateRenderSynchronization::VSyncNotifierSyncWithUpdateAndRender( unsigned int frameNumber, unsigned int seconds, unsigned int microseconds )
308 {
309   boost::unique_lock< boost::mutex > lock( mMutex );
310
311   mVSyncFrameNumber = frameNumber;
312   mVSyncSeconds = seconds;
313   mVSyncMicroseconds = microseconds;
314
315   mVSyncReceivedCondition.notify_all();
316
317   AddPerformanceMarker( PerformanceMarker::V_SYNC );
318
319   while( mRunning && // sleep on condition variable WHILE still running
320          !mAllowUpdateWhilePaused &&             // AND NOT allowing updates while paused
321          ( mVSyncSleep || mPaused ) )            // AND sleeping OR paused
322   {
323     // Wait will atomically add the thread to the set of threads waiting on
324     // the condition variable mVSyncSleepCondition and unlock the mutex.
325     mVSyncSleepCondition.wait( lock );
326   }
327
328   return mRunning;
329 }
330
331 unsigned int UpdateRenderSynchronization::GetFrameNumber() const
332 {
333   return mVSyncFrameNumber;
334 }
335
336 uint64_t UpdateRenderSynchronization::GetTimeMicroseconds()
337 {
338   uint64_t currentTime(0);
339
340   {
341     boost::unique_lock< boost::mutex > lock( mMutex );
342
343     currentTime = mVSyncSeconds;
344     currentTime *= MICROSECONDS_PER_SECOND;
345     currentTime += mVSyncMicroseconds;
346   }
347
348   return currentTime;
349 }
350
351 inline void UpdateRenderSynchronization::AddPerformanceMarker( PerformanceMarker::MarkerType type )
352 {
353   if( mPerformanceInterface )
354   {
355     mPerformanceInterface->AddMarker( type );
356   }
357 }
358
359 } // namespace Adaptor
360
361 } // namespace Internal
362
363 } // namespace Dali