Merge "(ThreadSync) Ensure elapsed time is passed into Core when required" into devel...
[platform/core/uifw/dali-adaptor.git] / adaptors / base / combined-update-render / combined-update-render-controller.cpp
1 /*
2  * Copyright (c) 2015 Samsung Electronics Co., Ltd.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
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
18 // CLASS HEADER
19 #include "combined-update-render-controller.h"
20
21 // EXTERNAL INCLUDES
22 #include <errno.h>
23 #include <dali/integration-api/platform-abstraction.h>
24
25 // INTERNAL INCLUDES
26 #include <trigger-event-factory.h>
27 #include <base/combined-update-render/combined-update-render-controller-debug.h>
28 #include <base/environment-options.h>
29 #include <base/time-service.h>
30 #include <base/interfaces/adaptor-internal-services.h>
31
32 namespace Dali
33 {
34
35 namespace Internal
36 {
37
38 namespace Adaptor
39 {
40
41 namespace
42 {
43 const unsigned int CREATED_THREAD_COUNT = 1u;
44
45 const int CONTINUOUS = -1;
46 const int ONCE = 1;
47
48 const unsigned int TRUE = 1u;
49 const unsigned int FALSE = 0u;
50
51 const unsigned int MILLISECONDS_PER_SECOND( 1e+3 );
52 const float        NANOSECONDS_TO_SECOND( 1e-9f );
53 const unsigned int NANOSECONDS_PER_SECOND( 1e+9 );
54 const unsigned int NANOSECONDS_PER_MILLISECOND( 1e+6 );
55
56 // The following values will get calculated at compile time
57 const float        DEFAULT_FRAME_DURATION_IN_SECONDS( 1.0f / 60.0f );
58 const unsigned int DEFAULT_FRAME_DURATION_IN_MILLISECONDS( DEFAULT_FRAME_DURATION_IN_SECONDS * MILLISECONDS_PER_SECOND );
59 const unsigned int DEFAULT_FRAME_DURATION_IN_NANOSECONDS( DEFAULT_FRAME_DURATION_IN_SECONDS * NANOSECONDS_PER_SECOND );
60
61 /**
62  * 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
63  * there is a danger that, on the event-thread we could have:
64  *  1) An update-request where we do nothing as Update/Render thread still running.
65  *  2) Quickly followed by a sleep-request being handled where we pause the Update/Render Thread (even though we have an update to process).
66  *
67  * 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:
68  *  1) MAIN THREAD:           Update Request: COUNTER = 1
69  *  2) UPDATE/RENDER THREAD:  Do Update/Render, then no Updates required -> Sleep Trigger
70  *  3) MAIN THREAD:           Update Request: COUNTER = 2
71  *  4) MAIN THREAD:           Sleep Request:  COUNTER = 1 -> We do not sleep just yet
72  *
73  * Also ensures we preserve battery life by only doing ONE update when the above use case is not triggered.
74  *  1) MAIN THREAD:           Update Request: COUNTER = 1
75  *  2) UPDATE/RENDER THREAD:  Do Update/Render, then no Updates required -> Sleep Trigger
76  *  3) MAIN THREAD:           Sleep Request:  COUNTER = 0 -> Go to sleep
77  */
78 const unsigned int MAXIMUM_UPDATE_REQUESTS = 2;
79 } // unnamed namespace
80
81 ///////////////////////////////////////////////////////////////////////////////////////////////////
82 // EVENT THREAD
83 ///////////////////////////////////////////////////////////////////////////////////////////////////
84
85 CombinedUpdateRenderController::CombinedUpdateRenderController( AdaptorInternalServices& adaptorInterfaces, const EnvironmentOptions& environmentOptions )
86 : mFpsTracker( environmentOptions ),
87   mUpdateStatusLogger( environmentOptions ),
88   mRenderHelper( adaptorInterfaces ),
89   mEventThreadSemaphore(),
90   mUpdateRenderThreadWaitCondition(),
91   mAdaptorInterfaces( adaptorInterfaces ),
92   mPerformanceInterface( adaptorInterfaces.GetPerformanceInterface() ),
93   mCore( adaptorInterfaces.GetCore() ),
94   mEnvironmentOptions( environmentOptions ),
95   mNotificationTrigger( adaptorInterfaces.GetProcessCoreEventsTrigger() ),
96   mSleepTrigger( NULL ),
97   mUpdateRenderThread( NULL ),
98   mDefaultFrameDelta( 0.0f ),
99   mDefaultFrameDurationMilliseconds( 0u ),
100   mDefaultFrameDurationNanoseconds( 0u ),
101   mUpdateRequestCount( 0u ),
102   mRunning( FALSE ),
103   mUpdateRenderRunCount( 0 ),
104   mDestroyUpdateRenderThread( FALSE ),
105   mNewSurface( NULL ),
106   mPostRendering( FALSE )
107 {
108   LOG_EVENT_TRACE;
109
110   // Initialise frame delta/duration variables first
111   SetRenderRefreshRate( environmentOptions.GetRenderRefreshRate() );
112
113   // Set the thread-synchronization interface on the render-surface
114   RenderSurface* currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
115   if( currentSurface )
116   {
117     currentSurface->SetThreadSynchronization( *this );
118   }
119
120   TriggerEventFactoryInterface& triggerFactory = mAdaptorInterfaces.GetTriggerEventFactoryInterface();
121   mSleepTrigger = triggerFactory.CreateTriggerEvent( MakeCallback( this, &CombinedUpdateRenderController::ProcessSleepRequest ), TriggerEventInterface::KEEP_ALIVE_AFTER_TRIGGER );
122
123   sem_init( &mEventThreadSemaphore, 0, 0 ); // Initialize to 0 so that it just waits if sem_post has not been called
124 }
125
126 CombinedUpdateRenderController::~CombinedUpdateRenderController()
127 {
128   LOG_EVENT_TRACE;
129
130   Stop();
131
132   delete mSleepTrigger;
133 }
134
135 void CombinedUpdateRenderController::Initialize()
136 {
137   LOG_EVENT_TRACE;
138
139   // Ensure Update/Render Thread not already created
140   DALI_ASSERT_ALWAYS( ! mUpdateRenderThread );
141
142   // Create Update/Render Thread
143   mUpdateRenderThread = new pthread_t();
144   int error = pthread_create( mUpdateRenderThread, NULL, InternalUpdateRenderThreadEntryFunc, this );
145   DALI_ASSERT_ALWAYS( !error && "Return code from pthread_create() when creating UpdateRenderThread" );
146
147   // The Update/Render thread will now run and initialise EGL etc. and will then wait for Start to be called
148   // When this function returns, the application initialisation on the event thread should occur
149 }
150
151 void CombinedUpdateRenderController::Start()
152 {
153   LOG_EVENT_TRACE;
154
155   DALI_ASSERT_ALWAYS( !mRunning && mUpdateRenderThread );
156
157   // Wait until all threads created in Initialise are up and running
158   for( unsigned int i = 0; i < CREATED_THREAD_COUNT; ++i )
159   {
160     sem_wait( &mEventThreadSemaphore );
161   }
162
163   mRenderHelper.Start();
164
165   mRunning = TRUE;
166
167   LOG_EVENT( "Startup Complete, starting Update/Render Thread" );
168
169   RunUpdateRenderThread( CONTINUOUS, false /* No animation progression */ );
170 }
171
172 void CombinedUpdateRenderController::Pause()
173 {
174   LOG_EVENT_TRACE;
175
176   mRunning = FALSE;
177
178   PauseUpdateRenderThread();
179
180   AddPerformanceMarker( PerformanceInterface::PAUSED );
181 }
182
183 void CombinedUpdateRenderController::Resume()
184 {
185   LOG_EVENT_TRACE;
186
187   if( !mRunning && IsUpdateRenderThreadPaused() )
188   {
189     LOG_EVENT( "Resuming" );
190
191     RunUpdateRenderThread( CONTINUOUS, true /* Animation progression required while we were paused */ );
192
193     AddPerformanceMarker( PerformanceInterface::RESUME );
194
195     mRunning = TRUE;
196   }
197 }
198
199 void CombinedUpdateRenderController::Stop()
200 {
201   LOG_EVENT_TRACE;
202
203   // Stop Rendering and the Update/Render Thread
204   mRenderHelper.Stop();
205
206   StopUpdateRenderThread();
207
208   if( mUpdateRenderThread )
209   {
210     LOG_EVENT( "Destroying UpdateRenderThread" );
211
212     // wait for the thread to finish
213     pthread_join( *mUpdateRenderThread, NULL );
214
215     delete mUpdateRenderThread;
216     mUpdateRenderThread = NULL;
217   }
218
219   mRunning = FALSE;
220 }
221
222 void CombinedUpdateRenderController::RequestUpdate()
223 {
224   LOG_EVENT_TRACE;
225
226   // Increment the update-request count to the maximum
227   if( mUpdateRequestCount < MAXIMUM_UPDATE_REQUESTS )
228   {
229     ++mUpdateRequestCount;
230   }
231
232   if( mRunning && IsUpdateRenderThreadPaused() )
233   {
234     LOG_EVENT( "Processing" );
235
236     RunUpdateRenderThread( CONTINUOUS, false /* No animation progression */ );
237   }
238 }
239
240 void CombinedUpdateRenderController::RequestUpdateOnce()
241 {
242   if( IsUpdateRenderThreadPaused() )
243   {
244     LOG_EVENT_TRACE;
245
246     // Run Update/Render once
247     RunUpdateRenderThread( ONCE, false /* No animation progression */ );
248   }
249 }
250
251 void CombinedUpdateRenderController::ReplaceSurface( RenderSurface* newSurface )
252 {
253   LOG_EVENT_TRACE;
254
255   // Set the ThreadSyncronizationInterface on the new surface
256   newSurface->SetThreadSynchronization( *this );
257
258   LOG_EVENT( "Starting to replace the surface, event-thread blocked" );
259
260   // Start replacing the surface.
261   {
262     ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
263     mPostRendering = FALSE; // Clear the post-rendering flag as Update/Render thread will replace the surface now
264     mNewSurface = newSurface;
265     mUpdateRenderThreadWaitCondition.Notify( lock );
266   }
267
268   // Wait until the surface has been replaced
269   sem_wait( &mEventThreadSemaphore );
270
271   LOG_EVENT( "Surface replaced, event-thread continuing" );
272 }
273
274 void CombinedUpdateRenderController::SetRenderRefreshRate( unsigned int numberOfFramesPerRender )
275 {
276   // Not protected by lock, but written to rarely so not worth adding a lock when reading
277   mDefaultFrameDelta                  = numberOfFramesPerRender * DEFAULT_FRAME_DURATION_IN_SECONDS;
278   mDefaultFrameDurationMilliseconds   = numberOfFramesPerRender * DEFAULT_FRAME_DURATION_IN_MILLISECONDS;
279   mDefaultFrameDurationNanoseconds    = numberOfFramesPerRender * DEFAULT_FRAME_DURATION_IN_NANOSECONDS;
280
281   LOG_EVENT( "mDefaultFrameDelta(%.6f), mDefaultFrameDurationMilliseconds(%lld), mDefaultFrameDurationNanoseconds(%lld)", mDefaultFrameDelta, mDefaultFrameDurationMilliseconds, mDefaultFrameDurationNanoseconds );
282 }
283
284 ///////////////////////////////////////////////////////////////////////////////////////////////////
285 // EVENT THREAD
286 ///////////////////////////////////////////////////////////////////////////////////////////////////
287
288 void CombinedUpdateRenderController::RunUpdateRenderThread( int numberOfCycles, bool useElapsedTime )
289 {
290   ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
291   mUpdateRenderRunCount = numberOfCycles;
292   mUseElapsedTimeAfterWait = useElapsedTime;
293   LOG_COUNTER_EVENT( "mUpdateRenderRunCount: %d, mUseElapsedTimeAfterWait: %d", mUpdateRenderRunCount, mUseElapsedTimeAfterWait );
294   mUpdateRenderThreadWaitCondition.Notify( lock );
295 }
296
297 void CombinedUpdateRenderController::PauseUpdateRenderThread()
298 {
299   ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
300   mUpdateRenderRunCount = 0;
301 }
302
303 void CombinedUpdateRenderController::StopUpdateRenderThread()
304 {
305   ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
306   mDestroyUpdateRenderThread = TRUE;
307   mUpdateRenderThreadWaitCondition.Notify( lock );
308 }
309
310 bool CombinedUpdateRenderController::IsUpdateRenderThreadPaused()
311 {
312   ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
313   return mUpdateRenderRunCount != CONTINUOUS; // Report paused if NOT continuously running
314 }
315
316 void CombinedUpdateRenderController::ProcessSleepRequest()
317 {
318   LOG_EVENT_TRACE;
319
320   // Decrement Update request count
321   if( mUpdateRequestCount > 0 )
322   {
323     --mUpdateRequestCount;
324   }
325
326   // Only sleep if our update-request count is 0
327   if( mUpdateRequestCount == 0 )
328   {
329     LOG_EVENT( "Going to sleep" );
330
331     PauseUpdateRenderThread();
332   }
333 }
334
335 ///////////////////////////////////////////////////////////////////////////////////////////////////
336 // UPDATE/RENDER THREAD
337 ///////////////////////////////////////////////////////////////////////////////////////////////////
338
339 void CombinedUpdateRenderController::UpdateRenderThread()
340 {
341   // Install a function for logging
342   mEnvironmentOptions.InstallLogFunction();
343
344   LOG_UPDATE_RENDER( "THREAD CREATED" );
345
346   mRenderHelper.InitializeEgl();
347
348   // tell core it has a context
349   mCore.ContextCreated();
350
351   NotifyThreadInitialised();
352
353   // Update time
354   uint64_t lastFrameTime;
355   TimeService::GetNanoseconds( lastFrameTime );
356
357   LOG_UPDATE_RENDER( "THREAD INITIALISED" );
358
359   bool useElapsedTime = true;
360
361   while( UpdateRenderReady( useElapsedTime ) )
362   {
363     LOG_UPDATE_RENDER_TRACE;
364
365     uint64_t currentFrameStartTime = 0;
366     TimeService::GetNanoseconds( currentFrameStartTime );
367
368     const uint64_t timeSinceLastFrame = currentFrameStartTime - lastFrameTime;
369
370     // Optional FPS Tracking
371     if( mFpsTracker.Enabled() )
372     {
373       float absoluteTimeSinceLastRender = timeSinceLastFrame * NANOSECONDS_TO_SECOND;
374       mFpsTracker.Track( absoluteTimeSinceLastRender );
375     }
376
377     lastFrameTime = currentFrameStartTime; // Store frame start time
378
379     //////////////////////////////
380     // REPLACE SURFACE
381     //////////////////////////////
382
383     RenderSurface* newSurface = ShouldSurfaceBeReplaced();
384     if( newSurface )
385     {
386       LOG_UPDATE_RENDER_TRACE_FMT( "Replacing Surface" );
387       mRenderHelper.ReplaceSurface( newSurface );
388       SurfaceReplaced();
389     }
390
391     //////////////////////////////
392     // UPDATE
393     //////////////////////////////
394
395     const unsigned int currentTime = currentFrameStartTime / NANOSECONDS_PER_MILLISECOND;
396     const unsigned int nextFrameTime = currentTime + mDefaultFrameDurationMilliseconds;
397
398     uint64_t noOfFramesSinceLastUpdate = 1;
399     float frameDelta = 0.0f;
400     if( useElapsedTime )
401     {
402       // If using the elapsed time, then calculate frameDelta as a multiple of mDefaultFrameDelta
403       noOfFramesSinceLastUpdate = std::max( timeSinceLastFrame / mDefaultFrameDurationNanoseconds, noOfFramesSinceLastUpdate );
404       frameDelta = mDefaultFrameDelta * noOfFramesSinceLastUpdate;
405     }
406     LOG_UPDATE_RENDER( "noOfFramesSinceLastUpdate(%u), frameDelta(%.6f)", noOfFramesSinceLastUpdate, frameDelta );
407
408     Integration::UpdateStatus updateStatus;
409
410     AddPerformanceMarker( PerformanceInterface::UPDATE_START );
411     mCore.Update( frameDelta, currentTime, nextFrameTime, updateStatus );
412     AddPerformanceMarker( PerformanceInterface::UPDATE_END );
413
414     unsigned int keepUpdatingStatus = updateStatus.KeepUpdating();
415
416     // Tell the event-thread to wake up (if asleep) and send a notification event to Core if required
417     if( updateStatus.NeedsNotification() )
418     {
419       mNotificationTrigger.Trigger();
420       LOG_UPDATE_RENDER( "Notification Triggered" );
421     }
422
423     // Optional logging of update/render status
424     mUpdateStatusLogger.Log( keepUpdatingStatus );
425
426     //////////////////////////////
427     // RENDER
428     //////////////////////////////
429
430     mRenderHelper.ConsumeEvents();
431     mRenderHelper.PreRender();
432
433     Integration::RenderStatus renderStatus;
434
435     AddPerformanceMarker( PerformanceInterface::RENDER_START );
436     mCore.Render( renderStatus );
437     AddPerformanceMarker( PerformanceInterface::RENDER_END );
438
439     if( renderStatus.HasRendered() )
440     {
441       mRenderHelper.PostRender();
442     }
443
444     // Trigger event thread to request Update/Render thread to sleep if update not required
445     if( ( Integration::KeepUpdating::NOT_REQUESTED == keepUpdatingStatus ) &&
446         ! renderStatus.NeedsUpdate() )
447     {
448       mSleepTrigger->Trigger();
449       LOG_UPDATE_RENDER( "Sleep Triggered" );
450     }
451
452     //////////////////////////////
453     // FRAME TIME
454     //////////////////////////////
455
456     // Sleep until at least the the default frame duration has elapsed. This will return immediately if the specified end-time has already passed.
457     TimeService::SleepUntil( currentFrameStartTime + mDefaultFrameDurationNanoseconds );
458   }
459
460   // Inform core of context destruction & shutdown EGL
461   mCore.ContextDestroyed();
462   mRenderHelper.ShutdownEgl();
463
464   LOG_UPDATE_RENDER( "THREAD DESTROYED" );
465
466   // Uninstall the logging function
467   mEnvironmentOptions.UnInstallLogFunction();
468 }
469
470 bool CombinedUpdateRenderController::UpdateRenderReady( bool& useElapsedTime )
471 {
472   useElapsedTime = true;
473
474   ConditionalWait::ScopedLock updateLock( mUpdateRenderThreadWaitCondition );
475   while( ! mUpdateRenderRunCount && // Should try to wait if event-thread has paused the Update/Render thread
476          ! mDestroyUpdateRenderThread && // Ensure we don't wait if the update-render-thread is supposed to be destroyed
477          ! mNewSurface ) // Ensure we don't wait if we need to replace the surface
478   {
479     mUpdateRenderThreadWaitCondition.Wait( updateLock );
480
481     if( ! mUseElapsedTimeAfterWait )
482     {
483       useElapsedTime = false;
484     }
485   }
486
487   mUseElapsedTimeAfterWait = FALSE;
488
489   LOG_COUNTER_UPDATE_RENDER( "mUpdateRenderRunCount: %d", mUpdateRenderRunCount );
490
491   // If we've been asked to run Update/Render cycles a finite number of times then decrement so we wait after the
492   // requested number of cycles
493   if( mUpdateRenderRunCount > 0 )
494   {
495     --mUpdateRenderRunCount;
496   }
497
498   // Keep the update-render thread alive if this thread is NOT to be destroyed
499   return ! mDestroyUpdateRenderThread;
500 }
501
502 RenderSurface* CombinedUpdateRenderController::ShouldSurfaceBeReplaced()
503 {
504   ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
505
506   RenderSurface* newSurface = mNewSurface;
507   mNewSurface = NULL;
508
509   return newSurface;
510 }
511
512 void CombinedUpdateRenderController::SurfaceReplaced()
513 {
514   // Just increment the semaphore
515   sem_post( &mEventThreadSemaphore );
516 }
517
518 ///////////////////////////////////////////////////////////////////////////////////////////////////
519 // ALL THREADS
520 ///////////////////////////////////////////////////////////////////////////////////////////////////
521
522 void CombinedUpdateRenderController::NotifyThreadInitialised()
523 {
524   // Just increment the semaphore
525   sem_post( &mEventThreadSemaphore );
526 }
527
528 void CombinedUpdateRenderController::AddPerformanceMarker( PerformanceInterface::MarkerType type )
529 {
530   if( mPerformanceInterface )
531   {
532     mPerformanceInterface->AddMarker( type );
533   }
534 }
535
536 /////////////////////////////////////////////////////////////////////////////////////////////////
537 // POST RENDERING: EVENT THREAD
538 /////////////////////////////////////////////////////////////////////////////////////////////////
539
540 void CombinedUpdateRenderController::PostRenderComplete()
541 {
542   ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
543   mPostRendering = FALSE;
544   mUpdateRenderThreadWaitCondition.Notify( lock );
545 }
546
547 ///////////////////////////////////////////////////////////////////////////////////////////////////
548 // POST RENDERING: RENDER THREAD
549 ///////////////////////////////////////////////////////////////////////////////////////////////////
550
551 void CombinedUpdateRenderController::PostRenderStarted()
552 {
553   ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
554   mPostRendering = TRUE;
555 }
556
557 void CombinedUpdateRenderController::PostRenderWaitForCompletion()
558 {
559   ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
560   while( mPostRendering &&
561          ! mNewSurface ) // We should NOT wait if we're replacing the surface
562   {
563     mUpdateRenderThreadWaitCondition.Wait( lock );
564   }
565 }
566
567 } // namespace Adaptor
568
569 } // namespace Internal
570
571 } // namespace Dali