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>
23 #include <dali/integration-api/shader-precompiler.h>
26 #include "dali/public-api/common/dali-common.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/system/common/environment-options.h>
35 #include <dali/internal/system/common/texture-upload-manager-impl.h>
36 #include <dali/internal/system/common/time-service.h>
37 #include <dali/internal/thread/common/thread-settings-impl.h>
38 #include <dali/internal/window-system/common/window-impl.h>
49 const unsigned int CREATED_THREAD_COUNT = 1u;
51 const int CONTINUOUS = -1;
54 const unsigned int TRUE = 1u;
55 const unsigned int FALSE = 0u;
57 const unsigned int MILLISECONDS_PER_SECOND(1e+3);
58 const float NANOSECONDS_TO_SECOND(1e-9f);
59 const unsigned int NANOSECONDS_PER_SECOND(1e+9);
60 const unsigned int NANOSECONDS_PER_MILLISECOND(1e+6);
62 // The following values will get calculated at compile time
63 const float DEFAULT_FRAME_DURATION_IN_SECONDS(1.0f / 60.0f);
64 const uint64_t DEFAULT_FRAME_DURATION_IN_MILLISECONDS(DEFAULT_FRAME_DURATION_IN_SECONDS* MILLISECONDS_PER_SECOND);
65 const uint64_t DEFAULT_FRAME_DURATION_IN_NANOSECONDS(DEFAULT_FRAME_DURATION_IN_SECONDS* NANOSECONDS_PER_SECOND);
68 * 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
69 * there is a danger that, on the event-thread we could have:
70 * 1) An update-request where we do nothing as Update/Render thread still running.
71 * 2) Quickly followed by a sleep-request being handled where we pause the Update/Render Thread (even though we have an update to process).
73 * 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:
74 * 1) MAIN THREAD: Update Request: COUNTER = 1
75 * 2) UPDATE/RENDER THREAD: Do Update/Render, then no Updates required -> Sleep Trigger
76 * 3) MAIN THREAD: Update Request: COUNTER = 2
77 * 4) MAIN THREAD: Sleep Request: COUNTER = 1 -> We do not sleep just yet
79 * Also ensures we preserve battery life by only doing ONE update when the above use case is not triggered.
80 * 1) MAIN THREAD: Update Request: COUNTER = 1
81 * 2) UPDATE/RENDER THREAD: Do Update/Render, then no Updates required -> Sleep Trigger
82 * 3) MAIN THREAD: Sleep Request: COUNTER = 0 -> Go to sleep
84 const unsigned int MAXIMUM_UPDATE_REQUESTS = 2;
85 } // unnamed namespace
87 ///////////////////////////////////////////////////////////////////////////////////////////////////
89 ///////////////////////////////////////////////////////////////////////////////////////////////////
91 CombinedUpdateRenderController::CombinedUpdateRenderController(AdaptorInternalServices& adaptorInterfaces, const EnvironmentOptions& environmentOptions, ThreadMode threadMode)
92 : mFpsTracker(environmentOptions),
93 mUpdateStatusLogger(environmentOptions),
94 mEventThreadSemaphore(0),
96 mUpdateRenderThreadWaitCondition(),
97 mAdaptorInterfaces(adaptorInterfaces),
98 mPerformanceInterface(adaptorInterfaces.GetPerformanceInterface()),
99 mCore(adaptorInterfaces.GetCore()),
100 mEnvironmentOptions(environmentOptions),
101 mNotificationTrigger(adaptorInterfaces.GetProcessCoreEventsTrigger()),
103 mPreRenderCallback(NULL),
104 mTextureUploadManager(adaptorInterfaces.GetTextureUploadManager()),
105 mUpdateRenderThread(NULL),
106 mDefaultFrameDelta(0.0f),
107 mDefaultFrameDurationMilliseconds(0u),
108 mDefaultFrameDurationNanoseconds(0u),
109 mDefaultHalfFrameNanoseconds(0u),
110 mUpdateRequestCount(0u),
113 mThreadMode(threadMode),
114 mUpdateRenderRunCount(0),
115 mDestroyUpdateRenderThread(FALSE),
116 mUpdateRenderThreadCanSleep(FALSE),
117 mPendingRequestUpdate(FALSE),
118 mUseElapsedTimeAfterWait(FALSE),
120 mDeletedSurface(nullptr),
121 mPostRendering(FALSE),
124 mUploadWithoutRendering(FALSE),
125 mFirstFrameAfterResume(FALSE)
129 // Initialise frame delta/duration variables first
130 SetRenderRefreshRate(environmentOptions.GetRenderRefreshRate());
132 // Set the thread-synchronization interface on the render-surface
133 Dali::RenderSurfaceInterface* currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
136 currentSurface->SetThreadSynchronization(*this);
139 mSleepTrigger = TriggerEventFactory::CreateTriggerEvent(MakeCallback(this, &CombinedUpdateRenderController::ProcessSleepRequest), TriggerEventInterface::KEEP_ALIVE_AFTER_TRIGGER);
142 CombinedUpdateRenderController::~CombinedUpdateRenderController()
148 delete mPreRenderCallback;
149 delete mSleepTrigger;
152 void CombinedUpdateRenderController::Initialize()
156 // Ensure Update/Render Thread not already created
157 DALI_ASSERT_ALWAYS(!mUpdateRenderThread);
159 // Create Update/Render Thread
160 ConditionalWait::ScopedLock lock(mGraphicsInitializeWait);
161 mUpdateRenderThread = new pthread_t();
162 int error = pthread_create(mUpdateRenderThread, NULL, InternalUpdateRenderThreadEntryFunc, this);
163 DALI_ASSERT_ALWAYS(!error && "Return code from pthread_create() when creating UpdateRenderThread");
165 // The Update/Render thread will now run and initialise the graphics interface etc. and will then wait for Start to be called
166 // When this function returns, the application initialisation on the event thread should occur
169 void CombinedUpdateRenderController::Start()
173 DALI_ASSERT_ALWAYS(!mRunning && mUpdateRenderThread);
175 // Wait until all threads created in Initialise are up and running
176 for(unsigned int i = 0; i < CREATED_THREAD_COUNT; ++i)
178 mEventThreadSemaphore.Acquire();
183 LOG_EVENT("Startup Complete, starting Update/Render Thread");
185 RunUpdateRenderThread(CONTINUOUS, AnimationProgression::NONE, UpdateMode::NORMAL);
187 Dali::RenderSurfaceInterface* currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
190 currentSurface->StartRender();
193 DALI_LOG_RELEASE_INFO("CombinedUpdateRenderController::Start\n");
196 void CombinedUpdateRenderController::Pause()
202 PauseUpdateRenderThread();
204 AddPerformanceMarker(PerformanceInterface::PAUSED);
206 DALI_LOG_RELEASE_INFO("CombinedUpdateRenderController::Pause\n");
209 void CombinedUpdateRenderController::Resume()
213 if(!mRunning && IsUpdateRenderThreadPaused())
215 LOG_EVENT("Resuming");
217 RunUpdateRenderThread(CONTINUOUS, AnimationProgression::USE_ELAPSED_TIME, UpdateMode::NORMAL);
219 AddPerformanceMarker(PerformanceInterface::RESUME);
223 mFirstFrameAfterResume = TRUE;
225 DALI_LOG_RELEASE_INFO("CombinedUpdateRenderController::Resume\n");
229 DALI_LOG_RELEASE_INFO("CombinedUpdateRenderController::Resume: Already resumed [%d, %d, %d]\n", mRunning, mUpdateRenderRunCount, mUpdateRenderThreadCanSleep);
233 void CombinedUpdateRenderController::Stop()
237 // Stop Rendering and the Update/Render Thread
238 Dali::RenderSurfaceInterface* currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
241 currentSurface->StopRender();
244 StopUpdateRenderThread();
246 if(mUpdateRenderThread)
248 LOG_EVENT("Destroying UpdateRenderThread");
250 // wait for the thread to finish
251 pthread_join(*mUpdateRenderThread, NULL);
253 delete mUpdateRenderThread;
254 mUpdateRenderThread = NULL;
259 DALI_LOG_RELEASE_INFO("CombinedUpdateRenderController::Stop\n");
262 void CombinedUpdateRenderController::RequestUpdate()
266 // Increment the update-request count to the maximum
267 if(mUpdateRequestCount < MAXIMUM_UPDATE_REQUESTS)
269 ++mUpdateRequestCount;
272 if(mRunning && IsUpdateRenderThreadPaused())
274 LOG_EVENT("Processing");
276 RunUpdateRenderThread(CONTINUOUS, AnimationProgression::NONE, UpdateMode::NORMAL);
279 ConditionalWait::ScopedLock updateLock(mUpdateRenderThreadWaitCondition);
280 mPendingRequestUpdate = TRUE;
283 void CombinedUpdateRenderController::RequestUpdateOnce(UpdateMode updateMode)
285 // Increment the update-request count to the maximum
286 if(mUpdateRequestCount < MAXIMUM_UPDATE_REQUESTS)
288 ++mUpdateRequestCount;
291 if(IsUpdateRenderThreadPaused() || updateMode == UpdateMode::FORCE_RENDER)
295 // Run Update/Render once
296 RunUpdateRenderThread(ONCE, AnimationProgression::NONE, updateMode);
300 void CombinedUpdateRenderController::ReplaceSurface(Dali::RenderSurfaceInterface* newSurface)
304 if(mUpdateRenderThread)
306 // Set the ThreadSyncronizationInterface on the new surface
307 newSurface->SetThreadSynchronization(*this);
309 LOG_EVENT("Starting to replace the surface, event-thread blocked");
311 // Start replacing the surface.
313 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
314 mPostRendering = FALSE; // Clear the post-rendering flag as Update/Render thread will replace the surface now
315 mNewSurface = newSurface;
316 mUpdateRenderThreadWaitCondition.Notify(lock);
319 // Wait until the surface has been replaced
320 mSurfaceSemaphore.Acquire();
322 LOG_EVENT("Surface replaced, event-thread continuing");
326 void CombinedUpdateRenderController::DeleteSurface(Dali::RenderSurfaceInterface* surface)
330 if(mUpdateRenderThread)
332 LOG_EVENT("Starting to delete the surface, event-thread blocked");
334 // Start replacing the surface.
336 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
337 mPostRendering = FALSE; // Clear the post-rendering flag as Update/Render thread will delete the surface now
338 mDeletedSurface = surface;
339 mUpdateRenderThreadWaitCondition.Notify(lock);
342 // Wait until the surface has been deleted
343 mSurfaceSemaphore.Acquire();
345 LOG_EVENT("Surface deleted, event-thread continuing");
349 void CombinedUpdateRenderController::WaitForGraphicsInitialization()
351 ConditionalWait::ScopedLock lk(mGraphicsInitializeWait);
354 if(mUpdateRenderThread)
356 LOG_EVENT("Waiting for graphics initialisation, event-thread blocked");
358 // Wait until the graphics has been initialised
359 mGraphicsInitializeWait.Wait(lk);
361 LOG_EVENT("graphics initialised, event-thread continuing");
365 void CombinedUpdateRenderController::ResizeSurface()
369 LOG_EVENT("Resize the surface");
372 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
373 // Surface is resized and the surface resized count is increased.
375 mUpdateRenderThreadWaitCondition.Notify(lock);
379 void CombinedUpdateRenderController::SetRenderRefreshRate(unsigned int numberOfFramesPerRender)
381 // Not protected by lock, but written to rarely so not worth adding a lock when reading
382 mDefaultFrameDelta = numberOfFramesPerRender * DEFAULT_FRAME_DURATION_IN_SECONDS;
383 mDefaultFrameDurationMilliseconds = uint64_t(numberOfFramesPerRender) * DEFAULT_FRAME_DURATION_IN_MILLISECONDS;
384 mDefaultFrameDurationNanoseconds = uint64_t(numberOfFramesPerRender) * DEFAULT_FRAME_DURATION_IN_NANOSECONDS;
385 mDefaultHalfFrameNanoseconds = mDefaultFrameDurationNanoseconds / 2u;
387 LOG_EVENT("mDefaultFrameDelta(%.6f), mDefaultFrameDurationMilliseconds(%lld), mDefaultFrameDurationNanoseconds(%lld)", mDefaultFrameDelta, mDefaultFrameDurationMilliseconds, mDefaultFrameDurationNanoseconds);
390 void CombinedUpdateRenderController::SetPreRenderCallback(CallbackBase* callback)
393 LOG_EVENT("Set PreRender Callback");
395 ConditionalWait::ScopedLock updateLock(mUpdateRenderThreadWaitCondition);
396 if(mPreRenderCallback)
398 delete mPreRenderCallback;
400 mPreRenderCallback = callback;
403 void CombinedUpdateRenderController::AddSurface(Dali::RenderSurfaceInterface* surface)
406 LOG_EVENT("Surface is added");
407 if(mUpdateRenderThread)
409 // Set the ThreadSyncronizationInterface on the added surface
410 surface->SetThreadSynchronization(*this);
414 int32_t CombinedUpdateRenderController::GetThreadId() const
419 ///////////////////////////////////////////////////////////////////////////////////////////////////
421 ///////////////////////////////////////////////////////////////////////////////////////////////////
423 void CombinedUpdateRenderController::RunUpdateRenderThread(int numberOfCycles, AnimationProgression animationProgression, UpdateMode updateMode)
425 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
429 case ThreadMode::NORMAL:
431 mUpdateRenderRunCount = numberOfCycles;
432 mUseElapsedTimeAfterWait = (animationProgression == AnimationProgression::USE_ELAPSED_TIME);
435 case ThreadMode::RUN_IF_REQUESTED:
437 if(updateMode != UpdateMode::FORCE_RENDER)
439 // Render only if the update mode is FORCE_RENDER which means the application requests it.
440 // We don't want to awake the update thread.
444 mUpdateRenderRunCount++; // Increase the update request count
445 mUseElapsedTimeAfterWait = TRUE; // The elapsed time should be used. We want animations to proceed.
450 mUpdateRenderThreadCanSleep = FALSE;
451 mUploadWithoutRendering = (updateMode == UpdateMode::SKIP_RENDER);
452 LOG_COUNTER_EVENT("mUpdateRenderRunCount: %d, mUseElapsedTimeAfterWait: %d", mUpdateRenderRunCount, mUseElapsedTimeAfterWait);
453 mUpdateRenderThreadWaitCondition.Notify(lock);
456 void CombinedUpdateRenderController::PauseUpdateRenderThread()
458 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
459 mUpdateRenderRunCount = 0;
462 void CombinedUpdateRenderController::StopUpdateRenderThread()
464 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
465 mDestroyUpdateRenderThread = TRUE;
466 mUpdateRenderThreadWaitCondition.Notify(lock);
469 bool CombinedUpdateRenderController::IsUpdateRenderThreadPaused()
471 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
473 if(mThreadMode == ThreadMode::RUN_IF_REQUESTED)
475 return !mRunning || mUpdateRenderThreadCanSleep;
478 return (mUpdateRenderRunCount != CONTINUOUS) || // Report paused if NOT continuously running
479 mUpdateRenderThreadCanSleep; // Report paused if sleeping
482 void CombinedUpdateRenderController::ProcessSleepRequest()
486 // Decrement Update request count
487 if(mUpdateRequestCount > 0)
489 --mUpdateRequestCount;
492 // Can sleep if our update-request count is 0
493 // Update/Render thread can choose to carry on updating if it determines more update/renders are required
494 if(mUpdateRequestCount == 0)
496 LOG_EVENT("Going to sleep");
498 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
499 mUpdateRenderThreadCanSleep = TRUE;
503 ///////////////////////////////////////////////////////////////////////////////////////////////////
504 // UPDATE/RENDER THREAD
505 ///////////////////////////////////////////////////////////////////////////////////////////////////
507 void CombinedUpdateRenderController::UpdateRenderThread()
509 ThreadSettings::SetThreadName("RenderThread\0");
510 mThreadId = ThreadSettings::GetThreadId();
512 // Install a function for logging
513 mEnvironmentOptions.InstallLogFunction();
515 // Install a function for tracing
516 mEnvironmentOptions.InstallTraceFunction();
518 TRACE_UPDATE_RENDER_BEGIN("DALI_RENDER_THREAD_INIT");
520 LOG_UPDATE_RENDER("THREAD CREATED");
522 // Initialize graphics
523 GraphicsInterface& graphics = mAdaptorInterfaces.GetGraphicsInterface();
524 graphics.Initialize();
526 Dali::DisplayConnection& displayConnection = mAdaptorInterfaces.GetDisplayConnectionInterface();
527 displayConnection.Initialize(); //@todo Move InitializeGraphics code into graphics implementation
529 // Setup graphics controller into upload manager.
530 GetImplementation(mTextureUploadManager).InitalizeGraphicsController(graphics.GetController());
532 NotifyGraphicsInitialised();
534 //@todo Vk swaps this around, but we need to support surfaceless context for multi-window
535 graphics.ConfigureSurface(mAdaptorInterfaces.GetRenderSurfaceInterface());
537 // Tell core it has a context
538 mCore.ContextCreated();
540 NotifyThreadInitialised();
542 // Initialize and create graphics resource for the shared context.
543 WindowContainer windows;
544 mAdaptorInterfaces.GetWindowContainerInterface(windows);
546 for(auto&& window : windows)
548 Dali::Integration::Scene scene = window->GetScene();
549 Dali::RenderSurfaceInterface* windowSurface = window->GetSurface();
551 if(scene && windowSurface)
553 windowSurface->InitializeGraphics();
558 uint64_t lastFrameTime;
559 TimeService::GetNanoseconds(lastFrameTime);
560 uint64_t lastMemPoolLogTime = lastFrameTime;
562 LOG_UPDATE_RENDER("THREAD INITIALISED");
564 bool useElapsedTime = true;
565 bool updateRequired = true;
566 uint64_t timeToSleepUntil = 0;
567 int extraFramesDropped = 0;
569 const uint64_t memPoolInterval = 1e9 * float(mEnvironmentOptions.GetMemoryPoolInterval());
571 const unsigned int renderToFboInterval = mEnvironmentOptions.GetRenderToFboInterval();
572 const bool renderToFboEnabled = 0u != renderToFboInterval;
573 unsigned int frameCount = 0u;
575 TRACE_UPDATE_RENDER_END("DALI_RENDER_THREAD_INIT");
577 if(Integration::ShaderPrecompiler::Get().IsEnable())
579 RawShaderData precompiledShader;
580 Integration::ShaderPrecompiler::Get().GetPrecompileShaderList(precompiledShader);
581 auto numberOfPrecomipledShader = precompiledShader.shaderCount;
582 for(int i= 0; i<numberOfPrecomipledShader; ++i)
584 auto vertexShader = std::string(graphics.GetController().GetGlAbstraction().GetVertexShaderPrefix() + precompiledShader.vertexPrefix[i].data() + precompiledShader.vertexShader.data());
585 auto fragmentShader = std::string(graphics.GetController().GetGlAbstraction().GetFragmentShaderPrefix() + precompiledShader.fragmentPrefix[i].data() + precompiledShader.fragmentShader.data());
586 mCore.PreCompileShader(vertexShader.data(), fragmentShader.data());
588 DALI_LOG_RELEASE_INFO("ShaderPrecompiler[ENABLE], shader :%d \n",numberOfPrecomipledShader);
592 DALI_LOG_RELEASE_INFO("ShaderPrecompiler[DISABLE] \n");
595 while(UpdateRenderReady(useElapsedTime, updateRequired, timeToSleepUntil))
597 LOG_UPDATE_RENDER_TRACE;
598 TRACE_UPDATE_RENDER_SCOPE("DALI_UPDATE_RENDER");
601 bool uploadOnly = mUploadWithoutRendering;
602 unsigned int surfaceResized = mSurfaceResized;
603 Dali::RenderSurfaceInterface* deletedSurface = ShouldSurfaceBeDeleted();
605 // Performance statistics are logged upon a VSYNC tick so use this point for a VSync marker
606 AddPerformanceMarker(PerformanceInterface::VSYNC);
608 uint64_t currentFrameStartTime = 0;
609 TimeService::GetNanoseconds(currentFrameStartTime);
611 uint64_t timeSinceLastFrame = currentFrameStartTime - lastFrameTime;
613 // Optional FPS Tracking when continuously rendering
614 if(useElapsedTime && mFpsTracker.Enabled())
616 float absoluteTimeSinceLastRender = timeSinceLastFrame * NANOSECONDS_TO_SECOND;
617 mFpsTracker.Track(absoluteTimeSinceLastRender);
620 lastFrameTime = currentFrameStartTime; // Store frame start time
622 //////////////////////////////
624 //////////////////////////////
626 Dali::RenderSurfaceInterface* newSurface = ShouldSurfaceBeReplaced();
627 if(DALI_UNLIKELY(newSurface))
629 LOG_UPDATE_RENDER_TRACE_FMT("Replacing Surface");
630 // This is designed for replacing pixmap surfaces, but should work for window as well
631 // we need to delete the surface and renderable (pixmap / window)
632 // Then create a new pixmap/window and new surface
633 // If the new surface has a different display connection, then the context will be lost
634 mAdaptorInterfaces.GetDisplayConnectionInterface().Initialize();
635 graphics.ActivateSurfaceContext(newSurface);
636 // TODO: ReplaceGraphicsSurface doesn't work, InitializeGraphics()
637 // already creates new surface window, the surface and the context.
638 // We probably don't need ReplaceGraphicsSurface at all.
639 // newSurface->ReplaceGraphicsSurface();
643 //////////////////////////////
644 // TextureUploadRequest (phase #1)
645 //////////////////////////////
647 // Upload requested resources after resource context activated.
648 graphics.ActivateResourceContext();
650 const bool textureUploaded = mTextureUploadManager.ResourceUpload();
652 // Update & Render forcely if there exist some uploaded texture.
653 uploadOnly = textureUploaded ? false : uploadOnly;
655 const bool isRenderingToFbo = renderToFboEnabled && ((0u == frameCount) || (0u != frameCount % renderToFboInterval));
658 //////////////////////////////
660 //////////////////////////////
662 const uint32_t currentTime = static_cast<uint32_t>(currentFrameStartTime / NANOSECONDS_PER_MILLISECOND);
663 const uint32_t nextFrameTime = currentTime + static_cast<uint32_t>(mDefaultFrameDurationMilliseconds);
665 uint64_t noOfFramesSinceLastUpdate = 1;
666 float frameDelta = 0.0f;
669 if(mThreadMode == ThreadMode::RUN_IF_REQUESTED)
671 extraFramesDropped = 0;
672 while(timeSinceLastFrame >= mDefaultFrameDurationNanoseconds)
674 timeSinceLastFrame -= mDefaultFrameDurationNanoseconds;
675 extraFramesDropped++;
679 // If using the elapsed time, then calculate frameDelta as a multiple of mDefaultFrameDelta
680 noOfFramesSinceLastUpdate += extraFramesDropped;
682 frameDelta = mDefaultFrameDelta * noOfFramesSinceLastUpdate;
684 LOG_UPDATE_RENDER("timeSinceLastFrame(%llu) noOfFramesSinceLastUpdate(%u) frameDelta(%.6f)", timeSinceLastFrame, noOfFramesSinceLastUpdate, frameDelta);
686 Integration::UpdateStatus updateStatus;
688 AddPerformanceMarker(PerformanceInterface::UPDATE_START);
689 TRACE_UPDATE_RENDER_BEGIN("DALI_UPDATE");
690 mCore.Update(frameDelta,
697 TRACE_UPDATE_RENDER_END("DALI_UPDATE");
698 AddPerformanceMarker(PerformanceInterface::UPDATE_END);
700 unsigned int keepUpdatingStatus = updateStatus.KeepUpdating();
702 // Tell the event-thread to wake up (if asleep) and send a notification event to Core if required
703 if(updateStatus.NeedsNotification())
705 mNotificationTrigger.Trigger();
706 LOG_UPDATE_RENDER("Notification Triggered");
709 // Optional logging of update/render status
710 mUpdateStatusLogger.Log(keepUpdatingStatus);
712 //////////////////////////////
714 //////////////////////////////
716 graphics.FrameStart();
717 mAdaptorInterfaces.GetDisplayConnectionInterface().ConsumeEvents();
719 if(mPreRenderCallback != NULL)
721 bool keepCallback = CallbackBase::ExecuteReturn<bool>(*mPreRenderCallback);
724 delete mPreRenderCallback;
725 mPreRenderCallback = NULL;
729 //////////////////////////////
730 // TextureUploadRequest (phase #2)
731 //////////////////////////////
733 // Upload requested resources after resource context activated.
734 graphics.ActivateResourceContext();
736 // Since uploadOnly value used at Update side, we should not change uploadOnly value now even some textures are uploaded.
737 mTextureUploadManager.ResourceUpload();
739 if(mFirstFrameAfterResume)
741 // mFirstFrameAfterResume is set to true when the thread is resumed
742 // Let graphics know the first frame after thread initialized or resumed.
743 graphics.SetFirstFrameAfterResume();
744 mFirstFrameAfterResume = FALSE;
747 Integration::RenderStatus renderStatus;
749 AddPerformanceMarker(PerformanceInterface::RENDER_START);
750 TRACE_UPDATE_RENDER_BEGIN("DALI_RENDER");
752 // Upload shared resources
753 TRACE_UPDATE_RENDER_BEGIN("DALI_PRE_RENDER");
754 mCore.PreRender(renderStatus, mForceClear);
755 TRACE_UPDATE_RENDER_END("DALI_PRE_RENDER");
757 if(!uploadOnly || surfaceResized)
759 // Go through each window
761 mAdaptorInterfaces.GetWindowContainerInterface(windows);
763 for(auto&& window : windows)
765 Dali::Integration::Scene scene = window->GetScene();
766 Dali::RenderSurfaceInterface* windowSurface = window->GetSurface();
768 if(scene && windowSurface)
770 TRACE_UPDATE_RENDER_SCOPE("DALI_RENDER_SCENE");
771 Integration::RenderStatus windowRenderStatus;
773 const bool sceneSurfaceResized = scene.IsSurfaceRectChanged();
775 // clear previous frame damaged render items rects, buffer history is tracked on surface level
776 mDamagedRects.clear();
778 // Collect damage rects
779 mCore.PreRender(scene, mDamagedRects);
781 // Render off-screen frame buffers first if any
782 mCore.RenderScene(windowRenderStatus, scene, true);
784 Rect<int> clippingRect; // Empty for fbo rendering
786 // Switch to the context of the surface, merge damaged areas for previous frames
787 windowSurface->PreRender(sceneSurfaceResized, mDamagedRects, clippingRect); // Switch GL context
789 // Render the surface
790 mCore.RenderScene(windowRenderStatus, scene, false, clippingRect);
792 // Buffer swapping now happens when the surface render target is presented.
794 // If surface is resized, the surface resized count is decreased.
795 if(DALI_UNLIKELY(sceneSurfaceResized))
803 TRACE_UPDATE_RENDER_BEGIN("DALI_POST_RENDER");
806 graphics.PostRender();
810 TRACE_UPDATE_RENDER_END("DALI_POST_RENDER");
812 //////////////////////////////
814 //////////////////////////////
815 if(DALI_UNLIKELY(deletedSurface))
817 LOG_UPDATE_RENDER_TRACE_FMT("Deleting Surface");
819 deletedSurface->DestroySurface();
824 TRACE_UPDATE_RENDER_END("DALI_RENDER");
825 AddPerformanceMarker(PerformanceInterface::RENDER_END);
827 // if the memory pool interval is set and has elapsed, log the graphics memory pools
828 if(0 < memPoolInterval && memPoolInterval < lastFrameTime - lastMemPoolLogTime)
830 lastMemPoolLogTime = lastFrameTime;
831 graphics.LogMemoryPools();
836 // Trigger event thread to request Update/Render thread to sleep if update not required
837 if((Integration::KeepUpdating::NOT_REQUESTED == keepUpdatingStatus) && !renderStatus.NeedsUpdate())
839 mSleepTrigger->Trigger();
840 updateRequired = false;
841 LOG_UPDATE_RENDER("Sleep Triggered");
845 updateRequired = true;
848 //////////////////////////////
850 //////////////////////////////
852 extraFramesDropped = 0;
854 if(timeToSleepUntil == 0)
856 // If this is the first frame after the thread is initialized or resumed, we
857 // use the actual time the current frame starts from to calculate the time to
858 // sleep until the next frame.
859 timeToSleepUntil = currentFrameStartTime + mDefaultFrameDurationNanoseconds;
863 // Otherwise, always use the sleep-until time calculated in the last frame to
864 // calculate the time to sleep until the next frame. In this way, if there is
865 // any time gap between the current frame and the next frame, or if update or
866 // rendering in the current frame takes too much time so that the specified
867 // sleep-until time has already passed, it will try to keep the frames syncing
868 // by shortening the duration of the next frame.
869 timeToSleepUntil += mDefaultFrameDurationNanoseconds;
871 // Check the current time at the end of the frame
872 uint64_t currentFrameEndTime = 0;
873 TimeService::GetNanoseconds(currentFrameEndTime);
874 while(currentFrameEndTime > timeToSleepUntil + mDefaultFrameDurationNanoseconds)
876 // We are more than one frame behind already, so just drop the next frames
877 // until the sleep-until time is later than the current time so that we can
879 timeToSleepUntil += mDefaultFrameDurationNanoseconds;
880 extraFramesDropped++;
884 // Render to FBO is intended to measure fps above 60 so sleep is not wanted.
885 if(0u == renderToFboInterval)
887 TRACE_UPDATE_RENDER_SCOPE("DALI_UPDATE_RENDER_SLEEP");
888 // Sleep until at least the the default frame duration has elapsed. This will return immediately if the specified end-time has already passed.
889 TimeService::SleepUntil(timeToSleepUntil);
892 TRACE_UPDATE_RENDER_BEGIN("DALI_RENDER_THREAD_FINISH");
894 // Inform core of context destruction
895 mCore.ContextDestroyed();
898 mAdaptorInterfaces.GetWindowContainerInterface(windows);
901 for(auto&& window : windows)
903 Dali::RenderSurfaceInterface* surface = window->GetSurface();
904 surface->DestroySurface();
909 LOG_UPDATE_RENDER("THREAD DESTROYED");
911 TRACE_UPDATE_RENDER_END("DALI_RENDER_THREAD_FINISH");
913 // Uninstall the logging function
914 mEnvironmentOptions.UnInstallLogFunction();
917 bool CombinedUpdateRenderController::UpdateRenderReady(bool& useElapsedTime, bool updateRequired, uint64_t& timeToSleepUntil)
919 useElapsedTime = true;
921 ConditionalWait::ScopedLock updateLock(mUpdateRenderThreadWaitCondition);
922 while((!mUpdateRenderRunCount || // Should try to wait if event-thread has paused the Update/Render thread
923 (mUpdateRenderThreadCanSleep && !updateRequired && !mPendingRequestUpdate)) && // Ensure we wait if we're supposed to be sleeping AND do not require another update
924 !mDestroyUpdateRenderThread && // Ensure we don't wait if the update-render-thread is supposed to be destroyed
925 !mNewSurface && // Ensure we don't wait if we need to replace the surface
926 !mDeletedSurface && // Ensure we don't wait if we need to delete the surface
927 !mSurfaceResized) // Ensure we don't wait if we need to resize the surface
929 LOG_UPDATE_RENDER("WAIT: mUpdateRenderRunCount: %d", mUpdateRenderRunCount);
930 LOG_UPDATE_RENDER(" mUpdateRenderThreadCanSleep: %d, updateRequired: %d, mPendingRequestUpdate: %d", mUpdateRenderThreadCanSleep, updateRequired, mPendingRequestUpdate);
931 LOG_UPDATE_RENDER(" mDestroyUpdateRenderThread: %d", mDestroyUpdateRenderThread);
932 LOG_UPDATE_RENDER(" mNewSurface: %d", mNewSurface);
933 LOG_UPDATE_RENDER(" mDeletedSurface: %d", mDeletedSurface);
934 LOG_UPDATE_RENDER(" mSurfaceResized: %d", mSurfaceResized);
936 // Reset the time when the thread is waiting, so the sleep-until time for
937 // the first frame after resuming should be based on the actual start time
938 // of the first frame.
939 timeToSleepUntil = 0;
941 TRACE_UPDATE_RENDER_BEGIN("DALI_UPDATE_RENDER_THREAD_WAIT_CONDITION");
942 mUpdateRenderThreadWaitCondition.Wait(updateLock);
943 TRACE_UPDATE_RENDER_END("DALI_UPDATE_RENDER_THREAD_WAIT_CONDITION");
945 if(!mUseElapsedTimeAfterWait)
947 useElapsedTime = false;
951 LOG_COUNTER_UPDATE_RENDER("mUpdateRenderRunCount: %d", mUpdateRenderRunCount);
952 LOG_COUNTER_UPDATE_RENDER("mUpdateRenderThreadCanSleep: %d, updateRequired: %d, mPendingRequestUpdate: %d", mUpdateRenderThreadCanSleep, updateRequired, mPendingRequestUpdate);
953 LOG_COUNTER_UPDATE_RENDER("mDestroyUpdateRenderThread: %d", mDestroyUpdateRenderThread);
954 LOG_COUNTER_UPDATE_RENDER("mNewSurface: %d", mNewSurface);
955 LOG_COUNTER_UPDATE_RENDER("mDeletedSurface: %d", mDeletedSurface);
956 LOG_COUNTER_UPDATE_RENDER("mSurfaceResized: %d", mSurfaceResized);
958 mUseElapsedTimeAfterWait = FALSE;
959 mUpdateRenderThreadCanSleep = FALSE;
960 mPendingRequestUpdate = FALSE;
962 // If we've been asked to run Update/Render cycles a finite number of times then decrement so we wait after the
963 // requested number of cycles
964 if(mUpdateRenderRunCount > 0)
966 --mUpdateRenderRunCount;
969 // Keep the update-render thread alive if this thread is NOT to be destroyed
970 return !mDestroyUpdateRenderThread;
973 Dali::RenderSurfaceInterface* CombinedUpdateRenderController::ShouldSurfaceBeReplaced()
975 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
977 Dali::RenderSurfaceInterface* newSurface = mNewSurface;
983 void CombinedUpdateRenderController::SurfaceReplaced()
985 // Just increment the semaphore
986 mSurfaceSemaphore.Release(1);
989 Dali::RenderSurfaceInterface* CombinedUpdateRenderController::ShouldSurfaceBeDeleted()
991 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
993 Dali::RenderSurfaceInterface* deletedSurface = mDeletedSurface;
994 mDeletedSurface = NULL;
996 return deletedSurface;
999 void CombinedUpdateRenderController::SurfaceDeleted()
1001 // Just increment the semaphore
1002 mSurfaceSemaphore.Release(1);
1005 void CombinedUpdateRenderController::SurfaceResized()
1007 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
1014 ///////////////////////////////////////////////////////////////////////////////////////////////////
1016 ///////////////////////////////////////////////////////////////////////////////////////////////////
1018 void CombinedUpdateRenderController::NotifyThreadInitialised()
1020 // Just increment the semaphore
1021 mEventThreadSemaphore.Release(1);
1024 void CombinedUpdateRenderController::NotifyGraphicsInitialised()
1026 mGraphicsInitializeWait.Notify();
1029 void CombinedUpdateRenderController::AddPerformanceMarker(PerformanceInterface::MarkerType type)
1031 if(mPerformanceInterface)
1033 mPerformanceInterface->AddMarker(type);
1037 /////////////////////////////////////////////////////////////////////////////////////////////////
1038 // POST RENDERING: EVENT THREAD
1039 /////////////////////////////////////////////////////////////////////////////////////////////////
1041 void CombinedUpdateRenderController::PostRenderComplete()
1043 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
1044 mPostRendering = FALSE;
1045 mUpdateRenderThreadWaitCondition.Notify(lock);
1048 ///////////////////////////////////////////////////////////////////////////////////////////////////
1049 // POST RENDERING: RENDER THREAD
1050 ///////////////////////////////////////////////////////////////////////////////////////////////////
1052 void CombinedUpdateRenderController::PostRenderStarted()
1054 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
1055 mPostRendering = TRUE;
1058 void CombinedUpdateRenderController::PostRenderWaitForCompletion()
1060 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
1061 while(mPostRendering &&
1062 !mNewSurface && // We should NOT wait if we're replacing the surface
1063 !mDeletedSurface && // We should NOT wait if we're deleting the surface
1064 !mDestroyUpdateRenderThread)
1066 mUpdateRenderThreadWaitCondition.Wait(lock);
1070 } // namespace Adaptor
1072 } // namespace Internal