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 mUpdateRequestCount( 0u ),
103 mUpdateRenderRunCount( 0 ),
104 mDestroyUpdateRenderThread( FALSE ),
106 mPostRendering( FALSE )
110 // Initialise frame delta/duration variables first
111 SetRenderRefreshRate( environmentOptions.GetRenderRefreshRate() );
113 // Set the thread-synchronization interface on the render-surface
114 RenderSurface* currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
117 currentSurface->SetThreadSynchronization( *this );
120 TriggerEventFactoryInterface& triggerFactory = mAdaptorInterfaces.GetTriggerEventFactoryInterface();
121 mSleepTrigger = triggerFactory.CreateTriggerEvent( MakeCallback( this, &CombinedUpdateRenderController::ProcessSleepRequest ), TriggerEventInterface::KEEP_ALIVE_AFTER_TRIGGER );
123 sem_init( &mEventThreadSemaphore, 0, 0 ); // Initialize to 0 so that it just waits if sem_post has not been called
126 CombinedUpdateRenderController::~CombinedUpdateRenderController()
132 delete mSleepTrigger;
135 void CombinedUpdateRenderController::Initialize()
139 // Ensure Update/Render Thread not already created
140 DALI_ASSERT_ALWAYS( ! mUpdateRenderThread );
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" );
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
151 void CombinedUpdateRenderController::Start()
155 DALI_ASSERT_ALWAYS( !mRunning && mUpdateRenderThread );
157 // Wait until all threads created in Initialise are up and running
158 for( unsigned int i = 0; i < CREATED_THREAD_COUNT; ++i )
160 sem_wait( &mEventThreadSemaphore );
163 mRenderHelper.Start();
167 LOG_EVENT( "Startup Complete, starting Update/Render Thread" );
169 RunUpdateRenderThread( CONTINUOUS, false /* No animation progression */ );
172 void CombinedUpdateRenderController::Pause()
178 PauseUpdateRenderThread();
180 AddPerformanceMarker( PerformanceInterface::PAUSED );
183 void CombinedUpdateRenderController::Resume()
187 if( !mRunning && IsUpdateRenderThreadPaused() )
189 LOG_EVENT( "Resuming" );
191 RunUpdateRenderThread( CONTINUOUS, true /* Animation progression required while we were paused */ );
193 AddPerformanceMarker( PerformanceInterface::RESUME );
199 void CombinedUpdateRenderController::Stop()
203 // Stop Rendering and the Update/Render Thread
204 mRenderHelper.Stop();
206 StopUpdateRenderThread();
208 if( mUpdateRenderThread )
210 LOG_EVENT( "Destroying UpdateRenderThread" );
212 // wait for the thread to finish
213 pthread_join( *mUpdateRenderThread, NULL );
215 delete mUpdateRenderThread;
216 mUpdateRenderThread = NULL;
222 void CombinedUpdateRenderController::RequestUpdate()
226 // Increment the update-request count to the maximum
227 if( mUpdateRequestCount < MAXIMUM_UPDATE_REQUESTS )
229 ++mUpdateRequestCount;
232 if( mRunning && IsUpdateRenderThreadPaused() )
234 LOG_EVENT( "Processing" );
236 RunUpdateRenderThread( CONTINUOUS, false /* No animation progression */ );
240 void CombinedUpdateRenderController::RequestUpdateOnce()
242 if( IsUpdateRenderThreadPaused() )
246 // Run Update/Render once
247 RunUpdateRenderThread( ONCE, false /* No animation progression */ );
251 void CombinedUpdateRenderController::ReplaceSurface( RenderSurface* newSurface )
255 // Set the ThreadSyncronizationInterface on the new surface
256 newSurface->SetThreadSynchronization( *this );
258 LOG_EVENT( "Starting to replace the surface, event-thread blocked" );
260 // Start replacing the surface.
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 );
268 // Wait until the surface has been replaced
269 sem_wait( &mEventThreadSemaphore );
271 LOG_EVENT( "Surface replaced, event-thread continuing" );
274 void CombinedUpdateRenderController::SetRenderRefreshRate( unsigned int numberOfFramesPerRender )
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;
281 LOG_EVENT( "mDefaultFrameDelta(%.6f), mDefaultFrameDurationMilliseconds(%lld), mDefaultFrameDurationNanoseconds(%lld)", mDefaultFrameDelta, mDefaultFrameDurationMilliseconds, mDefaultFrameDurationNanoseconds );
284 ///////////////////////////////////////////////////////////////////////////////////////////////////
286 ///////////////////////////////////////////////////////////////////////////////////////////////////
288 void CombinedUpdateRenderController::RunUpdateRenderThread( int numberOfCycles, bool useElapsedTime )
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 );
297 void CombinedUpdateRenderController::PauseUpdateRenderThread()
299 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
300 mUpdateRenderRunCount = 0;
303 void CombinedUpdateRenderController::StopUpdateRenderThread()
305 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
306 mDestroyUpdateRenderThread = TRUE;
307 mUpdateRenderThreadWaitCondition.Notify( lock );
310 bool CombinedUpdateRenderController::IsUpdateRenderThreadPaused()
312 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
313 return mUpdateRenderRunCount != CONTINUOUS; // Report paused if NOT continuously running
316 void CombinedUpdateRenderController::ProcessSleepRequest()
320 // Decrement Update request count
321 if( mUpdateRequestCount > 0 )
323 --mUpdateRequestCount;
326 // Only sleep if our update-request count is 0
327 if( mUpdateRequestCount == 0 )
329 LOG_EVENT( "Going to sleep" );
331 PauseUpdateRenderThread();
335 ///////////////////////////////////////////////////////////////////////////////////////////////////
336 // UPDATE/RENDER THREAD
337 ///////////////////////////////////////////////////////////////////////////////////////////////////
339 void CombinedUpdateRenderController::UpdateRenderThread()
341 // Install a function for logging
342 mEnvironmentOptions.InstallLogFunction();
344 LOG_UPDATE_RENDER( "THREAD CREATED" );
346 mRenderHelper.InitializeEgl();
348 // tell core it has a context
349 mCore.ContextCreated();
351 NotifyThreadInitialised();
354 uint64_t lastFrameTime;
355 TimeService::GetNanoseconds( lastFrameTime );
357 LOG_UPDATE_RENDER( "THREAD INITIALISED" );
359 bool useElapsedTime = true;
361 while( UpdateRenderReady( useElapsedTime ) )
363 LOG_UPDATE_RENDER_TRACE;
365 uint64_t currentFrameStartTime = 0;
366 TimeService::GetNanoseconds( currentFrameStartTime );
368 const uint64_t timeSinceLastFrame = currentFrameStartTime - lastFrameTime;
370 // Optional FPS Tracking
371 if( mFpsTracker.Enabled() )
373 float absoluteTimeSinceLastRender = timeSinceLastFrame * NANOSECONDS_TO_SECOND;
374 mFpsTracker.Track( absoluteTimeSinceLastRender );
377 lastFrameTime = currentFrameStartTime; // Store frame start time
379 //////////////////////////////
381 //////////////////////////////
383 RenderSurface* newSurface = ShouldSurfaceBeReplaced();
386 LOG_UPDATE_RENDER_TRACE_FMT( "Replacing Surface" );
387 mRenderHelper.ReplaceSurface( newSurface );
391 //////////////////////////////
393 //////////////////////////////
395 const unsigned int currentTime = currentFrameStartTime / NANOSECONDS_PER_MILLISECOND;
396 const unsigned int nextFrameTime = currentTime + mDefaultFrameDurationMilliseconds;
398 uint64_t noOfFramesSinceLastUpdate = 1;
399 float frameDelta = 0.0f;
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;
406 LOG_UPDATE_RENDER( "noOfFramesSinceLastUpdate(%u), frameDelta(%.6f)", noOfFramesSinceLastUpdate, frameDelta );
408 Integration::UpdateStatus updateStatus;
410 AddPerformanceMarker( PerformanceInterface::UPDATE_START );
411 mCore.Update( frameDelta, currentTime, nextFrameTime, updateStatus );
412 AddPerformanceMarker( PerformanceInterface::UPDATE_END );
414 unsigned int keepUpdatingStatus = updateStatus.KeepUpdating();
416 // Tell the event-thread to wake up (if asleep) and send a notification event to Core if required
417 if( updateStatus.NeedsNotification() )
419 mNotificationTrigger.Trigger();
420 LOG_UPDATE_RENDER( "Notification Triggered" );
423 // Optional logging of update/render status
424 mUpdateStatusLogger.Log( keepUpdatingStatus );
426 //////////////////////////////
428 //////////////////////////////
430 mRenderHelper.ConsumeEvents();
431 mRenderHelper.PreRender();
433 Integration::RenderStatus renderStatus;
435 AddPerformanceMarker( PerformanceInterface::RENDER_START );
436 mCore.Render( renderStatus );
437 AddPerformanceMarker( PerformanceInterface::RENDER_END );
439 if( renderStatus.HasRendered() )
441 mRenderHelper.PostRender();
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() )
448 mSleepTrigger->Trigger();
449 LOG_UPDATE_RENDER( "Sleep Triggered" );
452 //////////////////////////////
454 //////////////////////////////
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 );
460 // Inform core of context destruction & shutdown EGL
461 mCore.ContextDestroyed();
462 mRenderHelper.ShutdownEgl();
464 LOG_UPDATE_RENDER( "THREAD DESTROYED" );
466 // Uninstall the logging function
467 mEnvironmentOptions.UnInstallLogFunction();
470 bool CombinedUpdateRenderController::UpdateRenderReady( bool& useElapsedTime )
472 useElapsedTime = true;
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
479 mUpdateRenderThreadWaitCondition.Wait( updateLock );
481 if( ! mUseElapsedTimeAfterWait )
483 useElapsedTime = false;
487 mUseElapsedTimeAfterWait = FALSE;
489 LOG_COUNTER_UPDATE_RENDER( "mUpdateRenderRunCount: %d", mUpdateRenderRunCount );
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 )
495 --mUpdateRenderRunCount;
498 // Keep the update-render thread alive if this thread is NOT to be destroyed
499 return ! mDestroyUpdateRenderThread;
502 RenderSurface* CombinedUpdateRenderController::ShouldSurfaceBeReplaced()
504 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
506 RenderSurface* newSurface = mNewSurface;
512 void CombinedUpdateRenderController::SurfaceReplaced()
514 // Just increment the semaphore
515 sem_post( &mEventThreadSemaphore );
518 ///////////////////////////////////////////////////////////////////////////////////////////////////
520 ///////////////////////////////////////////////////////////////////////////////////////////////////
522 void CombinedUpdateRenderController::NotifyThreadInitialised()
524 // Just increment the semaphore
525 sem_post( &mEventThreadSemaphore );
528 void CombinedUpdateRenderController::AddPerformanceMarker( PerformanceInterface::MarkerType type )
530 if( mPerformanceInterface )
532 mPerformanceInterface->AddMarker( type );
536 /////////////////////////////////////////////////////////////////////////////////////////////////
537 // POST RENDERING: EVENT THREAD
538 /////////////////////////////////////////////////////////////////////////////////////////////////
540 void CombinedUpdateRenderController::PostRenderComplete()
542 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
543 mPostRendering = FALSE;
544 mUpdateRenderThreadWaitCondition.Notify( lock );
547 ///////////////////////////////////////////////////////////////////////////////////////////////////
548 // POST RENDERING: RENDER THREAD
549 ///////////////////////////////////////////////////////////////////////////////////////////////////
551 void CombinedUpdateRenderController::PostRenderStarted()
553 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
554 mPostRendering = TRUE;
557 void CombinedUpdateRenderController::PostRenderWaitForCompletion()
559 ConditionalWait::ScopedLock lock( mUpdateRenderThreadWaitCondition );
560 while( mPostRendering &&
561 ! mNewSurface ) // We should NOT wait if we're replacing the surface
563 mUpdateRenderThreadWaitCondition.Wait( lock );
567 } // namespace Adaptor
569 } // namespace Internal