2 * Copyright (c) 2024 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/shader-precompiler.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>
48 const unsigned int CREATED_THREAD_COUNT = 1u;
50 const int CONTINUOUS = -1;
53 const unsigned int TRUE = 1u;
54 const unsigned int FALSE = 0u;
56 const unsigned int MILLISECONDS_PER_SECOND(1e+3);
57 const float NANOSECONDS_TO_SECOND(1e-9f);
58 const unsigned int NANOSECONDS_PER_SECOND(1e+9);
59 const unsigned int NANOSECONDS_PER_MILLISECOND(1e+6);
61 // The following values will get calculated at compile time
62 const float DEFAULT_FRAME_DURATION_IN_SECONDS(1.0f / 60.0f);
63 const uint64_t DEFAULT_FRAME_DURATION_IN_MILLISECONDS(DEFAULT_FRAME_DURATION_IN_SECONDS* MILLISECONDS_PER_SECOND);
64 const uint64_t DEFAULT_FRAME_DURATION_IN_NANOSECONDS(DEFAULT_FRAME_DURATION_IN_SECONDS* NANOSECONDS_PER_SECOND);
67 * 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
68 * there is a danger that, on the event-thread we could have:
69 * 1) An update-request where we do nothing as Update/Render thread still running.
70 * 2) Quickly followed by a sleep-request being handled where we pause the Update/Render Thread (even though we have an update to process).
72 * 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:
73 * 1) MAIN THREAD: Update Request: COUNTER = 1
74 * 2) UPDATE/RENDER THREAD: Do Update/Render, then no Updates required -> Sleep Trigger
75 * 3) MAIN THREAD: Update Request: COUNTER = 2
76 * 4) MAIN THREAD: Sleep Request: COUNTER = 1 -> We do not sleep just yet
78 * Also ensures we preserve battery life by only doing ONE update when the above use case is not triggered.
79 * 1) MAIN THREAD: Update Request: COUNTER = 1
80 * 2) UPDATE/RENDER THREAD: Do Update/Render, then no Updates required -> Sleep Trigger
81 * 3) MAIN THREAD: Sleep Request: COUNTER = 0 -> Go to sleep
83 const unsigned int MAXIMUM_UPDATE_REQUESTS = 2;
85 inline std::vector<char> StringToVector(const std::string& str)
87 auto retval = std::vector<char>{};
88 retval.insert(retval.begin(), str.begin(), str.end());
89 retval.push_back('\0');
93 } // unnamed namespace
95 ///////////////////////////////////////////////////////////////////////////////////////////////////
97 ///////////////////////////////////////////////////////////////////////////////////////////////////
99 CombinedUpdateRenderController::CombinedUpdateRenderController(AdaptorInternalServices& adaptorInterfaces, const EnvironmentOptions& environmentOptions, ThreadMode threadMode)
100 : mFpsTracker(environmentOptions),
101 mUpdateStatusLogger(environmentOptions),
102 mEventThreadSemaphore(0),
103 mSurfaceSemaphore(0),
104 mUpdateRenderThreadWaitCondition(),
105 mAdaptorInterfaces(adaptorInterfaces),
106 mPerformanceInterface(adaptorInterfaces.GetPerformanceInterface()),
107 mCore(adaptorInterfaces.GetCore()),
108 mEnvironmentOptions(environmentOptions),
109 mNotificationTrigger(adaptorInterfaces.GetProcessCoreEventsTrigger()),
111 mPreRenderCallback(NULL),
112 mTextureUploadManager(adaptorInterfaces.GetTextureUploadManager()),
113 mUpdateRenderThread(NULL),
114 mDefaultFrameDelta(0.0f),
115 mDefaultFrameDurationMilliseconds(0u),
116 mDefaultFrameDurationNanoseconds(0u),
117 mDefaultHalfFrameNanoseconds(0u),
118 mUpdateRequestCount(0u),
122 mThreadMode(threadMode),
123 mUpdateRenderRunCount(0),
124 mDestroyUpdateRenderThread(FALSE),
125 mUpdateRenderThreadCanSleep(FALSE),
126 mPendingRequestUpdate(FALSE),
127 mUseElapsedTimeAfterWait(FALSE),
128 mIsPreCompileCancelled(FALSE),
130 mDeletedSurface(nullptr),
131 mPostRendering(FALSE),
134 mUploadWithoutRendering(FALSE),
135 mFirstFrameAfterResume(FALSE)
139 // Initialise frame delta/duration variables first
140 SetRenderRefreshRate(environmentOptions.GetRenderRefreshRate());
142 // Set the thread-synchronization interface on the render-surface
143 Dali::RenderSurfaceInterface* currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
146 currentSurface->SetThreadSynchronization(*this);
149 mVsyncRender = environmentOptions.VsyncRenderRequired();
151 mSleepTrigger = TriggerEventFactory::CreateTriggerEvent(MakeCallback(this, &CombinedUpdateRenderController::ProcessSleepRequest), TriggerEventInterface::KEEP_ALIVE_AFTER_TRIGGER);
154 CombinedUpdateRenderController::~CombinedUpdateRenderController()
160 delete mPreRenderCallback;
161 delete mSleepTrigger;
164 void CombinedUpdateRenderController::Initialize()
168 // Ensure Update/Render Thread not already created
169 DALI_ASSERT_ALWAYS(!mUpdateRenderThread);
171 // Create Update/Render Thread
172 ConditionalWait::ScopedLock lock(mGraphicsInitializeWait);
173 mUpdateRenderThread = new pthread_t();
174 int error = pthread_create(mUpdateRenderThread, NULL, InternalUpdateRenderThreadEntryFunc, this);
175 DALI_ASSERT_ALWAYS(!error && "Return code from pthread_create() when creating UpdateRenderThread");
177 // The Update/Render thread will now run and initialise the graphics interface etc. and will then wait for Start to be called
178 // When this function returns, the application initialisation on the event thread should occur
181 void CombinedUpdateRenderController::Start()
185 DALI_ASSERT_ALWAYS(!mRunning && mUpdateRenderThread);
187 // Wait until all threads created in Initialise are up and running
188 for(unsigned int i = 0; i < CREATED_THREAD_COUNT; ++i)
190 mEventThreadSemaphore.Acquire();
195 LOG_EVENT("Startup Complete, starting Update/Render Thread");
197 RunUpdateRenderThread(CONTINUOUS, AnimationProgression::NONE, UpdateMode::NORMAL);
199 Dali::RenderSurfaceInterface* currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
202 currentSurface->StartRender();
205 DALI_LOG_RELEASE_INFO("CombinedUpdateRenderController::Start\n");
208 void CombinedUpdateRenderController::Pause()
214 PauseUpdateRenderThread();
216 AddPerformanceMarker(PerformanceInterface::PAUSED);
218 DALI_LOG_RELEASE_INFO("CombinedUpdateRenderController::Pause\n");
221 void CombinedUpdateRenderController::Resume()
225 if(!mRunning && IsUpdateRenderThreadPaused())
227 LOG_EVENT("Resuming");
229 RunUpdateRenderThread(CONTINUOUS, AnimationProgression::USE_ELAPSED_TIME, UpdateMode::NORMAL);
231 AddPerformanceMarker(PerformanceInterface::RESUME);
235 mFirstFrameAfterResume = TRUE;
237 DALI_LOG_RELEASE_INFO("CombinedUpdateRenderController::Resume\n");
241 DALI_LOG_RELEASE_INFO("CombinedUpdateRenderController::Resume: Already resumed [%d, %d, %d]\n", mRunning, mUpdateRenderRunCount, mUpdateRenderThreadCanSleep);
245 void CombinedUpdateRenderController::Stop()
249 // Stop Rendering and the Update/Render Thread
250 Dali::RenderSurfaceInterface* currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
253 currentSurface->StopRender();
256 StopUpdateRenderThread();
258 if(mUpdateRenderThread)
260 LOG_EVENT("Destroying UpdateRenderThread");
262 // wait for the thread to finish
263 pthread_join(*mUpdateRenderThread, NULL);
265 delete mUpdateRenderThread;
266 mUpdateRenderThread = NULL;
271 DALI_LOG_RELEASE_INFO("CombinedUpdateRenderController::Stop\n");
274 void CombinedUpdateRenderController::RequestUpdate()
278 // Increment the update-request count to the maximum
279 if(mUpdateRequestCount < MAXIMUM_UPDATE_REQUESTS)
281 ++mUpdateRequestCount;
284 if(mRunning && IsUpdateRenderThreadPaused())
286 LOG_EVENT("Processing");
288 RunUpdateRenderThread(CONTINUOUS, AnimationProgression::NONE, UpdateMode::NORMAL);
291 ConditionalWait::ScopedLock updateLock(mUpdateRenderThreadWaitCondition);
292 mPendingRequestUpdate = TRUE;
295 void CombinedUpdateRenderController::RequestUpdateOnce(UpdateMode updateMode)
297 // Increment the update-request count to the maximum
298 if(mUpdateRequestCount < MAXIMUM_UPDATE_REQUESTS)
300 ++mUpdateRequestCount;
303 if(IsUpdateRenderThreadPaused() || updateMode == UpdateMode::FORCE_RENDER)
307 // Run Update/Render once
308 RunUpdateRenderThread(ONCE, AnimationProgression::NONE, updateMode);
312 void CombinedUpdateRenderController::ReplaceSurface(Dali::RenderSurfaceInterface* newSurface)
316 if(mUpdateRenderThread)
318 // Set the ThreadSyncronizationInterface on the new surface
319 newSurface->SetThreadSynchronization(*this);
321 LOG_EVENT("Starting to replace the surface, event-thread blocked");
323 // Start replacing the surface.
325 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
326 mPostRendering = FALSE; // Clear the post-rendering flag as Update/Render thread will replace the surface now
327 mNewSurface = newSurface;
329 mUpdateRenderThreadWaitCondition.Notify(lock);
332 // Wait until the surface has been replaced
333 mSurfaceSemaphore.Acquire();
335 LOG_EVENT("Surface replaced, event-thread continuing");
339 void CombinedUpdateRenderController::DeleteSurface(Dali::RenderSurfaceInterface* surface)
343 if(mUpdateRenderThread)
345 LOG_EVENT("Starting to delete the surface, event-thread blocked");
347 // Start replacing the surface.
349 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
350 mPostRendering = FALSE; // Clear the post-rendering flag as Update/Render thread will delete the surface now
351 mDeletedSurface = surface;
353 mUpdateRenderThreadWaitCondition.Notify(lock);
356 // Wait until the surface has been deleted
357 mSurfaceSemaphore.Acquire();
359 LOG_EVENT("Surface deleted, event-thread continuing");
363 void CombinedUpdateRenderController::WaitForGraphicsInitialization()
365 ConditionalWait::ScopedLock lk(mGraphicsInitializeWait);
368 if(mUpdateRenderThread)
370 LOG_EVENT("Waiting for graphics initialisation, event-thread blocked");
372 // Wait until the graphics has been initialised
373 mGraphicsInitializeWait.Wait(lk);
375 LOG_EVENT("graphics initialised, event-thread continuing");
379 void CombinedUpdateRenderController::ResizeSurface()
383 LOG_EVENT("Resize the surface");
386 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
387 // Surface is resized and the surface resized count is increased.
390 mUpdateRenderThreadWaitCondition.Notify(lock);
394 void CombinedUpdateRenderController::SetRenderRefreshRate(unsigned int numberOfFramesPerRender)
396 // Not protected by lock, but written to rarely so not worth adding a lock when reading
397 mDefaultFrameDelta = numberOfFramesPerRender * DEFAULT_FRAME_DURATION_IN_SECONDS;
398 mDefaultFrameDurationMilliseconds = uint64_t(numberOfFramesPerRender) * DEFAULT_FRAME_DURATION_IN_MILLISECONDS;
399 mDefaultFrameDurationNanoseconds = uint64_t(numberOfFramesPerRender) * DEFAULT_FRAME_DURATION_IN_NANOSECONDS;
400 mDefaultHalfFrameNanoseconds = mDefaultFrameDurationNanoseconds / 2u;
402 LOG_EVENT("mDefaultFrameDelta(%.6f), mDefaultFrameDurationMilliseconds(%lld), mDefaultFrameDurationNanoseconds(%lld)", mDefaultFrameDelta, mDefaultFrameDurationMilliseconds, mDefaultFrameDurationNanoseconds);
405 void CombinedUpdateRenderController::SetPreRenderCallback(CallbackBase* callback)
408 LOG_EVENT("Set PreRender Callback");
410 ConditionalWait::ScopedLock updateLock(mUpdateRenderThreadWaitCondition);
411 if(mPreRenderCallback)
413 delete mPreRenderCallback;
415 mPreRenderCallback = callback;
418 void CombinedUpdateRenderController::AddSurface(Dali::RenderSurfaceInterface* surface)
421 LOG_EVENT("Surface is added");
422 if(mUpdateRenderThread)
424 // Set the ThreadSyncronizationInterface on the added surface
425 surface->SetThreadSynchronization(*this);
429 int32_t CombinedUpdateRenderController::GetThreadId() const
434 ///////////////////////////////////////////////////////////////////////////////////////////////////
436 ///////////////////////////////////////////////////////////////////////////////////////////////////
438 void CombinedUpdateRenderController::RunUpdateRenderThread(int numberOfCycles, AnimationProgression animationProgression, UpdateMode updateMode)
440 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
444 case ThreadMode::NORMAL:
446 mUpdateRenderRunCount = numberOfCycles;
447 mUseElapsedTimeAfterWait = (animationProgression == AnimationProgression::USE_ELAPSED_TIME);
450 case ThreadMode::RUN_IF_REQUESTED:
452 if(updateMode != UpdateMode::FORCE_RENDER)
454 // Render only if the update mode is FORCE_RENDER which means the application requests it.
455 // We don't want to awake the update thread.
459 mUpdateRenderRunCount++; // Increase the update request count
460 mUseElapsedTimeAfterWait = TRUE; // The elapsed time should be used. We want animations to proceed.
465 mUpdateRenderThreadCanSleep = FALSE;
466 mUploadWithoutRendering = (updateMode == UpdateMode::SKIP_RENDER);
467 LOG_COUNTER_EVENT("mUpdateRenderRunCount: %d, mUseElapsedTimeAfterWait: %d", mUpdateRenderRunCount, mUseElapsedTimeAfterWait);
469 mUpdateRenderThreadWaitCondition.Notify(lock);
472 void CombinedUpdateRenderController::PauseUpdateRenderThread()
474 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
475 mUpdateRenderRunCount = 0;
478 void CombinedUpdateRenderController::StopUpdateRenderThread()
480 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
481 mDestroyUpdateRenderThread = TRUE;
483 mUpdateRenderThreadWaitCondition.Notify(lock);
486 bool CombinedUpdateRenderController::IsUpdateRenderThreadPaused()
488 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
490 if(mThreadMode == ThreadMode::RUN_IF_REQUESTED)
492 return !mRunning || mUpdateRenderThreadCanSleep;
495 return (mUpdateRenderRunCount != CONTINUOUS) || // Report paused if NOT continuously running
496 mUpdateRenderThreadCanSleep; // Report paused if sleeping
499 void CombinedUpdateRenderController::ProcessSleepRequest()
503 // Decrement Update request count
504 if(mUpdateRequestCount > 0)
506 --mUpdateRequestCount;
509 // Can sleep if our update-request count is 0
510 // Update/Render thread can choose to carry on updating if it determines more update/renders are required
511 if(mUpdateRequestCount == 0)
513 LOG_EVENT("Going to sleep");
515 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
516 mUpdateRenderThreadCanSleep = TRUE;
520 ///////////////////////////////////////////////////////////////////////////////////////////////////
521 // UPDATE/RENDER THREAD
522 ///////////////////////////////////////////////////////////////////////////////////////////////////
524 void CombinedUpdateRenderController::UpdateRenderThread()
526 ThreadSettings::SetThreadName("RenderThread\0");
527 mThreadId = ThreadSettings::GetThreadId();
529 // Install a function for logging
530 mEnvironmentOptions.InstallLogFunction();
532 // Install a function for tracing
533 mEnvironmentOptions.InstallTraceFunction();
535 TRACE_UPDATE_RENDER_BEGIN("DALI_RENDER_THREAD_INIT");
537 LOG_UPDATE_RENDER("THREAD CREATED");
539 // Initialize graphics
540 GraphicsInterface& graphics = mAdaptorInterfaces.GetGraphicsInterface();
541 graphics.Initialize();
543 Dali::DisplayConnection& displayConnection = mAdaptorInterfaces.GetDisplayConnectionInterface();
544 displayConnection.Initialize(); //@todo Move InitializeGraphics code into graphics implementation
546 // Setup graphics controller into upload manager.
547 GetImplementation(mTextureUploadManager).InitalizeGraphicsController(graphics.GetController());
549 NotifyGraphicsInitialised();
551 //@todo Vk swaps this around, but we need to support surfaceless context for multi-window
552 graphics.ConfigureSurface(mAdaptorInterfaces.GetRenderSurfaceInterface());
554 // Tell core it has a context
555 mCore.ContextCreated();
557 NotifyThreadInitialised();
559 // Initialize and create graphics resource for the shared context.
560 WindowContainer windows;
561 mAdaptorInterfaces.GetWindowContainerInterface(windows);
563 for(auto&& window : windows)
565 Dali::Integration::Scene scene = window->GetScene();
566 Dali::RenderSurfaceInterface* windowSurface = window->GetSurface();
568 if(scene && windowSurface)
570 windowSurface->InitializeGraphics();
575 uint64_t lastFrameTime;
576 TimeService::GetNanoseconds(lastFrameTime);
577 uint64_t lastMemPoolLogTime = lastFrameTime;
579 LOG_UPDATE_RENDER("THREAD INITIALISED");
581 bool useElapsedTime = true;
582 bool updateRequired = true;
583 uint64_t timeToSleepUntil = 0;
584 int extraFramesDropped = 0;
586 const uint64_t memPoolInterval = 1e9 * float(mEnvironmentOptions.GetMemoryPoolInterval());
588 const unsigned int renderToFboInterval = mEnvironmentOptions.GetRenderToFboInterval();
589 const bool renderToFboEnabled = 0u != renderToFboInterval;
590 unsigned int frameCount = 0u;
592 TRACE_UPDATE_RENDER_END("DALI_RENDER_THREAD_INIT");
593 if(!mDestroyUpdateRenderThread)
595 ShaderPreCompiler::Get().Wait();
596 if(ShaderPreCompiler::Get().IsEnable())
598 std::vector<RawShaderData> precompiledShaderList;
599 ShaderPreCompiler::Get().GetPreCompileShaderList(precompiledShaderList);
600 DALI_LOG_RELEASE_INFO("ShaderPreCompiler[ENABLE], list size:%d \n", precompiledShaderList.size());
601 for(auto precompiledShader = precompiledShaderList.begin(); precompiledShader != precompiledShaderList.end(); ++precompiledShader)
603 if(mIsPreCompileCancelled == TRUE)
605 ShaderPreCompiler::Get().Awake();
606 DALI_LOG_RELEASE_INFO("ShaderPreCompiler[ENABLE], but stop precompile");
610 auto numberOfPrecompiledShader = precompiledShader->shaderCount;
611 for(int i = 0; i < numberOfPrecompiledShader; ++i)
613 auto vertexShader = graphics.GetController().GetGraphicsConfig().GetVertexShaderPrefix() + std::string(precompiledShader->vertexPrefix[i].data()) + std::string(precompiledShader->vertexShader.data());
614 auto fragmentShader = graphics.GetController().GetGraphicsConfig().GetFragmentShaderPrefix() + std::string(precompiledShader->fragmentPrefix[i].data()) + std::string(precompiledShader->fragmentShader.data());
615 PreCompileShader(std::move(vertexShader), std::move(fragmentShader), static_cast<uint32_t>(i) < precompiledShader->shaderName.size() ? std::string(precompiledShader->shaderName[i]) : "");
617 DALI_LOG_RELEASE_INFO("ShaderPreCompiler[ENABLE], shader count :%d \n", numberOfPrecompiledShader);
622 DALI_LOG_RELEASE_INFO("ShaderPreCompiler[DISABLE] \n");
626 while(UpdateRenderReady(useElapsedTime, updateRequired, timeToSleepUntil))
628 LOG_UPDATE_RENDER_TRACE;
629 TRACE_UPDATE_RENDER_BEGIN("DALI_UPDATE_RENDER");
632 bool uploadOnly = mUploadWithoutRendering;
633 unsigned int surfaceResized = mSurfaceResized;
634 Dali::RenderSurfaceInterface* deletedSurface = ShouldSurfaceBeDeleted();
636 // Performance statistics are logged upon a VSYNC tick so use this point for a VSync marker
637 AddPerformanceMarker(PerformanceInterface::VSYNC);
639 uint64_t currentFrameStartTime = 0;
640 TimeService::GetNanoseconds(currentFrameStartTime);
642 uint64_t timeSinceLastFrame = currentFrameStartTime - lastFrameTime;
644 // Optional FPS Tracking when continuously rendering
645 if(useElapsedTime && mFpsTracker.Enabled())
647 float absoluteTimeSinceLastRender = timeSinceLastFrame * NANOSECONDS_TO_SECOND;
648 mFpsTracker.Track(absoluteTimeSinceLastRender);
651 lastFrameTime = currentFrameStartTime; // Store frame start time
653 //////////////////////////////
655 //////////////////////////////
657 Dali::RenderSurfaceInterface* newSurface = ShouldSurfaceBeReplaced();
658 if(DALI_UNLIKELY(newSurface))
660 LOG_UPDATE_RENDER_TRACE_FMT("Replacing Surface");
661 // This is designed for replacing pixmap surfaces, but should work for window as well
662 // we need to delete the surface and renderable (pixmap / window)
663 // Then create a new pixmap/window and new surface
664 // If the new surface has a different display connection, then the context will be lost
665 mAdaptorInterfaces.GetDisplayConnectionInterface().Initialize();
666 graphics.ActivateSurfaceContext(newSurface);
667 // TODO: ReplaceGraphicsSurface doesn't work, InitializeGraphics()
668 // already creates new surface window, the surface and the context.
669 // We probably don't need ReplaceGraphicsSurface at all.
670 // newSurface->ReplaceGraphicsSurface();
674 //////////////////////////////
675 // TextureUploadRequest (phase #1)
676 //////////////////////////////
678 // Upload requested resources after resource context activated.
679 graphics.ActivateResourceContext();
681 const bool textureUploaded = mTextureUploadManager.ResourceUpload();
683 // Update & Render forcely if there exist some uploaded texture.
684 uploadOnly = textureUploaded ? false : uploadOnly;
686 const bool isRenderingToFbo = renderToFboEnabled && ((0u == frameCount) || (0u != frameCount % renderToFboInterval));
689 //////////////////////////////
691 //////////////////////////////
693 const uint32_t currentTime = static_cast<uint32_t>(currentFrameStartTime / NANOSECONDS_PER_MILLISECOND);
694 const uint32_t nextFrameTime = currentTime + static_cast<uint32_t>(mDefaultFrameDurationMilliseconds);
696 uint64_t noOfFramesSinceLastUpdate = 1;
697 float frameDelta = 0.0f;
700 if(mThreadMode == ThreadMode::RUN_IF_REQUESTED)
702 extraFramesDropped = 0;
703 while(timeSinceLastFrame >= mDefaultFrameDurationNanoseconds)
705 timeSinceLastFrame -= mDefaultFrameDurationNanoseconds;
706 extraFramesDropped++;
710 // If using the elapsed time, then calculate frameDelta as a multiple of mDefaultFrameDelta
711 noOfFramesSinceLastUpdate += extraFramesDropped;
713 frameDelta = mDefaultFrameDelta * noOfFramesSinceLastUpdate;
715 LOG_UPDATE_RENDER("timeSinceLastFrame(%llu) noOfFramesSinceLastUpdate(%u) frameDelta(%.6f)", timeSinceLastFrame, noOfFramesSinceLastUpdate, frameDelta);
717 Integration::UpdateStatus updateStatus;
719 AddPerformanceMarker(PerformanceInterface::UPDATE_START);
720 TRACE_UPDATE_RENDER_BEGIN("DALI_UPDATE");
721 mCore.Update(frameDelta,
728 TRACE_UPDATE_RENDER_END("DALI_UPDATE");
729 AddPerformanceMarker(PerformanceInterface::UPDATE_END);
731 unsigned int keepUpdatingStatus = updateStatus.KeepUpdating();
733 // Tell the event-thread to wake up (if asleep) and send a notification event to Core if required
734 if(updateStatus.NeedsNotification())
736 mNotificationTrigger.Trigger();
737 LOG_UPDATE_RENDER("Notification Triggered");
740 // Optional logging of update/render status
741 mUpdateStatusLogger.Log(keepUpdatingStatus);
743 //////////////////////////////
745 //////////////////////////////
747 graphics.FrameStart();
748 mAdaptorInterfaces.GetDisplayConnectionInterface().ConsumeEvents();
750 if(mPreRenderCallback != NULL)
752 bool keepCallback = CallbackBase::ExecuteReturn<bool>(*mPreRenderCallback);
755 delete mPreRenderCallback;
756 mPreRenderCallback = NULL;
760 //////////////////////////////
761 // TextureUploadRequest (phase #2)
762 //////////////////////////////
764 // Upload requested resources after resource context activated.
765 graphics.ActivateResourceContext();
767 // Since uploadOnly value used at Update side, we should not change uploadOnly value now even some textures are uploaded.
768 mTextureUploadManager.ResourceUpload();
770 if(mFirstFrameAfterResume)
772 // mFirstFrameAfterResume is set to true when the thread is resumed
773 // Let graphics know the first frame after thread initialized or resumed.
774 graphics.SetFirstFrameAfterResume();
775 mFirstFrameAfterResume = FALSE;
778 Integration::RenderStatus renderStatus;
780 AddPerformanceMarker(PerformanceInterface::RENDER_START);
781 TRACE_UPDATE_RENDER_BEGIN("DALI_RENDER");
783 // Upload shared resources
784 TRACE_UPDATE_RENDER_BEGIN("DALI_PRE_RENDER");
785 mCore.PreRender(renderStatus, mForceClear);
786 TRACE_UPDATE_RENDER_END("DALI_PRE_RENDER");
788 if(!uploadOnly || surfaceResized)
790 // Go through each window
792 mAdaptorInterfaces.GetWindowContainerInterface(windows);
794 for(auto&& window : windows)
796 Dali::Integration::Scene scene = window->GetScene();
797 Dali::RenderSurfaceInterface* windowSurface = window->GetSurface();
799 if(scene && windowSurface)
801 TRACE_UPDATE_RENDER_SCOPE("DALI_RENDER_SCENE");
802 Integration::RenderStatus windowRenderStatus;
804 const uint32_t sceneSurfaceResized = scene.GetSurfaceRectChangedCount();
806 // clear previous frame damaged render items rects, buffer history is tracked on surface level
807 mDamagedRects.clear();
809 // Collect damage rects
810 mCore.PreRender(scene, mDamagedRects);
812 // Render off-screen frame buffers first if any
813 mCore.RenderScene(windowRenderStatus, scene, true);
815 Rect<int> clippingRect; // Empty for fbo rendering
817 // Switch to the context of the surface, merge damaged areas for previous frames
818 windowSurface->PreRender(sceneSurfaceResized > 0u, mDamagedRects, clippingRect); // Switch GL context
820 // Render the surface
821 mCore.RenderScene(windowRenderStatus, scene, false, clippingRect);
823 // Buffer swapping now happens when the surface render target is presented.
825 // If surface is resized, the surface resized count is decreased.
826 if(DALI_UNLIKELY(sceneSurfaceResized > 0u))
828 SurfaceResized(sceneSurfaceResized);
834 TRACE_UPDATE_RENDER_BEGIN("DALI_POST_RENDER");
837 graphics.PostRender();
841 TRACE_UPDATE_RENDER_END("DALI_POST_RENDER");
843 //////////////////////////////
845 //////////////////////////////
846 if(DALI_UNLIKELY(deletedSurface))
848 LOG_UPDATE_RENDER_TRACE_FMT("Deleting Surface");
850 deletedSurface->DestroySurface();
855 TRACE_UPDATE_RENDER_END("DALI_RENDER");
856 AddPerformanceMarker(PerformanceInterface::RENDER_END);
858 // if the memory pool interval is set and has elapsed, log the graphics memory pools
859 if(0 < memPoolInterval && memPoolInterval < lastFrameTime - lastMemPoolLogTime)
861 lastMemPoolLogTime = lastFrameTime;
862 graphics.LogMemoryPools();
867 // Trigger event thread to request Update/Render thread to sleep if update not required
868 if((Integration::KeepUpdating::NOT_REQUESTED == keepUpdatingStatus) && !renderStatus.NeedsUpdate())
870 mSleepTrigger->Trigger();
871 updateRequired = false;
872 LOG_UPDATE_RENDER("Sleep Triggered");
876 updateRequired = true;
879 //////////////////////////////
881 //////////////////////////////
883 extraFramesDropped = 0;
885 if(timeToSleepUntil == 0)
887 // If this is the first frame after the thread is initialized or resumed, we
888 // use the actual time the current frame starts from to calculate the time to
889 // sleep until the next frame.
890 timeToSleepUntil = currentFrameStartTime + mDefaultFrameDurationNanoseconds;
894 // Otherwise, always use the sleep-until time calculated in the last frame to
895 // calculate the time to sleep until the next frame. In this way, if there is
896 // any time gap between the current frame and the next frame, or if update or
897 // rendering in the current frame takes too much time so that the specified
898 // sleep-until time has already passed, it will try to keep the frames syncing
899 // by shortening the duration of the next frame.
900 timeToSleepUntil += mDefaultFrameDurationNanoseconds;
902 // Check the current time at the end of the frame
903 uint64_t currentFrameEndTime = 0;
904 TimeService::GetNanoseconds(currentFrameEndTime);
905 while(currentFrameEndTime > timeToSleepUntil + mDefaultFrameDurationNanoseconds)
907 // We are more than one frame behind already, so just drop the next frames
908 // until the sleep-until time is later than the current time so that we can
910 timeToSleepUntil += mDefaultFrameDurationNanoseconds;
911 extraFramesDropped++;
915 TRACE_UPDATE_RENDER_END("DALI_UPDATE_RENDER");
917 // Render to FBO is intended to measure fps above 60 so sleep is not wanted.
918 if(mVsyncRender && 0u == renderToFboInterval)
920 TRACE_UPDATE_RENDER_SCOPE("DALI_UPDATE_RENDER_SLEEP");
921 // Sleep until at least the the default frame duration has elapsed. This will return immediately if the specified end-time has already passed.
922 TimeService::SleepUntil(timeToSleepUntil);
925 TRACE_UPDATE_RENDER_BEGIN("DALI_RENDER_THREAD_FINISH");
927 // Inform core of context destruction
928 mCore.ContextDestroyed();
931 mAdaptorInterfaces.GetWindowContainerInterface(windows);
934 for(auto&& window : windows)
936 Dali::RenderSurfaceInterface* surface = window->GetSurface();
937 surface->DestroySurface();
942 LOG_UPDATE_RENDER("THREAD DESTROYED");
944 TRACE_UPDATE_RENDER_END("DALI_RENDER_THREAD_FINISH");
946 // Uninstall the logging function
947 mEnvironmentOptions.UnInstallLogFunction();
950 bool CombinedUpdateRenderController::UpdateRenderReady(bool& useElapsedTime, bool updateRequired, uint64_t& timeToSleepUntil)
952 useElapsedTime = true;
954 ConditionalWait::ScopedLock updateLock(mUpdateRenderThreadWaitCondition);
955 while((!mUpdateRenderRunCount || // Should try to wait if event-thread has paused the Update/Render thread
956 (mUpdateRenderThreadCanSleep && !updateRequired && !mPendingRequestUpdate)) && // Ensure we wait if we're supposed to be sleeping AND do not require another update
957 !mDestroyUpdateRenderThread && // Ensure we don't wait if the update-render-thread is supposed to be destroyed
958 !mNewSurface && // Ensure we don't wait if we need to replace the surface
959 !mDeletedSurface && // Ensure we don't wait if we need to delete the surface
960 !mSurfaceResized) // Ensure we don't wait if we need to resize the surface
962 LOG_UPDATE_RENDER("WAIT: mUpdateRenderRunCount: %d", mUpdateRenderRunCount);
963 LOG_UPDATE_RENDER(" mUpdateRenderThreadCanSleep: %d, updateRequired: %d, mPendingRequestUpdate: %d", mUpdateRenderThreadCanSleep, updateRequired, mPendingRequestUpdate);
964 LOG_UPDATE_RENDER(" mDestroyUpdateRenderThread: %d", mDestroyUpdateRenderThread);
965 LOG_UPDATE_RENDER(" mNewSurface: %d", mNewSurface);
966 LOG_UPDATE_RENDER(" mDeletedSurface: %d", mDeletedSurface);
967 LOG_UPDATE_RENDER(" mSurfaceResized: %d", mSurfaceResized);
969 // Reset the time when the thread is waiting, so the sleep-until time for
970 // the first frame after resuming should be based on the actual start time
971 // of the first frame.
972 timeToSleepUntil = 0;
974 TRACE_UPDATE_RENDER_BEGIN("DALI_UPDATE_RENDER_THREAD_WAIT_CONDITION");
975 mUpdateRenderThreadWaitCondition.Wait(updateLock);
976 TRACE_UPDATE_RENDER_END("DALI_UPDATE_RENDER_THREAD_WAIT_CONDITION");
978 if(!mUseElapsedTimeAfterWait)
980 useElapsedTime = false;
984 LOG_COUNTER_UPDATE_RENDER("mUpdateRenderRunCount: %d", mUpdateRenderRunCount);
985 LOG_COUNTER_UPDATE_RENDER("mUpdateRenderThreadCanSleep: %d, updateRequired: %d, mPendingRequestUpdate: %d", mUpdateRenderThreadCanSleep, updateRequired, mPendingRequestUpdate);
986 LOG_COUNTER_UPDATE_RENDER("mDestroyUpdateRenderThread: %d", mDestroyUpdateRenderThread);
987 LOG_COUNTER_UPDATE_RENDER("mNewSurface: %d", mNewSurface);
988 LOG_COUNTER_UPDATE_RENDER("mDeletedSurface: %d", mDeletedSurface);
989 LOG_COUNTER_UPDATE_RENDER("mSurfaceResized: %d", mSurfaceResized);
991 mUseElapsedTimeAfterWait = FALSE;
992 mUpdateRenderThreadCanSleep = FALSE;
993 mPendingRequestUpdate = FALSE;
995 // If we've been asked to run Update/Render cycles a finite number of times then decrement so we wait after the
996 // requested number of cycles
997 if(mUpdateRenderRunCount > 0)
999 --mUpdateRenderRunCount;
1002 // Keep the update-render thread alive if this thread is NOT to be destroyed
1003 return !mDestroyUpdateRenderThread;
1006 Dali::RenderSurfaceInterface* CombinedUpdateRenderController::ShouldSurfaceBeReplaced()
1008 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
1010 Dali::RenderSurfaceInterface* newSurface = mNewSurface;
1016 void CombinedUpdateRenderController::SurfaceReplaced()
1018 // Just increment the semaphore
1019 mSurfaceSemaphore.Release(1);
1022 Dali::RenderSurfaceInterface* CombinedUpdateRenderController::ShouldSurfaceBeDeleted()
1024 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
1026 Dali::RenderSurfaceInterface* deletedSurface = mDeletedSurface;
1027 mDeletedSurface = NULL;
1029 return deletedSurface;
1032 void CombinedUpdateRenderController::SurfaceDeleted()
1034 // Just increment the semaphore
1035 mSurfaceSemaphore.Release(1);
1038 void CombinedUpdateRenderController::SurfaceResized(uint32_t resizedCount)
1040 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
1042 if(mSurfaceResized >= resizedCount)
1044 mSurfaceResized -= resizedCount;
1048 mSurfaceResized = 0u;
1052 void CombinedUpdateRenderController::PreCompileShader(std::string vertexShader, std::string fragmentShader, std::string shaderName)
1054 GraphicsInterface& graphics = mAdaptorInterfaces.GetGraphicsInterface();
1056 Graphics::ShaderCreateInfo vertexShaderCreateInfo;
1057 vertexShaderCreateInfo.SetPipelineStage(Graphics::PipelineStage::VERTEX_SHADER);
1058 vertexShaderCreateInfo.SetSourceMode(Graphics::ShaderSourceMode::TEXT);
1059 const std::vector<char>& vertexShaderSrc = StringToVector(std::move(vertexShader));
1060 vertexShaderCreateInfo.SetSourceSize(vertexShaderSrc.size());
1061 vertexShaderCreateInfo.SetSourceData(static_cast<const void*>(vertexShaderSrc.data()));
1062 auto vertexGraphicsShader = graphics.GetController().CreateShader(vertexShaderCreateInfo, nullptr);
1064 Graphics::ShaderCreateInfo fragmentShaderCreateInfo;
1065 fragmentShaderCreateInfo.SetPipelineStage(Graphics::PipelineStage::FRAGMENT_SHADER);
1066 fragmentShaderCreateInfo.SetSourceMode(Graphics::ShaderSourceMode::TEXT);
1067 const std::vector<char>& fragmentShaderSrc = StringToVector(std::move(fragmentShader));
1068 fragmentShaderCreateInfo.SetSourceSize(fragmentShaderSrc.size());
1069 fragmentShaderCreateInfo.SetSourceData(static_cast<const void*>(fragmentShaderSrc.data()));
1070 auto fragmentGraphicsShader = graphics.GetController().CreateShader(fragmentShaderCreateInfo, nullptr);
1072 std::vector<Graphics::ShaderState> shaderStates{
1073 Graphics::ShaderState()
1074 .SetShader(*vertexGraphicsShader.get())
1075 .SetPipelineStage(Graphics::PipelineStage::VERTEX_SHADER),
1076 Graphics::ShaderState()
1077 .SetShader(*fragmentGraphicsShader.get())
1078 .SetPipelineStage(Graphics::PipelineStage::FRAGMENT_SHADER)};
1080 auto createInfo = Graphics::ProgramCreateInfo();
1081 createInfo.SetShaderState(shaderStates);
1082 createInfo.SetName(shaderName);
1084 auto graphicsProgram = graphics.GetController().CreateProgram(createInfo, nullptr);
1085 ShaderPreCompiler::Get().AddPreCompiledProgram(std::move(graphicsProgram));
1088 void CombinedUpdateRenderController::CancelPreCompile()
1090 if(mIsPreCompileCancelled == FALSE)
1092 mIsPreCompileCancelled = TRUE;
1093 ShaderPreCompiler::Get().Awake();
1097 ///////////////////////////////////////////////////////////////////////////////////////////////////
1099 ///////////////////////////////////////////////////////////////////////////////////////////////////
1101 void CombinedUpdateRenderController::NotifyThreadInitialised()
1103 // Just increment the semaphore
1104 mEventThreadSemaphore.Release(1);
1107 void CombinedUpdateRenderController::NotifyGraphicsInitialised()
1109 mGraphicsInitializeWait.Notify();
1112 void CombinedUpdateRenderController::AddPerformanceMarker(PerformanceInterface::MarkerType type)
1114 if(mPerformanceInterface)
1116 mPerformanceInterface->AddMarker(type);
1120 /////////////////////////////////////////////////////////////////////////////////////////////////
1121 // POST RENDERING: EVENT THREAD
1122 /////////////////////////////////////////////////////////////////////////////////////////////////
1124 void CombinedUpdateRenderController::PostRenderComplete()
1126 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
1127 mPostRendering = FALSE;
1129 mUpdateRenderThreadWaitCondition.Notify(lock);
1132 ///////////////////////////////////////////////////////////////////////////////////////////////////
1133 // POST RENDERING: RENDER THREAD
1134 ///////////////////////////////////////////////////////////////////////////////////////////////////
1136 void CombinedUpdateRenderController::PostRenderStarted()
1138 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
1139 mPostRendering = TRUE;
1142 void CombinedUpdateRenderController::PostRenderWaitForCompletion()
1144 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
1145 while(mPostRendering &&
1146 !mNewSurface && // We should NOT wait if we're replacing the surface
1147 !mDeletedSurface && // We should NOT wait if we're deleting the surface
1148 !mDestroyUpdateRenderThread)
1150 mUpdateRenderThreadWaitCondition.Wait(lock);
1154 } // namespace Adaptor
1156 } // namespace Internal