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 TRACE_UPDATE_RENDER_BEGIN("DALI_RENDER_THREAD_INIT");
518 LOG_UPDATE_RENDER("THREAD CREATED");
520 // Initialize graphics
521 GraphicsInterface& graphics = mAdaptorInterfaces.GetGraphicsInterface();
522 graphics.Initialize();
524 Dali::DisplayConnection& displayConnection = mAdaptorInterfaces.GetDisplayConnectionInterface();
525 displayConnection.Initialize(); //@todo Move InitializeGraphics code into graphics implementation
527 // Setup graphics controller into upload manager.
528 GetImplementation(mTextureUploadManager).InitalizeGraphicsController(graphics.GetController());
530 NotifyGraphicsInitialised();
532 //@todo Vk swaps this around, but we need to support surfaceless context for multi-window
533 graphics.ConfigureSurface(mAdaptorInterfaces.GetRenderSurfaceInterface());
535 // Tell core it has a context
536 mCore.ContextCreated();
538 NotifyThreadInitialised();
541 uint64_t lastFrameTime;
542 TimeService::GetNanoseconds(lastFrameTime);
543 uint64_t lastMemPoolLogTime = lastFrameTime;
545 LOG_UPDATE_RENDER("THREAD INITIALISED");
547 bool useElapsedTime = true;
548 bool updateRequired = true;
549 uint64_t timeToSleepUntil = 0;
550 int extraFramesDropped = 0;
552 const uint64_t memPoolInterval = 1e9 * float(mEnvironmentOptions.GetMemoryPoolInterval());
554 const unsigned int renderToFboInterval = mEnvironmentOptions.GetRenderToFboInterval();
555 const bool renderToFboEnabled = 0u != renderToFboInterval;
556 unsigned int frameCount = 0u;
558 TRACE_UPDATE_RENDER_END("DALI_RENDER_THREAD_INIT");
560 while(UpdateRenderReady(useElapsedTime, updateRequired, timeToSleepUntil))
562 LOG_UPDATE_RENDER_TRACE;
563 TRACE_UPDATE_RENDER_SCOPE("DALI_UPDATE_RENDER");
566 bool uploadOnly = mUploadWithoutRendering;
567 unsigned int surfaceResized = mSurfaceResized;
568 Dali::RenderSurfaceInterface* deletedSurface = ShouldSurfaceBeDeleted();
570 // Performance statistics are logged upon a VSYNC tick so use this point for a VSync marker
571 AddPerformanceMarker(PerformanceInterface::VSYNC);
573 uint64_t currentFrameStartTime = 0;
574 TimeService::GetNanoseconds(currentFrameStartTime);
576 uint64_t timeSinceLastFrame = currentFrameStartTime - lastFrameTime;
578 // Optional FPS Tracking when continuously rendering
579 if(useElapsedTime && mFpsTracker.Enabled())
581 float absoluteTimeSinceLastRender = timeSinceLastFrame * NANOSECONDS_TO_SECOND;
582 mFpsTracker.Track(absoluteTimeSinceLastRender);
585 lastFrameTime = currentFrameStartTime; // Store frame start time
587 //////////////////////////////
589 //////////////////////////////
591 Dali::RenderSurfaceInterface* newSurface = ShouldSurfaceBeReplaced();
592 if(DALI_UNLIKELY(newSurface))
594 LOG_UPDATE_RENDER_TRACE_FMT("Replacing Surface");
595 // This is designed for replacing pixmap surfaces, but should work for window as well
596 // we need to delete the surface and renderable (pixmap / window)
597 // Then create a new pixmap/window and new surface
598 // If the new surface has a different display connection, then the context will be lost
599 mAdaptorInterfaces.GetDisplayConnectionInterface().Initialize();
600 graphics.ActivateSurfaceContext(newSurface);
601 // TODO: ReplaceGraphicsSurface doesn't work, InitializeGraphics()
602 // already creates new surface window, the surface and the context.
603 // We probably don't need ReplaceGraphicsSurface at all.
604 // newSurface->ReplaceGraphicsSurface();
608 //////////////////////////////
609 // TextureUploadRequest (phase #1)
610 //////////////////////////////
612 // Upload requested resources after resource context activated.
613 graphics.ActivateResourceContext();
615 const bool textureUploaded = mTextureUploadManager.ResourceUpload();
617 // Update & Render forcely if there exist some uploaded texture.
618 uploadOnly = textureUploaded ? false : uploadOnly;
620 const bool isRenderingToFbo = renderToFboEnabled && ((0u == frameCount) || (0u != frameCount % renderToFboInterval));
623 //////////////////////////////
625 //////////////////////////////
627 const uint32_t currentTime = static_cast<uint32_t>(currentFrameStartTime / NANOSECONDS_PER_MILLISECOND);
628 const uint32_t nextFrameTime = currentTime + static_cast<uint32_t>(mDefaultFrameDurationMilliseconds);
630 uint64_t noOfFramesSinceLastUpdate = 1;
631 float frameDelta = 0.0f;
634 if(mThreadMode == ThreadMode::RUN_IF_REQUESTED)
636 extraFramesDropped = 0;
637 while(timeSinceLastFrame >= mDefaultFrameDurationNanoseconds)
639 timeSinceLastFrame -= mDefaultFrameDurationNanoseconds;
640 extraFramesDropped++;
644 // If using the elapsed time, then calculate frameDelta as a multiple of mDefaultFrameDelta
645 noOfFramesSinceLastUpdate += extraFramesDropped;
647 frameDelta = mDefaultFrameDelta * noOfFramesSinceLastUpdate;
649 LOG_UPDATE_RENDER("timeSinceLastFrame(%llu) noOfFramesSinceLastUpdate(%u) frameDelta(%.6f)", timeSinceLastFrame, noOfFramesSinceLastUpdate, frameDelta);
651 Integration::UpdateStatus updateStatus;
653 AddPerformanceMarker(PerformanceInterface::UPDATE_START);
654 TRACE_UPDATE_RENDER_BEGIN("DALI_UPDATE");
655 mCore.Update(frameDelta,
662 TRACE_UPDATE_RENDER_END("DALI_UPDATE");
663 AddPerformanceMarker(PerformanceInterface::UPDATE_END);
665 unsigned int keepUpdatingStatus = updateStatus.KeepUpdating();
667 // Tell the event-thread to wake up (if asleep) and send a notification event to Core if required
668 if(updateStatus.NeedsNotification())
670 mNotificationTrigger.Trigger();
671 LOG_UPDATE_RENDER("Notification Triggered");
674 // Optional logging of update/render status
675 mUpdateStatusLogger.Log(keepUpdatingStatus);
677 //////////////////////////////
679 //////////////////////////////
681 graphics.FrameStart();
682 mAdaptorInterfaces.GetDisplayConnectionInterface().ConsumeEvents();
684 if(mPreRenderCallback != NULL)
686 bool keepCallback = CallbackBase::ExecuteReturn<bool>(*mPreRenderCallback);
689 delete mPreRenderCallback;
690 mPreRenderCallback = NULL;
694 //////////////////////////////
695 // TextureUploadRequest (phase #2)
696 //////////////////////////////
698 // Upload requested resources after resource context activated.
699 graphics.ActivateResourceContext();
701 // Since uploadOnly value used at Update side, we should not change uploadOnly value now even some textures are uploaded.
702 mTextureUploadManager.ResourceUpload();
704 if(mFirstFrameAfterResume)
706 // mFirstFrameAfterResume is set to true when the thread is resumed
707 // Let graphics know the first frame after thread initialized or resumed.
708 graphics.SetFirstFrameAfterResume();
709 mFirstFrameAfterResume = FALSE;
712 Integration::RenderStatus renderStatus;
714 AddPerformanceMarker(PerformanceInterface::RENDER_START);
715 TRACE_UPDATE_RENDER_BEGIN("DALI_RENDER");
717 // Upload shared resources
718 TRACE_UPDATE_RENDER_BEGIN("DALI_PRE_RENDER");
719 mCore.PreRender(renderStatus, mForceClear);
720 TRACE_UPDATE_RENDER_END("DALI_PRE_RENDER");
722 if(!uploadOnly || surfaceResized)
724 // Go through each window
725 WindowContainer windows;
726 mAdaptorInterfaces.GetWindowContainerInterface(windows);
728 for(auto&& window : windows)
730 Dali::Integration::Scene scene = window->GetScene();
731 Dali::RenderSurfaceInterface* windowSurface = window->GetSurface();
733 if(scene && windowSurface)
735 TRACE_UPDATE_RENDER_SCOPE("DALI_RENDER_SCENE");
736 Integration::RenderStatus windowRenderStatus;
738 const bool sceneSurfaceResized = scene.IsSurfaceRectChanged();
740 // clear previous frame damaged render items rects, buffer history is tracked on surface level
741 mDamagedRects.clear();
743 // Collect damage rects
744 mCore.PreRender(scene, mDamagedRects);
746 // Render off-screen frame buffers first if any
747 mCore.RenderScene(windowRenderStatus, scene, true);
749 Rect<int> clippingRect; // Empty for fbo rendering
751 // Switch to the context of the surface, merge damaged areas for previous frames
752 windowSurface->PreRender(sceneSurfaceResized, mDamagedRects, clippingRect); // Switch GL context
754 // Render the surface
755 mCore.RenderScene(windowRenderStatus, scene, false, clippingRect);
757 // Buffer swapping now happens when the surface render target is presented.
759 // If surface is resized, the surface resized count is decreased.
760 if(DALI_UNLIKELY(sceneSurfaceResized))
768 TRACE_UPDATE_RENDER_BEGIN("DALI_POST_RENDER");
771 graphics.PostRender();
775 TRACE_UPDATE_RENDER_END("DALI_POST_RENDER");
777 //////////////////////////////
779 //////////////////////////////
780 if(DALI_UNLIKELY(deletedSurface))
782 LOG_UPDATE_RENDER_TRACE_FMT("Deleting Surface");
784 deletedSurface->DestroySurface();
789 TRACE_UPDATE_RENDER_END("DALI_RENDER");
790 AddPerformanceMarker(PerformanceInterface::RENDER_END);
792 // if the memory pool interval is set and has elapsed, log the graphics memory pools
793 if(0 < memPoolInterval && memPoolInterval < lastFrameTime - lastMemPoolLogTime)
795 lastMemPoolLogTime = lastFrameTime;
796 graphics.LogMemoryPools();
801 // Trigger event thread to request Update/Render thread to sleep if update not required
802 if((Integration::KeepUpdating::NOT_REQUESTED == keepUpdatingStatus) && !renderStatus.NeedsUpdate())
804 mSleepTrigger->Trigger();
805 updateRequired = false;
806 LOG_UPDATE_RENDER("Sleep Triggered");
810 updateRequired = true;
813 //////////////////////////////
815 //////////////////////////////
817 extraFramesDropped = 0;
819 if(timeToSleepUntil == 0)
821 // If this is the first frame after the thread is initialized or resumed, we
822 // use the actual time the current frame starts from to calculate the time to
823 // sleep until the next frame.
824 timeToSleepUntil = currentFrameStartTime + mDefaultFrameDurationNanoseconds;
828 // Otherwise, always use the sleep-until time calculated in the last frame to
829 // calculate the time to sleep until the next frame. In this way, if there is
830 // any time gap between the current frame and the next frame, or if update or
831 // rendering in the current frame takes too much time so that the specified
832 // sleep-until time has already passed, it will try to keep the frames syncing
833 // by shortening the duration of the next frame.
834 timeToSleepUntil += mDefaultFrameDurationNanoseconds;
836 // Check the current time at the end of the frame
837 uint64_t currentFrameEndTime = 0;
838 TimeService::GetNanoseconds(currentFrameEndTime);
839 while(currentFrameEndTime > timeToSleepUntil + mDefaultFrameDurationNanoseconds)
841 // We are more than one frame behind already, so just drop the next frames
842 // until the sleep-until time is later than the current time so that we can
844 timeToSleepUntil += mDefaultFrameDurationNanoseconds;
845 extraFramesDropped++;
849 // Render to FBO is intended to measure fps above 60 so sleep is not wanted.
850 if(0u == renderToFboInterval)
852 TRACE_UPDATE_RENDER_SCOPE("DALI_UPDATE_RENDER_SLEEP");
853 // Sleep until at least the the default frame duration has elapsed. This will return immediately if the specified end-time has already passed.
854 TimeService::SleepUntil(timeToSleepUntil);
857 TRACE_UPDATE_RENDER_BEGIN("DALI_RENDER_THREAD_FINISH");
859 // Inform core of context destruction
860 mCore.ContextDestroyed();
862 WindowContainer windows;
863 mAdaptorInterfaces.GetWindowContainerInterface(windows);
866 for(auto&& window : windows)
868 Dali::RenderSurfaceInterface* surface = window->GetSurface();
869 surface->DestroySurface();
874 LOG_UPDATE_RENDER("THREAD DESTROYED");
876 TRACE_UPDATE_RENDER_END("DALI_RENDER_THREAD_FINISH");
878 // Uninstall the logging function
879 mEnvironmentOptions.UnInstallLogFunction();
882 bool CombinedUpdateRenderController::UpdateRenderReady(bool& useElapsedTime, bool updateRequired, uint64_t& timeToSleepUntil)
884 useElapsedTime = true;
886 ConditionalWait::ScopedLock updateLock(mUpdateRenderThreadWaitCondition);
887 while((!mUpdateRenderRunCount || // Should try to wait if event-thread has paused the Update/Render thread
888 (mUpdateRenderThreadCanSleep && !updateRequired && !mPendingRequestUpdate)) && // Ensure we wait if we're supposed to be sleeping AND do not require another update
889 !mDestroyUpdateRenderThread && // Ensure we don't wait if the update-render-thread is supposed to be destroyed
890 !mNewSurface && // Ensure we don't wait if we need to replace the surface
891 !mDeletedSurface && // Ensure we don't wait if we need to delete the surface
892 !mSurfaceResized) // Ensure we don't wait if we need to resize the surface
894 LOG_UPDATE_RENDER("WAIT: mUpdateRenderRunCount: %d", mUpdateRenderRunCount);
895 LOG_UPDATE_RENDER(" mUpdateRenderThreadCanSleep: %d, updateRequired: %d, mPendingRequestUpdate: %d", mUpdateRenderThreadCanSleep, updateRequired, mPendingRequestUpdate);
896 LOG_UPDATE_RENDER(" mDestroyUpdateRenderThread: %d", mDestroyUpdateRenderThread);
897 LOG_UPDATE_RENDER(" mNewSurface: %d", mNewSurface);
898 LOG_UPDATE_RENDER(" mDeletedSurface: %d", mDeletedSurface);
899 LOG_UPDATE_RENDER(" mSurfaceResized: %d", mSurfaceResized);
901 // Reset the time when the thread is waiting, so the sleep-until time for
902 // the first frame after resuming should be based on the actual start time
903 // of the first frame.
904 timeToSleepUntil = 0;
906 TRACE_UPDATE_RENDER_BEGIN("DALI_UPDATE_RENDER_THREAD_WAIT_CONDITION");
907 mUpdateRenderThreadWaitCondition.Wait(updateLock);
908 TRACE_UPDATE_RENDER_END("DALI_UPDATE_RENDER_THREAD_WAIT_CONDITION");
910 if(!mUseElapsedTimeAfterWait)
912 useElapsedTime = false;
916 LOG_COUNTER_UPDATE_RENDER("mUpdateRenderRunCount: %d", mUpdateRenderRunCount);
917 LOG_COUNTER_UPDATE_RENDER("mUpdateRenderThreadCanSleep: %d, updateRequired: %d, mPendingRequestUpdate: %d", mUpdateRenderThreadCanSleep, updateRequired, mPendingRequestUpdate);
918 LOG_COUNTER_UPDATE_RENDER("mDestroyUpdateRenderThread: %d", mDestroyUpdateRenderThread);
919 LOG_COUNTER_UPDATE_RENDER("mNewSurface: %d", mNewSurface);
920 LOG_COUNTER_UPDATE_RENDER("mDeletedSurface: %d", mDeletedSurface);
921 LOG_COUNTER_UPDATE_RENDER("mSurfaceResized: %d", mSurfaceResized);
923 mUseElapsedTimeAfterWait = FALSE;
924 mUpdateRenderThreadCanSleep = FALSE;
925 mPendingRequestUpdate = FALSE;
927 // If we've been asked to run Update/Render cycles a finite number of times then decrement so we wait after the
928 // requested number of cycles
929 if(mUpdateRenderRunCount > 0)
931 --mUpdateRenderRunCount;
934 // Keep the update-render thread alive if this thread is NOT to be destroyed
935 return !mDestroyUpdateRenderThread;
938 Dali::RenderSurfaceInterface* CombinedUpdateRenderController::ShouldSurfaceBeReplaced()
940 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
942 Dali::RenderSurfaceInterface* newSurface = mNewSurface;
948 void CombinedUpdateRenderController::SurfaceReplaced()
950 // Just increment the semaphore
951 mSurfaceSemaphore.Release(1);
954 Dali::RenderSurfaceInterface* CombinedUpdateRenderController::ShouldSurfaceBeDeleted()
956 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
958 Dali::RenderSurfaceInterface* deletedSurface = mDeletedSurface;
959 mDeletedSurface = NULL;
961 return deletedSurface;
964 void CombinedUpdateRenderController::SurfaceDeleted()
966 // Just increment the semaphore
967 mSurfaceSemaphore.Release(1);
970 void CombinedUpdateRenderController::SurfaceResized()
972 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
979 ///////////////////////////////////////////////////////////////////////////////////////////////////
981 ///////////////////////////////////////////////////////////////////////////////////////////////////
983 void CombinedUpdateRenderController::NotifyThreadInitialised()
985 // Just increment the semaphore
986 mEventThreadSemaphore.Release(1);
989 void CombinedUpdateRenderController::NotifyGraphicsInitialised()
991 mGraphicsInitializeWait.Notify();
994 void CombinedUpdateRenderController::AddPerformanceMarker(PerformanceInterface::MarkerType type)
996 if(mPerformanceInterface)
998 mPerformanceInterface->AddMarker(type);
1002 /////////////////////////////////////////////////////////////////////////////////////////////////
1003 // POST RENDERING: EVENT THREAD
1004 /////////////////////////////////////////////////////////////////////////////////////////////////
1006 void CombinedUpdateRenderController::PostRenderComplete()
1008 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
1009 mPostRendering = FALSE;
1010 mUpdateRenderThreadWaitCondition.Notify(lock);
1013 ///////////////////////////////////////////////////////////////////////////////////////////////////
1014 // POST RENDERING: RENDER THREAD
1015 ///////////////////////////////////////////////////////////////////////////////////////////////////
1017 void CombinedUpdateRenderController::PostRenderStarted()
1019 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
1020 mPostRendering = TRUE;
1023 void CombinedUpdateRenderController::PostRenderWaitForCompletion()
1025 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
1026 while(mPostRendering &&
1027 !mNewSurface && // We should NOT wait if we're replacing the surface
1028 !mDeletedSurface && // We should NOT wait if we're deleting the surface
1029 !mDestroyUpdateRenderThread)
1031 mUpdateRenderThreadWaitCondition.Wait(lock);
1035 } // namespace Adaptor
1037 } // namespace Internal