2 * Copyright (c) 2017 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 uint64_t DEFAULT_FRAME_DURATION_IN_MILLISECONDS( DEFAULT_FRAME_DURATION_IN_SECONDS * MILLISECONDS_PER_SECOND );
59 const uint64_t 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 // Increment the update-request count to the maximum
250 if( mUpdateRequestCount < MAXIMUM_UPDATE_REQUESTS )
252 ++mUpdateRequestCount;
255 if( IsUpdateRenderThreadPaused() )
259 // Run Update/Render once
260 RunUpdateRenderThread( ONCE, false /* No animation progression */ );
264 void CombinedUpdateRenderController::ReplaceSurface( RenderSurface* newSurface )
268 // Set the ThreadSyncronizationInterface on the new surface
269 newSurface->SetThreadSynchronization( *this );
271 LOG_EVENT( "Starting to replace the surface, event-thread blocked" );
273 // Start replacing the surface.
275 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
276 mPostRendering = FALSE; // Clear the post-rendering flag as Update/Render thread will replace the surface now
277 mNewSurface = newSurface;
278 mUpdateRenderThreadWaitCondition.Notify( lock );
281 // Wait until the surface has been replaced
282 sem_wait( &mEventThreadSemaphore );
284 LOG_EVENT( "Surface replaced, event-thread continuing" );
287 void CombinedUpdateRenderController::SetRenderRefreshRate( unsigned int numberOfFramesPerRender )
289 // Not protected by lock, but written to rarely so not worth adding a lock when reading
290 mDefaultFrameDelta = numberOfFramesPerRender * DEFAULT_FRAME_DURATION_IN_SECONDS;
291 mDefaultFrameDurationMilliseconds = uint64_t( numberOfFramesPerRender ) * DEFAULT_FRAME_DURATION_IN_MILLISECONDS;
292 mDefaultFrameDurationNanoseconds = uint64_t( numberOfFramesPerRender ) * DEFAULT_FRAME_DURATION_IN_NANOSECONDS;
293 mDefaultHalfFrameNanoseconds = mDefaultFrameDurationNanoseconds / 2u;
295 LOG_EVENT( "mDefaultFrameDelta(%.6f), mDefaultFrameDurationMilliseconds(%lld), mDefaultFrameDurationNanoseconds(%lld)", mDefaultFrameDelta, mDefaultFrameDurationMilliseconds, mDefaultFrameDurationNanoseconds );
298 ///////////////////////////////////////////////////////////////////////////////////////////////////
300 ///////////////////////////////////////////////////////////////////////////////////////////////////
302 void CombinedUpdateRenderController::RunUpdateRenderThread( int numberOfCycles, bool useElapsedTime )
304 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
305 mUpdateRenderRunCount = numberOfCycles;
306 mUpdateRenderThreadCanSleep = FALSE;
307 mUseElapsedTimeAfterWait = useElapsedTime;
308 LOG_COUNTER_EVENT( "mUpdateRenderRunCount: %d, mUseElapsedTimeAfterWait: %d", mUpdateRenderRunCount, mUseElapsedTimeAfterWait );
309 mUpdateRenderThreadWaitCondition.Notify( lock );
312 void CombinedUpdateRenderController::PauseUpdateRenderThread()
314 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
315 mUpdateRenderRunCount = 0;
318 void CombinedUpdateRenderController::StopUpdateRenderThread()
320 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
321 mDestroyUpdateRenderThread = TRUE;
322 mUpdateRenderThreadWaitCondition.Notify( lock );
325 bool CombinedUpdateRenderController::IsUpdateRenderThreadPaused()
327 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
328 return ( mUpdateRenderRunCount != CONTINUOUS ) || // Report paused if NOT continuously running
329 mUpdateRenderThreadCanSleep; // Report paused if sleeping
332 void CombinedUpdateRenderController::ProcessSleepRequest()
336 // Decrement Update request count
337 if( mUpdateRequestCount > 0 )
339 --mUpdateRequestCount;
342 // Can sleep if our update-request count is 0
343 // Update/Render thread can choose to carry on updating if it determines more update/renders are required
344 if( mUpdateRequestCount == 0 )
346 LOG_EVENT( "Going to sleep" );
348 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
349 mUpdateRenderThreadCanSleep = TRUE;
353 ///////////////////////////////////////////////////////////////////////////////////////////////////
354 // UPDATE/RENDER THREAD
355 ///////////////////////////////////////////////////////////////////////////////////////////////////
357 void CombinedUpdateRenderController::UpdateRenderThread()
359 // Install a function for logging
360 mEnvironmentOptions.InstallLogFunction();
362 LOG_UPDATE_RENDER( "THREAD CREATED" );
364 mRenderHelper.InitializeEgl();
366 // tell core it has a context
367 mCore.ContextCreated();
369 NotifyThreadInitialised();
372 uint64_t lastFrameTime;
373 TimeService::GetNanoseconds( lastFrameTime );
375 LOG_UPDATE_RENDER( "THREAD INITIALISED" );
377 bool useElapsedTime = true;
378 bool updateRequired = true;
380 while( UpdateRenderReady( useElapsedTime, updateRequired ) )
382 LOG_UPDATE_RENDER_TRACE;
384 // Performance statistics are logged upon a VSYNC tick so use this point for a VSync marker
385 AddPerformanceMarker( PerformanceInterface::VSYNC );
387 uint64_t currentFrameStartTime = 0;
388 TimeService::GetNanoseconds( currentFrameStartTime );
390 const uint64_t timeSinceLastFrame = currentFrameStartTime - lastFrameTime;
392 // Optional FPS Tracking when continuously rendering
393 if( useElapsedTime && mFpsTracker.Enabled() )
395 float absoluteTimeSinceLastRender = timeSinceLastFrame * NANOSECONDS_TO_SECOND;
396 mFpsTracker.Track( absoluteTimeSinceLastRender );
399 lastFrameTime = currentFrameStartTime; // Store frame start time
401 //////////////////////////////
403 //////////////////////////////
405 RenderSurface* newSurface = ShouldSurfaceBeReplaced();
406 if( DALI_UNLIKELY( newSurface ) )
408 LOG_UPDATE_RENDER_TRACE_FMT( "Replacing Surface" );
409 mRenderHelper.ReplaceSurface( newSurface );
413 //////////////////////////////
415 //////////////////////////////
417 const unsigned int currentTime = currentFrameStartTime / NANOSECONDS_PER_MILLISECOND;
418 const unsigned int nextFrameTime = currentTime + mDefaultFrameDurationMilliseconds;
420 uint64_t noOfFramesSinceLastUpdate = 1;
421 float frameDelta = 0.0f;
424 // If using the elapsed time, then calculate frameDelta as a multiple of mDefaultFrameDelta
425 // Round up if remainder is more than half the default frame time
426 noOfFramesSinceLastUpdate = ( timeSinceLastFrame + mDefaultHalfFrameNanoseconds) / mDefaultFrameDurationNanoseconds;
427 frameDelta = mDefaultFrameDelta * noOfFramesSinceLastUpdate;
429 LOG_UPDATE_RENDER( "timeSinceLastFrame(%llu) noOfFramesSinceLastUpdate(%u) frameDelta(%.6f)", timeSinceLastFrame, noOfFramesSinceLastUpdate, frameDelta );
431 Integration::UpdateStatus updateStatus;
433 AddPerformanceMarker( PerformanceInterface::UPDATE_START );
434 mCore.Update( frameDelta, currentTime, nextFrameTime, updateStatus );
435 AddPerformanceMarker( PerformanceInterface::UPDATE_END );
437 unsigned int keepUpdatingStatus = updateStatus.KeepUpdating();
439 // Tell the event-thread to wake up (if asleep) and send a notification event to Core if required
440 if( updateStatus.NeedsNotification() )
442 mNotificationTrigger.Trigger();
443 LOG_UPDATE_RENDER( "Notification Triggered" );
446 // Optional logging of update/render status
447 mUpdateStatusLogger.Log( keepUpdatingStatus );
449 //////////////////////////////
451 //////////////////////////////
453 mRenderHelper.ConsumeEvents();
454 mRenderHelper.PreRender();
456 Integration::RenderStatus renderStatus;
458 AddPerformanceMarker( PerformanceInterface::RENDER_START );
459 mCore.Render( renderStatus );
460 AddPerformanceMarker( PerformanceInterface::RENDER_END );
462 mRenderHelper.PostRender();
464 // Trigger event thread to request Update/Render thread to sleep if update not required
465 if( ( Integration::KeepUpdating::NOT_REQUESTED == keepUpdatingStatus ) &&
466 ! renderStatus.NeedsUpdate() )
468 mSleepTrigger->Trigger();
469 updateRequired = false;
470 LOG_UPDATE_RENDER( "Sleep Triggered" );
474 updateRequired = true;
477 //////////////////////////////
479 //////////////////////////////
481 // Sleep until at least the the default frame duration has elapsed. This will return immediately if the specified end-time has already passed.
482 TimeService::SleepUntil( currentFrameStartTime + mDefaultFrameDurationNanoseconds );
485 // Inform core of context destruction & shutdown EGL
486 mCore.ContextDestroyed();
487 mRenderHelper.ShutdownEgl();
489 LOG_UPDATE_RENDER( "THREAD DESTROYED" );
491 // Uninstall the logging function
492 mEnvironmentOptions.UnInstallLogFunction();
495 bool CombinedUpdateRenderController::UpdateRenderReady( bool& useElapsedTime, bool updateRequired )
497 useElapsedTime = true;
499 ConditionalWait::ScopedLock updateLock( mUpdateRenderThreadWaitCondition );
500 while( ( ! mUpdateRenderRunCount || // Should try to wait if event-thread has paused the Update/Render thread
501 ( mUpdateRenderThreadCanSleep && ! updateRequired && ! mPendingRequestUpdate ) ) && // Ensure we wait if we're supposed to be sleeping AND do not require another update
502 ! mDestroyUpdateRenderThread && // Ensure we don't wait if the update-render-thread is supposed to be destroyed
503 ! mNewSurface ) // Ensure we don't wait if we need to replace the surface
505 LOG_UPDATE_RENDER( "WAIT: mUpdateRenderRunCount: %d", mUpdateRenderRunCount );
506 LOG_UPDATE_RENDER( " mUpdateRenderThreadCanSleep: %d, updateRequired: %d, mPendingRequestUpdate: %d", mUpdateRenderThreadCanSleep, updateRequired, mPendingRequestUpdate );
507 LOG_UPDATE_RENDER( " mDestroyUpdateRenderThread: %d", mDestroyUpdateRenderThread );
508 LOG_UPDATE_RENDER( " mNewSurface: %d", mNewSurface );
510 mUpdateRenderThreadWaitCondition.Wait( updateLock );
512 if( ! mUseElapsedTimeAfterWait )
514 useElapsedTime = false;
518 LOG_COUNTER_UPDATE_RENDER( "mUpdateRenderRunCount: %d", mUpdateRenderRunCount );
519 LOG_COUNTER_UPDATE_RENDER( "mUpdateRenderThreadCanSleep: %d, updateRequired: %d, mPendingRequestUpdate: %d", mUpdateRenderThreadCanSleep, updateRequired, mPendingRequestUpdate );
520 LOG_COUNTER_UPDATE_RENDER( "mDestroyUpdateRenderThread: %d", mDestroyUpdateRenderThread );
521 LOG_COUNTER_UPDATE_RENDER( "mNewSurface: %d", mNewSurface );
523 mUseElapsedTimeAfterWait = FALSE;
524 mUpdateRenderThreadCanSleep = FALSE;
525 mPendingRequestUpdate = FALSE;
527 // If we've been asked to run Update/Render cycles a finite number of times then decrement so we wait after the
528 // requested number of cycles
529 if( mUpdateRenderRunCount > 0 )
531 --mUpdateRenderRunCount;
534 // Keep the update-render thread alive if this thread is NOT to be destroyed
535 return ! mDestroyUpdateRenderThread;
538 RenderSurface* CombinedUpdateRenderController::ShouldSurfaceBeReplaced()
540 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
542 RenderSurface* newSurface = mNewSurface;
548 void CombinedUpdateRenderController::SurfaceReplaced()
550 // Just increment the semaphore
551 sem_post( &mEventThreadSemaphore );
554 ///////////////////////////////////////////////////////////////////////////////////////////////////
556 ///////////////////////////////////////////////////////////////////////////////////////////////////
558 void CombinedUpdateRenderController::NotifyThreadInitialised()
560 // Just increment the semaphore
561 sem_post( &mEventThreadSemaphore );
564 void CombinedUpdateRenderController::AddPerformanceMarker( PerformanceInterface::MarkerType type )
566 if( mPerformanceInterface )
568 mPerformanceInterface->AddMarker( type );
572 /////////////////////////////////////////////////////////////////////////////////////////////////
573 // POST RENDERING: EVENT THREAD
574 /////////////////////////////////////////////////////////////////////////////////////////////////
576 void CombinedUpdateRenderController::PostRenderComplete()
578 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
579 mPostRendering = FALSE;
580 mUpdateRenderThreadWaitCondition.Notify( lock );
583 ///////////////////////////////////////////////////////////////////////////////////////////////////
584 // POST RENDERING: RENDER THREAD
585 ///////////////////////////////////////////////////////////////////////////////////////////////////
587 void CombinedUpdateRenderController::PostRenderStarted()
589 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
590 mPostRendering = TRUE;
593 void CombinedUpdateRenderController::PostRenderWaitForCompletion()
595 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
596 while( mPostRendering &&
597 ! mNewSurface && // We should NOT wait if we're replacing the surface
598 ! mDestroyUpdateRenderThread )
600 mUpdateRenderThreadWaitCondition.Wait( lock );
604 } // namespace Adaptor
606 } // namespace Internal