2 * Copyright (c) 2016 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 "combined-update-render-controller.h"
23 #include <dali/integration-api/platform-abstraction.h>
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>
43 const unsigned int CREATED_THREAD_COUNT = 1u;
45 const int CONTINUOUS = -1;
48 const unsigned int TRUE = 1u;
49 const unsigned int FALSE = 0u;
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 );
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 );
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).
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
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
78 const unsigned int MAXIMUM_UPDATE_REQUESTS = 2;
79 } // unnamed namespace
81 ///////////////////////////////////////////////////////////////////////////////////////////////////
83 ///////////////////////////////////////////////////////////////////////////////////////////////////
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 mDefaultHalfFrameNanoseconds( 0u ),
102 mUpdateRequestCount( 0u ),
104 mUpdateRenderRunCount( 0 ),
105 mDestroyUpdateRenderThread( FALSE ),
106 mUpdateRenderThreadCanSleep( FALSE ),
107 mPendingRequestUpdate( FALSE ),
108 mUseElapsedTimeAfterWait( FALSE ),
110 mPostRendering( FALSE )
114 // Initialise frame delta/duration variables first
115 SetRenderRefreshRate( environmentOptions.GetRenderRefreshRate() );
117 // Set the thread-synchronization interface on the render-surface
118 RenderSurface* currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
121 currentSurface->SetThreadSynchronization( *this );
124 TriggerEventFactoryInterface& triggerFactory = mAdaptorInterfaces.GetTriggerEventFactoryInterface();
125 mSleepTrigger = triggerFactory.CreateTriggerEvent( MakeCallback( this, &CombinedUpdateRenderController::ProcessSleepRequest ), TriggerEventInterface::KEEP_ALIVE_AFTER_TRIGGER );
127 sem_init( &mEventThreadSemaphore, 0, 0 ); // Initialize to 0 so that it just waits if sem_post has not been called
130 CombinedUpdateRenderController::~CombinedUpdateRenderController()
136 delete mSleepTrigger;
139 void CombinedUpdateRenderController::Initialize()
143 // Ensure Update/Render Thread not already created
144 DALI_ASSERT_ALWAYS( ! mUpdateRenderThread );
146 // Create Update/Render Thread
147 mUpdateRenderThread = new pthread_t();
148 int error = pthread_create( mUpdateRenderThread, NULL, InternalUpdateRenderThreadEntryFunc, this );
149 DALI_ASSERT_ALWAYS( !error && "Return code from pthread_create() when creating UpdateRenderThread" );
151 // The Update/Render thread will now run and initialise EGL etc. and will then wait for Start to be called
152 // When this function returns, the application initialisation on the event thread should occur
155 void CombinedUpdateRenderController::Start()
159 DALI_ASSERT_ALWAYS( !mRunning && mUpdateRenderThread );
161 // Wait until all threads created in Initialise are up and running
162 for( unsigned int i = 0; i < CREATED_THREAD_COUNT; ++i )
164 sem_wait( &mEventThreadSemaphore );
167 mRenderHelper.Start();
171 LOG_EVENT( "Startup Complete, starting Update/Render Thread" );
173 RunUpdateRenderThread( CONTINUOUS, false /* No animation progression */ );
176 void CombinedUpdateRenderController::Pause()
182 PauseUpdateRenderThread();
184 AddPerformanceMarker( PerformanceInterface::PAUSED );
187 void CombinedUpdateRenderController::Resume()
191 if( !mRunning && IsUpdateRenderThreadPaused() )
193 LOG_EVENT( "Resuming" );
195 RunUpdateRenderThread( CONTINUOUS, true /* Animation progression required while we were paused */ );
197 AddPerformanceMarker( PerformanceInterface::RESUME );
203 void CombinedUpdateRenderController::Stop()
207 // Stop Rendering and the Update/Render Thread
208 mRenderHelper.Stop();
210 StopUpdateRenderThread();
212 if( mUpdateRenderThread )
214 LOG_EVENT( "Destroying UpdateRenderThread" );
216 // wait for the thread to finish
217 pthread_join( *mUpdateRenderThread, NULL );
219 delete mUpdateRenderThread;
220 mUpdateRenderThread = NULL;
226 void CombinedUpdateRenderController::RequestUpdate()
230 // Increment the update-request count to the maximum
231 if( mUpdateRequestCount < MAXIMUM_UPDATE_REQUESTS )
233 ++mUpdateRequestCount;
236 if( mRunning && IsUpdateRenderThreadPaused() )
238 LOG_EVENT( "Processing" );
240 RunUpdateRenderThread( CONTINUOUS, false /* No animation progression */ );
243 ConditionalWait::ScopedLock updateLock( mUpdateRenderThreadWaitCondition );
244 mPendingRequestUpdate = TRUE;
247 void CombinedUpdateRenderController::RequestUpdateOnce()
249 if( IsUpdateRenderThreadPaused() )
253 // Run Update/Render once
254 RunUpdateRenderThread( ONCE, false /* No animation progression */ );
258 void CombinedUpdateRenderController::ReplaceSurface( RenderSurface* newSurface )
262 // Set the ThreadSyncronizationInterface on the new surface
263 newSurface->SetThreadSynchronization( *this );
265 LOG_EVENT( "Starting to replace the surface, event-thread blocked" );
267 // Start replacing the surface.
269 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
270 mPostRendering = FALSE; // Clear the post-rendering flag as Update/Render thread will replace the surface now
271 mNewSurface = newSurface;
272 mUpdateRenderThreadWaitCondition.Notify( lock );
275 // Wait until the surface has been replaced
276 sem_wait( &mEventThreadSemaphore );
278 LOG_EVENT( "Surface replaced, event-thread continuing" );
281 void CombinedUpdateRenderController::SetRenderRefreshRate( unsigned int numberOfFramesPerRender )
283 // Not protected by lock, but written to rarely so not worth adding a lock when reading
284 mDefaultFrameDelta = numberOfFramesPerRender * DEFAULT_FRAME_DURATION_IN_SECONDS;
285 mDefaultFrameDurationMilliseconds = (uint64_t)numberOfFramesPerRender * DEFAULT_FRAME_DURATION_IN_MILLISECONDS;
286 mDefaultFrameDurationNanoseconds = (uint64_t)numberOfFramesPerRender * DEFAULT_FRAME_DURATION_IN_NANOSECONDS;
287 mDefaultHalfFrameNanoseconds = mDefaultFrameDurationNanoseconds / 2;
289 LOG_EVENT( "mDefaultFrameDelta(%.6f), mDefaultFrameDurationMilliseconds(%lld), mDefaultFrameDurationNanoseconds(%lld)", mDefaultFrameDelta, mDefaultFrameDurationMilliseconds, mDefaultFrameDurationNanoseconds );
292 ///////////////////////////////////////////////////////////////////////////////////////////////////
294 ///////////////////////////////////////////////////////////////////////////////////////////////////
296 void CombinedUpdateRenderController::RunUpdateRenderThread( int numberOfCycles, bool useElapsedTime )
298 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
299 mUpdateRenderRunCount = numberOfCycles;
300 mUpdateRenderThreadCanSleep = FALSE;
301 mUseElapsedTimeAfterWait = useElapsedTime;
302 LOG_COUNTER_EVENT( "mUpdateRenderRunCount: %d, mUseElapsedTimeAfterWait: %d", mUpdateRenderRunCount, mUseElapsedTimeAfterWait );
303 mUpdateRenderThreadWaitCondition.Notify( lock );
306 void CombinedUpdateRenderController::PauseUpdateRenderThread()
308 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
309 mUpdateRenderRunCount = 0;
312 void CombinedUpdateRenderController::StopUpdateRenderThread()
314 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
315 mDestroyUpdateRenderThread = TRUE;
316 mUpdateRenderThreadWaitCondition.Notify( lock );
319 bool CombinedUpdateRenderController::IsUpdateRenderThreadPaused()
321 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
322 return ( mUpdateRenderRunCount != CONTINUOUS ) || // Report paused if NOT continuously running
323 mUpdateRenderThreadCanSleep; // Report paused if sleeping
326 void CombinedUpdateRenderController::ProcessSleepRequest()
330 // Decrement Update request count
331 if( mUpdateRequestCount > 0 )
333 --mUpdateRequestCount;
336 // Can sleep if our update-request count is 0
337 // Update/Render thread can choose to carry on updating if it determines more update/renders are required
338 if( mUpdateRequestCount == 0 )
340 LOG_EVENT( "Going to sleep" );
342 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
343 mUpdateRenderThreadCanSleep = TRUE;
347 ///////////////////////////////////////////////////////////////////////////////////////////////////
348 // UPDATE/RENDER THREAD
349 ///////////////////////////////////////////////////////////////////////////////////////////////////
351 void CombinedUpdateRenderController::UpdateRenderThread()
353 // Install a function for logging
354 mEnvironmentOptions.InstallLogFunction();
356 LOG_UPDATE_RENDER( "THREAD CREATED" );
358 mRenderHelper.InitializeEgl();
360 // tell core it has a context
361 mCore.ContextCreated();
363 NotifyThreadInitialised();
366 uint64_t lastFrameTime;
367 TimeService::GetNanoseconds( lastFrameTime );
369 LOG_UPDATE_RENDER( "THREAD INITIALISED" );
371 bool useElapsedTime = true;
372 bool updateRequired = true;
374 while( UpdateRenderReady( useElapsedTime, updateRequired ) )
376 LOG_UPDATE_RENDER_TRACE;
378 // Performance statistics are logged upon a VSYNC tick so use this point for a VSync marker
379 AddPerformanceMarker( PerformanceInterface::VSYNC );
381 uint64_t currentFrameStartTime = 0;
382 TimeService::GetNanoseconds( currentFrameStartTime );
384 const uint64_t timeSinceLastFrame = currentFrameStartTime - lastFrameTime;
386 // Optional FPS Tracking when continuously rendering
387 if( useElapsedTime && mFpsTracker.Enabled() )
389 float absoluteTimeSinceLastRender = timeSinceLastFrame * NANOSECONDS_TO_SECOND;
390 mFpsTracker.Track( absoluteTimeSinceLastRender );
393 lastFrameTime = currentFrameStartTime; // Store frame start time
395 //////////////////////////////
397 //////////////////////////////
399 RenderSurface* newSurface = ShouldSurfaceBeReplaced();
400 if( DALI_UNLIKELY( newSurface ) )
402 LOG_UPDATE_RENDER_TRACE_FMT( "Replacing Surface" );
403 mRenderHelper.ReplaceSurface( newSurface );
407 //////////////////////////////
409 //////////////////////////////
411 const unsigned int currentTime = currentFrameStartTime / NANOSECONDS_PER_MILLISECOND;
412 const unsigned int nextFrameTime = currentTime + mDefaultFrameDurationMilliseconds;
414 uint64_t noOfFramesSinceLastUpdate = 1;
415 float frameDelta = 0.0f;
418 // If using the elapsed time, then calculate frameDelta as a multiple of mDefaultFrameDelta
419 // Round up if remainder is more than half the default frame time
420 noOfFramesSinceLastUpdate = ( timeSinceLastFrame + mDefaultHalfFrameNanoseconds) / mDefaultFrameDurationNanoseconds;
421 frameDelta = mDefaultFrameDelta * noOfFramesSinceLastUpdate;
423 LOG_UPDATE_RENDER( "timeSinceLastFrame(%llu) noOfFramesSinceLastUpdate(%u) frameDelta(%.6f)", timeSinceLastFrame, noOfFramesSinceLastUpdate, frameDelta );
425 Integration::UpdateStatus updateStatus;
427 AddPerformanceMarker( PerformanceInterface::UPDATE_START );
428 mCore.Update( frameDelta, currentTime, nextFrameTime, updateStatus );
429 AddPerformanceMarker( PerformanceInterface::UPDATE_END );
431 unsigned int keepUpdatingStatus = updateStatus.KeepUpdating();
433 // Tell the event-thread to wake up (if asleep) and send a notification event to Core if required
434 if( updateStatus.NeedsNotification() )
436 mNotificationTrigger.Trigger();
437 LOG_UPDATE_RENDER( "Notification Triggered" );
440 // Optional logging of update/render status
441 mUpdateStatusLogger.Log( keepUpdatingStatus );
443 //////////////////////////////
445 //////////////////////////////
447 mRenderHelper.ConsumeEvents();
448 mRenderHelper.PreRender();
450 Integration::RenderStatus renderStatus;
452 AddPerformanceMarker( PerformanceInterface::RENDER_START );
453 mCore.Render( renderStatus );
454 AddPerformanceMarker( PerformanceInterface::RENDER_END );
456 mRenderHelper.PostRender();
458 // Trigger event thread to request Update/Render thread to sleep if update not required
459 if( ( Integration::KeepUpdating::NOT_REQUESTED == keepUpdatingStatus ) &&
460 ! renderStatus.NeedsUpdate() )
462 mSleepTrigger->Trigger();
463 updateRequired = false;
464 LOG_UPDATE_RENDER( "Sleep Triggered" );
468 updateRequired = true;
471 //////////////////////////////
473 //////////////////////////////
475 // Sleep until at least the the default frame duration has elapsed. This will return immediately if the specified end-time has already passed.
476 TimeService::SleepUntil( currentFrameStartTime + mDefaultFrameDurationNanoseconds );
479 // Inform core of context destruction & shutdown EGL
480 mCore.ContextDestroyed();
481 mRenderHelper.ShutdownEgl();
483 LOG_UPDATE_RENDER( "THREAD DESTROYED" );
485 // Uninstall the logging function
486 mEnvironmentOptions.UnInstallLogFunction();
489 bool CombinedUpdateRenderController::UpdateRenderReady( bool& useElapsedTime, bool updateRequired )
491 useElapsedTime = true;
493 ConditionalWait::ScopedLock updateLock( mUpdateRenderThreadWaitCondition );
494 while( ( ! mUpdateRenderRunCount || // Should try to wait if event-thread has paused the Update/Render thread
495 ( mUpdateRenderThreadCanSleep && ! updateRequired && ! mPendingRequestUpdate ) ) && // Ensure we wait if we're supposed to be sleeping AND do not require another update
496 ! mDestroyUpdateRenderThread && // Ensure we don't wait if the update-render-thread is supposed to be destroyed
497 ! mNewSurface ) // Ensure we don't wait if we need to replace the surface
499 LOG_UPDATE_RENDER( "WAIT: mUpdateRenderRunCount: %d", mUpdateRenderRunCount );
500 LOG_UPDATE_RENDER( " mUpdateRenderThreadCanSleep: %d, updateRequired: %d, mPendingRequestUpdate: %d", mUpdateRenderThreadCanSleep, updateRequired, mPendingRequestUpdate );
501 LOG_UPDATE_RENDER( " mDestroyUpdateRenderThread: %d", mDestroyUpdateRenderThread );
502 LOG_UPDATE_RENDER( " mNewSurface: %d", mNewSurface );
504 mUpdateRenderThreadWaitCondition.Wait( updateLock );
506 if( ! mUseElapsedTimeAfterWait )
508 useElapsedTime = false;
512 LOG_COUNTER_UPDATE_RENDER( "mUpdateRenderRunCount: %d", mUpdateRenderRunCount );
513 LOG_COUNTER_UPDATE_RENDER( "mUpdateRenderThreadCanSleep: %d, updateRequired: %d, mPendingRequestUpdate: %d", mUpdateRenderThreadCanSleep, updateRequired, mPendingRequestUpdate );
514 LOG_COUNTER_UPDATE_RENDER( "mDestroyUpdateRenderThread: %d", mDestroyUpdateRenderThread );
515 LOG_COUNTER_UPDATE_RENDER( "mNewSurface: %d", mNewSurface );
517 mUseElapsedTimeAfterWait = FALSE;
518 mUpdateRenderThreadCanSleep = FALSE;
519 mPendingRequestUpdate = FALSE;
521 // If we've been asked to run Update/Render cycles a finite number of times then decrement so we wait after the
522 // requested number of cycles
523 if( mUpdateRenderRunCount > 0 )
525 --mUpdateRenderRunCount;
528 // Keep the update-render thread alive if this thread is NOT to be destroyed
529 return ! mDestroyUpdateRenderThread;
532 RenderSurface* CombinedUpdateRenderController::ShouldSurfaceBeReplaced()
534 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
536 RenderSurface* newSurface = mNewSurface;
542 void CombinedUpdateRenderController::SurfaceReplaced()
544 // Just increment the semaphore
545 sem_post( &mEventThreadSemaphore );
548 ///////////////////////////////////////////////////////////////////////////////////////////////////
550 ///////////////////////////////////////////////////////////////////////////////////////////////////
552 void CombinedUpdateRenderController::NotifyThreadInitialised()
554 // Just increment the semaphore
555 sem_post( &mEventThreadSemaphore );
558 void CombinedUpdateRenderController::AddPerformanceMarker( PerformanceInterface::MarkerType type )
560 if( mPerformanceInterface )
562 mPerformanceInterface->AddMarker( type );
566 /////////////////////////////////////////////////////////////////////////////////////////////////
567 // POST RENDERING: EVENT THREAD
568 /////////////////////////////////////////////////////////////////////////////////////////////////
570 void CombinedUpdateRenderController::PostRenderComplete()
572 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
573 mPostRendering = FALSE;
574 mUpdateRenderThreadWaitCondition.Notify( lock );
577 ///////////////////////////////////////////////////////////////////////////////////////////////////
578 // POST RENDERING: RENDER THREAD
579 ///////////////////////////////////////////////////////////////////////////////////////////////////
581 void CombinedUpdateRenderController::PostRenderStarted()
583 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
584 mPostRendering = TRUE;
587 void CombinedUpdateRenderController::PostRenderWaitForCompletion()
589 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
590 while( mPostRendering &&
591 ! mNewSurface && // We should NOT wait if we're replacing the surface
592 ! mDestroyUpdateRenderThread )
594 mUpdateRenderThreadWaitCondition.Wait( lock );
598 } // namespace Adaptor
600 } // namespace Internal