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 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),
109 mThreadMode(threadMode),
110 mUpdateRenderRunCount(0),
111 mDestroyUpdateRenderThread(FALSE),
112 mUpdateRenderThreadCanSleep(FALSE),
113 mPendingRequestUpdate(FALSE),
114 mUseElapsedTimeAfterWait(FALSE),
116 mDeletedSurface(nullptr),
117 mPostRendering(FALSE),
120 mUploadWithoutRendering(FALSE),
121 mFirstFrameAfterResume(FALSE)
125 // Initialise frame delta/duration variables first
126 SetRenderRefreshRate(environmentOptions.GetRenderRefreshRate());
128 // Set the thread-synchronization interface on the render-surface
129 Dali::RenderSurfaceInterface* currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
132 currentSurface->SetThreadSynchronization(*this);
135 mSleepTrigger = TriggerEventFactory::CreateTriggerEvent(MakeCallback(this, &CombinedUpdateRenderController::ProcessSleepRequest), TriggerEventInterface::KEEP_ALIVE_AFTER_TRIGGER);
138 CombinedUpdateRenderController::~CombinedUpdateRenderController()
144 delete mPreRenderCallback;
145 delete mSleepTrigger;
148 void CombinedUpdateRenderController::Initialize()
152 // Ensure Update/Render Thread not already created
153 DALI_ASSERT_ALWAYS(!mUpdateRenderThread);
155 // Create Update/Render Thread
156 ConditionalWait::ScopedLock lock(mGraphicsInitializeWait);
157 mUpdateRenderThread = new pthread_t();
158 int error = pthread_create(mUpdateRenderThread, NULL, InternalUpdateRenderThreadEntryFunc, this);
159 DALI_ASSERT_ALWAYS(!error && "Return code from pthread_create() when creating UpdateRenderThread");
161 // The Update/Render thread will now run and initialise the graphics interface etc. and will then wait for Start to be called
162 // When this function returns, the application initialisation on the event thread should occur
165 void CombinedUpdateRenderController::Start()
169 DALI_ASSERT_ALWAYS(!mRunning && mUpdateRenderThread);
171 // Wait until all threads created in Initialise are up and running
172 for(unsigned int i = 0; i < CREATED_THREAD_COUNT; ++i)
174 mEventThreadSemaphore.Acquire();
179 LOG_EVENT("Startup Complete, starting Update/Render Thread");
181 RunUpdateRenderThread(CONTINUOUS, AnimationProgression::NONE, UpdateMode::NORMAL);
183 Dali::RenderSurfaceInterface* currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
186 currentSurface->StartRender();
189 DALI_LOG_RELEASE_INFO("CombinedUpdateRenderController::Start\n");
192 void CombinedUpdateRenderController::Pause()
198 PauseUpdateRenderThread();
200 AddPerformanceMarker(PerformanceInterface::PAUSED);
202 DALI_LOG_RELEASE_INFO("CombinedUpdateRenderController::Pause\n");
205 void CombinedUpdateRenderController::Resume()
209 if(!mRunning && IsUpdateRenderThreadPaused())
211 LOG_EVENT("Resuming");
213 RunUpdateRenderThread(CONTINUOUS, AnimationProgression::USE_ELAPSED_TIME, UpdateMode::NORMAL);
215 AddPerformanceMarker(PerformanceInterface::RESUME);
219 mFirstFrameAfterResume = TRUE;
221 DALI_LOG_RELEASE_INFO("CombinedUpdateRenderController::Resume\n");
225 DALI_LOG_RELEASE_INFO("CombinedUpdateRenderController::Resume: Already resumed [%d, %d, %d]\n", mRunning, mUpdateRenderRunCount, mUpdateRenderThreadCanSleep);
229 void CombinedUpdateRenderController::Stop()
233 // Stop Rendering and the Update/Render Thread
234 Dali::RenderSurfaceInterface* currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
237 currentSurface->StopRender();
240 StopUpdateRenderThread();
242 if(mUpdateRenderThread)
244 LOG_EVENT("Destroying UpdateRenderThread");
246 // wait for the thread to finish
247 pthread_join(*mUpdateRenderThread, NULL);
249 delete mUpdateRenderThread;
250 mUpdateRenderThread = NULL;
255 DALI_LOG_RELEASE_INFO("CombinedUpdateRenderController::Stop\n");
258 void CombinedUpdateRenderController::RequestUpdate()
262 // Increment the update-request count to the maximum
263 if(mUpdateRequestCount < MAXIMUM_UPDATE_REQUESTS)
265 ++mUpdateRequestCount;
268 if(mRunning && IsUpdateRenderThreadPaused())
270 LOG_EVENT("Processing");
272 RunUpdateRenderThread(CONTINUOUS, AnimationProgression::NONE, UpdateMode::NORMAL);
275 ConditionalWait::ScopedLock updateLock(mUpdateRenderThreadWaitCondition);
276 mPendingRequestUpdate = TRUE;
279 void CombinedUpdateRenderController::RequestUpdateOnce(UpdateMode updateMode)
281 // Increment the update-request count to the maximum
282 if(mUpdateRequestCount < MAXIMUM_UPDATE_REQUESTS)
284 ++mUpdateRequestCount;
287 if(IsUpdateRenderThreadPaused() || updateMode == UpdateMode::FORCE_RENDER)
291 // Run Update/Render once
292 RunUpdateRenderThread(ONCE, AnimationProgression::NONE, updateMode);
296 void CombinedUpdateRenderController::ReplaceSurface(Dali::RenderSurfaceInterface* newSurface)
300 if(mUpdateRenderThread)
302 // Set the ThreadSyncronizationInterface on the new surface
303 newSurface->SetThreadSynchronization(*this);
305 LOG_EVENT("Starting to replace the surface, event-thread blocked");
307 // Start replacing the surface.
309 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
310 mPostRendering = FALSE; // Clear the post-rendering flag as Update/Render thread will replace the surface now
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 mPostRendering = FALSE; // Clear the post-rendering flag as Update/Render thread will delete the surface now
334 mDeletedSurface = surface;
335 mUpdateRenderThreadWaitCondition.Notify(lock);
338 // Wait until the surface has been deleted
339 mSurfaceSemaphore.Acquire();
341 LOG_EVENT("Surface deleted, event-thread continuing");
345 void CombinedUpdateRenderController::WaitForGraphicsInitialization()
347 ConditionalWait::ScopedLock lk(mGraphicsInitializeWait);
350 if(mUpdateRenderThread)
352 LOG_EVENT("Waiting for graphics initialisation, event-thread blocked");
354 // Wait until the graphics has been initialised
355 mGraphicsInitializeWait.Wait(lk);
357 LOG_EVENT("graphics initialised, event-thread continuing");
361 void CombinedUpdateRenderController::ResizeSurface()
365 LOG_EVENT("Resize the surface");
368 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
369 // Surface is resized and the surface resized count is increased.
371 mUpdateRenderThreadWaitCondition.Notify(lock);
375 void CombinedUpdateRenderController::SetRenderRefreshRate(unsigned int numberOfFramesPerRender)
377 // Not protected by lock, but written to rarely so not worth adding a lock when reading
378 mDefaultFrameDelta = numberOfFramesPerRender * DEFAULT_FRAME_DURATION_IN_SECONDS;
379 mDefaultFrameDurationMilliseconds = uint64_t(numberOfFramesPerRender) * DEFAULT_FRAME_DURATION_IN_MILLISECONDS;
380 mDefaultFrameDurationNanoseconds = uint64_t(numberOfFramesPerRender) * DEFAULT_FRAME_DURATION_IN_NANOSECONDS;
381 mDefaultHalfFrameNanoseconds = mDefaultFrameDurationNanoseconds / 2u;
383 LOG_EVENT("mDefaultFrameDelta(%.6f), mDefaultFrameDurationMilliseconds(%lld), mDefaultFrameDurationNanoseconds(%lld)", mDefaultFrameDelta, mDefaultFrameDurationMilliseconds, mDefaultFrameDurationNanoseconds);
386 void CombinedUpdateRenderController::SetPreRenderCallback(CallbackBase* callback)
389 LOG_EVENT("Set PreRender Callback");
391 ConditionalWait::ScopedLock updateLock(mUpdateRenderThreadWaitCondition);
392 if(mPreRenderCallback)
394 delete mPreRenderCallback;
396 mPreRenderCallback = callback;
399 void CombinedUpdateRenderController::AddSurface(Dali::RenderSurfaceInterface* surface)
402 LOG_EVENT("Surface is added");
403 if(mUpdateRenderThread)
405 // Set the ThreadSyncronizationInterface on the added surface
406 surface->SetThreadSynchronization(*this);
410 int32_t CombinedUpdateRenderController::GetThreadId() const
415 ///////////////////////////////////////////////////////////////////////////////////////////////////
417 ///////////////////////////////////////////////////////////////////////////////////////////////////
419 void CombinedUpdateRenderController::RunUpdateRenderThread(int numberOfCycles, AnimationProgression animationProgression, UpdateMode updateMode)
421 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
425 case ThreadMode::NORMAL:
427 mUpdateRenderRunCount = numberOfCycles;
428 mUseElapsedTimeAfterWait = (animationProgression == AnimationProgression::USE_ELAPSED_TIME);
431 case ThreadMode::RUN_IF_REQUESTED:
433 if(updateMode != UpdateMode::FORCE_RENDER)
435 // Render only if the update mode is FORCE_RENDER which means the application requests it.
436 // We don't want to awake the update thread.
440 mUpdateRenderRunCount++; // Increase the update request count
441 mUseElapsedTimeAfterWait = TRUE; // The elapsed time should be used. We want animations to proceed.
446 mUpdateRenderThreadCanSleep = FALSE;
447 mUploadWithoutRendering = (updateMode == UpdateMode::SKIP_RENDER);
448 LOG_COUNTER_EVENT("mUpdateRenderRunCount: %d, mUseElapsedTimeAfterWait: %d", mUpdateRenderRunCount, mUseElapsedTimeAfterWait);
449 mUpdateRenderThreadWaitCondition.Notify(lock);
452 void CombinedUpdateRenderController::PauseUpdateRenderThread()
454 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
455 mUpdateRenderRunCount = 0;
458 void CombinedUpdateRenderController::StopUpdateRenderThread()
460 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
461 mDestroyUpdateRenderThread = TRUE;
462 mUpdateRenderThreadWaitCondition.Notify(lock);
465 bool CombinedUpdateRenderController::IsUpdateRenderThreadPaused()
467 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
469 if(mThreadMode == ThreadMode::RUN_IF_REQUESTED)
471 return !mRunning || mUpdateRenderThreadCanSleep;
474 return (mUpdateRenderRunCount != CONTINUOUS) || // Report paused if NOT continuously running
475 mUpdateRenderThreadCanSleep; // Report paused if sleeping
478 void CombinedUpdateRenderController::ProcessSleepRequest()
482 // Decrement Update request count
483 if(mUpdateRequestCount > 0)
485 --mUpdateRequestCount;
488 // Can sleep if our update-request count is 0
489 // Update/Render thread can choose to carry on updating if it determines more update/renders are required
490 if(mUpdateRequestCount == 0)
492 LOG_EVENT("Going to sleep");
494 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
495 mUpdateRenderThreadCanSleep = TRUE;
499 ///////////////////////////////////////////////////////////////////////////////////////////////////
500 // UPDATE/RENDER THREAD
501 ///////////////////////////////////////////////////////////////////////////////////////////////////
503 void CombinedUpdateRenderController::UpdateRenderThread()
505 ThreadSettings::SetThreadName("RenderThread\0");
506 mThreadId = ThreadSettings::GetThreadId();
508 // Install a function for logging
509 mEnvironmentOptions.InstallLogFunction();
511 // Install a function for tracing
512 mEnvironmentOptions.InstallTraceFunction();
514 LOG_UPDATE_RENDER("THREAD CREATED");
516 // Initialize graphics
517 GraphicsInterface& graphics = mAdaptorInterfaces.GetGraphicsInterface();
518 graphics.Initialize();
520 Dali::DisplayConnection& displayConnection = mAdaptorInterfaces.GetDisplayConnectionInterface();
521 displayConnection.Initialize(); //@todo Move InitializeGraphics code into graphics implementation
523 NotifyGraphicsInitialised();
525 //@todo Vk swaps this around, but we need to support surfaceless context for multi-window
526 graphics.ConfigureSurface(mAdaptorInterfaces.GetRenderSurfaceInterface());
528 // Tell core it has a context
529 mCore.ContextCreated();
531 NotifyThreadInitialised();
534 uint64_t lastFrameTime;
535 TimeService::GetNanoseconds(lastFrameTime);
536 uint64_t lastMemPoolLogTime = lastFrameTime;
538 LOG_UPDATE_RENDER("THREAD INITIALISED");
540 bool useElapsedTime = true;
541 bool updateRequired = true;
542 uint64_t timeToSleepUntil = 0;
543 int extraFramesDropped = 0;
545 const uint64_t memPoolInterval = 1e9 * float(mEnvironmentOptions.GetMemoryPoolInterval());
547 const unsigned int renderToFboInterval = mEnvironmentOptions.GetRenderToFboInterval();
548 const bool renderToFboEnabled = 0u != renderToFboInterval;
549 unsigned int frameCount = 0u;
551 while(UpdateRenderReady(useElapsedTime, updateRequired, timeToSleepUntil))
553 LOG_UPDATE_RENDER_TRACE;
556 bool uploadOnly = mUploadWithoutRendering;
557 unsigned int surfaceResized = mSurfaceResized;
558 Dali::RenderSurfaceInterface* deletedSurface = ShouldSurfaceBeDeleted();
560 // Performance statistics are logged upon a VSYNC tick so use this point for a VSync marker
561 AddPerformanceMarker(PerformanceInterface::VSYNC);
563 uint64_t currentFrameStartTime = 0;
564 TimeService::GetNanoseconds(currentFrameStartTime);
566 uint64_t timeSinceLastFrame = currentFrameStartTime - lastFrameTime;
568 // Optional FPS Tracking when continuously rendering
569 if(useElapsedTime && mFpsTracker.Enabled())
571 float absoluteTimeSinceLastRender = timeSinceLastFrame * NANOSECONDS_TO_SECOND;
572 mFpsTracker.Track(absoluteTimeSinceLastRender);
575 lastFrameTime = currentFrameStartTime; // Store frame start time
577 //////////////////////////////
579 //////////////////////////////
581 Dali::RenderSurfaceInterface* newSurface = ShouldSurfaceBeReplaced();
582 if(DALI_UNLIKELY(newSurface))
584 LOG_UPDATE_RENDER_TRACE_FMT("Replacing Surface");
585 // This is designed for replacing pixmap surfaces, but should work for window as well
586 // we need to delete the surface and renderable (pixmap / window)
587 // Then create a new pixmap/window and new surface
588 // If the new surface has a different display connection, then the context will be lost
589 mAdaptorInterfaces.GetDisplayConnectionInterface().Initialize();
590 graphics.ActivateSurfaceContext(newSurface);
591 // TODO: ReplaceGraphicsSurface doesn't work, InitializeGraphics()
592 // already creates new surface window, the surface and the context.
593 // We probably don't need ReplaceGraphicsSurface at all.
594 // newSurface->ReplaceGraphicsSurface();
598 const bool isRenderingToFbo = renderToFboEnabled && ((0u == frameCount) || (0u != frameCount % renderToFboInterval));
601 //////////////////////////////
603 //////////////////////////////
605 const uint32_t currentTime = static_cast<uint32_t>(currentFrameStartTime / NANOSECONDS_PER_MILLISECOND);
606 const uint32_t nextFrameTime = currentTime + static_cast<uint32_t>(mDefaultFrameDurationMilliseconds);
608 uint64_t noOfFramesSinceLastUpdate = 1;
609 float frameDelta = 0.0f;
612 if(mThreadMode == ThreadMode::RUN_IF_REQUESTED)
614 extraFramesDropped = 0;
615 while(timeSinceLastFrame >= mDefaultFrameDurationNanoseconds)
617 timeSinceLastFrame -= mDefaultFrameDurationNanoseconds;
618 extraFramesDropped++;
622 // If using the elapsed time, then calculate frameDelta as a multiple of mDefaultFrameDelta
623 noOfFramesSinceLastUpdate += extraFramesDropped;
625 frameDelta = mDefaultFrameDelta * noOfFramesSinceLastUpdate;
627 LOG_UPDATE_RENDER("timeSinceLastFrame(%llu) noOfFramesSinceLastUpdate(%u) frameDelta(%.6f)", timeSinceLastFrame, noOfFramesSinceLastUpdate, frameDelta);
629 Integration::UpdateStatus updateStatus;
631 AddPerformanceMarker(PerformanceInterface::UPDATE_START);
632 TRACE_UPDATE_RENDER_BEGIN("DALI_UPDATE");
633 mCore.Update(frameDelta,
640 TRACE_UPDATE_RENDER_END("DALI_UPDATE");
641 AddPerformanceMarker(PerformanceInterface::UPDATE_END);
643 unsigned int keepUpdatingStatus = updateStatus.KeepUpdating();
645 // Tell the event-thread to wake up (if asleep) and send a notification event to Core if required
646 if(updateStatus.NeedsNotification())
648 mNotificationTrigger.Trigger();
649 LOG_UPDATE_RENDER("Notification Triggered");
652 // Optional logging of update/render status
653 mUpdateStatusLogger.Log(keepUpdatingStatus);
655 //////////////////////////////
657 //////////////////////////////
659 graphics.FrameStart();
660 mAdaptorInterfaces.GetDisplayConnectionInterface().ConsumeEvents();
662 if(mPreRenderCallback != NULL)
664 bool keepCallback = CallbackBase::ExecuteReturn<bool>(*mPreRenderCallback);
667 delete mPreRenderCallback;
668 mPreRenderCallback = NULL;
672 graphics.ActivateResourceContext();
674 if(mFirstFrameAfterResume)
676 // mFirstFrameAfterResume is set to true when the thread is resumed
677 // Let graphics know the first frame after thread initialized or resumed.
678 graphics.SetFirstFrameAfterResume();
679 mFirstFrameAfterResume = FALSE;
682 Integration::RenderStatus renderStatus;
684 AddPerformanceMarker(PerformanceInterface::RENDER_START);
685 TRACE_UPDATE_RENDER_BEGIN("DALI_RENDER");
687 // Upload shared resources
688 mCore.PreRender(renderStatus, mForceClear);
690 if(!uploadOnly || surfaceResized)
692 // Go through each window
693 WindowContainer windows;
694 mAdaptorInterfaces.GetWindowContainerInterface(windows);
696 for(auto&& window : windows)
698 Dali::Integration::Scene scene = window->GetScene();
699 Dali::RenderSurfaceInterface* windowSurface = window->GetSurface();
701 if(scene && windowSurface)
703 Integration::RenderStatus windowRenderStatus;
705 const bool sceneSurfaceResized = scene.IsSurfaceRectChanged();
707 // clear previous frame damaged render items rects, buffer history is tracked on surface level
708 mDamagedRects.clear();
710 // Collect damage rects
711 mCore.PreRender(scene, mDamagedRects);
713 // Render off-screen frame buffers first if any
714 mCore.RenderScene(windowRenderStatus, scene, true);
716 Rect<int> clippingRect; // Empty for fbo rendering
718 // Switch to the context of the surface, merge damaged areas for previous frames
719 windowSurface->PreRender(sceneSurfaceResized, mDamagedRects, clippingRect); // Switch GL context
721 // Render the surface
722 mCore.RenderScene(windowRenderStatus, scene, false, clippingRect);
724 // Buffer swapping now happens when the surface render target is presented.
726 // If surface is resized, the surface resized count is decreased.
727 if(DALI_UNLIKELY(sceneSurfaceResized))
737 graphics.PostRender();
742 //////////////////////////////
744 //////////////////////////////
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(mUpdateRenderThreadWaitCondition);
968 mPostRendering = FALSE;
969 mUpdateRenderThreadWaitCondition.Notify(lock);
972 ///////////////////////////////////////////////////////////////////////////////////////////////////
973 // POST RENDERING: RENDER THREAD
974 ///////////////////////////////////////////////////////////////////////////////////////////////////
976 void CombinedUpdateRenderController::PostRenderStarted()
978 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
979 mPostRendering = TRUE;
982 void CombinedUpdateRenderController::PostRenderWaitForCompletion()
984 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
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 mUpdateRenderThreadWaitCondition.Wait(lock);
994 } // namespace Adaptor
996 } // namespace Internal