2 * Copyright (c) 2023 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 <dali/internal/adaptor/common/combined-update-render-controller.h>
22 #include <dali/integration-api/platform-abstraction.h>
25 #include "dali/public-api/common/dali-common.h"
28 #include <dali/integration-api/adaptor-framework/trigger-event-factory.h>
29 #include <dali/internal/adaptor/common/adaptor-internal-services.h>
30 #include <dali/internal/adaptor/common/combined-update-render-controller-debug.h>
31 #include <dali/internal/graphics/common/graphics-interface.h>
32 #include <dali/internal/graphics/gles/egl-graphics.h>
33 #include <dali/internal/graphics/gles/egl-implementation.h>
34 #include <dali/internal/system/common/environment-options.h>
35 #include <dali/internal/system/common/time-service.h>
36 #include <dali/internal/thread/common/thread-settings-impl.h>
37 #include <dali/internal/window-system/common/window-impl.h>
47 const unsigned int CREATED_THREAD_COUNT = 1u;
49 const int CONTINUOUS = -1;
52 const unsigned int TRUE = 1u;
53 const unsigned int FALSE = 0u;
55 const unsigned int MILLISECONDS_PER_SECOND(1e+3);
56 const float NANOSECONDS_TO_SECOND(1e-9f);
57 const unsigned int NANOSECONDS_PER_SECOND(1e+9);
58 const unsigned int NANOSECONDS_PER_MILLISECOND(1e+6);
60 // The following values will get calculated at compile time
61 const float DEFAULT_FRAME_DURATION_IN_SECONDS(1.0f / 60.0f);
62 const uint64_t DEFAULT_FRAME_DURATION_IN_MILLISECONDS(DEFAULT_FRAME_DURATION_IN_SECONDS* MILLISECONDS_PER_SECOND);
63 const uint64_t DEFAULT_FRAME_DURATION_IN_NANOSECONDS(DEFAULT_FRAME_DURATION_IN_SECONDS* NANOSECONDS_PER_SECOND);
66 * 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
67 * there is a danger that, on the event-thread we could have:
68 * 1) An update-request where we do nothing as Update/Render thread still running.
69 * 2) Quickly followed by a sleep-request being handled where we pause the Update/Render Thread (even though we have an update to process).
71 * 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:
72 * 1) MAIN THREAD: Update Request: COUNTER = 1
73 * 2) UPDATE/RENDER THREAD: Do Update/Render, then no Updates required -> Sleep Trigger
74 * 3) MAIN THREAD: Update Request: COUNTER = 2
75 * 4) MAIN THREAD: Sleep Request: COUNTER = 1 -> We do not sleep just yet
77 * Also ensures we preserve battery life by only doing ONE update when the above use case is not triggered.
78 * 1) MAIN THREAD: Update Request: COUNTER = 1
79 * 2) UPDATE/RENDER THREAD: Do Update/Render, then no Updates required -> Sleep Trigger
80 * 3) MAIN THREAD: Sleep Request: COUNTER = 0 -> Go to sleep
82 const unsigned int MAXIMUM_UPDATE_REQUESTS = 2;
83 } // unnamed namespace
85 ///////////////////////////////////////////////////////////////////////////////////////////////////
87 ///////////////////////////////////////////////////////////////////////////////////////////////////
89 CombinedUpdateRenderController::CombinedUpdateRenderController(AdaptorInternalServices& adaptorInterfaces, const EnvironmentOptions& environmentOptions, ThreadMode threadMode)
90 : mFpsTracker(environmentOptions),
91 mUpdateStatusLogger(environmentOptions),
92 mEventThreadSemaphore(0),
94 mUpdateRenderThreadWaitCondition(),
95 mPostRenderWaitCondition(),
96 mAdaptorInterfaces(adaptorInterfaces),
97 mPerformanceInterface(adaptorInterfaces.GetPerformanceInterface()),
98 mCore(adaptorInterfaces.GetCore()),
99 mEnvironmentOptions(environmentOptions),
100 mNotificationTrigger(adaptorInterfaces.GetProcessCoreEventsTrigger()),
102 mPreRenderCallback(NULL),
103 mUpdateRenderThread(NULL),
104 mDefaultFrameDelta(0.0f),
105 mDefaultFrameDurationMilliseconds(0u),
106 mDefaultFrameDurationNanoseconds(0u),
107 mDefaultHalfFrameNanoseconds(0u),
108 mUpdateRequestCount(0u),
111 mThreadMode(threadMode),
112 mUpdateRenderRunCount(0),
113 mDestroyUpdateRenderThread(FALSE),
114 mUpdateRenderThreadCanSleep(FALSE),
115 mPendingRequestUpdate(FALSE),
116 mUseElapsedTimeAfterWait(FALSE),
118 mDeletedSurface(nullptr),
119 mPostRendering(FALSE),
122 mUploadWithoutRendering(FALSE),
123 mFirstFrameAfterResume(FALSE)
127 // Initialise frame delta/duration variables first
128 SetRenderRefreshRate(environmentOptions.GetRenderRefreshRate());
130 // Set the thread-synchronization interface on the render-surface
131 Dali::RenderSurfaceInterface* currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
134 currentSurface->SetThreadSynchronization(*this);
137 mSleepTrigger = TriggerEventFactory::CreateTriggerEvent(MakeCallback(this, &CombinedUpdateRenderController::ProcessSleepRequest), TriggerEventInterface::KEEP_ALIVE_AFTER_TRIGGER);
140 CombinedUpdateRenderController::~CombinedUpdateRenderController()
146 delete mPreRenderCallback;
147 delete mSleepTrigger;
150 void CombinedUpdateRenderController::Initialize()
154 // Ensure Update/Render Thread not already created
155 DALI_ASSERT_ALWAYS(!mUpdateRenderThread);
157 // Create Update/Render Thread
158 ConditionalWait::ScopedLock lock(mGraphicsInitializeWait);
159 mUpdateRenderThread = new pthread_t();
160 int error = pthread_create(mUpdateRenderThread, NULL, InternalUpdateRenderThreadEntryFunc, this);
161 DALI_ASSERT_ALWAYS(!error && "Return code from pthread_create() when creating UpdateRenderThread");
163 // The Update/Render thread will now run and initialise the graphics interface etc. and will then wait for Start to be called
164 // When this function returns, the application initialisation on the event thread should occur
167 void CombinedUpdateRenderController::Start()
171 DALI_ASSERT_ALWAYS(!mRunning && mUpdateRenderThread);
173 // Wait until all threads created in Initialise are up and running
174 for(unsigned int i = 0; i < CREATED_THREAD_COUNT; ++i)
176 mEventThreadSemaphore.Acquire();
181 LOG_EVENT("Startup Complete, starting Update/Render Thread");
183 RunUpdateRenderThread(CONTINUOUS, AnimationProgression::NONE, UpdateMode::NORMAL);
185 Dali::RenderSurfaceInterface* currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
188 currentSurface->StartRender();
191 DALI_LOG_RELEASE_INFO("CombinedUpdateRenderController::Start\n");
194 void CombinedUpdateRenderController::Pause()
200 PauseUpdateRenderThread();
202 AddPerformanceMarker(PerformanceInterface::PAUSED);
204 DALI_LOG_RELEASE_INFO("CombinedUpdateRenderController::Pause\n");
207 void CombinedUpdateRenderController::Resume()
211 if(!mRunning && IsUpdateRenderThreadPaused())
213 LOG_EVENT("Resuming");
215 RunUpdateRenderThread(CONTINUOUS, AnimationProgression::USE_ELAPSED_TIME, UpdateMode::NORMAL);
217 AddPerformanceMarker(PerformanceInterface::RESUME);
221 mFirstFrameAfterResume = TRUE;
223 DALI_LOG_RELEASE_INFO("CombinedUpdateRenderController::Resume\n");
227 DALI_LOG_RELEASE_INFO("CombinedUpdateRenderController::Resume: Already resumed [%d, %d, %d]\n", mRunning, mUpdateRenderRunCount, mUpdateRenderThreadCanSleep);
231 void CombinedUpdateRenderController::Stop()
235 // Stop Rendering and the Update/Render Thread
236 Dali::RenderSurfaceInterface* currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
239 currentSurface->StopRender();
242 StopUpdateRenderThread();
244 if(mUpdateRenderThread)
246 LOG_EVENT("Destroying UpdateRenderThread");
248 // wait for the thread to finish
249 pthread_join(*mUpdateRenderThread, NULL);
251 delete mUpdateRenderThread;
252 mUpdateRenderThread = NULL;
257 DALI_LOG_RELEASE_INFO("CombinedUpdateRenderController::Stop\n");
260 void CombinedUpdateRenderController::RequestUpdate()
264 // Increment the update-request count to the maximum
265 if(mUpdateRequestCount < MAXIMUM_UPDATE_REQUESTS)
267 ++mUpdateRequestCount;
270 if(mRunning && IsUpdateRenderThreadPaused())
272 LOG_EVENT("Processing");
274 RunUpdateRenderThread(CONTINUOUS, AnimationProgression::NONE, UpdateMode::NORMAL);
277 ConditionalWait::ScopedLock updateLock(mUpdateRenderThreadWaitCondition);
278 mPendingRequestUpdate = TRUE;
281 void CombinedUpdateRenderController::RequestUpdateOnce(UpdateMode updateMode)
283 // Increment the update-request count to the maximum
284 if(mUpdateRequestCount < MAXIMUM_UPDATE_REQUESTS)
286 ++mUpdateRequestCount;
289 if(IsUpdateRenderThreadPaused() || updateMode == UpdateMode::FORCE_RENDER)
293 // Run Update/Render once
294 RunUpdateRenderThread(ONCE, AnimationProgression::NONE, updateMode);
298 void CombinedUpdateRenderController::ReplaceSurface(Dali::RenderSurfaceInterface* newSurface)
302 if(mUpdateRenderThread)
304 // Set the ThreadSyncronizationInterface on the new surface
305 newSurface->SetThreadSynchronization(*this);
307 LOG_EVENT("Starting to replace the surface, event-thread blocked");
309 // Start replacing the surface.
311 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
312 mNewSurface = newSurface;
313 mUpdateRenderThreadWaitCondition.Notify(lock);
316 // Wait until the surface has been replaced
317 mSurfaceSemaphore.Acquire();
319 LOG_EVENT("Surface replaced, event-thread continuing");
323 void CombinedUpdateRenderController::DeleteSurface(Dali::RenderSurfaceInterface* surface)
327 if(mUpdateRenderThread)
329 LOG_EVENT("Starting to delete the surface, event-thread blocked");
331 // Start replacing the surface.
333 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
334 mDeletedSurface = surface;
335 mUpdateRenderThreadWaitCondition.Notify(lock);
338 // Wait until the surface has been deleted
339 mSurfaceSemaphore.Acquire();
341 LOG_EVENT("Surface deleted, event-thread continuing");
345 void CombinedUpdateRenderController::WaitForGraphicsInitialization()
347 ConditionalWait::ScopedLock lk(mGraphicsInitializeWait);
350 if(mUpdateRenderThread)
352 LOG_EVENT("Waiting for graphics initialisation, event-thread blocked");
354 // Wait until the graphics has been initialised
355 mGraphicsInitializeWait.Wait(lk);
357 LOG_EVENT("graphics initialised, event-thread continuing");
361 void CombinedUpdateRenderController::ResizeSurface()
365 LOG_EVENT("Resize the surface");
368 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
369 // Surface is resized and the surface resized count is increased.
371 mUpdateRenderThreadWaitCondition.Notify(lock);
375 void CombinedUpdateRenderController::SetRenderRefreshRate(unsigned int numberOfFramesPerRender)
377 // Not protected by lock, but written to rarely so not worth adding a lock when reading
378 mDefaultFrameDelta = numberOfFramesPerRender * DEFAULT_FRAME_DURATION_IN_SECONDS;
379 mDefaultFrameDurationMilliseconds = uint64_t(numberOfFramesPerRender) * DEFAULT_FRAME_DURATION_IN_MILLISECONDS;
380 mDefaultFrameDurationNanoseconds = uint64_t(numberOfFramesPerRender) * DEFAULT_FRAME_DURATION_IN_NANOSECONDS;
381 mDefaultHalfFrameNanoseconds = mDefaultFrameDurationNanoseconds / 2u;
383 LOG_EVENT("mDefaultFrameDelta(%.6f), mDefaultFrameDurationMilliseconds(%lld), mDefaultFrameDurationNanoseconds(%lld)", mDefaultFrameDelta, mDefaultFrameDurationMilliseconds, mDefaultFrameDurationNanoseconds);
386 void CombinedUpdateRenderController::SetPreRenderCallback(CallbackBase* callback)
389 LOG_EVENT("Set PreRender Callback");
391 ConditionalWait::ScopedLock updateLock(mUpdateRenderThreadWaitCondition);
392 if(mPreRenderCallback)
394 delete mPreRenderCallback;
396 mPreRenderCallback = callback;
399 void CombinedUpdateRenderController::AddSurface(Dali::RenderSurfaceInterface* surface)
402 LOG_EVENT("Surface is added");
403 if(mUpdateRenderThread)
405 // Set the ThreadSyncronizationInterface on the added surface
406 surface->SetThreadSynchronization(*this);
410 int32_t CombinedUpdateRenderController::GetThreadId() const
415 ///////////////////////////////////////////////////////////////////////////////////////////////////
417 ///////////////////////////////////////////////////////////////////////////////////////////////////
419 void CombinedUpdateRenderController::RunUpdateRenderThread(int numberOfCycles, AnimationProgression animationProgression, UpdateMode updateMode)
421 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
425 case ThreadMode::NORMAL:
427 mUpdateRenderRunCount = numberOfCycles;
428 mUseElapsedTimeAfterWait = (animationProgression == AnimationProgression::USE_ELAPSED_TIME);
431 case ThreadMode::RUN_IF_REQUESTED:
433 if(updateMode != UpdateMode::FORCE_RENDER)
435 // Render only if the update mode is FORCE_RENDER which means the application requests it.
436 // We don't want to awake the update thread.
440 mUpdateRenderRunCount++; // Increase the update request count
441 mUseElapsedTimeAfterWait = TRUE; // The elapsed time should be used. We want animations to proceed.
446 mUpdateRenderThreadCanSleep = FALSE;
447 mUploadWithoutRendering = (updateMode == UpdateMode::SKIP_RENDER);
448 LOG_COUNTER_EVENT("mUpdateRenderRunCount: %d, mUseElapsedTimeAfterWait: %d", mUpdateRenderRunCount, mUseElapsedTimeAfterWait);
449 mUpdateRenderThreadWaitCondition.Notify(lock);
452 void CombinedUpdateRenderController::PauseUpdateRenderThread()
454 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
455 mUpdateRenderRunCount = 0;
458 void CombinedUpdateRenderController::StopUpdateRenderThread()
460 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
461 mDestroyUpdateRenderThread = TRUE;
462 mUpdateRenderThreadWaitCondition.Notify(lock);
465 bool CombinedUpdateRenderController::IsUpdateRenderThreadPaused()
467 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
469 if(mThreadMode == ThreadMode::RUN_IF_REQUESTED)
471 return !mRunning || mUpdateRenderThreadCanSleep;
474 return (mUpdateRenderRunCount != CONTINUOUS) || // Report paused if NOT continuously running
475 mUpdateRenderThreadCanSleep; // Report paused if sleeping
478 void CombinedUpdateRenderController::ProcessSleepRequest()
482 // Decrement Update request count
483 if(mUpdateRequestCount > 0)
485 --mUpdateRequestCount;
488 // Can sleep if our update-request count is 0
489 // Update/Render thread can choose to carry on updating if it determines more update/renders are required
490 if(mUpdateRequestCount == 0)
492 LOG_EVENT("Going to sleep");
494 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
495 mUpdateRenderThreadCanSleep = TRUE;
499 ///////////////////////////////////////////////////////////////////////////////////////////////////
500 // UPDATE/RENDER THREAD
501 ///////////////////////////////////////////////////////////////////////////////////////////////////
503 void CombinedUpdateRenderController::UpdateRenderThread()
505 ThreadSettings::SetThreadName("RenderThread\0");
506 mThreadId = ThreadSettings::GetThreadId();
508 // Install a function for logging
509 mEnvironmentOptions.InstallLogFunction();
511 // Install a function for tracing
512 mEnvironmentOptions.InstallTraceFunction();
514 LOG_UPDATE_RENDER("THREAD CREATED");
516 // Initialize graphics
517 GraphicsInterface& graphics = mAdaptorInterfaces.GetGraphicsInterface();
518 graphics.Initialize();
520 Dali::DisplayConnection& displayConnection = mAdaptorInterfaces.GetDisplayConnectionInterface();
521 displayConnection.Initialize(); //@todo Move InitializeGraphics code into graphics implementation
523 NotifyGraphicsInitialised();
525 //@todo Vk swaps this around, but we need to support surfaceless context for multi-window
526 graphics.ConfigureSurface(mAdaptorInterfaces.GetRenderSurfaceInterface());
528 // Tell core it has a context
529 mCore.ContextCreated();
531 NotifyThreadInitialised();
534 uint64_t lastFrameTime;
535 TimeService::GetNanoseconds(lastFrameTime);
537 LOG_UPDATE_RENDER("THREAD INITIALISED");
539 bool useElapsedTime = true;
540 bool updateRequired = true;
541 uint64_t timeToSleepUntil = 0;
542 int extraFramesDropped = 0;
544 const unsigned int renderToFboInterval = mEnvironmentOptions.GetRenderToFboInterval();
545 const bool renderToFboEnabled = 0u != renderToFboInterval;
546 unsigned int frameCount = 0u;
548 while(UpdateRenderReady(useElapsedTime, updateRequired, timeToSleepUntil))
550 LOG_UPDATE_RENDER_TRACE;
553 bool uploadOnly = mUploadWithoutRendering;
554 unsigned int surfaceResized = mSurfaceResized;
556 // Performance statistics are logged upon a VSYNC tick so use this point for a VSync marker
557 AddPerformanceMarker(PerformanceInterface::VSYNC);
559 uint64_t currentFrameStartTime = 0;
560 TimeService::GetNanoseconds(currentFrameStartTime);
562 uint64_t timeSinceLastFrame = currentFrameStartTime - lastFrameTime;
564 // Optional FPS Tracking when continuously rendering
565 if(useElapsedTime && mFpsTracker.Enabled())
567 float absoluteTimeSinceLastRender = timeSinceLastFrame * NANOSECONDS_TO_SECOND;
568 mFpsTracker.Track(absoluteTimeSinceLastRender);
571 lastFrameTime = currentFrameStartTime; // Store frame start time
573 //////////////////////////////
575 //////////////////////////////
577 Dali::RenderSurfaceInterface* newSurface = ShouldSurfaceBeReplaced();
578 if(DALI_UNLIKELY(newSurface))
580 LOG_UPDATE_RENDER_TRACE_FMT("Replacing Surface");
581 // This is designed for replacing pixmap surfaces, but should work for window as well
582 // we need to delete the surface and renderable (pixmap / window)
583 // Then create a new pixmap/window and new surface
584 // If the new surface has a different display connection, then the context will be lost
585 mAdaptorInterfaces.GetDisplayConnectionInterface().Initialize();
586 graphics.ActivateSurfaceContext(newSurface);
587 // TODO: ReplaceGraphicsSurface doesn't work, InitializeGraphics()
588 // already creates new surface window, the surface and the context.
589 // We probably don't need ReplaceGraphicsSurface at all.
590 // newSurface->ReplaceGraphicsSurface();
594 const bool isRenderingToFbo = renderToFboEnabled && ((0u == frameCount) || (0u != frameCount % renderToFboInterval));
597 //////////////////////////////
599 //////////////////////////////
601 const unsigned int currentTime = currentFrameStartTime / NANOSECONDS_PER_MILLISECOND;
602 const unsigned int nextFrameTime = currentTime + mDefaultFrameDurationMilliseconds;
604 uint64_t noOfFramesSinceLastUpdate = 1;
605 float frameDelta = 0.0f;
608 if(mThreadMode == ThreadMode::RUN_IF_REQUESTED)
610 extraFramesDropped = 0;
611 while(timeSinceLastFrame >= mDefaultFrameDurationNanoseconds)
613 timeSinceLastFrame -= mDefaultFrameDurationNanoseconds;
614 extraFramesDropped++;
618 // If using the elapsed time, then calculate frameDelta as a multiple of mDefaultFrameDelta
619 noOfFramesSinceLastUpdate += extraFramesDropped;
621 frameDelta = mDefaultFrameDelta * noOfFramesSinceLastUpdate;
623 LOG_UPDATE_RENDER("timeSinceLastFrame(%llu) noOfFramesSinceLastUpdate(%u) frameDelta(%.6f)", timeSinceLastFrame, noOfFramesSinceLastUpdate, frameDelta);
625 Integration::UpdateStatus updateStatus;
627 AddPerformanceMarker(PerformanceInterface::UPDATE_START);
628 TRACE_UPDATE_RENDER_BEGIN("DALI_UPDATE");
629 mCore.Update(frameDelta,
636 TRACE_UPDATE_RENDER_END("DALI_UPDATE");
637 AddPerformanceMarker(PerformanceInterface::UPDATE_END);
639 unsigned int keepUpdatingStatus = updateStatus.KeepUpdating();
641 // Tell the event-thread to wake up (if asleep) and send a notification event to Core if required
642 if(updateStatus.NeedsNotification())
644 mNotificationTrigger.Trigger();
645 LOG_UPDATE_RENDER("Notification Triggered");
648 // Optional logging of update/render status
649 mUpdateStatusLogger.Log(keepUpdatingStatus);
651 //////////////////////////////
653 //////////////////////////////
655 mAdaptorInterfaces.GetDisplayConnectionInterface().ConsumeEvents();
657 if(mPreRenderCallback != NULL)
659 bool keepCallback = CallbackBase::ExecuteReturn<bool>(*mPreRenderCallback);
662 delete mPreRenderCallback;
663 mPreRenderCallback = NULL;
667 graphics.ActivateResourceContext();
669 if(mFirstFrameAfterResume)
671 // mFirstFrameAfterResume is set to true when the thread is resumed
672 // Let graphics know the first frame after thread initialized or resumed.
673 graphics.SetFirstFrameAfterResume();
674 mFirstFrameAfterResume = FALSE;
677 Integration::RenderStatus renderStatus;
679 AddPerformanceMarker(PerformanceInterface::RENDER_START);
680 TRACE_UPDATE_RENDER_BEGIN("DALI_RENDER");
682 // Upload shared resources
683 mCore.PreRender(renderStatus, mForceClear);
685 if(!uploadOnly || surfaceResized)
687 // Go through each window
688 WindowContainer windows;
689 mAdaptorInterfaces.GetWindowContainerInterface(windows);
691 for(auto&& window : windows)
693 Dali::Integration::Scene scene = window->GetScene();
694 Dali::RenderSurfaceInterface* windowSurface = window->GetSurface();
696 if(scene && windowSurface)
698 Integration::RenderStatus windowRenderStatus;
700 const bool sceneSurfaceResized = scene.IsSurfaceRectChanged();
702 // clear previous frame damaged render items rects, buffer history is tracked on surface level
703 mDamagedRects.clear();
705 // Collect damage rects
706 mCore.PreRender(scene, mDamagedRects);
708 // Render off-screen frame buffers first if any
709 mCore.RenderScene(windowRenderStatus, scene, true);
711 Rect<int> clippingRect; // Empty for fbo rendering
713 // Switch to the context of the surface, merge damaged areas for previous frames
714 windowSurface->PreRender(sceneSurfaceResized, mDamagedRects, clippingRect); // Switch GL context
716 // Render the surface
717 mCore.RenderScene(windowRenderStatus, scene, false, clippingRect);
719 // Buffer swapping now happens when the surface render target is presented.
721 // If surface is resized, the surface resized count is decreased.
722 if(DALI_UNLIKELY(sceneSurfaceResized))
732 graphics.PostRender();
737 //////////////////////////////
739 //////////////////////////////
741 Dali::RenderSurfaceInterface* deletedSurface = ShouldSurfaceBeDeleted();
742 if(DALI_UNLIKELY(deletedSurface))
744 LOG_UPDATE_RENDER_TRACE_FMT("Deleting Surface");
746 deletedSurface->DestroySurface();
751 TRACE_UPDATE_RENDER_END("DALI_RENDER");
752 AddPerformanceMarker(PerformanceInterface::RENDER_END);
756 // Trigger event thread to request Update/Render thread to sleep if update not required
757 if((Integration::KeepUpdating::NOT_REQUESTED == keepUpdatingStatus) && !renderStatus.NeedsUpdate())
759 mSleepTrigger->Trigger();
760 updateRequired = false;
761 LOG_UPDATE_RENDER("Sleep Triggered");
765 updateRequired = true;
768 //////////////////////////////
770 //////////////////////////////
772 extraFramesDropped = 0;
774 if(timeToSleepUntil == 0)
776 // If this is the first frame after the thread is initialized or resumed, we
777 // use the actual time the current frame starts from to calculate the time to
778 // sleep until the next frame.
779 timeToSleepUntil = currentFrameStartTime + mDefaultFrameDurationNanoseconds;
783 // Otherwise, always use the sleep-until time calculated in the last frame to
784 // calculate the time to sleep until the next frame. In this way, if there is
785 // any time gap between the current frame and the next frame, or if update or
786 // rendering in the current frame takes too much time so that the specified
787 // sleep-until time has already passed, it will try to keep the frames syncing
788 // by shortening the duration of the next frame.
789 timeToSleepUntil += mDefaultFrameDurationNanoseconds;
791 // Check the current time at the end of the frame
792 uint64_t currentFrameEndTime = 0;
793 TimeService::GetNanoseconds(currentFrameEndTime);
794 while(currentFrameEndTime > timeToSleepUntil + mDefaultFrameDurationNanoseconds)
796 // We are more than one frame behind already, so just drop the next frames
797 // until the sleep-until time is later than the current time so that we can
799 timeToSleepUntil += mDefaultFrameDurationNanoseconds;
800 extraFramesDropped++;
804 // Render to FBO is intended to measure fps above 60 so sleep is not wanted.
805 if(0u == renderToFboInterval)
807 // Sleep until at least the the default frame duration has elapsed. This will return immediately if the specified end-time has already passed.
808 TimeService::SleepUntil(timeToSleepUntil);
812 // Inform core of context destruction
813 mCore.ContextDestroyed();
815 WindowContainer windows;
816 mAdaptorInterfaces.GetWindowContainerInterface(windows);
819 for(auto&& window : windows)
821 Dali::RenderSurfaceInterface* surface = window->GetSurface();
822 surface->DestroySurface();
827 LOG_UPDATE_RENDER("THREAD DESTROYED");
829 // Uninstall the logging function
830 mEnvironmentOptions.UnInstallLogFunction();
833 bool CombinedUpdateRenderController::UpdateRenderReady(bool& useElapsedTime, bool updateRequired, uint64_t& timeToSleepUntil)
835 useElapsedTime = true;
837 ConditionalWait::ScopedLock updateLock(mUpdateRenderThreadWaitCondition);
838 while((!mUpdateRenderRunCount || // Should try to wait if event-thread has paused the Update/Render thread
839 (mUpdateRenderThreadCanSleep && !updateRequired && !mPendingRequestUpdate)) && // Ensure we wait if we're supposed to be sleeping AND do not require another update
840 !mDestroyUpdateRenderThread && // Ensure we don't wait if the update-render-thread is supposed to be destroyed
841 !mNewSurface && // Ensure we don't wait if we need to replace the surface
842 !mDeletedSurface && // Ensure we don't wait if we need to delete the surface
843 !mSurfaceResized) // Ensure we don't wait if we need to resize the surface
845 LOG_UPDATE_RENDER("WAIT: mUpdateRenderRunCount: %d", mUpdateRenderRunCount);
846 LOG_UPDATE_RENDER(" mUpdateRenderThreadCanSleep: %d, updateRequired: %d, mPendingRequestUpdate: %d", mUpdateRenderThreadCanSleep, updateRequired, mPendingRequestUpdate);
847 LOG_UPDATE_RENDER(" mDestroyUpdateRenderThread: %d", mDestroyUpdateRenderThread);
848 LOG_UPDATE_RENDER(" mNewSurface: %d", mNewSurface);
849 LOG_UPDATE_RENDER(" mDeletedSurface: %d", mDeletedSurface);
850 LOG_UPDATE_RENDER(" mSurfaceResized: %d", mSurfaceResized);
852 // Reset the time when the thread is waiting, so the sleep-until time for
853 // the first frame after resuming should be based on the actual start time
854 // of the first frame.
855 timeToSleepUntil = 0;
857 mUpdateRenderThreadWaitCondition.Wait(updateLock);
859 if(!mUseElapsedTimeAfterWait)
861 useElapsedTime = false;
865 LOG_COUNTER_UPDATE_RENDER("mUpdateRenderRunCount: %d", mUpdateRenderRunCount);
866 LOG_COUNTER_UPDATE_RENDER("mUpdateRenderThreadCanSleep: %d, updateRequired: %d, mPendingRequestUpdate: %d", mUpdateRenderThreadCanSleep, updateRequired, mPendingRequestUpdate);
867 LOG_COUNTER_UPDATE_RENDER("mDestroyUpdateRenderThread: %d", mDestroyUpdateRenderThread);
868 LOG_COUNTER_UPDATE_RENDER("mNewSurface: %d", mNewSurface);
869 LOG_COUNTER_UPDATE_RENDER("mDeletedSurface: %d", mDeletedSurface);
870 LOG_COUNTER_UPDATE_RENDER("mSurfaceResized: %d", mSurfaceResized);
872 mUseElapsedTimeAfterWait = FALSE;
873 mUpdateRenderThreadCanSleep = FALSE;
874 mPendingRequestUpdate = FALSE;
876 // If we've been asked to run Update/Render cycles a finite number of times then decrement so we wait after the
877 // requested number of cycles
878 if(mUpdateRenderRunCount > 0)
880 --mUpdateRenderRunCount;
883 // Keep the update-render thread alive if this thread is NOT to be destroyed
884 return !mDestroyUpdateRenderThread;
887 Dali::RenderSurfaceInterface* CombinedUpdateRenderController::ShouldSurfaceBeReplaced()
889 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
891 Dali::RenderSurfaceInterface* newSurface = mNewSurface;
897 void CombinedUpdateRenderController::SurfaceReplaced()
899 // Just increment the semaphore
900 mSurfaceSemaphore.Release(1);
903 Dali::RenderSurfaceInterface* CombinedUpdateRenderController::ShouldSurfaceBeDeleted()
905 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
907 Dali::RenderSurfaceInterface* deletedSurface = mDeletedSurface;
908 mDeletedSurface = NULL;
910 return deletedSurface;
913 void CombinedUpdateRenderController::SurfaceDeleted()
915 // Just increment the semaphore
916 mSurfaceSemaphore.Release(1);
919 void CombinedUpdateRenderController::SurfaceResized()
921 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
928 ///////////////////////////////////////////////////////////////////////////////////////////////////
930 ///////////////////////////////////////////////////////////////////////////////////////////////////
932 void CombinedUpdateRenderController::NotifyThreadInitialised()
934 // Just increment the semaphore
935 mEventThreadSemaphore.Release(1);
938 void CombinedUpdateRenderController::NotifyGraphicsInitialised()
940 mGraphicsInitializeWait.Notify();
943 void CombinedUpdateRenderController::AddPerformanceMarker(PerformanceInterface::MarkerType type)
945 if(mPerformanceInterface)
947 mPerformanceInterface->AddMarker(type);
951 /////////////////////////////////////////////////////////////////////////////////////////////////
952 // POST RENDERING: EVENT THREAD
953 /////////////////////////////////////////////////////////////////////////////////////////////////
955 void CombinedUpdateRenderController::PostRenderComplete()
957 ConditionalWait::ScopedLock lock(mPostRenderWaitCondition);
958 mPostRendering = FALSE;
959 mPostRenderWaitCondition.Notify(lock);
962 ///////////////////////////////////////////////////////////////////////////////////////////////////
963 // POST RENDERING: RENDER THREAD
964 ///////////////////////////////////////////////////////////////////////////////////////////////////
966 void CombinedUpdateRenderController::PostRenderStarted()
968 ConditionalWait::ScopedLock lock(mPostRenderWaitCondition);
969 mPostRendering = TRUE;
972 void CombinedUpdateRenderController::PostRenderWaitForCompletion()
974 ConditionalWait::ScopedLock lock(mPostRenderWaitCondition);
975 while(mPostRendering &&
976 !mNewSurface && // We should NOT wait if we're replacing the surface
977 !mDeletedSurface && // We should NOT wait if we're deleting the surface
978 !mDestroyUpdateRenderThread)
980 mPostRenderWaitCondition.Wait(lock);
984 } // namespace Adaptor
986 } // namespace Internal