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/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 mPostRendering = FALSE; // Clear the post-rendering flag as Update/Render thread will resize the surface now
370 // Surface is resized and the surface resized count is increased.
372 mUpdateRenderThreadWaitCondition.Notify(lock);
376 void CombinedUpdateRenderController::SetRenderRefreshRate(unsigned int numberOfFramesPerRender)
378 // Not protected by lock, but written to rarely so not worth adding a lock when reading
379 mDefaultFrameDelta = numberOfFramesPerRender * DEFAULT_FRAME_DURATION_IN_SECONDS;
380 mDefaultFrameDurationMilliseconds = uint64_t(numberOfFramesPerRender) * DEFAULT_FRAME_DURATION_IN_MILLISECONDS;
381 mDefaultFrameDurationNanoseconds = uint64_t(numberOfFramesPerRender) * DEFAULT_FRAME_DURATION_IN_NANOSECONDS;
382 mDefaultHalfFrameNanoseconds = mDefaultFrameDurationNanoseconds / 2u;
384 LOG_EVENT("mDefaultFrameDelta(%.6f), mDefaultFrameDurationMilliseconds(%lld), mDefaultFrameDurationNanoseconds(%lld)", mDefaultFrameDelta, mDefaultFrameDurationMilliseconds, mDefaultFrameDurationNanoseconds);
387 void CombinedUpdateRenderController::SetPreRenderCallback(CallbackBase* callback)
390 LOG_EVENT("Set PreRender Callback");
392 ConditionalWait::ScopedLock updateLock(mUpdateRenderThreadWaitCondition);
393 if(mPreRenderCallback)
395 delete mPreRenderCallback;
397 mPreRenderCallback = callback;
400 void CombinedUpdateRenderController::AddSurface(Dali::RenderSurfaceInterface* surface)
403 LOG_EVENT("Surface is added");
404 if(mUpdateRenderThread)
406 // Set the ThreadSyncronizationInterface on the added surface
407 surface->SetThreadSynchronization(*this);
411 int32_t CombinedUpdateRenderController::GetThreadId() const
416 ///////////////////////////////////////////////////////////////////////////////////////////////////
418 ///////////////////////////////////////////////////////////////////////////////////////////////////
420 void CombinedUpdateRenderController::RunUpdateRenderThread(int numberOfCycles, AnimationProgression animationProgression, UpdateMode updateMode)
422 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
426 case ThreadMode::NORMAL:
428 mUpdateRenderRunCount = numberOfCycles;
429 mUseElapsedTimeAfterWait = (animationProgression == AnimationProgression::USE_ELAPSED_TIME);
432 case ThreadMode::RUN_IF_REQUESTED:
434 if(updateMode != UpdateMode::FORCE_RENDER)
436 // Render only if the update mode is FORCE_RENDER which means the application requests it.
437 // We don't want to awake the update thread.
441 mUpdateRenderRunCount++; // Increase the update request count
442 mUseElapsedTimeAfterWait = TRUE; // The elapsed time should be used. We want animations to proceed.
447 mUpdateRenderThreadCanSleep = FALSE;
448 mUploadWithoutRendering = (updateMode == UpdateMode::SKIP_RENDER);
449 LOG_COUNTER_EVENT("mUpdateRenderRunCount: %d, mUseElapsedTimeAfterWait: %d", mUpdateRenderRunCount, mUseElapsedTimeAfterWait);
450 mUpdateRenderThreadWaitCondition.Notify(lock);
453 void CombinedUpdateRenderController::PauseUpdateRenderThread()
455 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
456 mUpdateRenderRunCount = 0;
459 void CombinedUpdateRenderController::StopUpdateRenderThread()
461 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
462 mDestroyUpdateRenderThread = TRUE;
463 mUpdateRenderThreadWaitCondition.Notify(lock);
466 bool CombinedUpdateRenderController::IsUpdateRenderThreadPaused()
468 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
470 if(mThreadMode == ThreadMode::RUN_IF_REQUESTED)
472 return !mRunning || mUpdateRenderThreadCanSleep;
475 return (mUpdateRenderRunCount != CONTINUOUS) || // Report paused if NOT continuously running
476 mUpdateRenderThreadCanSleep; // Report paused if sleeping
479 void CombinedUpdateRenderController::ProcessSleepRequest()
483 // Decrement Update request count
484 if(mUpdateRequestCount > 0)
486 --mUpdateRequestCount;
489 // Can sleep if our update-request count is 0
490 // Update/Render thread can choose to carry on updating if it determines more update/renders are required
491 if(mUpdateRequestCount == 0)
493 LOG_EVENT("Going to sleep");
495 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
496 mUpdateRenderThreadCanSleep = TRUE;
500 ///////////////////////////////////////////////////////////////////////////////////////////////////
501 // UPDATE/RENDER THREAD
502 ///////////////////////////////////////////////////////////////////////////////////////////////////
504 void CombinedUpdateRenderController::UpdateRenderThread()
506 ThreadSettings::SetThreadName("RenderThread\0");
507 mThreadId = ThreadSettings::GetThreadId();
509 // Install a function for logging
510 mEnvironmentOptions.InstallLogFunction();
512 // Install a function for tracing
513 mEnvironmentOptions.InstallTraceFunction();
515 LOG_UPDATE_RENDER("THREAD CREATED");
517 // Initialize graphics
518 GraphicsInterface& graphics = mAdaptorInterfaces.GetGraphicsInterface();
519 graphics.Initialize();
521 Dali::DisplayConnection& displayConnection = mAdaptorInterfaces.GetDisplayConnectionInterface();
522 displayConnection.Initialize(); //@todo Move InitializeGraphics code into graphics implementation
524 NotifyGraphicsInitialised();
526 //@todo Vk swaps this around, but we need to support surfaceless context for multi-window
527 graphics.ConfigureSurface(mAdaptorInterfaces.GetRenderSurfaceInterface());
529 // Tell core it has a context
530 mCore.ContextCreated();
532 NotifyThreadInitialised();
535 uint64_t lastFrameTime;
536 TimeService::GetNanoseconds(lastFrameTime);
537 uint64_t lastMemPoolLogTime = lastFrameTime;
539 LOG_UPDATE_RENDER("THREAD INITIALISED");
541 bool useElapsedTime = true;
542 bool updateRequired = true;
543 uint64_t timeToSleepUntil = 0;
544 int extraFramesDropped = 0;
546 const uint64_t memPoolInterval = 1e9 * float(mEnvironmentOptions.GetMemoryPoolInterval());
548 const unsigned int renderToFboInterval = mEnvironmentOptions.GetRenderToFboInterval();
549 const bool renderToFboEnabled = 0u != renderToFboInterval;
550 unsigned int frameCount = 0u;
552 while(UpdateRenderReady(useElapsedTime, updateRequired, timeToSleepUntil))
554 LOG_UPDATE_RENDER_TRACE;
557 bool uploadOnly = mUploadWithoutRendering;
558 unsigned int surfaceResized = mSurfaceResized;
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 //////////////////////////////
746 Dali::RenderSurfaceInterface* deletedSurface = ShouldSurfaceBeDeleted();
747 if(DALI_UNLIKELY(deletedSurface))
749 LOG_UPDATE_RENDER_TRACE_FMT("Deleting Surface");
751 deletedSurface->DestroySurface();
756 TRACE_UPDATE_RENDER_END("DALI_RENDER");
757 AddPerformanceMarker(PerformanceInterface::RENDER_END);
759 // if the memory pool interval is set and has elapsed, log the graphics memory pools
760 if(0 < memPoolInterval && memPoolInterval < lastFrameTime - lastMemPoolLogTime)
762 lastMemPoolLogTime = lastFrameTime;
763 graphics.LogMemoryPools();
768 // Trigger event thread to request Update/Render thread to sleep if update not required
769 if((Integration::KeepUpdating::NOT_REQUESTED == keepUpdatingStatus) && !renderStatus.NeedsUpdate())
771 mSleepTrigger->Trigger();
772 updateRequired = false;
773 LOG_UPDATE_RENDER("Sleep Triggered");
777 updateRequired = true;
780 //////////////////////////////
782 //////////////////////////////
784 extraFramesDropped = 0;
786 if(timeToSleepUntil == 0)
788 // If this is the first frame after the thread is initialized or resumed, we
789 // use the actual time the current frame starts from to calculate the time to
790 // sleep until the next frame.
791 timeToSleepUntil = currentFrameStartTime + mDefaultFrameDurationNanoseconds;
795 // Otherwise, always use the sleep-until time calculated in the last frame to
796 // calculate the time to sleep until the next frame. In this way, if there is
797 // any time gap between the current frame and the next frame, or if update or
798 // rendering in the current frame takes too much time so that the specified
799 // sleep-until time has already passed, it will try to keep the frames syncing
800 // by shortening the duration of the next frame.
801 timeToSleepUntil += mDefaultFrameDurationNanoseconds;
803 // Check the current time at the end of the frame
804 uint64_t currentFrameEndTime = 0;
805 TimeService::GetNanoseconds(currentFrameEndTime);
806 while(currentFrameEndTime > timeToSleepUntil + mDefaultFrameDurationNanoseconds)
808 // We are more than one frame behind already, so just drop the next frames
809 // until the sleep-until time is later than the current time so that we can
811 timeToSleepUntil += mDefaultFrameDurationNanoseconds;
812 extraFramesDropped++;
816 // Render to FBO is intended to measure fps above 60 so sleep is not wanted.
817 if(0u == renderToFboInterval)
819 // Sleep until at least the the default frame duration has elapsed. This will return immediately if the specified end-time has already passed.
820 TimeService::SleepUntil(timeToSleepUntil);
824 // Inform core of context destruction
825 mCore.ContextDestroyed();
827 WindowContainer windows;
828 mAdaptorInterfaces.GetWindowContainerInterface(windows);
831 for(auto&& window : windows)
833 Dali::RenderSurfaceInterface* surface = window->GetSurface();
834 surface->DestroySurface();
839 LOG_UPDATE_RENDER("THREAD DESTROYED");
841 // Uninstall the logging function
842 mEnvironmentOptions.UnInstallLogFunction();
845 bool CombinedUpdateRenderController::UpdateRenderReady(bool& useElapsedTime, bool updateRequired, uint64_t& timeToSleepUntil)
847 useElapsedTime = true;
849 ConditionalWait::ScopedLock updateLock(mUpdateRenderThreadWaitCondition);
850 while((!mUpdateRenderRunCount || // Should try to wait if event-thread has paused the Update/Render thread
851 (mUpdateRenderThreadCanSleep && !updateRequired && !mPendingRequestUpdate)) && // Ensure we wait if we're supposed to be sleeping AND do not require another update
852 !mDestroyUpdateRenderThread && // Ensure we don't wait if the update-render-thread is supposed to be destroyed
853 !mNewSurface && // Ensure we don't wait if we need to replace the surface
854 !mDeletedSurface && // Ensure we don't wait if we need to delete the surface
855 !mSurfaceResized) // Ensure we don't wait if we need to resize the surface
857 LOG_UPDATE_RENDER("WAIT: mUpdateRenderRunCount: %d", mUpdateRenderRunCount);
858 LOG_UPDATE_RENDER(" mUpdateRenderThreadCanSleep: %d, updateRequired: %d, mPendingRequestUpdate: %d", mUpdateRenderThreadCanSleep, updateRequired, mPendingRequestUpdate);
859 LOG_UPDATE_RENDER(" mDestroyUpdateRenderThread: %d", mDestroyUpdateRenderThread);
860 LOG_UPDATE_RENDER(" mNewSurface: %d", mNewSurface);
861 LOG_UPDATE_RENDER(" mDeletedSurface: %d", mDeletedSurface);
862 LOG_UPDATE_RENDER(" mSurfaceResized: %d", mSurfaceResized);
864 // Reset the time when the thread is waiting, so the sleep-until time for
865 // the first frame after resuming should be based on the actual start time
866 // of the first frame.
867 timeToSleepUntil = 0;
869 mUpdateRenderThreadWaitCondition.Wait(updateLock);
871 if(!mUseElapsedTimeAfterWait)
873 useElapsedTime = false;
877 LOG_COUNTER_UPDATE_RENDER("mUpdateRenderRunCount: %d", mUpdateRenderRunCount);
878 LOG_COUNTER_UPDATE_RENDER("mUpdateRenderThreadCanSleep: %d, updateRequired: %d, mPendingRequestUpdate: %d", mUpdateRenderThreadCanSleep, updateRequired, mPendingRequestUpdate);
879 LOG_COUNTER_UPDATE_RENDER("mDestroyUpdateRenderThread: %d", mDestroyUpdateRenderThread);
880 LOG_COUNTER_UPDATE_RENDER("mNewSurface: %d", mNewSurface);
881 LOG_COUNTER_UPDATE_RENDER("mDeletedSurface: %d", mDeletedSurface);
882 LOG_COUNTER_UPDATE_RENDER("mSurfaceResized: %d", mSurfaceResized);
884 mUseElapsedTimeAfterWait = FALSE;
885 mUpdateRenderThreadCanSleep = FALSE;
886 mPendingRequestUpdate = FALSE;
888 // If we've been asked to run Update/Render cycles a finite number of times then decrement so we wait after the
889 // requested number of cycles
890 if(mUpdateRenderRunCount > 0)
892 --mUpdateRenderRunCount;
895 // Keep the update-render thread alive if this thread is NOT to be destroyed
896 return !mDestroyUpdateRenderThread;
899 Dali::RenderSurfaceInterface* CombinedUpdateRenderController::ShouldSurfaceBeReplaced()
901 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
903 Dali::RenderSurfaceInterface* newSurface = mNewSurface;
909 void CombinedUpdateRenderController::SurfaceReplaced()
911 // Just increment the semaphore
912 mSurfaceSemaphore.Release(1);
915 Dali::RenderSurfaceInterface* CombinedUpdateRenderController::ShouldSurfaceBeDeleted()
917 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
919 Dali::RenderSurfaceInterface* deletedSurface = mDeletedSurface;
920 mDeletedSurface = NULL;
922 return deletedSurface;
925 void CombinedUpdateRenderController::SurfaceDeleted()
927 // Just increment the semaphore
928 mSurfaceSemaphore.Release(1);
931 void CombinedUpdateRenderController::SurfaceResized()
933 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
940 ///////////////////////////////////////////////////////////////////////////////////////////////////
942 ///////////////////////////////////////////////////////////////////////////////////////////////////
944 void CombinedUpdateRenderController::NotifyThreadInitialised()
946 // Just increment the semaphore
947 mEventThreadSemaphore.Release(1);
950 void CombinedUpdateRenderController::NotifyGraphicsInitialised()
952 mGraphicsInitializeWait.Notify();
955 void CombinedUpdateRenderController::AddPerformanceMarker(PerformanceInterface::MarkerType type)
957 if(mPerformanceInterface)
959 mPerformanceInterface->AddMarker(type);
963 /////////////////////////////////////////////////////////////////////////////////////////////////
964 // POST RENDERING: EVENT THREAD
965 /////////////////////////////////////////////////////////////////////////////////////////////////
967 void CombinedUpdateRenderController::PostRenderComplete()
969 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
970 mPostRendering = FALSE;
971 mUpdateRenderThreadWaitCondition.Notify(lock);
974 ///////////////////////////////////////////////////////////////////////////////////////////////////
975 // POST RENDERING: RENDER THREAD
976 ///////////////////////////////////////////////////////////////////////////////////////////////////
978 void CombinedUpdateRenderController::PostRenderStarted()
980 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
981 mPostRendering = TRUE;
984 void CombinedUpdateRenderController::PostRenderWaitForCompletion()
986 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
987 while(mPostRendering &&
988 !mNewSurface && // We should NOT wait if we're replacing the surface
989 !mDeletedSurface && // We should NOT wait if we're deleting the surface
990 !mDestroyUpdateRenderThread)
992 mUpdateRenderThreadWaitCondition.Wait(lock);
996 } // namespace Adaptor
998 } // namespace Internal