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/system/common/environment-options.h>
34 #include <dali/internal/system/common/time-service.h>
35 #include <dali/internal/thread/common/thread-settings-impl.h>
36 #include <dali/internal/window-system/common/window-impl.h>
46 const unsigned int CREATED_THREAD_COUNT = 1u;
48 const int CONTINUOUS = -1;
51 const unsigned int TRUE = 1u;
52 const unsigned int FALSE = 0u;
54 const unsigned int MILLISECONDS_PER_SECOND(1e+3);
55 const float NANOSECONDS_TO_SECOND(1e-9f);
56 const unsigned int NANOSECONDS_PER_SECOND(1e+9);
57 const unsigned int NANOSECONDS_PER_MILLISECOND(1e+6);
59 // The following values will get calculated at compile time
60 const float DEFAULT_FRAME_DURATION_IN_SECONDS(1.0f / 60.0f);
61 const uint64_t DEFAULT_FRAME_DURATION_IN_MILLISECONDS(DEFAULT_FRAME_DURATION_IN_SECONDS* MILLISECONDS_PER_SECOND);
62 const uint64_t DEFAULT_FRAME_DURATION_IN_NANOSECONDS(DEFAULT_FRAME_DURATION_IN_SECONDS* NANOSECONDS_PER_SECOND);
65 * 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
66 * there is a danger that, on the event-thread we could have:
67 * 1) An update-request where we do nothing as Update/Render thread still running.
68 * 2) Quickly followed by a sleep-request being handled where we pause the Update/Render Thread (even though we have an update to process).
70 * 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:
71 * 1) MAIN THREAD: Update Request: COUNTER = 1
72 * 2) UPDATE/RENDER THREAD: Do Update/Render, then no Updates required -> Sleep Trigger
73 * 3) MAIN THREAD: Update Request: COUNTER = 2
74 * 4) MAIN THREAD: Sleep Request: COUNTER = 1 -> We do not sleep just yet
76 * Also ensures we preserve battery life by only doing ONE update when the above use case is not triggered.
77 * 1) MAIN THREAD: Update Request: COUNTER = 1
78 * 2) UPDATE/RENDER THREAD: Do Update/Render, then no Updates required -> Sleep Trigger
79 * 3) MAIN THREAD: Sleep Request: COUNTER = 0 -> Go to sleep
81 const unsigned int MAXIMUM_UPDATE_REQUESTS = 2;
82 } // unnamed namespace
84 ///////////////////////////////////////////////////////////////////////////////////////////////////
86 ///////////////////////////////////////////////////////////////////////////////////////////////////
88 CombinedUpdateRenderController::CombinedUpdateRenderController(AdaptorInternalServices& adaptorInterfaces, const EnvironmentOptions& environmentOptions, ThreadMode threadMode)
89 : mFpsTracker(environmentOptions),
90 mUpdateStatusLogger(environmentOptions),
91 mEventThreadSemaphore(0),
93 mUpdateRenderThreadWaitCondition(),
94 mPostRenderWaitCondition(),
95 mAdaptorInterfaces(adaptorInterfaces),
96 mPerformanceInterface(adaptorInterfaces.GetPerformanceInterface()),
97 mCore(adaptorInterfaces.GetCore()),
98 mEnvironmentOptions(environmentOptions),
99 mNotificationTrigger(adaptorInterfaces.GetProcessCoreEventsTrigger()),
101 mPreRenderCallback(NULL),
102 mUpdateRenderThread(NULL),
103 mDefaultFrameDelta(0.0f),
104 mDefaultFrameDurationMilliseconds(0u),
105 mDefaultFrameDurationNanoseconds(0u),
106 mDefaultHalfFrameNanoseconds(0u),
107 mUpdateRequestCount(0u),
110 mThreadMode(threadMode),
111 mUpdateRenderRunCount(0),
112 mDestroyUpdateRenderThread(FALSE),
113 mUpdateRenderThreadCanSleep(FALSE),
114 mPendingRequestUpdate(FALSE),
115 mUseElapsedTimeAfterWait(FALSE),
117 mDeletedSurface(nullptr),
118 mPostRendering(FALSE),
121 mUploadWithoutRendering(FALSE),
122 mFirstFrameAfterResume(FALSE)
126 // Initialise frame delta/duration variables first
127 SetRenderRefreshRate(environmentOptions.GetRenderRefreshRate());
129 // Set the thread-synchronization interface on the render-surface
130 Dali::RenderSurfaceInterface* currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
133 currentSurface->SetThreadSynchronization(*this);
136 mSleepTrigger = TriggerEventFactory::CreateTriggerEvent(MakeCallback(this, &CombinedUpdateRenderController::ProcessSleepRequest), TriggerEventInterface::KEEP_ALIVE_AFTER_TRIGGER);
139 CombinedUpdateRenderController::~CombinedUpdateRenderController()
145 delete mPreRenderCallback;
146 delete mSleepTrigger;
149 void CombinedUpdateRenderController::Initialize()
153 // Ensure Update/Render Thread not already created
154 DALI_ASSERT_ALWAYS(!mUpdateRenderThread);
156 // Create Update/Render Thread
157 ConditionalWait::ScopedLock lock(mGraphicsInitializeWait);
158 mUpdateRenderThread = new pthread_t();
159 int error = pthread_create(mUpdateRenderThread, NULL, InternalUpdateRenderThreadEntryFunc, this);
160 DALI_ASSERT_ALWAYS(!error && "Return code from pthread_create() when creating UpdateRenderThread");
162 // The Update/Render thread will now run and initialise the graphics interface etc. and will then wait for Start to be called
163 // When this function returns, the application initialisation on the event thread should occur
166 void CombinedUpdateRenderController::Start()
170 DALI_ASSERT_ALWAYS(!mRunning && mUpdateRenderThread);
172 // Wait until all threads created in Initialise are up and running
173 for(unsigned int i = 0; i < CREATED_THREAD_COUNT; ++i)
175 mEventThreadSemaphore.Acquire();
180 LOG_EVENT("Startup Complete, starting Update/Render Thread");
182 RunUpdateRenderThread(CONTINUOUS, AnimationProgression::NONE, UpdateMode::NORMAL);
184 Dali::RenderSurfaceInterface* currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
187 currentSurface->StartRender();
190 DALI_LOG_RELEASE_INFO("CombinedUpdateRenderController::Start\n");
193 void CombinedUpdateRenderController::Pause()
199 PauseUpdateRenderThread();
201 AddPerformanceMarker(PerformanceInterface::PAUSED);
203 DALI_LOG_RELEASE_INFO("CombinedUpdateRenderController::Pause\n");
206 void CombinedUpdateRenderController::Resume()
210 if(!mRunning && IsUpdateRenderThreadPaused())
212 LOG_EVENT("Resuming");
214 RunUpdateRenderThread(CONTINUOUS, AnimationProgression::USE_ELAPSED_TIME, UpdateMode::NORMAL);
216 AddPerformanceMarker(PerformanceInterface::RESUME);
220 mFirstFrameAfterResume = TRUE;
222 DALI_LOG_RELEASE_INFO("CombinedUpdateRenderController::Resume\n");
226 DALI_LOG_RELEASE_INFO("CombinedUpdateRenderController::Resume: Already resumed [%d, %d, %d]\n", mRunning, mUpdateRenderRunCount, mUpdateRenderThreadCanSleep);
230 void CombinedUpdateRenderController::Stop()
234 // Stop Rendering and the Update/Render Thread
235 Dali::RenderSurfaceInterface* currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
238 currentSurface->StopRender();
241 StopUpdateRenderThread();
243 if(mUpdateRenderThread)
245 LOG_EVENT("Destroying UpdateRenderThread");
247 // wait for the thread to finish
248 pthread_join(*mUpdateRenderThread, NULL);
250 delete mUpdateRenderThread;
251 mUpdateRenderThread = NULL;
256 DALI_LOG_RELEASE_INFO("CombinedUpdateRenderController::Stop\n");
259 void CombinedUpdateRenderController::RequestUpdate()
263 // Increment the update-request count to the maximum
264 if(mUpdateRequestCount < MAXIMUM_UPDATE_REQUESTS)
266 ++mUpdateRequestCount;
269 if(mRunning && IsUpdateRenderThreadPaused())
271 LOG_EVENT("Processing");
273 RunUpdateRenderThread(CONTINUOUS, AnimationProgression::NONE, UpdateMode::NORMAL);
276 ConditionalWait::ScopedLock updateLock(mUpdateRenderThreadWaitCondition);
277 mPendingRequestUpdate = TRUE;
280 void CombinedUpdateRenderController::RequestUpdateOnce(UpdateMode updateMode)
282 // Increment the update-request count to the maximum
283 if(mUpdateRequestCount < MAXIMUM_UPDATE_REQUESTS)
285 ++mUpdateRequestCount;
288 if(IsUpdateRenderThreadPaused() || updateMode == UpdateMode::FORCE_RENDER)
292 // Run Update/Render once
293 RunUpdateRenderThread(ONCE, AnimationProgression::NONE, updateMode);
297 void CombinedUpdateRenderController::ReplaceSurface(Dali::RenderSurfaceInterface* newSurface)
301 if(mUpdateRenderThread)
303 // Set the ThreadSyncronizationInterface on the new surface
304 newSurface->SetThreadSynchronization(*this);
306 LOG_EVENT("Starting to replace the surface, event-thread blocked");
308 // Start replacing the surface.
310 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
311 mNewSurface = newSurface;
312 mUpdateRenderThreadWaitCondition.Notify(lock);
315 // Wait until the surface has been replaced
316 mSurfaceSemaphore.Acquire();
318 LOG_EVENT("Surface replaced, event-thread continuing");
322 void CombinedUpdateRenderController::DeleteSurface(Dali::RenderSurfaceInterface* surface)
326 if(mUpdateRenderThread)
328 LOG_EVENT("Starting to delete the surface, event-thread blocked");
330 // Start replacing the surface.
332 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
333 mDeletedSurface = surface;
334 mUpdateRenderThreadWaitCondition.Notify(lock);
337 // Wait until the surface has been deleted
338 mSurfaceSemaphore.Acquire();
340 LOG_EVENT("Surface deleted, event-thread continuing");
344 void CombinedUpdateRenderController::WaitForGraphicsInitialization()
346 ConditionalWait::ScopedLock lk(mGraphicsInitializeWait);
349 if(mUpdateRenderThread)
351 LOG_EVENT("Waiting for graphics initialisation, event-thread blocked");
353 // Wait until the graphics has been initialised
354 mGraphicsInitializeWait.Wait(lk);
356 LOG_EVENT("graphics initialised, event-thread continuing");
360 void CombinedUpdateRenderController::ResizeSurface()
364 LOG_EVENT("Resize the surface");
367 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
368 // Surface is resized and the surface resized count is increased.
370 mUpdateRenderThreadWaitCondition.Notify(lock);
374 void CombinedUpdateRenderController::SetRenderRefreshRate(unsigned int numberOfFramesPerRender)
376 // Not protected by lock, but written to rarely so not worth adding a lock when reading
377 mDefaultFrameDelta = numberOfFramesPerRender * DEFAULT_FRAME_DURATION_IN_SECONDS;
378 mDefaultFrameDurationMilliseconds = uint64_t(numberOfFramesPerRender) * DEFAULT_FRAME_DURATION_IN_MILLISECONDS;
379 mDefaultFrameDurationNanoseconds = uint64_t(numberOfFramesPerRender) * DEFAULT_FRAME_DURATION_IN_NANOSECONDS;
380 mDefaultHalfFrameNanoseconds = mDefaultFrameDurationNanoseconds / 2u;
382 LOG_EVENT("mDefaultFrameDelta(%.6f), mDefaultFrameDurationMilliseconds(%lld), mDefaultFrameDurationNanoseconds(%lld)", mDefaultFrameDelta, mDefaultFrameDurationMilliseconds, mDefaultFrameDurationNanoseconds);
385 void CombinedUpdateRenderController::SetPreRenderCallback(CallbackBase* callback)
388 LOG_EVENT("Set PreRender Callback");
390 ConditionalWait::ScopedLock updateLock(mUpdateRenderThreadWaitCondition);
391 if(mPreRenderCallback)
393 delete mPreRenderCallback;
395 mPreRenderCallback = callback;
398 void CombinedUpdateRenderController::AddSurface(Dali::RenderSurfaceInterface* surface)
401 LOG_EVENT("Surface is added");
402 if(mUpdateRenderThread)
404 // Set the ThreadSyncronizationInterface on the added surface
405 surface->SetThreadSynchronization(*this);
409 int32_t CombinedUpdateRenderController::GetThreadId() const
414 ///////////////////////////////////////////////////////////////////////////////////////////////////
416 ///////////////////////////////////////////////////////////////////////////////////////////////////
418 void CombinedUpdateRenderController::RunUpdateRenderThread(int numberOfCycles, AnimationProgression animationProgression, UpdateMode updateMode)
420 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
424 case ThreadMode::NORMAL:
426 mUpdateRenderRunCount = numberOfCycles;
427 mUseElapsedTimeAfterWait = (animationProgression == AnimationProgression::USE_ELAPSED_TIME);
430 case ThreadMode::RUN_IF_REQUESTED:
432 if(updateMode != UpdateMode::FORCE_RENDER)
434 // Render only if the update mode is FORCE_RENDER which means the application requests it.
435 // We don't want to awake the update thread.
439 mUpdateRenderRunCount++; // Increase the update request count
440 mUseElapsedTimeAfterWait = TRUE; // The elapsed time should be used. We want animations to proceed.
445 mUpdateRenderThreadCanSleep = FALSE;
446 mUploadWithoutRendering = (updateMode == UpdateMode::SKIP_RENDER);
447 LOG_COUNTER_EVENT("mUpdateRenderRunCount: %d, mUseElapsedTimeAfterWait: %d", mUpdateRenderRunCount, mUseElapsedTimeAfterWait);
448 mUpdateRenderThreadWaitCondition.Notify(lock);
451 void CombinedUpdateRenderController::PauseUpdateRenderThread()
453 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
454 mUpdateRenderRunCount = 0;
457 void CombinedUpdateRenderController::StopUpdateRenderThread()
459 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
460 mDestroyUpdateRenderThread = TRUE;
461 mUpdateRenderThreadWaitCondition.Notify(lock);
464 bool CombinedUpdateRenderController::IsUpdateRenderThreadPaused()
466 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
468 if(mThreadMode == ThreadMode::RUN_IF_REQUESTED)
470 return !mRunning || mUpdateRenderThreadCanSleep;
473 return (mUpdateRenderRunCount != CONTINUOUS) || // Report paused if NOT continuously running
474 mUpdateRenderThreadCanSleep; // Report paused if sleeping
477 void CombinedUpdateRenderController::ProcessSleepRequest()
481 // Decrement Update request count
482 if(mUpdateRequestCount > 0)
484 --mUpdateRequestCount;
487 // Can sleep if our update-request count is 0
488 // Update/Render thread can choose to carry on updating if it determines more update/renders are required
489 if(mUpdateRequestCount == 0)
491 LOG_EVENT("Going to sleep");
493 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
494 mUpdateRenderThreadCanSleep = TRUE;
498 ///////////////////////////////////////////////////////////////////////////////////////////////////
499 // UPDATE/RENDER THREAD
500 ///////////////////////////////////////////////////////////////////////////////////////////////////
502 void CombinedUpdateRenderController::UpdateRenderThread()
504 ThreadSettings::SetThreadName("RenderThread\0");
505 mThreadId = ThreadSettings::GetThreadId();
507 // Install a function for logging
508 mEnvironmentOptions.InstallLogFunction();
510 // Install a function for tracing
511 mEnvironmentOptions.InstallTraceFunction();
513 LOG_UPDATE_RENDER("THREAD CREATED");
515 // Initialize graphics
516 GraphicsInterface& graphics = mAdaptorInterfaces.GetGraphicsInterface();
517 graphics.Initialize();
519 Dali::DisplayConnection& displayConnection = mAdaptorInterfaces.GetDisplayConnectionInterface();
520 displayConnection.Initialize(); //@todo Move InitializeGraphics code into graphics implementation
522 NotifyGraphicsInitialised();
524 //@todo Vk swaps this around, but we need to support surfaceless context for multi-window
525 graphics.ConfigureSurface(mAdaptorInterfaces.GetRenderSurfaceInterface());
527 // Tell core it has a context
528 mCore.ContextCreated();
530 NotifyThreadInitialised();
533 uint64_t lastFrameTime;
534 TimeService::GetNanoseconds(lastFrameTime);
535 uint64_t lastMemPoolLogTime = 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 uint64_t memPoolInterval = 1e9 * float(mEnvironmentOptions.GetMemoryPoolInterval());
546 const unsigned int renderToFboInterval = mEnvironmentOptions.GetRenderToFboInterval();
547 const bool renderToFboEnabled = 0u != renderToFboInterval;
548 unsigned int frameCount = 0u;
550 while(UpdateRenderReady(useElapsedTime, updateRequired, timeToSleepUntil))
552 LOG_UPDATE_RENDER_TRACE;
555 bool uploadOnly = mUploadWithoutRendering;
556 unsigned int surfaceResized = mSurfaceResized;
557 Dali::RenderSurfaceInterface* deletedSurface = ShouldSurfaceBeDeleted();
559 // Performance statistics are logged upon a VSYNC tick so use this point for a VSync marker
560 AddPerformanceMarker(PerformanceInterface::VSYNC);
562 uint64_t currentFrameStartTime = 0;
563 TimeService::GetNanoseconds(currentFrameStartTime);
565 uint64_t timeSinceLastFrame = currentFrameStartTime - lastFrameTime;
567 // Optional FPS Tracking when continuously rendering
568 if(useElapsedTime && mFpsTracker.Enabled())
570 float absoluteTimeSinceLastRender = timeSinceLastFrame * NANOSECONDS_TO_SECOND;
571 mFpsTracker.Track(absoluteTimeSinceLastRender);
574 lastFrameTime = currentFrameStartTime; // Store frame start time
576 //////////////////////////////
578 //////////////////////////////
580 Dali::RenderSurfaceInterface* newSurface = ShouldSurfaceBeReplaced();
581 if(DALI_UNLIKELY(newSurface))
583 LOG_UPDATE_RENDER_TRACE_FMT("Replacing Surface");
584 // This is designed for replacing pixmap surfaces, but should work for window as well
585 // we need to delete the surface and renderable (pixmap / window)
586 // Then create a new pixmap/window and new surface
587 // If the new surface has a different display connection, then the context will be lost
588 mAdaptorInterfaces.GetDisplayConnectionInterface().Initialize();
589 graphics.ActivateSurfaceContext(newSurface);
590 // TODO: ReplaceGraphicsSurface doesn't work, InitializeGraphics()
591 // already creates new surface window, the surface and the context.
592 // We probably don't need ReplaceGraphicsSurface at all.
593 // newSurface->ReplaceGraphicsSurface();
597 const bool isRenderingToFbo = renderToFboEnabled && ((0u == frameCount) || (0u != frameCount % renderToFboInterval));
600 //////////////////////////////
602 //////////////////////////////
604 const uint32_t currentTime = static_cast<uint32_t>(currentFrameStartTime / NANOSECONDS_PER_MILLISECOND);
605 const uint32_t nextFrameTime = currentTime + static_cast<uint32_t>(mDefaultFrameDurationMilliseconds);
607 uint64_t noOfFramesSinceLastUpdate = 1;
608 float frameDelta = 0.0f;
611 if(mThreadMode == ThreadMode::RUN_IF_REQUESTED)
613 extraFramesDropped = 0;
614 while(timeSinceLastFrame >= mDefaultFrameDurationNanoseconds)
616 timeSinceLastFrame -= mDefaultFrameDurationNanoseconds;
617 extraFramesDropped++;
621 // If using the elapsed time, then calculate frameDelta as a multiple of mDefaultFrameDelta
622 noOfFramesSinceLastUpdate += extraFramesDropped;
624 frameDelta = mDefaultFrameDelta * noOfFramesSinceLastUpdate;
626 LOG_UPDATE_RENDER("timeSinceLastFrame(%llu) noOfFramesSinceLastUpdate(%u) frameDelta(%.6f)", timeSinceLastFrame, noOfFramesSinceLastUpdate, frameDelta);
628 Integration::UpdateStatus updateStatus;
630 AddPerformanceMarker(PerformanceInterface::UPDATE_START);
631 TRACE_UPDATE_RENDER_BEGIN("DALI_UPDATE");
632 mCore.Update(frameDelta,
639 TRACE_UPDATE_RENDER_END("DALI_UPDATE");
640 AddPerformanceMarker(PerformanceInterface::UPDATE_END);
642 unsigned int keepUpdatingStatus = updateStatus.KeepUpdating();
644 // Tell the event-thread to wake up (if asleep) and send a notification event to Core if required
645 if(updateStatus.NeedsNotification())
647 mNotificationTrigger.Trigger();
648 LOG_UPDATE_RENDER("Notification Triggered");
651 // Optional logging of update/render status
652 mUpdateStatusLogger.Log(keepUpdatingStatus);
654 //////////////////////////////
656 //////////////////////////////
658 graphics.FrameStart();
659 mAdaptorInterfaces.GetDisplayConnectionInterface().ConsumeEvents();
661 if(mPreRenderCallback != NULL)
663 bool keepCallback = CallbackBase::ExecuteReturn<bool>(*mPreRenderCallback);
666 delete mPreRenderCallback;
667 mPreRenderCallback = NULL;
671 graphics.ActivateResourceContext();
673 if(mFirstFrameAfterResume)
675 // mFirstFrameAfterResume is set to true when the thread is resumed
676 // Let graphics know the first frame after thread initialized or resumed.
677 graphics.SetFirstFrameAfterResume();
678 mFirstFrameAfterResume = FALSE;
681 Integration::RenderStatus renderStatus;
683 AddPerformanceMarker(PerformanceInterface::RENDER_START);
684 TRACE_UPDATE_RENDER_BEGIN("DALI_RENDER");
686 // Upload shared resources
687 mCore.PreRender(renderStatus, mForceClear);
689 if(!uploadOnly || surfaceResized)
691 // Go through each window
692 WindowContainer windows;
693 mAdaptorInterfaces.GetWindowContainerInterface(windows);
695 for(auto&& window : windows)
697 Dali::Integration::Scene scene = window->GetScene();
698 Dali::RenderSurfaceInterface* windowSurface = window->GetSurface();
700 if(scene && windowSurface)
702 Integration::RenderStatus windowRenderStatus;
704 const bool sceneSurfaceResized = scene.IsSurfaceRectChanged();
706 // clear previous frame damaged render items rects, buffer history is tracked on surface level
707 mDamagedRects.clear();
709 // Collect damage rects
710 mCore.PreRender(scene, mDamagedRects);
712 // Render off-screen frame buffers first if any
713 mCore.RenderScene(windowRenderStatus, scene, true);
715 Rect<int> clippingRect; // Empty for fbo rendering
717 // Switch to the context of the surface, merge damaged areas for previous frames
718 windowSurface->PreRender(sceneSurfaceResized, mDamagedRects, clippingRect); // Switch GL context
720 // Render the surface
721 mCore.RenderScene(windowRenderStatus, scene, false, clippingRect);
723 // Buffer swapping now happens when the surface render target is presented.
725 // If surface is resized, the surface resized count is decreased.
726 if(DALI_UNLIKELY(sceneSurfaceResized))
736 graphics.PostRender();
741 //////////////////////////////
743 //////////////////////////////
744 if(DALI_UNLIKELY(deletedSurface))
746 LOG_UPDATE_RENDER_TRACE_FMT("Deleting Surface");
748 deletedSurface->DestroySurface();
753 TRACE_UPDATE_RENDER_END("DALI_RENDER");
754 AddPerformanceMarker(PerformanceInterface::RENDER_END);
756 // if the memory pool interval is set and has elapsed, log the graphics memory pools
757 if(0 < memPoolInterval && memPoolInterval < lastFrameTime - lastMemPoolLogTime)
759 lastMemPoolLogTime = lastFrameTime;
760 graphics.LogMemoryPools();
765 // Trigger event thread to request Update/Render thread to sleep if update not required
766 if((Integration::KeepUpdating::NOT_REQUESTED == keepUpdatingStatus) && !renderStatus.NeedsUpdate())
768 mSleepTrigger->Trigger();
769 updateRequired = false;
770 LOG_UPDATE_RENDER("Sleep Triggered");
774 updateRequired = true;
777 //////////////////////////////
779 //////////////////////////////
781 extraFramesDropped = 0;
783 if(timeToSleepUntil == 0)
785 // If this is the first frame after the thread is initialized or resumed, we
786 // use the actual time the current frame starts from to calculate the time to
787 // sleep until the next frame.
788 timeToSleepUntil = currentFrameStartTime + mDefaultFrameDurationNanoseconds;
792 // Otherwise, always use the sleep-until time calculated in the last frame to
793 // calculate the time to sleep until the next frame. In this way, if there is
794 // any time gap between the current frame and the next frame, or if update or
795 // rendering in the current frame takes too much time so that the specified
796 // sleep-until time has already passed, it will try to keep the frames syncing
797 // by shortening the duration of the next frame.
798 timeToSleepUntil += mDefaultFrameDurationNanoseconds;
800 // Check the current time at the end of the frame
801 uint64_t currentFrameEndTime = 0;
802 TimeService::GetNanoseconds(currentFrameEndTime);
803 while(currentFrameEndTime > timeToSleepUntil + mDefaultFrameDurationNanoseconds)
805 // We are more than one frame behind already, so just drop the next frames
806 // until the sleep-until time is later than the current time so that we can
808 timeToSleepUntil += mDefaultFrameDurationNanoseconds;
809 extraFramesDropped++;
813 // Render to FBO is intended to measure fps above 60 so sleep is not wanted.
814 if(0u == renderToFboInterval)
816 // Sleep until at least the the default frame duration has elapsed. This will return immediately if the specified end-time has already passed.
817 TimeService::SleepUntil(timeToSleepUntil);
821 // Inform core of context destruction
822 mCore.ContextDestroyed();
824 WindowContainer windows;
825 mAdaptorInterfaces.GetWindowContainerInterface(windows);
828 for(auto&& window : windows)
830 Dali::RenderSurfaceInterface* surface = window->GetSurface();
831 surface->DestroySurface();
836 LOG_UPDATE_RENDER("THREAD DESTROYED");
838 // Uninstall the logging function
839 mEnvironmentOptions.UnInstallLogFunction();
842 bool CombinedUpdateRenderController::UpdateRenderReady(bool& useElapsedTime, bool updateRequired, uint64_t& timeToSleepUntil)
844 useElapsedTime = true;
846 ConditionalWait::ScopedLock updateLock(mUpdateRenderThreadWaitCondition);
847 while((!mUpdateRenderRunCount || // Should try to wait if event-thread has paused the Update/Render thread
848 (mUpdateRenderThreadCanSleep && !updateRequired && !mPendingRequestUpdate)) && // Ensure we wait if we're supposed to be sleeping AND do not require another update
849 !mDestroyUpdateRenderThread && // Ensure we don't wait if the update-render-thread is supposed to be destroyed
850 !mNewSurface && // Ensure we don't wait if we need to replace the surface
851 !mDeletedSurface && // Ensure we don't wait if we need to delete the surface
852 !mSurfaceResized) // Ensure we don't wait if we need to resize the surface
854 LOG_UPDATE_RENDER("WAIT: mUpdateRenderRunCount: %d", mUpdateRenderRunCount);
855 LOG_UPDATE_RENDER(" mUpdateRenderThreadCanSleep: %d, updateRequired: %d, mPendingRequestUpdate: %d", mUpdateRenderThreadCanSleep, updateRequired, mPendingRequestUpdate);
856 LOG_UPDATE_RENDER(" mDestroyUpdateRenderThread: %d", mDestroyUpdateRenderThread);
857 LOG_UPDATE_RENDER(" mNewSurface: %d", mNewSurface);
858 LOG_UPDATE_RENDER(" mDeletedSurface: %d", mDeletedSurface);
859 LOG_UPDATE_RENDER(" mSurfaceResized: %d", mSurfaceResized);
861 // Reset the time when the thread is waiting, so the sleep-until time for
862 // the first frame after resuming should be based on the actual start time
863 // of the first frame.
864 timeToSleepUntil = 0;
866 mUpdateRenderThreadWaitCondition.Wait(updateLock);
868 if(!mUseElapsedTimeAfterWait)
870 useElapsedTime = false;
874 LOG_COUNTER_UPDATE_RENDER("mUpdateRenderRunCount: %d", mUpdateRenderRunCount);
875 LOG_COUNTER_UPDATE_RENDER("mUpdateRenderThreadCanSleep: %d, updateRequired: %d, mPendingRequestUpdate: %d", mUpdateRenderThreadCanSleep, updateRequired, mPendingRequestUpdate);
876 LOG_COUNTER_UPDATE_RENDER("mDestroyUpdateRenderThread: %d", mDestroyUpdateRenderThread);
877 LOG_COUNTER_UPDATE_RENDER("mNewSurface: %d", mNewSurface);
878 LOG_COUNTER_UPDATE_RENDER("mDeletedSurface: %d", mDeletedSurface);
879 LOG_COUNTER_UPDATE_RENDER("mSurfaceResized: %d", mSurfaceResized);
881 mUseElapsedTimeAfterWait = FALSE;
882 mUpdateRenderThreadCanSleep = FALSE;
883 mPendingRequestUpdate = FALSE;
885 // If we've been asked to run Update/Render cycles a finite number of times then decrement so we wait after the
886 // requested number of cycles
887 if(mUpdateRenderRunCount > 0)
889 --mUpdateRenderRunCount;
892 // Keep the update-render thread alive if this thread is NOT to be destroyed
893 return !mDestroyUpdateRenderThread;
896 Dali::RenderSurfaceInterface* CombinedUpdateRenderController::ShouldSurfaceBeReplaced()
898 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
900 Dali::RenderSurfaceInterface* newSurface = mNewSurface;
906 void CombinedUpdateRenderController::SurfaceReplaced()
908 // Just increment the semaphore
909 mSurfaceSemaphore.Release(1);
912 Dali::RenderSurfaceInterface* CombinedUpdateRenderController::ShouldSurfaceBeDeleted()
914 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
916 Dali::RenderSurfaceInterface* deletedSurface = mDeletedSurface;
917 mDeletedSurface = NULL;
919 return deletedSurface;
922 void CombinedUpdateRenderController::SurfaceDeleted()
924 // Just increment the semaphore
925 mSurfaceSemaphore.Release(1);
928 void CombinedUpdateRenderController::SurfaceResized()
930 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
937 ///////////////////////////////////////////////////////////////////////////////////////////////////
939 ///////////////////////////////////////////////////////////////////////////////////////////////////
941 void CombinedUpdateRenderController::NotifyThreadInitialised()
943 // Just increment the semaphore
944 mEventThreadSemaphore.Release(1);
947 void CombinedUpdateRenderController::NotifyGraphicsInitialised()
949 mGraphicsInitializeWait.Notify();
952 void CombinedUpdateRenderController::AddPerformanceMarker(PerformanceInterface::MarkerType type)
954 if(mPerformanceInterface)
956 mPerformanceInterface->AddMarker(type);
960 /////////////////////////////////////////////////////////////////////////////////////////////////
961 // POST RENDERING: EVENT THREAD
962 /////////////////////////////////////////////////////////////////////////////////////////////////
964 void CombinedUpdateRenderController::PostRenderComplete()
966 ConditionalWait::ScopedLock lock(mPostRenderWaitCondition);
967 mPostRendering = FALSE;
968 mPostRenderWaitCondition.Notify(lock);
971 ///////////////////////////////////////////////////////////////////////////////////////////////////
972 // POST RENDERING: RENDER THREAD
973 ///////////////////////////////////////////////////////////////////////////////////////////////////
975 void CombinedUpdateRenderController::PostRenderStarted()
977 ConditionalWait::ScopedLock lock(mPostRenderWaitCondition);
978 mPostRendering = TRUE;
981 void CombinedUpdateRenderController::PostRenderWaitForCompletion()
983 ConditionalWait::ScopedLock lock(mPostRenderWaitCondition);
984 while(mPostRendering &&
985 !mNewSurface && // We should NOT wait if we're replacing the surface
986 !mDeletedSurface && // We should NOT wait if we're deleting the surface
987 !mDestroyUpdateRenderThread)
989 mPostRenderWaitCondition.Wait(lock);
993 } // namespace Adaptor
995 } // namespace Internal