2 * Copyright (c) 2022 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 #include <dali/internal/adaptor/common/combined-update-render-controller.h>
22 #include <dali/integration-api/platform-abstraction.h>
25 #include "dali/public-api/common/dali-common.h"
28 #include <dali/devel-api/adaptor-framework/thread-settings.h>
29 #include <dali/integration-api/adaptor-framework/trigger-event-factory.h>
30 #include <dali/internal/adaptor/common/adaptor-internal-services.h>
31 #include <dali/internal/adaptor/common/combined-update-render-controller-debug.h>
32 #include <dali/internal/graphics/common/graphics-interface.h>
33 #include <dali/internal/graphics/gles/egl-graphics.h>
34 #include <dali/internal/graphics/gles/egl-implementation.h>
35 #include <dali/internal/system/common/environment-options.h>
36 #include <dali/internal/system/common/time-service.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),
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 ///////////////////////////////////////////////////////////////////////////////////////////////////
413 ///////////////////////////////////////////////////////////////////////////////////////////////////
415 void CombinedUpdateRenderController::RunUpdateRenderThread(int numberOfCycles, AnimationProgression animationProgression, UpdateMode updateMode)
417 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
421 case ThreadMode::NORMAL:
423 mUpdateRenderRunCount = numberOfCycles;
424 mUseElapsedTimeAfterWait = (animationProgression == AnimationProgression::USE_ELAPSED_TIME);
427 case ThreadMode::RUN_IF_REQUESTED:
429 if(updateMode != UpdateMode::FORCE_RENDER)
431 // Render only if the update mode is FORCE_RENDER which means the application requests it.
432 // We don't want to awake the update thread.
436 mUpdateRenderRunCount++; // Increase the update request count
437 mUseElapsedTimeAfterWait = TRUE; // The elapsed time should be used. We want animations to proceed.
442 mUpdateRenderThreadCanSleep = FALSE;
443 mUploadWithoutRendering = (updateMode == UpdateMode::SKIP_RENDER);
444 LOG_COUNTER_EVENT("mUpdateRenderRunCount: %d, mUseElapsedTimeAfterWait: %d", mUpdateRenderRunCount, mUseElapsedTimeAfterWait);
445 mUpdateRenderThreadWaitCondition.Notify(lock);
448 void CombinedUpdateRenderController::PauseUpdateRenderThread()
450 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
451 mUpdateRenderRunCount = 0;
454 void CombinedUpdateRenderController::StopUpdateRenderThread()
456 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
457 mDestroyUpdateRenderThread = TRUE;
458 mUpdateRenderThreadWaitCondition.Notify(lock);
461 bool CombinedUpdateRenderController::IsUpdateRenderThreadPaused()
463 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
465 if(mThreadMode == ThreadMode::RUN_IF_REQUESTED)
467 return !mRunning || mUpdateRenderThreadCanSleep;
470 return (mUpdateRenderRunCount != CONTINUOUS) || // Report paused if NOT continuously running
471 mUpdateRenderThreadCanSleep; // Report paused if sleeping
474 void CombinedUpdateRenderController::ProcessSleepRequest()
478 // Decrement Update request count
479 if(mUpdateRequestCount > 0)
481 --mUpdateRequestCount;
484 // Can sleep if our update-request count is 0
485 // Update/Render thread can choose to carry on updating if it determines more update/renders are required
486 if(mUpdateRequestCount == 0)
488 LOG_EVENT("Going to sleep");
490 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
491 mUpdateRenderThreadCanSleep = TRUE;
495 ///////////////////////////////////////////////////////////////////////////////////////////////////
496 // UPDATE/RENDER THREAD
497 ///////////////////////////////////////////////////////////////////////////////////////////////////
499 void CombinedUpdateRenderController::UpdateRenderThread()
501 SetThreadName("RenderThread\0");
503 // Install a function for logging
504 mEnvironmentOptions.InstallLogFunction();
506 // Install a function for tracing
507 mEnvironmentOptions.InstallTraceFunction();
509 LOG_UPDATE_RENDER("THREAD CREATED");
511 // Initialize graphics
512 GraphicsInterface& graphics = mAdaptorInterfaces.GetGraphicsInterface();
513 graphics.Initialize();
515 Dali::DisplayConnection& displayConnection = mAdaptorInterfaces.GetDisplayConnectionInterface();
516 displayConnection.Initialize(); //@todo Move InitializeGraphics code into graphics implementation
518 NotifyGraphicsInitialised();
520 //@todo Vk swaps this around, but we need to support surfaceless context for multi-window
521 graphics.ConfigureSurface(mAdaptorInterfaces.GetRenderSurfaceInterface());
523 // Tell core it has a context
524 mCore.ContextCreated();
526 NotifyThreadInitialised();
529 uint64_t lastFrameTime;
530 TimeService::GetNanoseconds(lastFrameTime);
532 LOG_UPDATE_RENDER("THREAD INITIALISED");
534 bool useElapsedTime = true;
535 bool updateRequired = true;
536 uint64_t timeToSleepUntil = 0;
537 int extraFramesDropped = 0;
539 const unsigned int renderToFboInterval = mEnvironmentOptions.GetRenderToFboInterval();
540 const bool renderToFboEnabled = 0u != renderToFboInterval;
541 unsigned int frameCount = 0u;
543 while(UpdateRenderReady(useElapsedTime, updateRequired, timeToSleepUntil))
545 LOG_UPDATE_RENDER_TRACE;
547 // Performance statistics are logged upon a VSYNC tick so use this point for a VSync marker
548 AddPerformanceMarker(PerformanceInterface::VSYNC);
550 uint64_t currentFrameStartTime = 0;
551 TimeService::GetNanoseconds(currentFrameStartTime);
553 uint64_t timeSinceLastFrame = currentFrameStartTime - lastFrameTime;
555 // Optional FPS Tracking when continuously rendering
556 if(useElapsedTime && mFpsTracker.Enabled())
558 float absoluteTimeSinceLastRender = timeSinceLastFrame * NANOSECONDS_TO_SECOND;
559 mFpsTracker.Track(absoluteTimeSinceLastRender);
562 lastFrameTime = currentFrameStartTime; // Store frame start time
564 //////////////////////////////
566 //////////////////////////////
568 Dali::RenderSurfaceInterface* newSurface = ShouldSurfaceBeReplaced();
569 if(DALI_UNLIKELY(newSurface))
571 LOG_UPDATE_RENDER_TRACE_FMT("Replacing Surface");
572 // This is designed for replacing pixmap surfaces, but should work for window as well
573 // we need to delete the surface and renderable (pixmap / window)
574 // Then create a new pixmap/window and new surface
575 // If the new surface has a different display connection, then the context will be lost
576 mAdaptorInterfaces.GetDisplayConnectionInterface().Initialize();
577 graphics.ActivateSurfaceContext(newSurface);
578 // TODO: ReplaceGraphicsSurface doesn't work, InitializeGraphics()
579 // already creates new surface window, the surface and the context.
580 // We probably don't need ReplaceGraphicsSurface at all.
581 // newSurface->ReplaceGraphicsSurface();
585 const bool isRenderingToFbo = renderToFboEnabled && ((0u == frameCount) || (0u != frameCount % renderToFboInterval));
588 //////////////////////////////
590 //////////////////////////////
592 const unsigned int currentTime = currentFrameStartTime / NANOSECONDS_PER_MILLISECOND;
593 const unsigned int nextFrameTime = currentTime + mDefaultFrameDurationMilliseconds;
595 uint64_t noOfFramesSinceLastUpdate = 1;
596 float frameDelta = 0.0f;
599 if(mThreadMode == ThreadMode::RUN_IF_REQUESTED)
601 extraFramesDropped = 0;
602 while(timeSinceLastFrame >= mDefaultFrameDurationNanoseconds)
604 timeSinceLastFrame -= mDefaultFrameDurationNanoseconds;
605 extraFramesDropped++;
609 // If using the elapsed time, then calculate frameDelta as a multiple of mDefaultFrameDelta
610 noOfFramesSinceLastUpdate += extraFramesDropped;
612 frameDelta = mDefaultFrameDelta * noOfFramesSinceLastUpdate;
614 LOG_UPDATE_RENDER("timeSinceLastFrame(%llu) noOfFramesSinceLastUpdate(%u) frameDelta(%.6f)", timeSinceLastFrame, noOfFramesSinceLastUpdate, frameDelta);
616 Integration::UpdateStatus updateStatus;
618 AddPerformanceMarker(PerformanceInterface::UPDATE_START);
619 TRACE_UPDATE_RENDER_BEGIN("DALI_UPDATE");
620 mCore.Update(frameDelta,
626 TRACE_UPDATE_RENDER_END("DALI_UPDATE");
627 AddPerformanceMarker(PerformanceInterface::UPDATE_END);
629 unsigned int keepUpdatingStatus = updateStatus.KeepUpdating();
631 // Tell the event-thread to wake up (if asleep) and send a notification event to Core if required
632 if(updateStatus.NeedsNotification())
634 mNotificationTrigger.Trigger();
635 LOG_UPDATE_RENDER("Notification Triggered");
638 // Optional logging of update/render status
639 mUpdateStatusLogger.Log(keepUpdatingStatus);
641 //////////////////////////////
643 //////////////////////////////
645 mAdaptorInterfaces.GetDisplayConnectionInterface().ConsumeEvents();
647 if(mPreRenderCallback != NULL)
649 bool keepCallback = CallbackBase::ExecuteReturn<bool>(*mPreRenderCallback);
652 delete mPreRenderCallback;
653 mPreRenderCallback = NULL;
657 graphics.ActivateResourceContext();
659 if(mFirstFrameAfterResume)
661 // mFirstFrameAfterResume is set to true when the thread is resumed
662 // Let graphics know the first frame after thread initialized or resumed.
663 graphics.SetFirstFrameAfterResume();
664 mFirstFrameAfterResume = FALSE;
667 Integration::RenderStatus renderStatus;
669 AddPerformanceMarker(PerformanceInterface::RENDER_START);
670 TRACE_UPDATE_RENDER_BEGIN("DALI_RENDER");
672 // Upload shared resources
673 mCore.PreRender(renderStatus, mForceClear, mUploadWithoutRendering);
675 if(!mUploadWithoutRendering)
677 // Go through each window
678 WindowContainer windows;
679 mAdaptorInterfaces.GetWindowContainerInterface(windows);
681 bool sceneSurfaceResized;
683 for(auto&& window : windows)
685 Dali::Integration::Scene scene = window->GetScene();
686 Dali::RenderSurfaceInterface* windowSurface = window->GetSurface();
688 if(scene && windowSurface)
690 Integration::RenderStatus windowRenderStatus;
692 // Get Surface Resized flag
693 sceneSurfaceResized = scene.IsSurfaceRectChanged();
694 windowSurface->SetIsResizing(sceneSurfaceResized);
696 // clear previous frame damaged render items rects, buffer history is tracked on surface level
697 mDamagedRects.clear();
699 // Collect damage rects
700 mCore.PreRender(scene, mDamagedRects);
702 // Render off-screen frame buffers first if any
703 mCore.RenderScene(windowRenderStatus, scene, true);
705 Rect<int> clippingRect; // Empty for fbo rendering
707 // Switch to the context of the surface, merge damaged areas for previous frames
708 windowSurface->PreRender(sceneSurfaceResized, mDamagedRects, clippingRect); // Switch GL context
710 // Render the surface
711 mCore.RenderScene(windowRenderStatus, scene, false, clippingRect);
713 // Buffer swapping now happens when the surface render target is presented.
715 // If surface is resized, the surface resized count is decreased.
716 if(DALI_UNLIKELY(sceneSurfaceResized))
724 if(!mUploadWithoutRendering)
726 graphics.PostRender();
729 mCore.PostRender(mUploadWithoutRendering);
731 //////////////////////////////
733 //////////////////////////////
735 Dali::RenderSurfaceInterface* deletedSurface = ShouldSurfaceBeDeleted();
736 if(DALI_UNLIKELY(deletedSurface))
738 LOG_UPDATE_RENDER_TRACE_FMT("Deleting Surface");
740 deletedSurface->DestroySurface();
745 TRACE_UPDATE_RENDER_END("DALI_RENDER");
746 AddPerformanceMarker(PerformanceInterface::RENDER_END);
750 // Trigger event thread to request Update/Render thread to sleep if update not required
751 if((Integration::KeepUpdating::NOT_REQUESTED == keepUpdatingStatus) && !renderStatus.NeedsUpdate())
753 mSleepTrigger->Trigger();
754 updateRequired = false;
755 LOG_UPDATE_RENDER("Sleep Triggered");
759 updateRequired = true;
762 //////////////////////////////
764 //////////////////////////////
766 extraFramesDropped = 0;
768 if(timeToSleepUntil == 0)
770 // If this is the first frame after the thread is initialized or resumed, we
771 // use the actual time the current frame starts from to calculate the time to
772 // sleep until the next frame.
773 timeToSleepUntil = currentFrameStartTime + mDefaultFrameDurationNanoseconds;
777 // Otherwise, always use the sleep-until time calculated in the last frame to
778 // calculate the time to sleep until the next frame. In this way, if there is
779 // any time gap between the current frame and the next frame, or if update or
780 // rendering in the current frame takes too much time so that the specified
781 // sleep-until time has already passed, it will try to keep the frames syncing
782 // by shortening the duration of the next frame.
783 timeToSleepUntil += mDefaultFrameDurationNanoseconds;
785 // Check the current time at the end of the frame
786 uint64_t currentFrameEndTime = 0;
787 TimeService::GetNanoseconds(currentFrameEndTime);
788 while(currentFrameEndTime > timeToSleepUntil + mDefaultFrameDurationNanoseconds)
790 // We are more than one frame behind already, so just drop the next frames
791 // until the sleep-until time is later than the current time so that we can
793 timeToSleepUntil += mDefaultFrameDurationNanoseconds;
794 extraFramesDropped++;
798 // Render to FBO is intended to measure fps above 60 so sleep is not wanted.
799 if(0u == renderToFboInterval)
801 // Sleep until at least the the default frame duration has elapsed. This will return immediately if the specified end-time has already passed.
802 TimeService::SleepUntil(timeToSleepUntil);
806 // Inform core of context destruction
807 mCore.ContextDestroyed();
809 WindowContainer windows;
810 mAdaptorInterfaces.GetWindowContainerInterface(windows);
813 for(auto&& window : windows)
815 Dali::RenderSurfaceInterface* surface = window->GetSurface();
816 surface->DestroySurface();
821 LOG_UPDATE_RENDER("THREAD DESTROYED");
823 // Uninstall the logging function
824 mEnvironmentOptions.UnInstallLogFunction();
827 bool CombinedUpdateRenderController::UpdateRenderReady(bool& useElapsedTime, bool updateRequired, uint64_t& timeToSleepUntil)
829 useElapsedTime = true;
831 ConditionalWait::ScopedLock updateLock(mUpdateRenderThreadWaitCondition);
832 while((!mUpdateRenderRunCount || // Should try to wait if event-thread has paused the Update/Render thread
833 (mUpdateRenderThreadCanSleep && !updateRequired && !mPendingRequestUpdate)) && // Ensure we wait if we're supposed to be sleeping AND do not require another update
834 !mDestroyUpdateRenderThread && // Ensure we don't wait if the update-render-thread is supposed to be destroyed
835 !mNewSurface && // Ensure we don't wait if we need to replace the surface
836 !mDeletedSurface && // Ensure we don't wait if we need to delete the surface
837 !mSurfaceResized) // Ensure we don't wait if we need to resize the surface
839 LOG_UPDATE_RENDER("WAIT: mUpdateRenderRunCount: %d", mUpdateRenderRunCount);
840 LOG_UPDATE_RENDER(" mUpdateRenderThreadCanSleep: %d, updateRequired: %d, mPendingRequestUpdate: %d", mUpdateRenderThreadCanSleep, updateRequired, mPendingRequestUpdate);
841 LOG_UPDATE_RENDER(" mDestroyUpdateRenderThread: %d", mDestroyUpdateRenderThread);
842 LOG_UPDATE_RENDER(" mNewSurface: %d", mNewSurface);
843 LOG_UPDATE_RENDER(" mDeletedSurface: %d", mDeletedSurface);
844 LOG_UPDATE_RENDER(" mSurfaceResized: %d", mSurfaceResized);
846 // Reset the time when the thread is waiting, so the sleep-until time for
847 // the first frame after resuming should be based on the actual start time
848 // of the first frame.
849 timeToSleepUntil = 0;
851 mUpdateRenderThreadWaitCondition.Wait(updateLock);
853 if(!mUseElapsedTimeAfterWait)
855 useElapsedTime = false;
859 LOG_COUNTER_UPDATE_RENDER("mUpdateRenderRunCount: %d", mUpdateRenderRunCount);
860 LOG_COUNTER_UPDATE_RENDER("mUpdateRenderThreadCanSleep: %d, updateRequired: %d, mPendingRequestUpdate: %d", mUpdateRenderThreadCanSleep, updateRequired, mPendingRequestUpdate);
861 LOG_COUNTER_UPDATE_RENDER("mDestroyUpdateRenderThread: %d", mDestroyUpdateRenderThread);
862 LOG_COUNTER_UPDATE_RENDER("mNewSurface: %d", mNewSurface);
863 LOG_COUNTER_UPDATE_RENDER("mDeletedSurface: %d", mDeletedSurface);
864 LOG_COUNTER_UPDATE_RENDER("mSurfaceResized: %d", mSurfaceResized);
866 mUseElapsedTimeAfterWait = FALSE;
867 mUpdateRenderThreadCanSleep = FALSE;
868 mPendingRequestUpdate = FALSE;
870 // If we've been asked to run Update/Render cycles a finite number of times then decrement so we wait after the
871 // requested number of cycles
872 if(mUpdateRenderRunCount > 0)
874 --mUpdateRenderRunCount;
877 // Keep the update-render thread alive if this thread is NOT to be destroyed
878 return !mDestroyUpdateRenderThread;
881 Dali::RenderSurfaceInterface* CombinedUpdateRenderController::ShouldSurfaceBeReplaced()
883 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
885 Dali::RenderSurfaceInterface* newSurface = mNewSurface;
891 void CombinedUpdateRenderController::SurfaceReplaced()
893 // Just increment the semaphore
894 mSurfaceSemaphore.Release(1);
897 Dali::RenderSurfaceInterface* CombinedUpdateRenderController::ShouldSurfaceBeDeleted()
899 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
901 Dali::RenderSurfaceInterface* deletedSurface = mDeletedSurface;
902 mDeletedSurface = NULL;
904 return deletedSurface;
907 void CombinedUpdateRenderController::SurfaceDeleted()
909 // Just increment the semaphore
910 mSurfaceSemaphore.Release(1);
913 void CombinedUpdateRenderController::SurfaceResized()
915 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
922 ///////////////////////////////////////////////////////////////////////////////////////////////////
924 ///////////////////////////////////////////////////////////////////////////////////////////////////
926 void CombinedUpdateRenderController::NotifyThreadInitialised()
928 // Just increment the semaphore
929 mEventThreadSemaphore.Release(1);
932 void CombinedUpdateRenderController::NotifyGraphicsInitialised()
934 mGraphicsInitializeWait.Notify();
937 void CombinedUpdateRenderController::AddPerformanceMarker(PerformanceInterface::MarkerType type)
939 if(mPerformanceInterface)
941 mPerformanceInterface->AddMarker(type);
945 /////////////////////////////////////////////////////////////////////////////////////////////////
946 // POST RENDERING: EVENT THREAD
947 /////////////////////////////////////////////////////////////////////////////////////////////////
949 void CombinedUpdateRenderController::PostRenderComplete()
951 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
952 mPostRendering = FALSE;
953 mUpdateRenderThreadWaitCondition.Notify(lock);
956 ///////////////////////////////////////////////////////////////////////////////////////////////////
957 // POST RENDERING: RENDER THREAD
958 ///////////////////////////////////////////////////////////////////////////////////////////////////
960 void CombinedUpdateRenderController::PostRenderStarted()
962 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
963 mPostRendering = TRUE;
966 void CombinedUpdateRenderController::PostRenderWaitForCompletion()
968 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
969 while(mPostRendering &&
970 !mNewSurface && // We should NOT wait if we're replacing the surface
971 !mDeletedSurface && // We should NOT wait if we're deleting the surface
972 !mDestroyUpdateRenderThread)
974 mUpdateRenderThreadWaitCondition.Wait(lock);
978 } // namespace Adaptor
980 } // namespace Internal