2 * Copyright (c) 2022 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/devel-api/adaptor-framework/thread-settings.h>
29 #include <dali/integration-api/adaptor-framework/trigger-event-factory.h>
30 #include <dali/internal/adaptor/common/adaptor-internal-services.h>
31 #include <dali/internal/adaptor/common/combined-update-render-controller-debug.h>
32 #include <dali/internal/graphics/common/graphics-interface.h>
33 #include <dali/internal/graphics/gles/egl-graphics.h>
34 #include <dali/internal/system/common/environment-options.h>
35 #include <dali/internal/system/common/time-service.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 mAdaptorInterfaces(adaptorInterfaces),
95 mPerformanceInterface(adaptorInterfaces.GetPerformanceInterface()),
96 mCore(adaptorInterfaces.GetCore()),
97 mEnvironmentOptions(environmentOptions),
98 mNotificationTrigger(adaptorInterfaces.GetProcessCoreEventsTrigger()),
100 mPreRenderCallback(NULL),
101 mUpdateRenderThread(NULL),
102 mDefaultFrameDelta(0.0f),
103 mDefaultFrameDurationMilliseconds(0u),
104 mDefaultFrameDurationNanoseconds(0u),
105 mDefaultHalfFrameNanoseconds(0u),
106 mUpdateRequestCount(0u),
108 mThreadMode(threadMode),
109 mUpdateRenderRunCount(0),
110 mDestroyUpdateRenderThread(FALSE),
111 mUpdateRenderThreadCanSleep(FALSE),
112 mPendingRequestUpdate(FALSE),
113 mUseElapsedTimeAfterWait(FALSE),
115 mDeletedSurface(nullptr),
116 mPostRendering(FALSE),
119 mUploadWithoutRendering(FALSE),
120 mFirstFrameAfterResume(FALSE)
124 // Initialise frame delta/duration variables first
125 SetRenderRefreshRate(environmentOptions.GetRenderRefreshRate());
127 // Set the thread-synchronization interface on the render-surface
128 Dali::RenderSurfaceInterface* currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
131 currentSurface->SetThreadSynchronization(*this);
134 mSleepTrigger = TriggerEventFactory::CreateTriggerEvent(MakeCallback(this, &CombinedUpdateRenderController::ProcessSleepRequest), TriggerEventInterface::KEEP_ALIVE_AFTER_TRIGGER);
137 CombinedUpdateRenderController::~CombinedUpdateRenderController()
143 delete mPreRenderCallback;
144 delete mSleepTrigger;
147 void CombinedUpdateRenderController::Initialize()
151 // Ensure Update/Render Thread not already created
152 DALI_ASSERT_ALWAYS(!mUpdateRenderThread);
154 // Create Update/Render Thread
155 ConditionalWait::ScopedLock lock(mGraphicsInitializeWait);
156 mUpdateRenderThread = new pthread_t();
157 int error = pthread_create(mUpdateRenderThread, NULL, InternalUpdateRenderThreadEntryFunc, this);
158 DALI_ASSERT_ALWAYS(!error && "Return code from pthread_create() when creating UpdateRenderThread");
160 // The Update/Render thread will now run and initialise the graphics interface etc. and will then wait for Start to be called
161 // When this function returns, the application initialisation on the event thread should occur
164 void CombinedUpdateRenderController::Start()
168 DALI_ASSERT_ALWAYS(!mRunning && mUpdateRenderThread);
170 // Wait until all threads created in Initialise are up and running
171 for(unsigned int i = 0; i < CREATED_THREAD_COUNT; ++i)
173 mEventThreadSemaphore.Acquire();
178 LOG_EVENT("Startup Complete, starting Update/Render Thread");
180 RunUpdateRenderThread(CONTINUOUS, AnimationProgression::NONE, UpdateMode::NORMAL);
182 Dali::RenderSurfaceInterface* currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
185 currentSurface->StartRender();
188 DALI_LOG_RELEASE_INFO("CombinedUpdateRenderController::Start\n");
191 void CombinedUpdateRenderController::Pause()
197 PauseUpdateRenderThread();
199 AddPerformanceMarker(PerformanceInterface::PAUSED);
201 DALI_LOG_RELEASE_INFO("CombinedUpdateRenderController::Pause\n");
204 void CombinedUpdateRenderController::Resume()
208 if(!mRunning && IsUpdateRenderThreadPaused())
210 LOG_EVENT("Resuming");
212 RunUpdateRenderThread(CONTINUOUS, AnimationProgression::USE_ELAPSED_TIME, UpdateMode::NORMAL);
214 AddPerformanceMarker(PerformanceInterface::RESUME);
218 mFirstFrameAfterResume = TRUE;
220 DALI_LOG_RELEASE_INFO("CombinedUpdateRenderController::Resume\n");
224 DALI_LOG_RELEASE_INFO("CombinedUpdateRenderController::Resume: Already resumed [%d, %d, %d]\n", mRunning, mUpdateRenderRunCount, mUpdateRenderThreadCanSleep);
228 void CombinedUpdateRenderController::Stop()
232 // Stop Rendering and the Update/Render Thread
233 Dali::RenderSurfaceInterface* currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
236 currentSurface->StopRender();
239 StopUpdateRenderThread();
241 if(mUpdateRenderThread)
243 LOG_EVENT("Destroying UpdateRenderThread");
245 // wait for the thread to finish
246 pthread_join(*mUpdateRenderThread, NULL);
248 delete mUpdateRenderThread;
249 mUpdateRenderThread = NULL;
254 DALI_LOG_RELEASE_INFO("CombinedUpdateRenderController::Stop\n");
257 void CombinedUpdateRenderController::RequestUpdate()
261 // Increment the update-request count to the maximum
262 if(mUpdateRequestCount < MAXIMUM_UPDATE_REQUESTS)
264 ++mUpdateRequestCount;
267 if(mRunning && IsUpdateRenderThreadPaused())
269 LOG_EVENT("Processing");
271 RunUpdateRenderThread(CONTINUOUS, AnimationProgression::NONE, UpdateMode::NORMAL);
274 ConditionalWait::ScopedLock updateLock(mUpdateRenderThreadWaitCondition);
275 mPendingRequestUpdate = TRUE;
278 void CombinedUpdateRenderController::RequestUpdateOnce(UpdateMode updateMode)
280 // Increment the update-request count to the maximum
281 if(mUpdateRequestCount < MAXIMUM_UPDATE_REQUESTS)
283 ++mUpdateRequestCount;
286 if(IsUpdateRenderThreadPaused() || updateMode == UpdateMode::FORCE_RENDER)
290 // Run Update/Render once
291 RunUpdateRenderThread(ONCE, AnimationProgression::NONE, updateMode);
295 void CombinedUpdateRenderController::ReplaceSurface(Dali::RenderSurfaceInterface* newSurface)
299 if(mUpdateRenderThread)
301 // Set the ThreadSyncronizationInterface on the new surface
302 newSurface->SetThreadSynchronization(*this);
304 LOG_EVENT("Starting to replace the surface, event-thread blocked");
306 // Start replacing the surface.
308 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
309 mPostRendering = FALSE; // Clear the post-rendering flag as Update/Render thread will replace the surface now
310 mNewSurface = newSurface;
311 mUpdateRenderThreadWaitCondition.Notify(lock);
314 // Wait until the surface has been replaced
315 mSurfaceSemaphore.Acquire();
317 LOG_EVENT("Surface replaced, event-thread continuing");
321 void CombinedUpdateRenderController::DeleteSurface(Dali::RenderSurfaceInterface* surface)
325 if(mUpdateRenderThread)
327 LOG_EVENT("Starting to delete the surface, event-thread blocked");
329 // Start replacing the surface.
331 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
332 mPostRendering = FALSE; // Clear the post-rendering flag as Update/Render thread will delete the surface now
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 mPostRendering = FALSE; // Clear the post-rendering flag as Update/Render thread will resize the surface now
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 ///////////////////////////////////////////////////////////////////////////////////////////////////
412 ///////////////////////////////////////////////////////////////////////////////////////////////////
414 void CombinedUpdateRenderController::RunUpdateRenderThread(int numberOfCycles, AnimationProgression animationProgression, UpdateMode updateMode)
416 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
420 case ThreadMode::NORMAL:
422 mUpdateRenderRunCount = numberOfCycles;
423 mUseElapsedTimeAfterWait = (animationProgression == AnimationProgression::USE_ELAPSED_TIME);
426 case ThreadMode::RUN_IF_REQUESTED:
428 if(updateMode != UpdateMode::FORCE_RENDER)
430 // Render only if the update mode is FORCE_RENDER which means the application requests it.
431 // We don't want to awake the update thread.
435 mUpdateRenderRunCount++; // Increase the update request count
436 mUseElapsedTimeAfterWait = TRUE; // The elapsed time should be used. We want animations to proceed.
441 mUpdateRenderThreadCanSleep = FALSE;
442 mUploadWithoutRendering = (updateMode == UpdateMode::SKIP_RENDER);
443 LOG_COUNTER_EVENT("mUpdateRenderRunCount: %d, mUseElapsedTimeAfterWait: %d", mUpdateRenderRunCount, mUseElapsedTimeAfterWait);
444 mUpdateRenderThreadWaitCondition.Notify(lock);
447 void CombinedUpdateRenderController::PauseUpdateRenderThread()
449 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
450 mUpdateRenderRunCount = 0;
453 void CombinedUpdateRenderController::StopUpdateRenderThread()
455 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
456 mDestroyUpdateRenderThread = TRUE;
457 mUpdateRenderThreadWaitCondition.Notify(lock);
460 bool CombinedUpdateRenderController::IsUpdateRenderThreadPaused()
462 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
464 if(mThreadMode == ThreadMode::RUN_IF_REQUESTED)
466 return !mRunning || mUpdateRenderThreadCanSleep;
469 return (mUpdateRenderRunCount != CONTINUOUS) || // Report paused if NOT continuously running
470 mUpdateRenderThreadCanSleep; // Report paused if sleeping
473 void CombinedUpdateRenderController::ProcessSleepRequest()
477 // Decrement Update request count
478 if(mUpdateRequestCount > 0)
480 --mUpdateRequestCount;
483 // Can sleep if our update-request count is 0
484 // Update/Render thread can choose to carry on updating if it determines more update/renders are required
485 if(mUpdateRequestCount == 0)
487 LOG_EVENT("Going to sleep");
489 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
490 mUpdateRenderThreadCanSleep = TRUE;
494 ///////////////////////////////////////////////////////////////////////////////////////////////////
495 // UPDATE/RENDER THREAD
496 ///////////////////////////////////////////////////////////////////////////////////////////////////
498 void CombinedUpdateRenderController::UpdateRenderThread()
500 SetThreadName("RenderThread\0");
502 // Install a function for logging
503 mEnvironmentOptions.InstallLogFunction();
505 // Install a function for tracing
506 mEnvironmentOptions.InstallTraceFunction();
508 LOG_UPDATE_RENDER("THREAD CREATED");
510 // Initialize graphics
511 GraphicsInterface& graphics = mAdaptorInterfaces.GetGraphicsInterface();
512 graphics.Initialize();
514 Dali::DisplayConnection& displayConnection = mAdaptorInterfaces.GetDisplayConnectionInterface();
515 displayConnection.Initialize(); //@todo Move InitializeGraphics code into graphics implementation
517 NotifyGraphicsInitialised();
519 //@todo Vk swaps this around, but we need to support surfaceless context for multi-window
520 graphics.ConfigureSurface(mAdaptorInterfaces.GetRenderSurfaceInterface());
522 // Tell core it has a context
523 mCore.ContextCreated();
525 NotifyThreadInitialised();
528 uint64_t lastFrameTime;
529 TimeService::GetNanoseconds(lastFrameTime);
530 uint64_t lastMemPoolLogTime = lastFrameTime;
532 LOG_UPDATE_RENDER("THREAD INITIALISED");
534 bool useElapsedTime = true;
535 bool updateRequired = true;
536 uint64_t timeToSleepUntil = 0;
537 int extraFramesDropped = 0;
539 const uint64_t memPoolInterval = 1e9 * float(mEnvironmentOptions.GetMemoryPoolInterval());
541 const unsigned int renderToFboInterval = mEnvironmentOptions.GetRenderToFboInterval();
542 const bool renderToFboEnabled = 0u != renderToFboInterval;
543 unsigned int frameCount = 0u;
545 while(UpdateRenderReady(useElapsedTime, updateRequired, timeToSleepUntil))
547 LOG_UPDATE_RENDER_TRACE;
550 bool uploadOnly = mUploadWithoutRendering;
551 unsigned int surfaceResized = mSurfaceResized;
553 // Performance statistics are logged upon a VSYNC tick so use this point for a VSync marker
554 AddPerformanceMarker(PerformanceInterface::VSYNC);
556 uint64_t currentFrameStartTime = 0;
557 TimeService::GetNanoseconds(currentFrameStartTime);
559 uint64_t timeSinceLastFrame = currentFrameStartTime - lastFrameTime;
561 // Optional FPS Tracking when continuously rendering
562 if(useElapsedTime && mFpsTracker.Enabled())
564 float absoluteTimeSinceLastRender = timeSinceLastFrame * NANOSECONDS_TO_SECOND;
565 mFpsTracker.Track(absoluteTimeSinceLastRender);
568 lastFrameTime = currentFrameStartTime; // Store frame start time
570 //////////////////////////////
572 //////////////////////////////
574 Dali::RenderSurfaceInterface* newSurface = ShouldSurfaceBeReplaced();
575 if(DALI_UNLIKELY(newSurface))
577 LOG_UPDATE_RENDER_TRACE_FMT("Replacing Surface");
578 // This is designed for replacing pixmap surfaces, but should work for window as well
579 // we need to delete the surface and renderable (pixmap / window)
580 // Then create a new pixmap/window and new surface
581 // If the new surface has a different display connection, then the context will be lost
582 mAdaptorInterfaces.GetDisplayConnectionInterface().Initialize();
583 graphics.ActivateSurfaceContext(newSurface);
584 // TODO: ReplaceGraphicsSurface doesn't work, InitializeGraphics()
585 // already creates new surface window, the surface and the context.
586 // We probably don't need ReplaceGraphicsSurface at all.
587 // newSurface->ReplaceGraphicsSurface();
591 const bool isRenderingToFbo = renderToFboEnabled && ((0u == frameCount) || (0u != frameCount % renderToFboInterval));
594 //////////////////////////////
596 //////////////////////////////
598 const uint32_t currentTime = static_cast<uint32_t>(currentFrameStartTime / NANOSECONDS_PER_MILLISECOND);
599 const uint32_t nextFrameTime = currentTime + static_cast<uint32_t>(mDefaultFrameDurationMilliseconds);
601 uint64_t noOfFramesSinceLastUpdate = 1;
602 float frameDelta = 0.0f;
605 if(mThreadMode == ThreadMode::RUN_IF_REQUESTED)
607 extraFramesDropped = 0;
608 while(timeSinceLastFrame >= mDefaultFrameDurationNanoseconds)
610 timeSinceLastFrame -= mDefaultFrameDurationNanoseconds;
611 extraFramesDropped++;
615 // If using the elapsed time, then calculate frameDelta as a multiple of mDefaultFrameDelta
616 noOfFramesSinceLastUpdate += extraFramesDropped;
618 frameDelta = mDefaultFrameDelta * noOfFramesSinceLastUpdate;
620 LOG_UPDATE_RENDER("timeSinceLastFrame(%llu) noOfFramesSinceLastUpdate(%u) frameDelta(%.6f)", timeSinceLastFrame, noOfFramesSinceLastUpdate, frameDelta);
622 Integration::UpdateStatus updateStatus;
624 AddPerformanceMarker(PerformanceInterface::UPDATE_START);
625 TRACE_UPDATE_RENDER_BEGIN("DALI_UPDATE");
626 mCore.Update(frameDelta,
633 TRACE_UPDATE_RENDER_END("DALI_UPDATE");
634 AddPerformanceMarker(PerformanceInterface::UPDATE_END);
636 unsigned int keepUpdatingStatus = updateStatus.KeepUpdating();
638 // Tell the event-thread to wake up (if asleep) and send a notification event to Core if required
639 if(updateStatus.NeedsNotification())
641 mNotificationTrigger.Trigger();
642 LOG_UPDATE_RENDER("Notification Triggered");
645 // Optional logging of update/render status
646 mUpdateStatusLogger.Log(keepUpdatingStatus);
648 //////////////////////////////
650 //////////////////////////////
652 graphics.FrameStart();
653 mAdaptorInterfaces.GetDisplayConnectionInterface().ConsumeEvents();
655 if(mPreRenderCallback != NULL)
657 bool keepCallback = CallbackBase::ExecuteReturn<bool>(*mPreRenderCallback);
660 delete mPreRenderCallback;
661 mPreRenderCallback = NULL;
665 graphics.ActivateResourceContext();
667 if(mFirstFrameAfterResume)
669 // mFirstFrameAfterResume is set to true when the thread is resumed
670 // Let graphics know the first frame after thread initialized or resumed.
671 graphics.SetFirstFrameAfterResume();
672 mFirstFrameAfterResume = FALSE;
675 Integration::RenderStatus renderStatus;
677 AddPerformanceMarker(PerformanceInterface::RENDER_START);
678 TRACE_UPDATE_RENDER_BEGIN("DALI_RENDER");
680 // Upload shared resources
681 mCore.PreRender(renderStatus, mForceClear);
683 if(!uploadOnly || surfaceResized)
685 // Go through each window
686 WindowContainer windows;
687 mAdaptorInterfaces.GetWindowContainerInterface(windows);
689 for(auto&& window : windows)
691 Dali::Integration::Scene scene = window->GetScene();
692 Dali::RenderSurfaceInterface* windowSurface = window->GetSurface();
694 if(scene && windowSurface)
696 Integration::RenderStatus windowRenderStatus;
698 const bool sceneSurfaceResized = scene.IsSurfaceRectChanged();
700 // clear previous frame damaged render items rects, buffer history is tracked on surface level
701 mDamagedRects.clear();
703 // Collect damage rects
704 mCore.PreRender(scene, mDamagedRects);
706 // Render off-screen frame buffers first if any
707 mCore.RenderScene(windowRenderStatus, scene, true);
709 Rect<int> clippingRect; // Empty for fbo rendering
711 // Switch to the context of the surface, merge damaged areas for previous frames
712 windowSurface->PreRender(sceneSurfaceResized, mDamagedRects, clippingRect); // Switch GL context
714 // Render the surface
715 mCore.RenderScene(windowRenderStatus, scene, false, clippingRect);
717 // Buffer swapping now happens when the surface render target is presented.
719 // If surface is resized, the surface resized count is decreased.
720 if(DALI_UNLIKELY(sceneSurfaceResized))
730 graphics.PostRender();
735 //////////////////////////////
737 //////////////////////////////
739 Dali::RenderSurfaceInterface* deletedSurface = ShouldSurfaceBeDeleted();
740 if(DALI_UNLIKELY(deletedSurface))
742 LOG_UPDATE_RENDER_TRACE_FMT("Deleting Surface");
744 deletedSurface->DestroySurface();
749 TRACE_UPDATE_RENDER_END("DALI_RENDER");
750 AddPerformanceMarker(PerformanceInterface::RENDER_END);
752 // if the memory pool interval is set and has elapsed, log the graphics memory pools
753 if(0 < memPoolInterval && memPoolInterval < lastFrameTime - lastMemPoolLogTime)
755 lastMemPoolLogTime = lastFrameTime;
756 graphics.LogMemoryPools();
761 // Trigger event thread to request Update/Render thread to sleep if update not required
762 if((Integration::KeepUpdating::NOT_REQUESTED == keepUpdatingStatus) && !renderStatus.NeedsUpdate())
764 mSleepTrigger->Trigger();
765 updateRequired = false;
766 LOG_UPDATE_RENDER("Sleep Triggered");
770 updateRequired = true;
773 //////////////////////////////
775 //////////////////////////////
777 extraFramesDropped = 0;
779 if(timeToSleepUntil == 0)
781 // If this is the first frame after the thread is initialized or resumed, we
782 // use the actual time the current frame starts from to calculate the time to
783 // sleep until the next frame.
784 timeToSleepUntil = currentFrameStartTime + mDefaultFrameDurationNanoseconds;
788 // Otherwise, always use the sleep-until time calculated in the last frame to
789 // calculate the time to sleep until the next frame. In this way, if there is
790 // any time gap between the current frame and the next frame, or if update or
791 // rendering in the current frame takes too much time so that the specified
792 // sleep-until time has already passed, it will try to keep the frames syncing
793 // by shortening the duration of the next frame.
794 timeToSleepUntil += mDefaultFrameDurationNanoseconds;
796 // Check the current time at the end of the frame
797 uint64_t currentFrameEndTime = 0;
798 TimeService::GetNanoseconds(currentFrameEndTime);
799 while(currentFrameEndTime > timeToSleepUntil + mDefaultFrameDurationNanoseconds)
801 // We are more than one frame behind already, so just drop the next frames
802 // until the sleep-until time is later than the current time so that we can
804 timeToSleepUntil += mDefaultFrameDurationNanoseconds;
805 extraFramesDropped++;
809 // Render to FBO is intended to measure fps above 60 so sleep is not wanted.
810 if(0u == renderToFboInterval)
812 // Sleep until at least the the default frame duration has elapsed. This will return immediately if the specified end-time has already passed.
813 TimeService::SleepUntil(timeToSleepUntil);
817 // Inform core of context destruction
818 mCore.ContextDestroyed();
820 WindowContainer windows;
821 mAdaptorInterfaces.GetWindowContainerInterface(windows);
824 for(auto&& window : windows)
826 Dali::RenderSurfaceInterface* surface = window->GetSurface();
827 surface->DestroySurface();
832 LOG_UPDATE_RENDER("THREAD DESTROYED");
834 // Uninstall the logging function
835 mEnvironmentOptions.UnInstallLogFunction();
838 bool CombinedUpdateRenderController::UpdateRenderReady(bool& useElapsedTime, bool updateRequired, uint64_t& timeToSleepUntil)
840 useElapsedTime = true;
842 ConditionalWait::ScopedLock updateLock(mUpdateRenderThreadWaitCondition);
843 while((!mUpdateRenderRunCount || // Should try to wait if event-thread has paused the Update/Render thread
844 (mUpdateRenderThreadCanSleep && !updateRequired && !mPendingRequestUpdate)) && // Ensure we wait if we're supposed to be sleeping AND do not require another update
845 !mDestroyUpdateRenderThread && // Ensure we don't wait if the update-render-thread is supposed to be destroyed
846 !mNewSurface && // Ensure we don't wait if we need to replace the surface
847 !mDeletedSurface && // Ensure we don't wait if we need to delete the surface
848 !mSurfaceResized) // Ensure we don't wait if we need to resize the surface
850 LOG_UPDATE_RENDER("WAIT: mUpdateRenderRunCount: %d", mUpdateRenderRunCount);
851 LOG_UPDATE_RENDER(" mUpdateRenderThreadCanSleep: %d, updateRequired: %d, mPendingRequestUpdate: %d", mUpdateRenderThreadCanSleep, updateRequired, mPendingRequestUpdate);
852 LOG_UPDATE_RENDER(" mDestroyUpdateRenderThread: %d", mDestroyUpdateRenderThread);
853 LOG_UPDATE_RENDER(" mNewSurface: %d", mNewSurface);
854 LOG_UPDATE_RENDER(" mDeletedSurface: %d", mDeletedSurface);
855 LOG_UPDATE_RENDER(" mSurfaceResized: %d", mSurfaceResized);
857 // Reset the time when the thread is waiting, so the sleep-until time for
858 // the first frame after resuming should be based on the actual start time
859 // of the first frame.
860 timeToSleepUntil = 0;
862 mUpdateRenderThreadWaitCondition.Wait(updateLock);
864 if(!mUseElapsedTimeAfterWait)
866 useElapsedTime = false;
870 LOG_COUNTER_UPDATE_RENDER("mUpdateRenderRunCount: %d", mUpdateRenderRunCount);
871 LOG_COUNTER_UPDATE_RENDER("mUpdateRenderThreadCanSleep: %d, updateRequired: %d, mPendingRequestUpdate: %d", mUpdateRenderThreadCanSleep, updateRequired, mPendingRequestUpdate);
872 LOG_COUNTER_UPDATE_RENDER("mDestroyUpdateRenderThread: %d", mDestroyUpdateRenderThread);
873 LOG_COUNTER_UPDATE_RENDER("mNewSurface: %d", mNewSurface);
874 LOG_COUNTER_UPDATE_RENDER("mDeletedSurface: %d", mDeletedSurface);
875 LOG_COUNTER_UPDATE_RENDER("mSurfaceResized: %d", mSurfaceResized);
877 mUseElapsedTimeAfterWait = FALSE;
878 mUpdateRenderThreadCanSleep = FALSE;
879 mPendingRequestUpdate = FALSE;
881 // If we've been asked to run Update/Render cycles a finite number of times then decrement so we wait after the
882 // requested number of cycles
883 if(mUpdateRenderRunCount > 0)
885 --mUpdateRenderRunCount;
888 // Keep the update-render thread alive if this thread is NOT to be destroyed
889 return !mDestroyUpdateRenderThread;
892 Dali::RenderSurfaceInterface* CombinedUpdateRenderController::ShouldSurfaceBeReplaced()
894 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
896 Dali::RenderSurfaceInterface* newSurface = mNewSurface;
902 void CombinedUpdateRenderController::SurfaceReplaced()
904 // Just increment the semaphore
905 mSurfaceSemaphore.Release(1);
908 Dali::RenderSurfaceInterface* CombinedUpdateRenderController::ShouldSurfaceBeDeleted()
910 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
912 Dali::RenderSurfaceInterface* deletedSurface = mDeletedSurface;
913 mDeletedSurface = NULL;
915 return deletedSurface;
918 void CombinedUpdateRenderController::SurfaceDeleted()
920 // Just increment the semaphore
921 mSurfaceSemaphore.Release(1);
924 void CombinedUpdateRenderController::SurfaceResized()
926 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
933 ///////////////////////////////////////////////////////////////////////////////////////////////////
935 ///////////////////////////////////////////////////////////////////////////////////////////////////
937 void CombinedUpdateRenderController::NotifyThreadInitialised()
939 // Just increment the semaphore
940 mEventThreadSemaphore.Release(1);
943 void CombinedUpdateRenderController::NotifyGraphicsInitialised()
945 mGraphicsInitializeWait.Notify();
948 void CombinedUpdateRenderController::AddPerformanceMarker(PerformanceInterface::MarkerType type)
950 if(mPerformanceInterface)
952 mPerformanceInterface->AddMarker(type);
956 /////////////////////////////////////////////////////////////////////////////////////////////////
957 // POST RENDERING: EVENT THREAD
958 /////////////////////////////////////////////////////////////////////////////////////////////////
960 void CombinedUpdateRenderController::PostRenderComplete()
962 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
963 mPostRendering = FALSE;
964 mUpdateRenderThreadWaitCondition.Notify(lock);
967 ///////////////////////////////////////////////////////////////////////////////////////////////////
968 // POST RENDERING: RENDER THREAD
969 ///////////////////////////////////////////////////////////////////////////////////////////////////
971 void CombinedUpdateRenderController::PostRenderStarted()
973 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
974 mPostRendering = TRUE;
977 void CombinedUpdateRenderController::PostRenderWaitForCompletion()
979 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
980 while(mPostRendering &&
981 !mNewSurface && // We should NOT wait if we're replacing the surface
982 !mDeletedSurface && // We should NOT wait if we're deleting the surface
983 !mDestroyUpdateRenderThread)
985 mUpdateRenderThreadWaitCondition.Wait(lock);
989 } // namespace Adaptor
991 } // namespace Internal