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/graphics/gles/egl-implementation.h>
34 #include <dali/internal/system/common/environment-options.h>
35 #include <dali/internal/system/common/time-service.h>
36 #include <dali/internal/thread/common/thread-settings-impl.h>
37 #include <dali/internal/window-system/common/window-impl.h>
47 const unsigned int CREATED_THREAD_COUNT = 1u;
49 const int CONTINUOUS = -1;
52 const unsigned int TRUE = 1u;
53 const unsigned int FALSE = 0u;
55 const unsigned int MILLISECONDS_PER_SECOND(1e+3);
56 const float NANOSECONDS_TO_SECOND(1e-9f);
57 const unsigned int NANOSECONDS_PER_SECOND(1e+9);
58 const unsigned int NANOSECONDS_PER_MILLISECOND(1e+6);
60 // The following values will get calculated at compile time
61 const float DEFAULT_FRAME_DURATION_IN_SECONDS(1.0f / 60.0f);
62 const uint64_t DEFAULT_FRAME_DURATION_IN_MILLISECONDS(DEFAULT_FRAME_DURATION_IN_SECONDS* MILLISECONDS_PER_SECOND);
63 const uint64_t DEFAULT_FRAME_DURATION_IN_NANOSECONDS(DEFAULT_FRAME_DURATION_IN_SECONDS* NANOSECONDS_PER_SECOND);
66 * 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
67 * there is a danger that, on the event-thread we could have:
68 * 1) An update-request where we do nothing as Update/Render thread still running.
69 * 2) Quickly followed by a sleep-request being handled where we pause the Update/Render Thread (even though we have an update to process).
71 * 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:
72 * 1) MAIN THREAD: Update Request: COUNTER = 1
73 * 2) UPDATE/RENDER THREAD: Do Update/Render, then no Updates required -> Sleep Trigger
74 * 3) MAIN THREAD: Update Request: COUNTER = 2
75 * 4) MAIN THREAD: Sleep Request: COUNTER = 1 -> We do not sleep just yet
77 * Also ensures we preserve battery life by only doing ONE update when the above use case is not triggered.
78 * 1) MAIN THREAD: Update Request: COUNTER = 1
79 * 2) UPDATE/RENDER THREAD: Do Update/Render, then no Updates required -> Sleep Trigger
80 * 3) MAIN THREAD: Sleep Request: COUNTER = 0 -> Go to sleep
82 const unsigned int MAXIMUM_UPDATE_REQUESTS = 2;
83 } // unnamed namespace
85 ///////////////////////////////////////////////////////////////////////////////////////////////////
87 ///////////////////////////////////////////////////////////////////////////////////////////////////
89 CombinedUpdateRenderController::CombinedUpdateRenderController(AdaptorInternalServices& adaptorInterfaces, const EnvironmentOptions& environmentOptions, ThreadMode threadMode)
90 : mFpsTracker(environmentOptions),
91 mUpdateStatusLogger(environmentOptions),
92 mEventThreadSemaphore(0),
94 mUpdateRenderThreadWaitCondition(),
95 mAdaptorInterfaces(adaptorInterfaces),
96 mPerformanceInterface(adaptorInterfaces.GetPerformanceInterface()),
97 mCore(adaptorInterfaces.GetCore()),
98 mEnvironmentOptions(environmentOptions),
99 mNotificationTrigger(adaptorInterfaces.GetProcessCoreEventsTrigger()),
101 mPreRenderCallback(NULL),
102 mUpdateRenderThread(NULL),
103 mDefaultFrameDelta(0.0f),
104 mDefaultFrameDurationMilliseconds(0u),
105 mDefaultFrameDurationNanoseconds(0u),
106 mDefaultHalfFrameNanoseconds(0u),
107 mUpdateRequestCount(0u),
110 mThreadMode(threadMode),
111 mUpdateRenderRunCount(0),
112 mDestroyUpdateRenderThread(FALSE),
113 mUpdateRenderThreadCanSleep(FALSE),
114 mPendingRequestUpdate(FALSE),
115 mUseElapsedTimeAfterWait(FALSE),
117 mDeletedSurface(nullptr),
118 mPostRendering(FALSE),
121 mUploadWithoutRendering(FALSE),
122 mFirstFrameAfterResume(FALSE)
126 // Initialise frame delta/duration variables first
127 SetRenderRefreshRate(environmentOptions.GetRenderRefreshRate());
129 // Set the thread-synchronization interface on the render-surface
130 Dali::RenderSurfaceInterface* currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
133 currentSurface->SetThreadSynchronization(*this);
136 mSleepTrigger = TriggerEventFactory::CreateTriggerEvent(MakeCallback(this, &CombinedUpdateRenderController::ProcessSleepRequest), TriggerEventInterface::KEEP_ALIVE_AFTER_TRIGGER);
139 CombinedUpdateRenderController::~CombinedUpdateRenderController()
145 delete mPreRenderCallback;
146 delete mSleepTrigger;
149 void CombinedUpdateRenderController::Initialize()
153 // Ensure Update/Render Thread not already created
154 DALI_ASSERT_ALWAYS(!mUpdateRenderThread);
156 // Create Update/Render Thread
157 ConditionalWait::ScopedLock lock(mGraphicsInitializeWait);
158 mUpdateRenderThread = new pthread_t();
159 int error = pthread_create(mUpdateRenderThread, NULL, InternalUpdateRenderThreadEntryFunc, this);
160 DALI_ASSERT_ALWAYS(!error && "Return code from pthread_create() when creating UpdateRenderThread");
162 // The Update/Render thread will now run and initialise the graphics interface etc. and will then wait for Start to be called
163 // When this function returns, the application initialisation on the event thread should occur
166 void CombinedUpdateRenderController::Start()
170 DALI_ASSERT_ALWAYS(!mRunning && mUpdateRenderThread);
172 // Wait until all threads created in Initialise are up and running
173 for(unsigned int i = 0; i < CREATED_THREAD_COUNT; ++i)
175 mEventThreadSemaphore.Acquire();
180 LOG_EVENT("Startup Complete, starting Update/Render Thread");
182 RunUpdateRenderThread(CONTINUOUS, AnimationProgression::NONE, UpdateMode::NORMAL);
184 Dali::RenderSurfaceInterface* currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
187 currentSurface->StartRender();
190 DALI_LOG_RELEASE_INFO("CombinedUpdateRenderController::Start\n");
193 void CombinedUpdateRenderController::Pause()
199 PauseUpdateRenderThread();
201 AddPerformanceMarker(PerformanceInterface::PAUSED);
203 DALI_LOG_RELEASE_INFO("CombinedUpdateRenderController::Pause\n");
206 void CombinedUpdateRenderController::Resume()
210 if(!mRunning && IsUpdateRenderThreadPaused())
212 LOG_EVENT("Resuming");
214 RunUpdateRenderThread(CONTINUOUS, AnimationProgression::USE_ELAPSED_TIME, UpdateMode::NORMAL);
216 AddPerformanceMarker(PerformanceInterface::RESUME);
220 mFirstFrameAfterResume = TRUE;
222 DALI_LOG_RELEASE_INFO("CombinedUpdateRenderController::Resume\n");
226 DALI_LOG_RELEASE_INFO("CombinedUpdateRenderController::Resume: Already resumed [%d, %d, %d]\n", mRunning, mUpdateRenderRunCount, mUpdateRenderThreadCanSleep);
230 void CombinedUpdateRenderController::Stop()
234 // Stop Rendering and the Update/Render Thread
235 Dali::RenderSurfaceInterface* currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
238 currentSurface->StopRender();
241 StopUpdateRenderThread();
243 if(mUpdateRenderThread)
245 LOG_EVENT("Destroying UpdateRenderThread");
247 // wait for the thread to finish
248 pthread_join(*mUpdateRenderThread, NULL);
250 delete mUpdateRenderThread;
251 mUpdateRenderThread = NULL;
256 DALI_LOG_RELEASE_INFO("CombinedUpdateRenderController::Stop\n");
259 void CombinedUpdateRenderController::RequestUpdate()
263 // Increment the update-request count to the maximum
264 if(mUpdateRequestCount < MAXIMUM_UPDATE_REQUESTS)
266 ++mUpdateRequestCount;
269 if(mRunning && IsUpdateRenderThreadPaused())
271 LOG_EVENT("Processing");
273 RunUpdateRenderThread(CONTINUOUS, AnimationProgression::NONE, UpdateMode::NORMAL);
276 ConditionalWait::ScopedLock updateLock(mUpdateRenderThreadWaitCondition);
277 mPendingRequestUpdate = TRUE;
280 void CombinedUpdateRenderController::RequestUpdateOnce(UpdateMode updateMode)
282 // Increment the update-request count to the maximum
283 if(mUpdateRequestCount < MAXIMUM_UPDATE_REQUESTS)
285 ++mUpdateRequestCount;
288 if(IsUpdateRenderThreadPaused() || updateMode == UpdateMode::FORCE_RENDER)
292 // Run Update/Render once
293 RunUpdateRenderThread(ONCE, AnimationProgression::NONE, updateMode);
297 void CombinedUpdateRenderController::ReplaceSurface(Dali::RenderSurfaceInterface* newSurface)
301 if(mUpdateRenderThread)
303 // Set the ThreadSyncronizationInterface on the new surface
304 newSurface->SetThreadSynchronization(*this);
306 LOG_EVENT("Starting to replace the surface, event-thread blocked");
308 // Start replacing the surface.
310 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
311 mPostRendering = FALSE; // Clear the post-rendering flag as Update/Render thread will replace the surface now
312 mNewSurface = newSurface;
313 mUpdateRenderThreadWaitCondition.Notify(lock);
316 // Wait until the surface has been replaced
317 mSurfaceSemaphore.Acquire();
319 LOG_EVENT("Surface replaced, event-thread continuing");
323 void CombinedUpdateRenderController::DeleteSurface(Dali::RenderSurfaceInterface* surface)
327 if(mUpdateRenderThread)
329 LOG_EVENT("Starting to delete the surface, event-thread blocked");
331 // Start replacing the surface.
333 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
334 mPostRendering = FALSE; // Clear the post-rendering flag as Update/Render thread will delete the surface now
335 mDeletedSurface = surface;
336 mUpdateRenderThreadWaitCondition.Notify(lock);
339 // Wait until the surface has been deleted
340 mSurfaceSemaphore.Acquire();
342 LOG_EVENT("Surface deleted, event-thread continuing");
346 void CombinedUpdateRenderController::WaitForGraphicsInitialization()
348 ConditionalWait::ScopedLock lk(mGraphicsInitializeWait);
351 if(mUpdateRenderThread)
353 LOG_EVENT("Waiting for graphics initialisation, event-thread blocked");
355 // Wait until the graphics has been initialised
356 mGraphicsInitializeWait.Wait(lk);
358 LOG_EVENT("graphics initialised, event-thread continuing");
362 void CombinedUpdateRenderController::ResizeSurface()
366 LOG_EVENT("Resize the surface");
369 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
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);
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 unsigned int renderToFboInterval = mEnvironmentOptions.GetRenderToFboInterval();
546 const bool renderToFboEnabled = 0u != renderToFboInterval;
547 unsigned int frameCount = 0u;
549 while(UpdateRenderReady(useElapsedTime, updateRequired, timeToSleepUntil))
551 LOG_UPDATE_RENDER_TRACE;
554 bool uploadOnly = mUploadWithoutRendering;
555 unsigned int surfaceResized = mSurfaceResized;
556 Dali::RenderSurfaceInterface* deletedSurface = ShouldSurfaceBeDeleted();
558 // Performance statistics are logged upon a VSYNC tick so use this point for a VSync marker
559 AddPerformanceMarker(PerformanceInterface::VSYNC);
561 uint64_t currentFrameStartTime = 0;
562 TimeService::GetNanoseconds(currentFrameStartTime);
564 uint64_t timeSinceLastFrame = currentFrameStartTime - lastFrameTime;
566 // Optional FPS Tracking when continuously rendering
567 if(useElapsedTime && mFpsTracker.Enabled())
569 float absoluteTimeSinceLastRender = timeSinceLastFrame * NANOSECONDS_TO_SECOND;
570 mFpsTracker.Track(absoluteTimeSinceLastRender);
573 lastFrameTime = currentFrameStartTime; // Store frame start time
575 //////////////////////////////
577 //////////////////////////////
579 Dali::RenderSurfaceInterface* newSurface = ShouldSurfaceBeReplaced();
580 if(DALI_UNLIKELY(newSurface))
582 LOG_UPDATE_RENDER_TRACE_FMT("Replacing Surface");
583 // This is designed for replacing pixmap surfaces, but should work for window as well
584 // we need to delete the surface and renderable (pixmap / window)
585 // Then create a new pixmap/window and new surface
586 // If the new surface has a different display connection, then the context will be lost
587 mAdaptorInterfaces.GetDisplayConnectionInterface().Initialize();
588 graphics.ActivateSurfaceContext(newSurface);
589 // TODO: ReplaceGraphicsSurface doesn't work, InitializeGraphics()
590 // already creates new surface window, the surface and the context.
591 // We probably don't need ReplaceGraphicsSurface at all.
592 // newSurface->ReplaceGraphicsSurface();
596 const bool isRenderingToFbo = renderToFboEnabled && ((0u == frameCount) || (0u != frameCount % renderToFboInterval));
599 //////////////////////////////
601 //////////////////////////////
603 const unsigned int currentTime = currentFrameStartTime / NANOSECONDS_PER_MILLISECOND;
604 const unsigned int nextFrameTime = currentTime + mDefaultFrameDurationMilliseconds;
606 uint64_t noOfFramesSinceLastUpdate = 1;
607 float frameDelta = 0.0f;
610 if(mThreadMode == ThreadMode::RUN_IF_REQUESTED)
612 extraFramesDropped = 0;
613 while(timeSinceLastFrame >= mDefaultFrameDurationNanoseconds)
615 timeSinceLastFrame -= mDefaultFrameDurationNanoseconds;
616 extraFramesDropped++;
620 // If using the elapsed time, then calculate frameDelta as a multiple of mDefaultFrameDelta
621 noOfFramesSinceLastUpdate += extraFramesDropped;
623 frameDelta = mDefaultFrameDelta * noOfFramesSinceLastUpdate;
625 LOG_UPDATE_RENDER("timeSinceLastFrame(%llu) noOfFramesSinceLastUpdate(%u) frameDelta(%.6f)", timeSinceLastFrame, noOfFramesSinceLastUpdate, frameDelta);
627 Integration::UpdateStatus updateStatus;
629 AddPerformanceMarker(PerformanceInterface::UPDATE_START);
630 TRACE_UPDATE_RENDER_BEGIN("DALI_UPDATE");
631 mCore.Update(frameDelta,
638 TRACE_UPDATE_RENDER_END("DALI_UPDATE");
639 AddPerformanceMarker(PerformanceInterface::UPDATE_END);
641 unsigned int keepUpdatingStatus = updateStatus.KeepUpdating();
643 // Tell the event-thread to wake up (if asleep) and send a notification event to Core if required
644 if(updateStatus.NeedsNotification())
646 mNotificationTrigger.Trigger();
647 LOG_UPDATE_RENDER("Notification Triggered");
650 // Optional logging of update/render status
651 mUpdateStatusLogger.Log(keepUpdatingStatus);
653 //////////////////////////////
655 //////////////////////////////
657 mAdaptorInterfaces.GetDisplayConnectionInterface().ConsumeEvents();
659 if(mPreRenderCallback != NULL)
661 bool keepCallback = CallbackBase::ExecuteReturn<bool>(*mPreRenderCallback);
664 delete mPreRenderCallback;
665 mPreRenderCallback = NULL;
669 graphics.ActivateResourceContext();
671 if(mFirstFrameAfterResume)
673 // mFirstFrameAfterResume is set to true when the thread is resumed
674 // Let graphics know the first frame after thread initialized or resumed.
675 graphics.SetFirstFrameAfterResume();
676 mFirstFrameAfterResume = FALSE;
679 Integration::RenderStatus renderStatus;
681 AddPerformanceMarker(PerformanceInterface::RENDER_START);
682 TRACE_UPDATE_RENDER_BEGIN("DALI_RENDER");
684 // Upload shared resources
685 mCore.PreRender(renderStatus, mForceClear);
687 if(!uploadOnly || surfaceResized)
689 // Go through each window
690 WindowContainer windows;
691 mAdaptorInterfaces.GetWindowContainerInterface(windows);
693 for(auto&& window : windows)
695 Dali::Integration::Scene scene = window->GetScene();
696 Dali::RenderSurfaceInterface* windowSurface = window->GetSurface();
698 if(scene && windowSurface)
700 Integration::RenderStatus windowRenderStatus;
702 const uint32_t sceneSurfaceResized = scene.GetSurfaceRectChangedCount();
704 // clear previous frame damaged render items rects, buffer history is tracked on surface level
705 mDamagedRects.clear();
707 // Collect damage rects
708 mCore.PreRender(scene, mDamagedRects);
710 // Render off-screen frame buffers first if any
711 mCore.RenderScene(windowRenderStatus, scene, true);
713 Rect<int> clippingRect; // Empty for fbo rendering
715 // Switch to the context of the surface, merge damaged areas for previous frames
716 windowSurface->PreRender(sceneSurfaceResized > 0u, mDamagedRects, clippingRect); // Switch GL context
718 // Render the surface
719 mCore.RenderScene(windowRenderStatus, scene, false, clippingRect);
721 // Buffer swapping now happens when the surface render target is presented.
723 // If surface is resized, the surface resized count is decreased.
724 if(DALI_UNLIKELY(sceneSurfaceResized > 0u))
726 SurfaceResized(sceneSurfaceResized);
734 graphics.PostRender();
739 //////////////////////////////
741 //////////////////////////////
742 if(DALI_UNLIKELY(deletedSurface))
744 LOG_UPDATE_RENDER_TRACE_FMT("Deleting Surface");
746 deletedSurface->DestroySurface();
751 TRACE_UPDATE_RENDER_END("DALI_RENDER");
752 AddPerformanceMarker(PerformanceInterface::RENDER_END);
756 // Trigger event thread to request Update/Render thread to sleep if update not required
757 if((Integration::KeepUpdating::NOT_REQUESTED == keepUpdatingStatus) && !renderStatus.NeedsUpdate())
759 mSleepTrigger->Trigger();
760 updateRequired = false;
761 LOG_UPDATE_RENDER("Sleep Triggered");
765 updateRequired = true;
768 //////////////////////////////
770 //////////////////////////////
772 extraFramesDropped = 0;
774 if(timeToSleepUntil == 0)
776 // If this is the first frame after the thread is initialized or resumed, we
777 // use the actual time the current frame starts from to calculate the time to
778 // sleep until the next frame.
779 timeToSleepUntil = currentFrameStartTime + mDefaultFrameDurationNanoseconds;
783 // Otherwise, always use the sleep-until time calculated in the last frame to
784 // calculate the time to sleep until the next frame. In this way, if there is
785 // any time gap between the current frame and the next frame, or if update or
786 // rendering in the current frame takes too much time so that the specified
787 // sleep-until time has already passed, it will try to keep the frames syncing
788 // by shortening the duration of the next frame.
789 timeToSleepUntil += mDefaultFrameDurationNanoseconds;
791 // Check the current time at the end of the frame
792 uint64_t currentFrameEndTime = 0;
793 TimeService::GetNanoseconds(currentFrameEndTime);
794 while(currentFrameEndTime > timeToSleepUntil + mDefaultFrameDurationNanoseconds)
796 // We are more than one frame behind already, so just drop the next frames
797 // until the sleep-until time is later than the current time so that we can
799 timeToSleepUntil += mDefaultFrameDurationNanoseconds;
800 extraFramesDropped++;
804 // Render to FBO is intended to measure fps above 60 so sleep is not wanted.
805 if(0u == renderToFboInterval)
807 // Sleep until at least the the default frame duration has elapsed. This will return immediately if the specified end-time has already passed.
808 TimeService::SleepUntil(timeToSleepUntil);
812 // Inform core of context destruction
813 mCore.ContextDestroyed();
815 WindowContainer windows;
816 mAdaptorInterfaces.GetWindowContainerInterface(windows);
819 for(auto&& window : windows)
821 Dali::RenderSurfaceInterface* surface = window->GetSurface();
822 surface->DestroySurface();
827 LOG_UPDATE_RENDER("THREAD DESTROYED");
829 // Uninstall the logging function
830 mEnvironmentOptions.UnInstallLogFunction();
833 bool CombinedUpdateRenderController::UpdateRenderReady(bool& useElapsedTime, bool updateRequired, uint64_t& timeToSleepUntil)
835 useElapsedTime = true;
837 ConditionalWait::ScopedLock updateLock(mUpdateRenderThreadWaitCondition);
838 while((!mUpdateRenderRunCount || // Should try to wait if event-thread has paused the Update/Render thread
839 (mUpdateRenderThreadCanSleep && !updateRequired && !mPendingRequestUpdate)) && // Ensure we wait if we're supposed to be sleeping AND do not require another update
840 !mDestroyUpdateRenderThread && // Ensure we don't wait if the update-render-thread is supposed to be destroyed
841 !mNewSurface && // Ensure we don't wait if we need to replace the surface
842 !mDeletedSurface && // Ensure we don't wait if we need to delete the surface
843 !mSurfaceResized) // Ensure we don't wait if we need to resize the surface
845 LOG_UPDATE_RENDER("WAIT: mUpdateRenderRunCount: %d", mUpdateRenderRunCount);
846 LOG_UPDATE_RENDER(" mUpdateRenderThreadCanSleep: %d, updateRequired: %d, mPendingRequestUpdate: %d", mUpdateRenderThreadCanSleep, updateRequired, mPendingRequestUpdate);
847 LOG_UPDATE_RENDER(" mDestroyUpdateRenderThread: %d", mDestroyUpdateRenderThread);
848 LOG_UPDATE_RENDER(" mNewSurface: %d", mNewSurface);
849 LOG_UPDATE_RENDER(" mDeletedSurface: %d", mDeletedSurface);
850 LOG_UPDATE_RENDER(" mSurfaceResized: %d", mSurfaceResized);
852 // Reset the time when the thread is waiting, so the sleep-until time for
853 // the first frame after resuming should be based on the actual start time
854 // of the first frame.
855 timeToSleepUntil = 0;
857 mUpdateRenderThreadWaitCondition.Wait(updateLock);
859 if(!mUseElapsedTimeAfterWait)
861 useElapsedTime = false;
865 LOG_COUNTER_UPDATE_RENDER("mUpdateRenderRunCount: %d", mUpdateRenderRunCount);
866 LOG_COUNTER_UPDATE_RENDER("mUpdateRenderThreadCanSleep: %d, updateRequired: %d, mPendingRequestUpdate: %d", mUpdateRenderThreadCanSleep, updateRequired, mPendingRequestUpdate);
867 LOG_COUNTER_UPDATE_RENDER("mDestroyUpdateRenderThread: %d", mDestroyUpdateRenderThread);
868 LOG_COUNTER_UPDATE_RENDER("mNewSurface: %d", mNewSurface);
869 LOG_COUNTER_UPDATE_RENDER("mDeletedSurface: %d", mDeletedSurface);
870 LOG_COUNTER_UPDATE_RENDER("mSurfaceResized: %d", mSurfaceResized);
872 mUseElapsedTimeAfterWait = FALSE;
873 mUpdateRenderThreadCanSleep = FALSE;
874 mPendingRequestUpdate = FALSE;
876 // If we've been asked to run Update/Render cycles a finite number of times then decrement so we wait after the
877 // requested number of cycles
878 if(mUpdateRenderRunCount > 0)
880 --mUpdateRenderRunCount;
883 // Keep the update-render thread alive if this thread is NOT to be destroyed
884 return !mDestroyUpdateRenderThread;
887 Dali::RenderSurfaceInterface* CombinedUpdateRenderController::ShouldSurfaceBeReplaced()
889 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
891 Dali::RenderSurfaceInterface* newSurface = mNewSurface;
897 void CombinedUpdateRenderController::SurfaceReplaced()
899 // Just increment the semaphore
900 mSurfaceSemaphore.Release(1);
903 Dali::RenderSurfaceInterface* CombinedUpdateRenderController::ShouldSurfaceBeDeleted()
905 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
907 Dali::RenderSurfaceInterface* deletedSurface = mDeletedSurface;
908 mDeletedSurface = NULL;
910 return deletedSurface;
913 void CombinedUpdateRenderController::SurfaceDeleted()
915 // Just increment the semaphore
916 mSurfaceSemaphore.Release(1);
919 void CombinedUpdateRenderController::SurfaceResized(uint32_t resizedCount)
921 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
923 if(mSurfaceResized >= resizedCount)
925 mSurfaceResized -= resizedCount;
929 mSurfaceResized = 0u;
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