Dali 3D User Interface Engine
combined-update-render-controller.cpp
Go to the documentation of this file.
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
20 
21 // EXTERNAL INCLUDES
22 #include <errno.h>
24 
25 // INTERNAL INCLUDES
26 #include <trigger-event-factory.h>
29 #include <base/time-service.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 );
60 
78 const unsigned int MAXIMUM_UPDATE_REQUESTS = 2;
79 } // unnamed namespace
80 
82 // EVENT THREAD
84 
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  mDefaultHalfFrameNanoseconds( 0u ),
102  mUpdateRequestCount( 0u ),
103  mRunning( FALSE ),
104  mUpdateRenderRunCount( 0 ),
105  mDestroyUpdateRenderThread( FALSE ),
106  mUpdateRenderThreadCanSleep( FALSE ),
107  mPendingRequestUpdate( FALSE ),
108  mNewSurface( NULL ),
109  mPostRendering( FALSE )
110 {
112 
113  // Initialise frame delta/duration variables first
114  SetRenderRefreshRate( environmentOptions.GetRenderRefreshRate() );
115 
116  // Set the thread-synchronization interface on the render-surface
117  RenderSurface* currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
118  if( currentSurface )
119  {
120  currentSurface->SetThreadSynchronization( *this );
121  }
122 
123  TriggerEventFactoryInterface& triggerFactory = mAdaptorInterfaces.GetTriggerEventFactoryInterface();
124  mSleepTrigger = triggerFactory.CreateTriggerEvent( MakeCallback( this, &CombinedUpdateRenderController::ProcessSleepRequest ), TriggerEventInterface::KEEP_ALIVE_AFTER_TRIGGER );
125 
126  sem_init( &mEventThreadSemaphore, 0, 0 ); // Initialize to 0 so that it just waits if sem_post has not been called
127 }
128 
130 {
132 
133  Stop();
134 
135  delete mSleepTrigger;
136 }
137 
139 {
141 
142  // Ensure Update/Render Thread not already created
144 
145  // Create Update/Render Thread
146  mUpdateRenderThread = new pthread_t();
147  int error = pthread_create( mUpdateRenderThread, NULL, InternalUpdateRenderThreadEntryFunc, this );
148  DALI_ASSERT_ALWAYS( !error && "Return code from pthread_create() when creating UpdateRenderThread" );
149 
150  // The Update/Render thread will now run and initialise EGL etc. and will then wait for Start to be called
151  // When this function returns, the application initialisation on the event thread should occur
152 }
153 
155 {
157 
159 
160  // Wait until all threads created in Initialise are up and running
161  for( unsigned int i = 0; i < CREATED_THREAD_COUNT; ++i )
162  {
163  sem_wait( &mEventThreadSemaphore );
164  }
165 
167 
168  mRunning = TRUE;
169 
170  LOG_EVENT( "Startup Complete, starting Update/Render Thread" );
171 
172  RunUpdateRenderThread( CONTINUOUS, false /* No animation progression */ );
173 }
174 
176 {
178 
179  mRunning = FALSE;
180 
182 
184 }
185 
187 {
189 
191  {
192  LOG_EVENT( "Resuming" );
193 
194  RunUpdateRenderThread( CONTINUOUS, true /* Animation progression required while we were paused */ );
195 
197 
198  mRunning = TRUE;
199  }
200 }
201 
203 {
205 
206  // Stop Rendering and the Update/Render Thread
208 
210 
211  if( mUpdateRenderThread )
212  {
213  LOG_EVENT( "Destroying UpdateRenderThread" );
214 
215  // wait for the thread to finish
216  pthread_join( *mUpdateRenderThread, NULL );
217 
218  delete mUpdateRenderThread;
219  mUpdateRenderThread = NULL;
220  }
221 
222  mRunning = FALSE;
223 }
224 
226 {
228 
229  // Increment the update-request count to the maximum
231  {
233  }
234 
236  {
237  LOG_EVENT( "Processing" );
238 
239  RunUpdateRenderThread( CONTINUOUS, false /* No animation progression */ );
240  }
241 
244 }
245 
247 {
249  {
251 
252  // Run Update/Render once
253  RunUpdateRenderThread( ONCE, false /* No animation progression */ );
254  }
255 }
256 
257 void CombinedUpdateRenderController::ReplaceSurface( RenderSurface* newSurface )
258 {
260 
261  // Set the ThreadSyncronizationInterface on the new surface
262  newSurface->SetThreadSynchronization( *this );
263 
264  LOG_EVENT( "Starting to replace the surface, event-thread blocked" );
265 
266  // Start replacing the surface.
267  {
269  mPostRendering = FALSE; // Clear the post-rendering flag as Update/Render thread will replace the surface now
270  mNewSurface = newSurface;
272  }
273 
274  // Wait until the surface has been replaced
275  sem_wait( &mEventThreadSemaphore );
276 
277  LOG_EVENT( "Surface replaced, event-thread continuing" );
278 }
279 
280 void CombinedUpdateRenderController::SetRenderRefreshRate( unsigned int numberOfFramesPerRender )
281 {
282  // Not protected by lock, but written to rarely so not worth adding a lock when reading
283  mDefaultFrameDelta = numberOfFramesPerRender * DEFAULT_FRAME_DURATION_IN_SECONDS;
287 
288  LOG_EVENT( "mDefaultFrameDelta(%.6f), mDefaultFrameDurationMilliseconds(%lld), mDefaultFrameDurationNanoseconds(%lld)", mDefaultFrameDelta, mDefaultFrameDurationMilliseconds, mDefaultFrameDurationNanoseconds );
289 }
290 
292 // EVENT THREAD
294 
295 void CombinedUpdateRenderController::RunUpdateRenderThread( int numberOfCycles, bool useElapsedTime )
296 {
298  mUpdateRenderRunCount = numberOfCycles;
300  mUseElapsedTimeAfterWait = useElapsedTime;
301  LOG_COUNTER_EVENT( "mUpdateRenderRunCount: %d, mUseElapsedTimeAfterWait: %d", mUpdateRenderRunCount, mUseElapsedTimeAfterWait );
303 }
304 
306 {
309 }
310 
312 {
316 }
317 
319 {
321  return ( mUpdateRenderRunCount != CONTINUOUS ) || // Report paused if NOT continuously running
322  mUpdateRenderThreadCanSleep; // Report paused if sleeping
323 }
324 
326 {
328 
329  // Decrement Update request count
330  if( mUpdateRequestCount > 0 )
331  {
333  }
334 
335  // Can sleep if our update-request count is 0
336  // Update/Render thread can choose to carry on updating if it determines more update/renders are required
337  if( mUpdateRequestCount == 0 )
338  {
339  LOG_EVENT( "Going to sleep" );
340 
343  }
344 }
345 
347 // UPDATE/RENDER THREAD
349 
351 {
352  // Install a function for logging
354 
355  LOG_UPDATE_RENDER( "THREAD CREATED" );
356 
358 
359  // tell core it has a context
361 
363 
364  // Update time
365  uint64_t lastFrameTime;
366  TimeService::GetNanoseconds( lastFrameTime );
367 
368  LOG_UPDATE_RENDER( "THREAD INITIALISED" );
369 
370  bool useElapsedTime = true;
371  bool updateRequired = true;
372 
373  while( UpdateRenderReady( useElapsedTime, updateRequired ) )
374  {
376 
377  uint64_t currentFrameStartTime = 0;
378  TimeService::GetNanoseconds( currentFrameStartTime );
379 
380  const uint64_t timeSinceLastFrame = currentFrameStartTime - lastFrameTime;
381 
382  // Optional FPS Tracking when continuously rendering
383  if( useElapsedTime && mFpsTracker.Enabled() )
384  {
385  float absoluteTimeSinceLastRender = timeSinceLastFrame * NANOSECONDS_TO_SECOND;
386  mFpsTracker.Track( absoluteTimeSinceLastRender );
387  }
388 
389  lastFrameTime = currentFrameStartTime; // Store frame start time
390 
392  // REPLACE SURFACE
394 
395  RenderSurface* newSurface = ShouldSurfaceBeReplaced();
396  if( DALI_UNLIKELY( newSurface ) )
397  {
398  LOG_UPDATE_RENDER_TRACE_FMT( "Replacing Surface" );
399  mRenderHelper.ReplaceSurface( newSurface );
400  SurfaceReplaced();
401  }
402 
404  // UPDATE
406 
407  const unsigned int currentTime = currentFrameStartTime / NANOSECONDS_PER_MILLISECOND;
408  const unsigned int nextFrameTime = currentTime + mDefaultFrameDurationMilliseconds;
409 
410  uint64_t noOfFramesSinceLastUpdate = 1;
411  float frameDelta = 0.0f;
412  if( useElapsedTime )
413  {
414  // If using the elapsed time, then calculate frameDelta as a multiple of mDefaultFrameDelta
415  // Round up if remainder is more than half the default frame time
416  noOfFramesSinceLastUpdate = ( timeSinceLastFrame + mDefaultHalfFrameNanoseconds) / mDefaultFrameDurationNanoseconds;
417  frameDelta = mDefaultFrameDelta * noOfFramesSinceLastUpdate;
418  }
419  LOG_UPDATE_RENDER( "timeSinceLastFrame(%llu) noOfFramesSinceLastUpdate(%u) frameDelta(%.6f)", timeSinceLastFrame, noOfFramesSinceLastUpdate, frameDelta );
420 
421  Integration::UpdateStatus updateStatus;
422 
424  mCore.Update( frameDelta, currentTime, nextFrameTime, updateStatus );
426 
427  unsigned int keepUpdatingStatus = updateStatus.KeepUpdating();
428 
429  // Tell the event-thread to wake up (if asleep) and send a notification event to Core if required
430  if( updateStatus.NeedsNotification() )
431  {
432  mNotificationTrigger.Trigger();
433  LOG_UPDATE_RENDER( "Notification Triggered" );
434  }
435 
436  // Optional logging of update/render status
437  mUpdateStatusLogger.Log( keepUpdatingStatus );
438 
440  // RENDER
442 
445 
446  Integration::RenderStatus renderStatus;
447 
449  mCore.Render( renderStatus );
451 
452  if( renderStatus.HasRendered() )
453  {
455  }
456 
457  // Trigger event thread to request Update/Render thread to sleep if update not required
458  if( ( Integration::KeepUpdating::NOT_REQUESTED == keepUpdatingStatus ) &&
459  ! renderStatus.NeedsUpdate() )
460  {
461  mSleepTrigger->Trigger();
462  updateRequired = false;
463  LOG_UPDATE_RENDER( "Sleep Triggered" );
464  }
465  else
466  {
467  updateRequired = true;
468  }
469 
471  // FRAME TIME
473 
474  // Sleep until at least the the default frame duration has elapsed. This will return immediately if the specified end-time has already passed.
476  }
477 
478  // Inform core of context destruction & shutdown EGL
481 
482  LOG_UPDATE_RENDER( "THREAD DESTROYED" );
483 
484  // Uninstall the logging function
486 }
487 
488 bool CombinedUpdateRenderController::UpdateRenderReady( bool& useElapsedTime, bool updateRequired )
489 {
490  useElapsedTime = true;
491 
493  while( ( ! mUpdateRenderRunCount || // Should try to wait if event-thread has paused the Update/Render thread
494  ( mUpdateRenderThreadCanSleep && ! updateRequired && ! mPendingRequestUpdate ) ) && // Ensure we wait if we're supposed to be sleeping AND do not require another update
495  ! mDestroyUpdateRenderThread && // Ensure we don't wait if the update-render-thread is supposed to be destroyed
496  ! mNewSurface ) // Ensure we don't wait if we need to replace the surface
497  {
498  LOG_UPDATE_RENDER( "WAIT: mUpdateRenderRunCount: %d", mUpdateRenderRunCount );
499  LOG_UPDATE_RENDER( " mUpdateRenderThreadCanSleep: %d, updateRequired: %d, mPendingRequestUpdate: %d", mUpdateRenderThreadCanSleep, updateRequired, mPendingRequestUpdate );
500  LOG_UPDATE_RENDER( " mDestroyUpdateRenderThread: %d", mDestroyUpdateRenderThread );
501  LOG_UPDATE_RENDER( " mNewSurface: %d", mNewSurface );
502 
504 
506  {
507  useElapsedTime = false;
508  }
509  }
510 
511  LOG_COUNTER_UPDATE_RENDER( "mUpdateRenderRunCount: %d", mUpdateRenderRunCount );
512  LOG_COUNTER_UPDATE_RENDER( "mUpdateRenderThreadCanSleep: %d, updateRequired: %d, mPendingRequestUpdate: %d", mUpdateRenderThreadCanSleep, updateRequired, mPendingRequestUpdate );
513  LOG_COUNTER_UPDATE_RENDER( "mDestroyUpdateRenderThread: %d", mDestroyUpdateRenderThread );
514  LOG_COUNTER_UPDATE_RENDER( "mNewSurface: %d", mNewSurface );
515 
519 
520  // If we've been asked to run Update/Render cycles a finite number of times then decrement so we wait after the
521  // requested number of cycles
522  if( mUpdateRenderRunCount > 0 )
523  {
525  }
526 
527  // Keep the update-render thread alive if this thread is NOT to be destroyed
529 }
530 
532 {
534 
535  RenderSurface* newSurface = mNewSurface;
536  mNewSurface = NULL;
537 
538  return newSurface;
539 }
540 
542 {
543  // Just increment the semaphore
544  sem_post( &mEventThreadSemaphore );
545 }
546 
548 // ALL THREADS
550 
552 {
553  // Just increment the semaphore
554  sem_post( &mEventThreadSemaphore );
555 }
556 
558 {
560  {
562  }
563 }
564 
566 // POST RENDERING: EVENT THREAD
568 
570 {
574 }
575 
577 // POST RENDERING: RENDER THREAD
579 
581 {
584 }
585 
587 {
589  while( mPostRendering &&
590  ! mNewSurface ) // We should NOT wait if we're replacing the surface
591  {
593  }
594 }
595 
596 } // namespace Adaptor
597 
598 } // namespace Internal
599 
600 } // namespace Dali
Dali Docs Home
Read more about Dali