2 * Copyright (c) 2015 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 ),
109 mPostRendering( FALSE )
113 // Initialise frame delta/duration variables first
114 SetRenderRefreshRate( environmentOptions.GetRenderRefreshRate() );
116 // Set the thread-synchronization interface on the render-surface
117 RenderSurface* currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
120 currentSurface->SetThreadSynchronization( *this );
123 TriggerEventFactoryInterface& triggerFactory = mAdaptorInterfaces.GetTriggerEventFactoryInterface();
124 mSleepTrigger = triggerFactory.CreateTriggerEvent( MakeCallback( this, &CombinedUpdateRenderController::ProcessSleepRequest ), TriggerEventInterface::KEEP_ALIVE_AFTER_TRIGGER );
126 sem_init( &mEventThreadSemaphore, 0, 0 ); // Initialize to 0 so that it just waits if sem_post has not been called
129 CombinedUpdateRenderController::~CombinedUpdateRenderController()
135 delete mSleepTrigger;
138 void CombinedUpdateRenderController::Initialize()
142 // Ensure Update/Render Thread not already created
143 DALI_ASSERT_ALWAYS( ! mUpdateRenderThread );
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" );
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
154 void CombinedUpdateRenderController::Start()
158 DALI_ASSERT_ALWAYS( !mRunning && mUpdateRenderThread );
160 // Wait until all threads created in Initialise are up and running
161 for( unsigned int i = 0; i < CREATED_THREAD_COUNT; ++i )
163 sem_wait( &mEventThreadSemaphore );
166 mRenderHelper.Start();
170 LOG_EVENT( "Startup Complete, starting Update/Render Thread" );
172 RunUpdateRenderThread( CONTINUOUS, false /* No animation progression */ );
175 void CombinedUpdateRenderController::Pause()
181 PauseUpdateRenderThread();
183 AddPerformanceMarker( PerformanceInterface::PAUSED );
186 void CombinedUpdateRenderController::Resume()
190 if( !mRunning && IsUpdateRenderThreadPaused() )
192 LOG_EVENT( "Resuming" );
194 RunUpdateRenderThread( CONTINUOUS, true /* Animation progression required while we were paused */ );
196 AddPerformanceMarker( PerformanceInterface::RESUME );
202 void CombinedUpdateRenderController::Stop()
206 // Stop Rendering and the Update/Render Thread
207 mRenderHelper.Stop();
209 StopUpdateRenderThread();
211 if( mUpdateRenderThread )
213 LOG_EVENT( "Destroying UpdateRenderThread" );
215 // wait for the thread to finish
216 pthread_join( *mUpdateRenderThread, NULL );
218 delete mUpdateRenderThread;
219 mUpdateRenderThread = NULL;
225 void CombinedUpdateRenderController::RequestUpdate()
229 // Increment the update-request count to the maximum
230 if( mUpdateRequestCount < MAXIMUM_UPDATE_REQUESTS )
232 ++mUpdateRequestCount;
235 if( mRunning && IsUpdateRenderThreadPaused() )
237 LOG_EVENT( "Processing" );
239 RunUpdateRenderThread( CONTINUOUS, false /* No animation progression */ );
242 ConditionalWait::ScopedLock updateLock( mUpdateRenderThreadWaitCondition );
243 mPendingRequestUpdate = TRUE;
246 void CombinedUpdateRenderController::RequestUpdateOnce()
248 // Increment the update-request count to the maximum
249 if( mUpdateRequestCount < MAXIMUM_UPDATE_REQUESTS )
251 ++mUpdateRequestCount;
254 if( IsUpdateRenderThreadPaused() )
258 // Run Update/Render once
259 RunUpdateRenderThread( ONCE, false /* No animation progression */ );
263 void CombinedUpdateRenderController::ReplaceSurface( RenderSurface* newSurface )
267 // Set the ThreadSyncronizationInterface on the new surface
268 newSurface->SetThreadSynchronization( *this );
270 LOG_EVENT( "Starting to replace the surface, event-thread blocked" );
272 // Start replacing the surface.
274 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
275 mPostRendering = FALSE; // Clear the post-rendering flag as Update/Render thread will replace the surface now
276 mNewSurface = newSurface;
277 mUpdateRenderThreadWaitCondition.Notify( lock );
280 // Wait until the surface has been replaced
281 sem_wait( &mEventThreadSemaphore );
283 LOG_EVENT( "Surface replaced, event-thread continuing" );
286 void CombinedUpdateRenderController::SetRenderRefreshRate( unsigned int numberOfFramesPerRender )
288 // Not protected by lock, but written to rarely so not worth adding a lock when reading
289 mDefaultFrameDelta = numberOfFramesPerRender * DEFAULT_FRAME_DURATION_IN_SECONDS;
290 mDefaultFrameDurationMilliseconds = (uint64_t)numberOfFramesPerRender * DEFAULT_FRAME_DURATION_IN_MILLISECONDS;
291 mDefaultFrameDurationNanoseconds = (uint64_t)numberOfFramesPerRender * DEFAULT_FRAME_DURATION_IN_NANOSECONDS;
292 mDefaultHalfFrameNanoseconds = mDefaultFrameDurationNanoseconds / 2;
294 LOG_EVENT( "mDefaultFrameDelta(%.6f), mDefaultFrameDurationMilliseconds(%lld), mDefaultFrameDurationNanoseconds(%lld)", mDefaultFrameDelta, mDefaultFrameDurationMilliseconds, mDefaultFrameDurationNanoseconds );
297 ///////////////////////////////////////////////////////////////////////////////////////////////////
299 ///////////////////////////////////////////////////////////////////////////////////////////////////
301 void CombinedUpdateRenderController::RunUpdateRenderThread( int numberOfCycles, bool useElapsedTime )
303 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
304 mUpdateRenderRunCount = numberOfCycles;
305 mUpdateRenderThreadCanSleep = FALSE;
306 mUseElapsedTimeAfterWait = useElapsedTime;
307 LOG_COUNTER_EVENT( "mUpdateRenderRunCount: %d, mUseElapsedTimeAfterWait: %d", mUpdateRenderRunCount, mUseElapsedTimeAfterWait );
308 mUpdateRenderThreadWaitCondition.Notify( lock );
311 void CombinedUpdateRenderController::PauseUpdateRenderThread()
313 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
314 mUpdateRenderRunCount = 0;
317 void CombinedUpdateRenderController::StopUpdateRenderThread()
319 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
320 mDestroyUpdateRenderThread = TRUE;
321 mUpdateRenderThreadWaitCondition.Notify( lock );
324 bool CombinedUpdateRenderController::IsUpdateRenderThreadPaused()
326 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
327 return ( mUpdateRenderRunCount != CONTINUOUS ) || // Report paused if NOT continuously running
328 mUpdateRenderThreadCanSleep; // Report paused if sleeping
331 void CombinedUpdateRenderController::ProcessSleepRequest()
335 // Decrement Update request count
336 if( mUpdateRequestCount > 0 )
338 --mUpdateRequestCount;
341 // Can sleep if our update-request count is 0
342 // Update/Render thread can choose to carry on updating if it determines more update/renders are required
343 if( mUpdateRequestCount == 0 )
345 LOG_EVENT( "Going to sleep" );
347 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
348 mUpdateRenderThreadCanSleep = TRUE;
352 ///////////////////////////////////////////////////////////////////////////////////////////////////
353 // UPDATE/RENDER THREAD
354 ///////////////////////////////////////////////////////////////////////////////////////////////////
356 void CombinedUpdateRenderController::UpdateRenderThread()
358 // Install a function for logging
359 mEnvironmentOptions.InstallLogFunction();
361 LOG_UPDATE_RENDER( "THREAD CREATED" );
363 mRenderHelper.InitializeEgl();
365 // tell core it has a context
366 mCore.ContextCreated();
368 NotifyThreadInitialised();
371 uint64_t lastFrameTime;
372 TimeService::GetNanoseconds( lastFrameTime );
374 LOG_UPDATE_RENDER( "THREAD INITIALISED" );
376 bool useElapsedTime = true;
377 bool updateRequired = true;
379 while( UpdateRenderReady( useElapsedTime, updateRequired ) )
381 LOG_UPDATE_RENDER_TRACE;
383 // Performance statistics are logged upon a VSYNC tick so use this point for a VSync marker
384 AddPerformanceMarker( PerformanceInterface::VSYNC );
386 uint64_t currentFrameStartTime = 0;
387 TimeService::GetNanoseconds( currentFrameStartTime );
389 const uint64_t timeSinceLastFrame = currentFrameStartTime - lastFrameTime;
391 // Optional FPS Tracking when continuously rendering
392 if( useElapsedTime && mFpsTracker.Enabled() )
394 float absoluteTimeSinceLastRender = timeSinceLastFrame * NANOSECONDS_TO_SECOND;
395 mFpsTracker.Track( absoluteTimeSinceLastRender );
398 lastFrameTime = currentFrameStartTime; // Store frame start time
400 //////////////////////////////
402 //////////////////////////////
404 RenderSurface* newSurface = ShouldSurfaceBeReplaced();
405 if( DALI_UNLIKELY( newSurface ) )
407 LOG_UPDATE_RENDER_TRACE_FMT( "Replacing Surface" );
408 mRenderHelper.ReplaceSurface( newSurface );
412 //////////////////////////////
414 //////////////////////////////
416 const unsigned int currentTime = currentFrameStartTime / NANOSECONDS_PER_MILLISECOND;
417 const unsigned int nextFrameTime = currentTime + mDefaultFrameDurationMilliseconds;
419 uint64_t noOfFramesSinceLastUpdate = 1;
420 float frameDelta = 0.0f;
423 // If using the elapsed time, then calculate frameDelta as a multiple of mDefaultFrameDelta
424 // Round up if remainder is more than half the default frame time
425 noOfFramesSinceLastUpdate = ( timeSinceLastFrame + mDefaultHalfFrameNanoseconds) / mDefaultFrameDurationNanoseconds;
426 frameDelta = mDefaultFrameDelta * noOfFramesSinceLastUpdate;
428 LOG_UPDATE_RENDER( "timeSinceLastFrame(%llu) noOfFramesSinceLastUpdate(%u) frameDelta(%.6f)", timeSinceLastFrame, noOfFramesSinceLastUpdate, frameDelta );
430 Integration::UpdateStatus updateStatus;
432 AddPerformanceMarker( PerformanceInterface::UPDATE_START );
433 mCore.Update( frameDelta, currentTime, nextFrameTime, updateStatus );
434 AddPerformanceMarker( PerformanceInterface::UPDATE_END );
436 unsigned int keepUpdatingStatus = updateStatus.KeepUpdating();
438 // Tell the event-thread to wake up (if asleep) and send a notification event to Core if required
439 if( updateStatus.NeedsNotification() )
441 mNotificationTrigger.Trigger();
442 LOG_UPDATE_RENDER( "Notification Triggered" );
445 // Optional logging of update/render status
446 mUpdateStatusLogger.Log( keepUpdatingStatus );
448 //////////////////////////////
450 //////////////////////////////
452 mRenderHelper.ConsumeEvents();
453 mRenderHelper.PreRender();
455 Integration::RenderStatus renderStatus;
457 AddPerformanceMarker( PerformanceInterface::RENDER_START );
458 mCore.Render( renderStatus );
459 AddPerformanceMarker( PerformanceInterface::RENDER_END );
461 mRenderHelper.PostRender();
463 // Trigger event thread to request Update/Render thread to sleep if update not required
464 if( ( Integration::KeepUpdating::NOT_REQUESTED == keepUpdatingStatus ) &&
465 ! renderStatus.NeedsUpdate() )
467 mSleepTrigger->Trigger();
468 updateRequired = false;
469 LOG_UPDATE_RENDER( "Sleep Triggered" );
473 updateRequired = true;
476 //////////////////////////////
478 //////////////////////////////
480 // Sleep until at least the the default frame duration has elapsed. This will return immediately if the specified end-time has already passed.
481 TimeService::SleepUntil( currentFrameStartTime + mDefaultFrameDurationNanoseconds );
484 // Inform core of context destruction & shutdown EGL
485 mCore.ContextDestroyed();
486 mRenderHelper.ShutdownEgl();
488 LOG_UPDATE_RENDER( "THREAD DESTROYED" );
490 // Uninstall the logging function
491 mEnvironmentOptions.UnInstallLogFunction();
494 bool CombinedUpdateRenderController::UpdateRenderReady( bool& useElapsedTime, bool updateRequired )
496 useElapsedTime = true;
498 ConditionalWait::ScopedLock updateLock( mUpdateRenderThreadWaitCondition );
499 while( ( ! mUpdateRenderRunCount || // Should try to wait if event-thread has paused the Update/Render thread
500 ( mUpdateRenderThreadCanSleep && ! updateRequired && ! mPendingRequestUpdate ) ) && // Ensure we wait if we're supposed to be sleeping AND do not require another update
501 ! mDestroyUpdateRenderThread && // Ensure we don't wait if the update-render-thread is supposed to be destroyed
502 ! mNewSurface ) // Ensure we don't wait if we need to replace the surface
504 LOG_UPDATE_RENDER( "WAIT: mUpdateRenderRunCount: %d", mUpdateRenderRunCount );
505 LOG_UPDATE_RENDER( " mUpdateRenderThreadCanSleep: %d, updateRequired: %d, mPendingRequestUpdate: %d", mUpdateRenderThreadCanSleep, updateRequired, mPendingRequestUpdate );
506 LOG_UPDATE_RENDER( " mDestroyUpdateRenderThread: %d", mDestroyUpdateRenderThread );
507 LOG_UPDATE_RENDER( " mNewSurface: %d", mNewSurface );
509 mUpdateRenderThreadWaitCondition.Wait( updateLock );
511 if( ! mUseElapsedTimeAfterWait )
513 useElapsedTime = false;
517 LOG_COUNTER_UPDATE_RENDER( "mUpdateRenderRunCount: %d", mUpdateRenderRunCount );
518 LOG_COUNTER_UPDATE_RENDER( "mUpdateRenderThreadCanSleep: %d, updateRequired: %d, mPendingRequestUpdate: %d", mUpdateRenderThreadCanSleep, updateRequired, mPendingRequestUpdate );
519 LOG_COUNTER_UPDATE_RENDER( "mDestroyUpdateRenderThread: %d", mDestroyUpdateRenderThread );
520 LOG_COUNTER_UPDATE_RENDER( "mNewSurface: %d", mNewSurface );
522 mUseElapsedTimeAfterWait = FALSE;
523 mUpdateRenderThreadCanSleep = FALSE;
524 mPendingRequestUpdate = FALSE;
526 // If we've been asked to run Update/Render cycles a finite number of times then decrement so we wait after the
527 // requested number of cycles
528 if( mUpdateRenderRunCount > 0 )
530 --mUpdateRenderRunCount;
533 // Keep the update-render thread alive if this thread is NOT to be destroyed
534 return ! mDestroyUpdateRenderThread;
537 RenderSurface* CombinedUpdateRenderController::ShouldSurfaceBeReplaced()
539 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
541 RenderSurface* newSurface = mNewSurface;
547 void CombinedUpdateRenderController::SurfaceReplaced()
549 // Just increment the semaphore
550 sem_post( &mEventThreadSemaphore );
553 ///////////////////////////////////////////////////////////////////////////////////////////////////
555 ///////////////////////////////////////////////////////////////////////////////////////////////////
557 void CombinedUpdateRenderController::NotifyThreadInitialised()
559 // Just increment the semaphore
560 sem_post( &mEventThreadSemaphore );
563 void CombinedUpdateRenderController::AddPerformanceMarker( PerformanceInterface::MarkerType type )
565 if( mPerformanceInterface )
567 mPerformanceInterface->AddMarker( type );
571 /////////////////////////////////////////////////////////////////////////////////////////////////
572 // POST RENDERING: EVENT THREAD
573 /////////////////////////////////////////////////////////////////////////////////////////////////
575 void CombinedUpdateRenderController::PostRenderComplete()
577 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
578 mPostRendering = FALSE;
579 mUpdateRenderThreadWaitCondition.Notify( lock );
582 ///////////////////////////////////////////////////////////////////////////////////////////////////
583 // POST RENDERING: RENDER THREAD
584 ///////////////////////////////////////////////////////////////////////////////////////////////////
586 void CombinedUpdateRenderController::PostRenderStarted()
588 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
589 mPostRendering = TRUE;
592 void CombinedUpdateRenderController::PostRenderWaitForCompletion()
594 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
595 while( mPostRendering &&
596 ! mNewSurface && // We should NOT wait if we're replacing the surface
597 ! mDestroyUpdateRenderThread )
599 mUpdateRenderThreadWaitCondition.Wait( lock );
603 } // namespace Adaptor
605 } // namespace Internal