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;
558 // Performance statistics are logged upon a VSYNC tick so use this point for a VSync marker
559 AddPerformanceMarker(PerformanceInterface::VSYNC);
561 uint64_t currentFrameStartTime = 0;
562 TimeService::GetNanoseconds(currentFrameStartTime);
564 uint64_t timeSinceLastFrame = currentFrameStartTime - lastFrameTime;
566 // Optional FPS Tracking when continuously rendering
567 if(useElapsedTime && mFpsTracker.Enabled())
569 float absoluteTimeSinceLastRender = timeSinceLastFrame * NANOSECONDS_TO_SECOND;
570 mFpsTracker.Track(absoluteTimeSinceLastRender);
573 lastFrameTime = currentFrameStartTime; // Store frame start time
575 //////////////////////////////
577 //////////////////////////////
579 Dali::RenderSurfaceInterface* newSurface = ShouldSurfaceBeReplaced();
580 if(DALI_UNLIKELY(newSurface))
582 LOG_UPDATE_RENDER_TRACE_FMT("Replacing Surface");
583 // This is designed for replacing pixmap surfaces, but should work for window as well
584 // we need to delete the surface and renderable (pixmap / window)
585 // Then create a new pixmap/window and new surface
586 // If the new surface has a different display connection, then the context will be lost
587 mAdaptorInterfaces.GetDisplayConnectionInterface().Initialize();
588 graphics.ActivateSurfaceContext(newSurface);
589 // TODO: ReplaceGraphicsSurface doesn't work, InitializeGraphics()
590 // already creates new surface window, the surface and the context.
591 // We probably don't need ReplaceGraphicsSurface at all.
592 // newSurface->ReplaceGraphicsSurface();
596 const bool isRenderingToFbo = renderToFboEnabled && ((0u == frameCount) || (0u != frameCount % renderToFboInterval));
599 //////////////////////////////
601 //////////////////////////////
603 const uint32_t currentTime = static_cast<uint32_t>(currentFrameStartTime / NANOSECONDS_PER_MILLISECOND);
604 const uint32_t nextFrameTime = currentTime + static_cast<uint32_t>(mDefaultFrameDurationMilliseconds);
606 uint64_t noOfFramesSinceLastUpdate = 1;
607 float frameDelta = 0.0f;
610 if(mThreadMode == ThreadMode::RUN_IF_REQUESTED)
612 extraFramesDropped = 0;
613 while(timeSinceLastFrame >= mDefaultFrameDurationNanoseconds)
615 timeSinceLastFrame -= mDefaultFrameDurationNanoseconds;
616 extraFramesDropped++;
620 // If using the elapsed time, then calculate frameDelta as a multiple of mDefaultFrameDelta
621 noOfFramesSinceLastUpdate += extraFramesDropped;
623 frameDelta = mDefaultFrameDelta * noOfFramesSinceLastUpdate;
625 LOG_UPDATE_RENDER("timeSinceLastFrame(%llu) noOfFramesSinceLastUpdate(%u) frameDelta(%.6f)", timeSinceLastFrame, noOfFramesSinceLastUpdate, frameDelta);
627 Integration::UpdateStatus updateStatus;
629 AddPerformanceMarker(PerformanceInterface::UPDATE_START);
630 TRACE_UPDATE_RENDER_BEGIN("DALI_UPDATE");
631 mCore.Update(frameDelta,
638 TRACE_UPDATE_RENDER_END("DALI_UPDATE");
639 AddPerformanceMarker(PerformanceInterface::UPDATE_END);
641 unsigned int keepUpdatingStatus = updateStatus.KeepUpdating();
643 // Tell the event-thread to wake up (if asleep) and send a notification event to Core if required
644 if(updateStatus.NeedsNotification())
646 mNotificationTrigger.Trigger();
647 LOG_UPDATE_RENDER("Notification Triggered");
650 // Optional logging of update/render status
651 mUpdateStatusLogger.Log(keepUpdatingStatus);
653 //////////////////////////////
655 //////////////////////////////
657 graphics.FrameStart();
658 mAdaptorInterfaces.GetDisplayConnectionInterface().ConsumeEvents();
660 if(mPreRenderCallback != NULL)
662 bool keepCallback = CallbackBase::ExecuteReturn<bool>(*mPreRenderCallback);
665 delete mPreRenderCallback;
666 mPreRenderCallback = NULL;
670 graphics.ActivateResourceContext();
672 if(mFirstFrameAfterResume)
674 // mFirstFrameAfterResume is set to true when the thread is resumed
675 // Let graphics know the first frame after thread initialized or resumed.
676 graphics.SetFirstFrameAfterResume();
677 mFirstFrameAfterResume = FALSE;
680 Integration::RenderStatus renderStatus;
682 AddPerformanceMarker(PerformanceInterface::RENDER_START);
683 TRACE_UPDATE_RENDER_BEGIN("DALI_RENDER");
685 // Upload shared resources
686 mCore.PreRender(renderStatus, mForceClear);
688 if(!uploadOnly || surfaceResized)
690 // Go through each window
691 WindowContainer windows;
692 mAdaptorInterfaces.GetWindowContainerInterface(windows);
694 for(auto&& window : windows)
696 Dali::Integration::Scene scene = window->GetScene();
697 Dali::RenderSurfaceInterface* windowSurface = window->GetSurface();
699 if(scene && windowSurface)
701 Integration::RenderStatus windowRenderStatus;
703 const bool sceneSurfaceResized = scene.IsSurfaceRectChanged();
705 // clear previous frame damaged render items rects, buffer history is tracked on surface level
706 mDamagedRects.clear();
708 // Collect damage rects
709 mCore.PreRender(scene, mDamagedRects);
711 // Render off-screen frame buffers first if any
712 mCore.RenderScene(windowRenderStatus, scene, true);
714 Rect<int> clippingRect; // Empty for fbo rendering
716 // Switch to the context of the surface, merge damaged areas for previous frames
717 windowSurface->PreRender(sceneSurfaceResized, mDamagedRects, clippingRect); // Switch GL context
719 // Render the surface
720 mCore.RenderScene(windowRenderStatus, scene, false, clippingRect);
722 // Buffer swapping now happens when the surface render target is presented.
724 // If surface is resized, the surface resized count is decreased.
725 if(DALI_UNLIKELY(sceneSurfaceResized))
735 graphics.PostRender();
740 //////////////////////////////
742 //////////////////////////////
744 Dali::RenderSurfaceInterface* deletedSurface = ShouldSurfaceBeDeleted();
745 if(DALI_UNLIKELY(deletedSurface))
747 LOG_UPDATE_RENDER_TRACE_FMT("Deleting Surface");
749 deletedSurface->DestroySurface();
754 TRACE_UPDATE_RENDER_END("DALI_RENDER");
755 AddPerformanceMarker(PerformanceInterface::RENDER_END);
757 // if the memory pool interval is set and has elapsed, log the graphics memory pools
758 if(0 < memPoolInterval && memPoolInterval < lastFrameTime - lastMemPoolLogTime)
760 lastMemPoolLogTime = lastFrameTime;
761 graphics.LogMemoryPools();
766 // Trigger event thread to request Update/Render thread to sleep if update not required
767 if((Integration::KeepUpdating::NOT_REQUESTED == keepUpdatingStatus) && !renderStatus.NeedsUpdate())
769 mSleepTrigger->Trigger();
770 updateRequired = false;
771 LOG_UPDATE_RENDER("Sleep Triggered");
775 updateRequired = true;
778 //////////////////////////////
780 //////////////////////////////
782 extraFramesDropped = 0;
784 if(timeToSleepUntil == 0)
786 // If this is the first frame after the thread is initialized or resumed, we
787 // use the actual time the current frame starts from to calculate the time to
788 // sleep until the next frame.
789 timeToSleepUntil = currentFrameStartTime + mDefaultFrameDurationNanoseconds;
793 // Otherwise, always use the sleep-until time calculated in the last frame to
794 // calculate the time to sleep until the next frame. In this way, if there is
795 // any time gap between the current frame and the next frame, or if update or
796 // rendering in the current frame takes too much time so that the specified
797 // sleep-until time has already passed, it will try to keep the frames syncing
798 // by shortening the duration of the next frame.
799 timeToSleepUntil += mDefaultFrameDurationNanoseconds;
801 // Check the current time at the end of the frame
802 uint64_t currentFrameEndTime = 0;
803 TimeService::GetNanoseconds(currentFrameEndTime);
804 while(currentFrameEndTime > timeToSleepUntil + mDefaultFrameDurationNanoseconds)
806 // We are more than one frame behind already, so just drop the next frames
807 // until the sleep-until time is later than the current time so that we can
809 timeToSleepUntil += mDefaultFrameDurationNanoseconds;
810 extraFramesDropped++;
814 // Render to FBO is intended to measure fps above 60 so sleep is not wanted.
815 if(0u == renderToFboInterval)
817 // Sleep until at least the the default frame duration has elapsed. This will return immediately if the specified end-time has already passed.
818 TimeService::SleepUntil(timeToSleepUntil);
822 // Inform core of context destruction
823 mCore.ContextDestroyed();
825 WindowContainer windows;
826 mAdaptorInterfaces.GetWindowContainerInterface(windows);
829 for(auto&& window : windows)
831 Dali::RenderSurfaceInterface* surface = window->GetSurface();
832 surface->DestroySurface();
837 LOG_UPDATE_RENDER("THREAD DESTROYED");
839 // Uninstall the logging function
840 mEnvironmentOptions.UnInstallLogFunction();
843 bool CombinedUpdateRenderController::UpdateRenderReady(bool& useElapsedTime, bool updateRequired, uint64_t& timeToSleepUntil)
845 useElapsedTime = true;
847 ConditionalWait::ScopedLock updateLock(mUpdateRenderThreadWaitCondition);
848 while((!mUpdateRenderRunCount || // Should try to wait if event-thread has paused the Update/Render thread
849 (mUpdateRenderThreadCanSleep && !updateRequired && !mPendingRequestUpdate)) && // Ensure we wait if we're supposed to be sleeping AND do not require another update
850 !mDestroyUpdateRenderThread && // Ensure we don't wait if the update-render-thread is supposed to be destroyed
851 !mNewSurface && // Ensure we don't wait if we need to replace the surface
852 !mDeletedSurface && // Ensure we don't wait if we need to delete the surface
853 !mSurfaceResized) // Ensure we don't wait if we need to resize the surface
855 LOG_UPDATE_RENDER("WAIT: mUpdateRenderRunCount: %d", mUpdateRenderRunCount);
856 LOG_UPDATE_RENDER(" mUpdateRenderThreadCanSleep: %d, updateRequired: %d, mPendingRequestUpdate: %d", mUpdateRenderThreadCanSleep, updateRequired, mPendingRequestUpdate);
857 LOG_UPDATE_RENDER(" mDestroyUpdateRenderThread: %d", mDestroyUpdateRenderThread);
858 LOG_UPDATE_RENDER(" mNewSurface: %d", mNewSurface);
859 LOG_UPDATE_RENDER(" mDeletedSurface: %d", mDeletedSurface);
860 LOG_UPDATE_RENDER(" mSurfaceResized: %d", mSurfaceResized);
862 // Reset the time when the thread is waiting, so the sleep-until time for
863 // the first frame after resuming should be based on the actual start time
864 // of the first frame.
865 timeToSleepUntil = 0;
867 mUpdateRenderThreadWaitCondition.Wait(updateLock);
869 if(!mUseElapsedTimeAfterWait)
871 useElapsedTime = false;
875 LOG_COUNTER_UPDATE_RENDER("mUpdateRenderRunCount: %d", mUpdateRenderRunCount);
876 LOG_COUNTER_UPDATE_RENDER("mUpdateRenderThreadCanSleep: %d, updateRequired: %d, mPendingRequestUpdate: %d", mUpdateRenderThreadCanSleep, updateRequired, mPendingRequestUpdate);
877 LOG_COUNTER_UPDATE_RENDER("mDestroyUpdateRenderThread: %d", mDestroyUpdateRenderThread);
878 LOG_COUNTER_UPDATE_RENDER("mNewSurface: %d", mNewSurface);
879 LOG_COUNTER_UPDATE_RENDER("mDeletedSurface: %d", mDeletedSurface);
880 LOG_COUNTER_UPDATE_RENDER("mSurfaceResized: %d", mSurfaceResized);
882 mUseElapsedTimeAfterWait = FALSE;
883 mUpdateRenderThreadCanSleep = FALSE;
884 mPendingRequestUpdate = FALSE;
886 // If we've been asked to run Update/Render cycles a finite number of times then decrement so we wait after the
887 // requested number of cycles
888 if(mUpdateRenderRunCount > 0)
890 --mUpdateRenderRunCount;
893 // Keep the update-render thread alive if this thread is NOT to be destroyed
894 return !mDestroyUpdateRenderThread;
897 Dali::RenderSurfaceInterface* CombinedUpdateRenderController::ShouldSurfaceBeReplaced()
899 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
901 Dali::RenderSurfaceInterface* newSurface = mNewSurface;
907 void CombinedUpdateRenderController::SurfaceReplaced()
909 // Just increment the semaphore
910 mSurfaceSemaphore.Release(1);
913 Dali::RenderSurfaceInterface* CombinedUpdateRenderController::ShouldSurfaceBeDeleted()
915 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
917 Dali::RenderSurfaceInterface* deletedSurface = mDeletedSurface;
918 mDeletedSurface = NULL;
920 return deletedSurface;
923 void CombinedUpdateRenderController::SurfaceDeleted()
925 // Just increment the semaphore
926 mSurfaceSemaphore.Release(1);
929 void CombinedUpdateRenderController::SurfaceResized()
931 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
938 ///////////////////////////////////////////////////////////////////////////////////////////////////
940 ///////////////////////////////////////////////////////////////////////////////////////////////////
942 void CombinedUpdateRenderController::NotifyThreadInitialised()
944 // Just increment the semaphore
945 mEventThreadSemaphore.Release(1);
948 void CombinedUpdateRenderController::NotifyGraphicsInitialised()
950 mGraphicsInitializeWait.Notify();
953 void CombinedUpdateRenderController::AddPerformanceMarker(PerformanceInterface::MarkerType type)
955 if(mPerformanceInterface)
957 mPerformanceInterface->AddMarker(type);
961 /////////////////////////////////////////////////////////////////////////////////////////////////
962 // POST RENDERING: EVENT THREAD
963 /////////////////////////////////////////////////////////////////////////////////////////////////
965 void CombinedUpdateRenderController::PostRenderComplete()
967 ConditionalWait::ScopedLock lock(mPostRenderWaitCondition);
968 mPostRendering = FALSE;
969 mPostRenderWaitCondition.Notify(lock);
972 ///////////////////////////////////////////////////////////////////////////////////////////////////
973 // POST RENDERING: RENDER THREAD
974 ///////////////////////////////////////////////////////////////////////////////////////////////////
976 void CombinedUpdateRenderController::PostRenderStarted()
978 ConditionalWait::ScopedLock lock(mPostRenderWaitCondition);
979 mPostRendering = TRUE;
982 void CombinedUpdateRenderController::PostRenderWaitForCompletion()
984 ConditionalWait::ScopedLock lock(mPostRenderWaitCondition);
985 while(mPostRendering &&
986 !mNewSurface && // We should NOT wait if we're replacing the surface
987 !mDeletedSurface && // We should NOT wait if we're deleting the surface
988 !mDestroyUpdateRenderThread)
990 mPostRenderWaitCondition.Wait(lock);
994 } // namespace Adaptor
996 } // namespace Internal