2 * Copyright (c) 2023 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 #include <dali/internal/adaptor/common/combined-update-render-controller.h>
22 #include <dali/integration-api/platform-abstraction.h>
25 #include "dali/public-api/common/dali-common.h"
28 #include <dali/integration-api/adaptor-framework/trigger-event-factory.h>
29 #include <dali/internal/adaptor/common/adaptor-internal-services.h>
30 #include <dali/internal/adaptor/common/combined-update-render-controller-debug.h>
31 #include <dali/internal/graphics/common/graphics-interface.h>
32 #include <dali/internal/graphics/gles/egl-graphics.h>
33 #include <dali/internal/system/common/environment-options.h>
34 #include <dali/internal/system/common/texture-upload-manager-impl.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 mTextureUploadManager(adaptorInterfaces.GetTextureUploadManager()),
103 mUpdateRenderThread(NULL),
104 mDefaultFrameDelta(0.0f),
105 mDefaultFrameDurationMilliseconds(0u),
106 mDefaultFrameDurationNanoseconds(0u),
107 mDefaultHalfFrameNanoseconds(0u),
108 mUpdateRequestCount(0u),
111 mThreadMode(threadMode),
112 mUpdateRenderRunCount(0),
113 mDestroyUpdateRenderThread(FALSE),
114 mUpdateRenderThreadCanSleep(FALSE),
115 mPendingRequestUpdate(FALSE),
116 mUseElapsedTimeAfterWait(FALSE),
118 mDeletedSurface(nullptr),
119 mPostRendering(FALSE),
122 mUploadWithoutRendering(FALSE),
123 mFirstFrameAfterResume(FALSE)
127 // Initialise frame delta/duration variables first
128 SetRenderRefreshRate(environmentOptions.GetRenderRefreshRate());
130 // Set the thread-synchronization interface on the render-surface
131 Dali::RenderSurfaceInterface* currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
134 currentSurface->SetThreadSynchronization(*this);
137 mSleepTrigger = TriggerEventFactory::CreateTriggerEvent(MakeCallback(this, &CombinedUpdateRenderController::ProcessSleepRequest), TriggerEventInterface::KEEP_ALIVE_AFTER_TRIGGER);
140 CombinedUpdateRenderController::~CombinedUpdateRenderController()
146 delete mPreRenderCallback;
147 delete mSleepTrigger;
150 void CombinedUpdateRenderController::Initialize()
154 // Ensure Update/Render Thread not already created
155 DALI_ASSERT_ALWAYS(!mUpdateRenderThread);
157 // Create Update/Render Thread
158 ConditionalWait::ScopedLock lock(mGraphicsInitializeWait);
159 mUpdateRenderThread = new pthread_t();
160 int error = pthread_create(mUpdateRenderThread, NULL, InternalUpdateRenderThreadEntryFunc, this);
161 DALI_ASSERT_ALWAYS(!error && "Return code from pthread_create() when creating UpdateRenderThread");
163 // The Update/Render thread will now run and initialise the graphics interface etc. and will then wait for Start to be called
164 // When this function returns, the application initialisation on the event thread should occur
167 void CombinedUpdateRenderController::Start()
171 DALI_ASSERT_ALWAYS(!mRunning && mUpdateRenderThread);
173 // Wait until all threads created in Initialise are up and running
174 for(unsigned int i = 0; i < CREATED_THREAD_COUNT; ++i)
176 mEventThreadSemaphore.Acquire();
181 LOG_EVENT("Startup Complete, starting Update/Render Thread");
183 RunUpdateRenderThread(CONTINUOUS, AnimationProgression::NONE, UpdateMode::NORMAL);
185 Dali::RenderSurfaceInterface* currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
188 currentSurface->StartRender();
191 DALI_LOG_RELEASE_INFO("CombinedUpdateRenderController::Start\n");
194 void CombinedUpdateRenderController::Pause()
200 PauseUpdateRenderThread();
202 AddPerformanceMarker(PerformanceInterface::PAUSED);
204 DALI_LOG_RELEASE_INFO("CombinedUpdateRenderController::Pause\n");
207 void CombinedUpdateRenderController::Resume()
211 if(!mRunning && IsUpdateRenderThreadPaused())
213 LOG_EVENT("Resuming");
215 RunUpdateRenderThread(CONTINUOUS, AnimationProgression::USE_ELAPSED_TIME, UpdateMode::NORMAL);
217 AddPerformanceMarker(PerformanceInterface::RESUME);
221 mFirstFrameAfterResume = TRUE;
223 DALI_LOG_RELEASE_INFO("CombinedUpdateRenderController::Resume\n");
227 DALI_LOG_RELEASE_INFO("CombinedUpdateRenderController::Resume: Already resumed [%d, %d, %d]\n", mRunning, mUpdateRenderRunCount, mUpdateRenderThreadCanSleep);
231 void CombinedUpdateRenderController::Stop()
235 // Stop Rendering and the Update/Render Thread
236 Dali::RenderSurfaceInterface* currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
239 currentSurface->StopRender();
242 StopUpdateRenderThread();
244 if(mUpdateRenderThread)
246 LOG_EVENT("Destroying UpdateRenderThread");
248 // wait for the thread to finish
249 pthread_join(*mUpdateRenderThread, NULL);
251 delete mUpdateRenderThread;
252 mUpdateRenderThread = NULL;
257 DALI_LOG_RELEASE_INFO("CombinedUpdateRenderController::Stop\n");
260 void CombinedUpdateRenderController::RequestUpdate()
264 // Increment the update-request count to the maximum
265 if(mUpdateRequestCount < MAXIMUM_UPDATE_REQUESTS)
267 ++mUpdateRequestCount;
270 if(mRunning && IsUpdateRenderThreadPaused())
272 LOG_EVENT("Processing");
274 RunUpdateRenderThread(CONTINUOUS, AnimationProgression::NONE, UpdateMode::NORMAL);
277 ConditionalWait::ScopedLock updateLock(mUpdateRenderThreadWaitCondition);
278 mPendingRequestUpdate = TRUE;
281 void CombinedUpdateRenderController::RequestUpdateOnce(UpdateMode updateMode)
283 // Increment the update-request count to the maximum
284 if(mUpdateRequestCount < MAXIMUM_UPDATE_REQUESTS)
286 ++mUpdateRequestCount;
289 if(IsUpdateRenderThreadPaused() || updateMode == UpdateMode::FORCE_RENDER)
293 // Run Update/Render once
294 RunUpdateRenderThread(ONCE, AnimationProgression::NONE, updateMode);
298 void CombinedUpdateRenderController::ReplaceSurface(Dali::RenderSurfaceInterface* newSurface)
302 if(mUpdateRenderThread)
304 // Set the ThreadSyncronizationInterface on the new surface
305 newSurface->SetThreadSynchronization(*this);
307 LOG_EVENT("Starting to replace the surface, event-thread blocked");
309 // Start replacing the surface.
311 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
312 mPostRendering = FALSE; // Clear the post-rendering flag as Update/Render thread will replace the surface now
313 mNewSurface = newSurface;
314 mUpdateRenderThreadWaitCondition.Notify(lock);
317 // Wait until the surface has been replaced
318 mSurfaceSemaphore.Acquire();
320 LOG_EVENT("Surface replaced, event-thread continuing");
324 void CombinedUpdateRenderController::DeleteSurface(Dali::RenderSurfaceInterface* surface)
328 if(mUpdateRenderThread)
330 LOG_EVENT("Starting to delete the surface, event-thread blocked");
332 // Start replacing the surface.
334 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
335 mPostRendering = FALSE; // Clear the post-rendering flag as Update/Render thread will delete the surface now
336 mDeletedSurface = surface;
337 mUpdateRenderThreadWaitCondition.Notify(lock);
340 // Wait until the surface has been deleted
341 mSurfaceSemaphore.Acquire();
343 LOG_EVENT("Surface deleted, event-thread continuing");
347 void CombinedUpdateRenderController::WaitForGraphicsInitialization()
349 ConditionalWait::ScopedLock lk(mGraphicsInitializeWait);
352 if(mUpdateRenderThread)
354 LOG_EVENT("Waiting for graphics initialisation, event-thread blocked");
356 // Wait until the graphics has been initialised
357 mGraphicsInitializeWait.Wait(lk);
359 LOG_EVENT("graphics initialised, event-thread continuing");
363 void CombinedUpdateRenderController::ResizeSurface()
367 LOG_EVENT("Resize the surface");
370 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
371 // Surface is resized and the surface resized count is increased.
373 mUpdateRenderThreadWaitCondition.Notify(lock);
377 void CombinedUpdateRenderController::SetRenderRefreshRate(unsigned int numberOfFramesPerRender)
379 // Not protected by lock, but written to rarely so not worth adding a lock when reading
380 mDefaultFrameDelta = numberOfFramesPerRender * DEFAULT_FRAME_DURATION_IN_SECONDS;
381 mDefaultFrameDurationMilliseconds = uint64_t(numberOfFramesPerRender) * DEFAULT_FRAME_DURATION_IN_MILLISECONDS;
382 mDefaultFrameDurationNanoseconds = uint64_t(numberOfFramesPerRender) * DEFAULT_FRAME_DURATION_IN_NANOSECONDS;
383 mDefaultHalfFrameNanoseconds = mDefaultFrameDurationNanoseconds / 2u;
385 LOG_EVENT("mDefaultFrameDelta(%.6f), mDefaultFrameDurationMilliseconds(%lld), mDefaultFrameDurationNanoseconds(%lld)", mDefaultFrameDelta, mDefaultFrameDurationMilliseconds, mDefaultFrameDurationNanoseconds);
388 void CombinedUpdateRenderController::SetPreRenderCallback(CallbackBase* callback)
391 LOG_EVENT("Set PreRender Callback");
393 ConditionalWait::ScopedLock updateLock(mUpdateRenderThreadWaitCondition);
394 if(mPreRenderCallback)
396 delete mPreRenderCallback;
398 mPreRenderCallback = callback;
401 void CombinedUpdateRenderController::AddSurface(Dali::RenderSurfaceInterface* surface)
404 LOG_EVENT("Surface is added");
405 if(mUpdateRenderThread)
407 // Set the ThreadSyncronizationInterface on the added surface
408 surface->SetThreadSynchronization(*this);
412 int32_t CombinedUpdateRenderController::GetThreadId() const
417 ///////////////////////////////////////////////////////////////////////////////////////////////////
419 ///////////////////////////////////////////////////////////////////////////////////////////////////
421 void CombinedUpdateRenderController::RunUpdateRenderThread(int numberOfCycles, AnimationProgression animationProgression, UpdateMode updateMode)
423 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
427 case ThreadMode::NORMAL:
429 mUpdateRenderRunCount = numberOfCycles;
430 mUseElapsedTimeAfterWait = (animationProgression == AnimationProgression::USE_ELAPSED_TIME);
433 case ThreadMode::RUN_IF_REQUESTED:
435 if(updateMode != UpdateMode::FORCE_RENDER)
437 // Render only if the update mode is FORCE_RENDER which means the application requests it.
438 // We don't want to awake the update thread.
442 mUpdateRenderRunCount++; // Increase the update request count
443 mUseElapsedTimeAfterWait = TRUE; // The elapsed time should be used. We want animations to proceed.
448 mUpdateRenderThreadCanSleep = FALSE;
449 mUploadWithoutRendering = (updateMode == UpdateMode::SKIP_RENDER);
450 LOG_COUNTER_EVENT("mUpdateRenderRunCount: %d, mUseElapsedTimeAfterWait: %d", mUpdateRenderRunCount, mUseElapsedTimeAfterWait);
451 mUpdateRenderThreadWaitCondition.Notify(lock);
454 void CombinedUpdateRenderController::PauseUpdateRenderThread()
456 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
457 mUpdateRenderRunCount = 0;
460 void CombinedUpdateRenderController::StopUpdateRenderThread()
462 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
463 mDestroyUpdateRenderThread = TRUE;
464 mUpdateRenderThreadWaitCondition.Notify(lock);
467 bool CombinedUpdateRenderController::IsUpdateRenderThreadPaused()
469 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
471 if(mThreadMode == ThreadMode::RUN_IF_REQUESTED)
473 return !mRunning || mUpdateRenderThreadCanSleep;
476 return (mUpdateRenderRunCount != CONTINUOUS) || // Report paused if NOT continuously running
477 mUpdateRenderThreadCanSleep; // Report paused if sleeping
480 void CombinedUpdateRenderController::ProcessSleepRequest()
484 // Decrement Update request count
485 if(mUpdateRequestCount > 0)
487 --mUpdateRequestCount;
490 // Can sleep if our update-request count is 0
491 // Update/Render thread can choose to carry on updating if it determines more update/renders are required
492 if(mUpdateRequestCount == 0)
494 LOG_EVENT("Going to sleep");
496 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
497 mUpdateRenderThreadCanSleep = TRUE;
501 ///////////////////////////////////////////////////////////////////////////////////////////////////
502 // UPDATE/RENDER THREAD
503 ///////////////////////////////////////////////////////////////////////////////////////////////////
505 void CombinedUpdateRenderController::UpdateRenderThread()
507 ThreadSettings::SetThreadName("RenderThread\0");
508 mThreadId = ThreadSettings::GetThreadId();
510 // Install a function for logging
511 mEnvironmentOptions.InstallLogFunction();
513 // Install a function for tracing
514 mEnvironmentOptions.InstallTraceFunction();
516 LOG_UPDATE_RENDER("THREAD CREATED");
518 // Initialize graphics
519 GraphicsInterface& graphics = mAdaptorInterfaces.GetGraphicsInterface();
520 graphics.Initialize();
522 Dali::DisplayConnection& displayConnection = mAdaptorInterfaces.GetDisplayConnectionInterface();
523 displayConnection.Initialize(); //@todo Move InitializeGraphics code into graphics implementation
525 // Setup graphics controller into upload manager.
526 GetImplementation(mTextureUploadManager).InitalizeGraphicsController(graphics.GetController());
528 NotifyGraphicsInitialised();
530 //@todo Vk swaps this around, but we need to support surfaceless context for multi-window
531 graphics.ConfigureSurface(mAdaptorInterfaces.GetRenderSurfaceInterface());
533 // Tell core it has a context
534 mCore.ContextCreated();
536 NotifyThreadInitialised();
539 uint64_t lastFrameTime;
540 TimeService::GetNanoseconds(lastFrameTime);
541 uint64_t lastMemPoolLogTime = lastFrameTime;
543 LOG_UPDATE_RENDER("THREAD INITIALISED");
545 bool useElapsedTime = true;
546 bool updateRequired = true;
547 uint64_t timeToSleepUntil = 0;
548 int extraFramesDropped = 0;
550 const uint64_t memPoolInterval = 1e9 * float(mEnvironmentOptions.GetMemoryPoolInterval());
552 const unsigned int renderToFboInterval = mEnvironmentOptions.GetRenderToFboInterval();
553 const bool renderToFboEnabled = 0u != renderToFboInterval;
554 unsigned int frameCount = 0u;
556 while(UpdateRenderReady(useElapsedTime, updateRequired, timeToSleepUntil))
558 LOG_UPDATE_RENDER_TRACE;
561 bool uploadOnly = mUploadWithoutRendering;
562 unsigned int surfaceResized = mSurfaceResized;
563 Dali::RenderSurfaceInterface* deletedSurface = ShouldSurfaceBeDeleted();
565 // Performance statistics are logged upon a VSYNC tick so use this point for a VSync marker
566 AddPerformanceMarker(PerformanceInterface::VSYNC);
568 uint64_t currentFrameStartTime = 0;
569 TimeService::GetNanoseconds(currentFrameStartTime);
571 uint64_t timeSinceLastFrame = currentFrameStartTime - lastFrameTime;
573 // Optional FPS Tracking when continuously rendering
574 if(useElapsedTime && mFpsTracker.Enabled())
576 float absoluteTimeSinceLastRender = timeSinceLastFrame * NANOSECONDS_TO_SECOND;
577 mFpsTracker.Track(absoluteTimeSinceLastRender);
580 lastFrameTime = currentFrameStartTime; // Store frame start time
582 //////////////////////////////
584 //////////////////////////////
586 Dali::RenderSurfaceInterface* newSurface = ShouldSurfaceBeReplaced();
587 if(DALI_UNLIKELY(newSurface))
589 LOG_UPDATE_RENDER_TRACE_FMT("Replacing Surface");
590 // This is designed for replacing pixmap surfaces, but should work for window as well
591 // we need to delete the surface and renderable (pixmap / window)
592 // Then create a new pixmap/window and new surface
593 // If the new surface has a different display connection, then the context will be lost
594 mAdaptorInterfaces.GetDisplayConnectionInterface().Initialize();
595 graphics.ActivateSurfaceContext(newSurface);
596 // TODO: ReplaceGraphicsSurface doesn't work, InitializeGraphics()
597 // already creates new surface window, the surface and the context.
598 // We probably don't need ReplaceGraphicsSurface at all.
599 // newSurface->ReplaceGraphicsSurface();
603 //////////////////////////////
604 // TextureUploadRequest
605 //////////////////////////////
607 // Upload requested resources after resource context activated.
608 graphics.ActivateResourceContext();
610 const bool textureUploaded = mTextureUploadManager.ResourceUpload();
612 // Update & Render forcely if there exist some uploaded texture.
613 uploadOnly = textureUploaded ? false : uploadOnly;
615 const bool isRenderingToFbo = renderToFboEnabled && ((0u == frameCount) || (0u != frameCount % renderToFboInterval));
618 //////////////////////////////
620 //////////////////////////////
622 const uint32_t currentTime = static_cast<uint32_t>(currentFrameStartTime / NANOSECONDS_PER_MILLISECOND);
623 const uint32_t nextFrameTime = currentTime + static_cast<uint32_t>(mDefaultFrameDurationMilliseconds);
625 uint64_t noOfFramesSinceLastUpdate = 1;
626 float frameDelta = 0.0f;
629 if(mThreadMode == ThreadMode::RUN_IF_REQUESTED)
631 extraFramesDropped = 0;
632 while(timeSinceLastFrame >= mDefaultFrameDurationNanoseconds)
634 timeSinceLastFrame -= mDefaultFrameDurationNanoseconds;
635 extraFramesDropped++;
639 // If using the elapsed time, then calculate frameDelta as a multiple of mDefaultFrameDelta
640 noOfFramesSinceLastUpdate += extraFramesDropped;
642 frameDelta = mDefaultFrameDelta * noOfFramesSinceLastUpdate;
644 LOG_UPDATE_RENDER("timeSinceLastFrame(%llu) noOfFramesSinceLastUpdate(%u) frameDelta(%.6f)", timeSinceLastFrame, noOfFramesSinceLastUpdate, frameDelta);
646 Integration::UpdateStatus updateStatus;
648 AddPerformanceMarker(PerformanceInterface::UPDATE_START);
649 TRACE_UPDATE_RENDER_BEGIN("DALI_UPDATE");
650 mCore.Update(frameDelta,
657 TRACE_UPDATE_RENDER_END("DALI_UPDATE");
658 AddPerformanceMarker(PerformanceInterface::UPDATE_END);
660 unsigned int keepUpdatingStatus = updateStatus.KeepUpdating();
662 // Tell the event-thread to wake up (if asleep) and send a notification event to Core if required
663 if(updateStatus.NeedsNotification())
665 mNotificationTrigger.Trigger();
666 LOG_UPDATE_RENDER("Notification Triggered");
669 // Optional logging of update/render status
670 mUpdateStatusLogger.Log(keepUpdatingStatus);
672 //////////////////////////////
674 //////////////////////////////
676 graphics.FrameStart();
677 mAdaptorInterfaces.GetDisplayConnectionInterface().ConsumeEvents();
679 if(mPreRenderCallback != NULL)
681 bool keepCallback = CallbackBase::ExecuteReturn<bool>(*mPreRenderCallback);
684 delete mPreRenderCallback;
685 mPreRenderCallback = NULL;
689 graphics.ActivateResourceContext();
691 if(mFirstFrameAfterResume)
693 // mFirstFrameAfterResume is set to true when the thread is resumed
694 // Let graphics know the first frame after thread initialized or resumed.
695 graphics.SetFirstFrameAfterResume();
696 mFirstFrameAfterResume = FALSE;
699 Integration::RenderStatus renderStatus;
701 AddPerformanceMarker(PerformanceInterface::RENDER_START);
702 TRACE_UPDATE_RENDER_BEGIN("DALI_RENDER");
704 // Upload shared resources
705 mCore.PreRender(renderStatus, mForceClear);
707 if(!uploadOnly || surfaceResized)
709 // Go through each window
710 WindowContainer windows;
711 mAdaptorInterfaces.GetWindowContainerInterface(windows);
713 for(auto&& window : windows)
715 Dali::Integration::Scene scene = window->GetScene();
716 Dali::RenderSurfaceInterface* windowSurface = window->GetSurface();
718 if(scene && windowSurface)
720 Integration::RenderStatus windowRenderStatus;
722 const bool sceneSurfaceResized = scene.IsSurfaceRectChanged();
724 // clear previous frame damaged render items rects, buffer history is tracked on surface level
725 mDamagedRects.clear();
727 // Collect damage rects
728 mCore.PreRender(scene, mDamagedRects);
730 // Render off-screen frame buffers first if any
731 mCore.RenderScene(windowRenderStatus, scene, true);
733 Rect<int> clippingRect; // Empty for fbo rendering
735 // Switch to the context of the surface, merge damaged areas for previous frames
736 windowSurface->PreRender(sceneSurfaceResized, mDamagedRects, clippingRect); // Switch GL context
738 // Render the surface
739 mCore.RenderScene(windowRenderStatus, scene, false, clippingRect);
741 // Buffer swapping now happens when the surface render target is presented.
743 // If surface is resized, the surface resized count is decreased.
744 if(DALI_UNLIKELY(sceneSurfaceResized))
754 graphics.PostRender();
759 //////////////////////////////
761 //////////////////////////////
762 if(DALI_UNLIKELY(deletedSurface))
764 LOG_UPDATE_RENDER_TRACE_FMT("Deleting Surface");
766 deletedSurface->DestroySurface();
771 TRACE_UPDATE_RENDER_END("DALI_RENDER");
772 AddPerformanceMarker(PerformanceInterface::RENDER_END);
774 // if the memory pool interval is set and has elapsed, log the graphics memory pools
775 if(0 < memPoolInterval && memPoolInterval < lastFrameTime - lastMemPoolLogTime)
777 lastMemPoolLogTime = lastFrameTime;
778 graphics.LogMemoryPools();
783 // Trigger event thread to request Update/Render thread to sleep if update not required
784 if((Integration::KeepUpdating::NOT_REQUESTED == keepUpdatingStatus) && !renderStatus.NeedsUpdate())
786 mSleepTrigger->Trigger();
787 updateRequired = false;
788 LOG_UPDATE_RENDER("Sleep Triggered");
792 updateRequired = true;
795 //////////////////////////////
797 //////////////////////////////
799 extraFramesDropped = 0;
801 if(timeToSleepUntil == 0)
803 // If this is the first frame after the thread is initialized or resumed, we
804 // use the actual time the current frame starts from to calculate the time to
805 // sleep until the next frame.
806 timeToSleepUntil = currentFrameStartTime + mDefaultFrameDurationNanoseconds;
810 // Otherwise, always use the sleep-until time calculated in the last frame to
811 // calculate the time to sleep until the next frame. In this way, if there is
812 // any time gap between the current frame and the next frame, or if update or
813 // rendering in the current frame takes too much time so that the specified
814 // sleep-until time has already passed, it will try to keep the frames syncing
815 // by shortening the duration of the next frame.
816 timeToSleepUntil += mDefaultFrameDurationNanoseconds;
818 // Check the current time at the end of the frame
819 uint64_t currentFrameEndTime = 0;
820 TimeService::GetNanoseconds(currentFrameEndTime);
821 while(currentFrameEndTime > timeToSleepUntil + mDefaultFrameDurationNanoseconds)
823 // We are more than one frame behind already, so just drop the next frames
824 // until the sleep-until time is later than the current time so that we can
826 timeToSleepUntil += mDefaultFrameDurationNanoseconds;
827 extraFramesDropped++;
831 // Render to FBO is intended to measure fps above 60 so sleep is not wanted.
832 if(0u == renderToFboInterval)
834 // Sleep until at least the the default frame duration has elapsed. This will return immediately if the specified end-time has already passed.
835 TimeService::SleepUntil(timeToSleepUntil);
839 // Inform core of context destruction
840 mCore.ContextDestroyed();
842 WindowContainer windows;
843 mAdaptorInterfaces.GetWindowContainerInterface(windows);
846 for(auto&& window : windows)
848 Dali::RenderSurfaceInterface* surface = window->GetSurface();
849 surface->DestroySurface();
854 LOG_UPDATE_RENDER("THREAD DESTROYED");
856 // Uninstall the logging function
857 mEnvironmentOptions.UnInstallLogFunction();
860 bool CombinedUpdateRenderController::UpdateRenderReady(bool& useElapsedTime, bool updateRequired, uint64_t& timeToSleepUntil)
862 useElapsedTime = true;
864 ConditionalWait::ScopedLock updateLock(mUpdateRenderThreadWaitCondition);
865 while((!mUpdateRenderRunCount || // Should try to wait if event-thread has paused the Update/Render thread
866 (mUpdateRenderThreadCanSleep && !updateRequired && !mPendingRequestUpdate)) && // Ensure we wait if we're supposed to be sleeping AND do not require another update
867 !mDestroyUpdateRenderThread && // Ensure we don't wait if the update-render-thread is supposed to be destroyed
868 !mNewSurface && // Ensure we don't wait if we need to replace the surface
869 !mDeletedSurface && // Ensure we don't wait if we need to delete the surface
870 !mSurfaceResized) // Ensure we don't wait if we need to resize the surface
872 LOG_UPDATE_RENDER("WAIT: mUpdateRenderRunCount: %d", mUpdateRenderRunCount);
873 LOG_UPDATE_RENDER(" mUpdateRenderThreadCanSleep: %d, updateRequired: %d, mPendingRequestUpdate: %d", mUpdateRenderThreadCanSleep, updateRequired, mPendingRequestUpdate);
874 LOG_UPDATE_RENDER(" mDestroyUpdateRenderThread: %d", mDestroyUpdateRenderThread);
875 LOG_UPDATE_RENDER(" mNewSurface: %d", mNewSurface);
876 LOG_UPDATE_RENDER(" mDeletedSurface: %d", mDeletedSurface);
877 LOG_UPDATE_RENDER(" mSurfaceResized: %d", mSurfaceResized);
879 // Reset the time when the thread is waiting, so the sleep-until time for
880 // the first frame after resuming should be based on the actual start time
881 // of the first frame.
882 timeToSleepUntil = 0;
884 mUpdateRenderThreadWaitCondition.Wait(updateLock);
886 if(!mUseElapsedTimeAfterWait)
888 useElapsedTime = false;
892 LOG_COUNTER_UPDATE_RENDER("mUpdateRenderRunCount: %d", mUpdateRenderRunCount);
893 LOG_COUNTER_UPDATE_RENDER("mUpdateRenderThreadCanSleep: %d, updateRequired: %d, mPendingRequestUpdate: %d", mUpdateRenderThreadCanSleep, updateRequired, mPendingRequestUpdate);
894 LOG_COUNTER_UPDATE_RENDER("mDestroyUpdateRenderThread: %d", mDestroyUpdateRenderThread);
895 LOG_COUNTER_UPDATE_RENDER("mNewSurface: %d", mNewSurface);
896 LOG_COUNTER_UPDATE_RENDER("mDeletedSurface: %d", mDeletedSurface);
897 LOG_COUNTER_UPDATE_RENDER("mSurfaceResized: %d", mSurfaceResized);
899 mUseElapsedTimeAfterWait = FALSE;
900 mUpdateRenderThreadCanSleep = FALSE;
901 mPendingRequestUpdate = FALSE;
903 // If we've been asked to run Update/Render cycles a finite number of times then decrement so we wait after the
904 // requested number of cycles
905 if(mUpdateRenderRunCount > 0)
907 --mUpdateRenderRunCount;
910 // Keep the update-render thread alive if this thread is NOT to be destroyed
911 return !mDestroyUpdateRenderThread;
914 Dali::RenderSurfaceInterface* CombinedUpdateRenderController::ShouldSurfaceBeReplaced()
916 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
918 Dali::RenderSurfaceInterface* newSurface = mNewSurface;
924 void CombinedUpdateRenderController::SurfaceReplaced()
926 // Just increment the semaphore
927 mSurfaceSemaphore.Release(1);
930 Dali::RenderSurfaceInterface* CombinedUpdateRenderController::ShouldSurfaceBeDeleted()
932 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
934 Dali::RenderSurfaceInterface* deletedSurface = mDeletedSurface;
935 mDeletedSurface = NULL;
937 return deletedSurface;
940 void CombinedUpdateRenderController::SurfaceDeleted()
942 // Just increment the semaphore
943 mSurfaceSemaphore.Release(1);
946 void CombinedUpdateRenderController::SurfaceResized()
948 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
955 ///////////////////////////////////////////////////////////////////////////////////////////////////
957 ///////////////////////////////////////////////////////////////////////////////////////////////////
959 void CombinedUpdateRenderController::NotifyThreadInitialised()
961 // Just increment the semaphore
962 mEventThreadSemaphore.Release(1);
965 void CombinedUpdateRenderController::NotifyGraphicsInitialised()
967 mGraphicsInitializeWait.Notify();
970 void CombinedUpdateRenderController::AddPerformanceMarker(PerformanceInterface::MarkerType type)
972 if(mPerformanceInterface)
974 mPerformanceInterface->AddMarker(type);
978 /////////////////////////////////////////////////////////////////////////////////////////////////
979 // POST RENDERING: EVENT THREAD
980 /////////////////////////////////////////////////////////////////////////////////////////////////
982 void CombinedUpdateRenderController::PostRenderComplete()
984 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
985 mPostRendering = FALSE;
986 mUpdateRenderThreadWaitCondition.Notify(lock);
989 ///////////////////////////////////////////////////////////////////////////////////////////////////
990 // POST RENDERING: RENDER THREAD
991 ///////////////////////////////////////////////////////////////////////////////////////////////////
993 void CombinedUpdateRenderController::PostRenderStarted()
995 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
996 mPostRendering = TRUE;
999 void CombinedUpdateRenderController::PostRenderWaitForCompletion()
1001 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
1002 while(mPostRendering &&
1003 !mNewSurface && // We should NOT wait if we're replacing the surface
1004 !mDeletedSurface && // We should NOT wait if we're deleting the surface
1005 !mDestroyUpdateRenderThread)
1007 mUpdateRenderThreadWaitCondition.Wait(lock);
1011 } // namespace Adaptor
1013 } // namespace Internal