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),
121 mThreadMode(threadMode),
122 mUpdateRenderRunCount(0),
123 mDestroyUpdateRenderThread(FALSE),
124 mUpdateRenderThreadCanSleep(FALSE),
125 mPendingRequestUpdate(FALSE),
126 mUseElapsedTimeAfterWait(FALSE),
127 mIsPreCompileCancelled(FALSE),
129 mDeletedSurface(nullptr),
130 mPostRendering(FALSE),
133 mUploadWithoutRendering(FALSE),
134 mFirstFrameAfterResume(FALSE)
138 // Initialise frame delta/duration variables first
139 SetRenderRefreshRate(environmentOptions.GetRenderRefreshRate());
141 // Set the thread-synchronization interface on the render-surface
142 Dali::RenderSurfaceInterface* currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
145 currentSurface->SetThreadSynchronization(*this);
148 mSleepTrigger = TriggerEventFactory::CreateTriggerEvent(MakeCallback(this, &CombinedUpdateRenderController::ProcessSleepRequest), TriggerEventInterface::KEEP_ALIVE_AFTER_TRIGGER);
151 CombinedUpdateRenderController::~CombinedUpdateRenderController()
157 delete mPreRenderCallback;
158 delete mSleepTrigger;
161 void CombinedUpdateRenderController::Initialize()
165 // Ensure Update/Render Thread not already created
166 DALI_ASSERT_ALWAYS(!mUpdateRenderThread);
168 // Create Update/Render Thread
169 ConditionalWait::ScopedLock lock(mGraphicsInitializeWait);
170 mUpdateRenderThread = new pthread_t();
171 int error = pthread_create(mUpdateRenderThread, NULL, InternalUpdateRenderThreadEntryFunc, this);
172 DALI_ASSERT_ALWAYS(!error && "Return code from pthread_create() when creating UpdateRenderThread");
174 // The Update/Render thread will now run and initialise the graphics interface etc. and will then wait for Start to be called
175 // When this function returns, the application initialisation on the event thread should occur
178 void CombinedUpdateRenderController::Start()
182 DALI_ASSERT_ALWAYS(!mRunning && mUpdateRenderThread);
184 // Wait until all threads created in Initialise are up and running
185 for(unsigned int i = 0; i < CREATED_THREAD_COUNT; ++i)
187 mEventThreadSemaphore.Acquire();
192 LOG_EVENT("Startup Complete, starting Update/Render Thread");
194 RunUpdateRenderThread(CONTINUOUS, AnimationProgression::NONE, UpdateMode::NORMAL);
196 Dali::RenderSurfaceInterface* currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
199 currentSurface->StartRender();
202 DALI_LOG_RELEASE_INFO("CombinedUpdateRenderController::Start\n");
205 void CombinedUpdateRenderController::Pause()
211 PauseUpdateRenderThread();
213 AddPerformanceMarker(PerformanceInterface::PAUSED);
215 DALI_LOG_RELEASE_INFO("CombinedUpdateRenderController::Pause\n");
218 void CombinedUpdateRenderController::Resume()
222 if(!mRunning && IsUpdateRenderThreadPaused())
224 LOG_EVENT("Resuming");
226 RunUpdateRenderThread(CONTINUOUS, AnimationProgression::USE_ELAPSED_TIME, UpdateMode::NORMAL);
228 AddPerformanceMarker(PerformanceInterface::RESUME);
232 mFirstFrameAfterResume = TRUE;
234 DALI_LOG_RELEASE_INFO("CombinedUpdateRenderController::Resume\n");
238 DALI_LOG_RELEASE_INFO("CombinedUpdateRenderController::Resume: Already resumed [%d, %d, %d]\n", mRunning, mUpdateRenderRunCount, mUpdateRenderThreadCanSleep);
242 void CombinedUpdateRenderController::Stop()
246 // Stop Rendering and the Update/Render Thread
247 Dali::RenderSurfaceInterface* currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
250 currentSurface->StopRender();
253 StopUpdateRenderThread();
255 if(mUpdateRenderThread)
257 LOG_EVENT("Destroying UpdateRenderThread");
259 // wait for the thread to finish
260 pthread_join(*mUpdateRenderThread, NULL);
262 delete mUpdateRenderThread;
263 mUpdateRenderThread = NULL;
268 DALI_LOG_RELEASE_INFO("CombinedUpdateRenderController::Stop\n");
271 void CombinedUpdateRenderController::RequestUpdate()
275 // Increment the update-request count to the maximum
276 if(mUpdateRequestCount < MAXIMUM_UPDATE_REQUESTS)
278 ++mUpdateRequestCount;
281 if(mRunning && IsUpdateRenderThreadPaused())
283 LOG_EVENT("Processing");
285 RunUpdateRenderThread(CONTINUOUS, AnimationProgression::NONE, UpdateMode::NORMAL);
288 ConditionalWait::ScopedLock updateLock(mUpdateRenderThreadWaitCondition);
289 mPendingRequestUpdate = TRUE;
292 void CombinedUpdateRenderController::RequestUpdateOnce(UpdateMode updateMode)
294 // Increment the update-request count to the maximum
295 if(mUpdateRequestCount < MAXIMUM_UPDATE_REQUESTS)
297 ++mUpdateRequestCount;
300 if(IsUpdateRenderThreadPaused() || updateMode == UpdateMode::FORCE_RENDER)
304 // Run Update/Render once
305 RunUpdateRenderThread(ONCE, AnimationProgression::NONE, updateMode);
309 void CombinedUpdateRenderController::ReplaceSurface(Dali::RenderSurfaceInterface* newSurface)
313 if(mUpdateRenderThread)
315 // Set the ThreadSyncronizationInterface on the new surface
316 newSurface->SetThreadSynchronization(*this);
318 LOG_EVENT("Starting to replace the surface, event-thread blocked");
320 // Start replacing the surface.
322 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
323 mPostRendering = FALSE; // Clear the post-rendering flag as Update/Render thread will replace the surface now
324 mNewSurface = newSurface;
326 mUpdateRenderThreadWaitCondition.Notify(lock);
329 // Wait until the surface has been replaced
330 mSurfaceSemaphore.Acquire();
332 LOG_EVENT("Surface replaced, event-thread continuing");
336 void CombinedUpdateRenderController::DeleteSurface(Dali::RenderSurfaceInterface* surface)
340 if(mUpdateRenderThread)
342 LOG_EVENT("Starting to delete the surface, event-thread blocked");
344 // Start replacing the surface.
346 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
347 mPostRendering = FALSE; // Clear the post-rendering flag as Update/Render thread will delete the surface now
348 mDeletedSurface = surface;
350 mUpdateRenderThreadWaitCondition.Notify(lock);
353 // Wait until the surface has been deleted
354 mSurfaceSemaphore.Acquire();
356 LOG_EVENT("Surface deleted, event-thread continuing");
360 void CombinedUpdateRenderController::WaitForGraphicsInitialization()
362 ConditionalWait::ScopedLock lk(mGraphicsInitializeWait);
365 if(mUpdateRenderThread)
367 LOG_EVENT("Waiting for graphics initialisation, event-thread blocked");
369 // Wait until the graphics has been initialised
370 mGraphicsInitializeWait.Wait(lk);
372 LOG_EVENT("graphics initialised, event-thread continuing");
376 void CombinedUpdateRenderController::ResizeSurface()
380 LOG_EVENT("Resize the surface");
383 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
384 // Surface is resized and the surface resized count is increased.
387 mUpdateRenderThreadWaitCondition.Notify(lock);
391 void CombinedUpdateRenderController::SetRenderRefreshRate(unsigned int numberOfFramesPerRender)
393 // Not protected by lock, but written to rarely so not worth adding a lock when reading
394 mDefaultFrameDelta = numberOfFramesPerRender * DEFAULT_FRAME_DURATION_IN_SECONDS;
395 mDefaultFrameDurationMilliseconds = uint64_t(numberOfFramesPerRender) * DEFAULT_FRAME_DURATION_IN_MILLISECONDS;
396 mDefaultFrameDurationNanoseconds = uint64_t(numberOfFramesPerRender) * DEFAULT_FRAME_DURATION_IN_NANOSECONDS;
397 mDefaultHalfFrameNanoseconds = mDefaultFrameDurationNanoseconds / 2u;
399 LOG_EVENT("mDefaultFrameDelta(%.6f), mDefaultFrameDurationMilliseconds(%lld), mDefaultFrameDurationNanoseconds(%lld)", mDefaultFrameDelta, mDefaultFrameDurationMilliseconds, mDefaultFrameDurationNanoseconds);
402 void CombinedUpdateRenderController::SetPreRenderCallback(CallbackBase* callback)
405 LOG_EVENT("Set PreRender Callback");
407 ConditionalWait::ScopedLock updateLock(mUpdateRenderThreadWaitCondition);
408 if(mPreRenderCallback)
410 delete mPreRenderCallback;
412 mPreRenderCallback = callback;
415 void CombinedUpdateRenderController::AddSurface(Dali::RenderSurfaceInterface* surface)
418 LOG_EVENT("Surface is added");
419 if(mUpdateRenderThread)
421 // Set the ThreadSyncronizationInterface on the added surface
422 surface->SetThreadSynchronization(*this);
426 int32_t CombinedUpdateRenderController::GetThreadId() const
431 ///////////////////////////////////////////////////////////////////////////////////////////////////
433 ///////////////////////////////////////////////////////////////////////////////////////////////////
435 void CombinedUpdateRenderController::RunUpdateRenderThread(int numberOfCycles, AnimationProgression animationProgression, UpdateMode updateMode)
437 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
441 case ThreadMode::NORMAL:
443 mUpdateRenderRunCount = numberOfCycles;
444 mUseElapsedTimeAfterWait = (animationProgression == AnimationProgression::USE_ELAPSED_TIME);
447 case ThreadMode::RUN_IF_REQUESTED:
449 if(updateMode != UpdateMode::FORCE_RENDER)
451 // Render only if the update mode is FORCE_RENDER which means the application requests it.
452 // We don't want to awake the update thread.
456 mUpdateRenderRunCount++; // Increase the update request count
457 mUseElapsedTimeAfterWait = TRUE; // The elapsed time should be used. We want animations to proceed.
462 mUpdateRenderThreadCanSleep = FALSE;
463 mUploadWithoutRendering = (updateMode == UpdateMode::SKIP_RENDER);
464 LOG_COUNTER_EVENT("mUpdateRenderRunCount: %d, mUseElapsedTimeAfterWait: %d", mUpdateRenderRunCount, mUseElapsedTimeAfterWait);
466 mUpdateRenderThreadWaitCondition.Notify(lock);
469 void CombinedUpdateRenderController::PauseUpdateRenderThread()
471 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
472 mUpdateRenderRunCount = 0;
475 void CombinedUpdateRenderController::StopUpdateRenderThread()
477 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
478 mDestroyUpdateRenderThread = TRUE;
480 mUpdateRenderThreadWaitCondition.Notify(lock);
483 bool CombinedUpdateRenderController::IsUpdateRenderThreadPaused()
485 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
487 if(mThreadMode == ThreadMode::RUN_IF_REQUESTED)
489 return !mRunning || mUpdateRenderThreadCanSleep;
492 return (mUpdateRenderRunCount != CONTINUOUS) || // Report paused if NOT continuously running
493 mUpdateRenderThreadCanSleep; // Report paused if sleeping
496 void CombinedUpdateRenderController::ProcessSleepRequest()
500 // Decrement Update request count
501 if(mUpdateRequestCount > 0)
503 --mUpdateRequestCount;
506 // Can sleep if our update-request count is 0
507 // Update/Render thread can choose to carry on updating if it determines more update/renders are required
508 if(mUpdateRequestCount == 0)
510 LOG_EVENT("Going to sleep");
512 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
513 mUpdateRenderThreadCanSleep = TRUE;
517 ///////////////////////////////////////////////////////////////////////////////////////////////////
518 // UPDATE/RENDER THREAD
519 ///////////////////////////////////////////////////////////////////////////////////////////////////
521 void CombinedUpdateRenderController::UpdateRenderThread()
523 ThreadSettings::SetThreadName("RenderThread\0");
524 mThreadId = ThreadSettings::GetThreadId();
526 // Install a function for logging
527 mEnvironmentOptions.InstallLogFunction();
529 // Install a function for tracing
530 mEnvironmentOptions.InstallTraceFunction();
532 TRACE_UPDATE_RENDER_BEGIN("DALI_RENDER_THREAD_INIT");
534 LOG_UPDATE_RENDER("THREAD CREATED");
536 // Initialize graphics
537 GraphicsInterface& graphics = mAdaptorInterfaces.GetGraphicsInterface();
538 graphics.Initialize();
540 Dali::DisplayConnection& displayConnection = mAdaptorInterfaces.GetDisplayConnectionInterface();
541 displayConnection.Initialize(); //@todo Move InitializeGraphics code into graphics implementation
543 // Setup graphics controller into upload manager.
544 GetImplementation(mTextureUploadManager).InitalizeGraphicsController(graphics.GetController());
546 NotifyGraphicsInitialised();
548 //@todo Vk swaps this around, but we need to support surfaceless context for multi-window
549 graphics.ConfigureSurface(mAdaptorInterfaces.GetRenderSurfaceInterface());
551 // Tell core it has a context
552 mCore.ContextCreated();
554 NotifyThreadInitialised();
556 // Initialize and create graphics resource for the shared context.
557 WindowContainer windows;
558 mAdaptorInterfaces.GetWindowContainerInterface(windows);
560 for(auto&& window : windows)
562 Dali::Integration::Scene scene = window->GetScene();
563 Dali::RenderSurfaceInterface* windowSurface = window->GetSurface();
565 if(scene && windowSurface)
567 windowSurface->InitializeGraphics();
572 uint64_t lastFrameTime;
573 TimeService::GetNanoseconds(lastFrameTime);
574 uint64_t lastMemPoolLogTime = lastFrameTime;
576 LOG_UPDATE_RENDER("THREAD INITIALISED");
578 bool useElapsedTime = true;
579 bool updateRequired = true;
580 uint64_t timeToSleepUntil = 0;
581 int extraFramesDropped = 0;
583 const uint64_t memPoolInterval = 1e9 * float(mEnvironmentOptions.GetMemoryPoolInterval());
585 const unsigned int renderToFboInterval = mEnvironmentOptions.GetRenderToFboInterval();
586 const bool renderToFboEnabled = 0u != renderToFboInterval;
587 unsigned int frameCount = 0u;
589 TRACE_UPDATE_RENDER_END("DALI_RENDER_THREAD_INIT");
590 if(!mDestroyUpdateRenderThread)
592 ShaderPreCompiler::Get().Wait();
593 if(ShaderPreCompiler::Get().IsEnable())
595 std::vector<RawShaderData> precompiledShaderList;
596 ShaderPreCompiler::Get().GetPreCompileShaderList(precompiledShaderList);
597 DALI_LOG_RELEASE_INFO("ShaderPreCompiler[ENABLE], list size:%d \n", precompiledShaderList.size());
598 for(auto precompiledShader = precompiledShaderList.begin(); precompiledShader != precompiledShaderList.end(); ++precompiledShader)
600 if(mIsPreCompileCancelled == TRUE)
602 ShaderPreCompiler::Get().Awake();
603 DALI_LOG_RELEASE_INFO("ShaderPreCompiler[ENABLE], but stop precompile");
607 auto numberOfPrecompiledShader = precompiledShader->shaderCount;
608 for(int i = 0; i < numberOfPrecompiledShader; ++i)
610 auto vertexShader = graphics.GetController().GetGlAbstraction().GetVertexShaderPrefix() + std::string(precompiledShader->vertexPrefix[i].data()) + std::string(precompiledShader->vertexShader.data());
611 auto fragmentShader = graphics.GetController().GetGlAbstraction().GetFragmentShaderPrefix() + std::string(precompiledShader->fragmentPrefix[i].data()) + std::string(precompiledShader->fragmentShader.data());
612 PreCompileShader(std::move(vertexShader), std::move(fragmentShader));
614 DALI_LOG_RELEASE_INFO("ShaderPreCompiler[ENABLE], shader count :%d \n", numberOfPrecompiledShader);
619 DALI_LOG_RELEASE_INFO("ShaderPreCompiler[DISABLE] \n");
623 while(UpdateRenderReady(useElapsedTime, updateRequired, timeToSleepUntil))
625 LOG_UPDATE_RENDER_TRACE;
626 TRACE_UPDATE_RENDER_BEGIN("DALI_UPDATE_RENDER");
629 bool uploadOnly = mUploadWithoutRendering;
630 unsigned int surfaceResized = mSurfaceResized;
631 Dali::RenderSurfaceInterface* deletedSurface = ShouldSurfaceBeDeleted();
633 // Performance statistics are logged upon a VSYNC tick so use this point for a VSync marker
634 AddPerformanceMarker(PerformanceInterface::VSYNC);
636 uint64_t currentFrameStartTime = 0;
637 TimeService::GetNanoseconds(currentFrameStartTime);
639 uint64_t timeSinceLastFrame = currentFrameStartTime - lastFrameTime;
641 // Optional FPS Tracking when continuously rendering
642 if(useElapsedTime && mFpsTracker.Enabled())
644 float absoluteTimeSinceLastRender = timeSinceLastFrame * NANOSECONDS_TO_SECOND;
645 mFpsTracker.Track(absoluteTimeSinceLastRender);
648 lastFrameTime = currentFrameStartTime; // Store frame start time
650 //////////////////////////////
652 //////////////////////////////
654 Dali::RenderSurfaceInterface* newSurface = ShouldSurfaceBeReplaced();
655 if(DALI_UNLIKELY(newSurface))
657 LOG_UPDATE_RENDER_TRACE_FMT("Replacing Surface");
658 // This is designed for replacing pixmap surfaces, but should work for window as well
659 // we need to delete the surface and renderable (pixmap / window)
660 // Then create a new pixmap/window and new surface
661 // If the new surface has a different display connection, then the context will be lost
662 mAdaptorInterfaces.GetDisplayConnectionInterface().Initialize();
663 graphics.ActivateSurfaceContext(newSurface);
664 // TODO: ReplaceGraphicsSurface doesn't work, InitializeGraphics()
665 // already creates new surface window, the surface and the context.
666 // We probably don't need ReplaceGraphicsSurface at all.
667 // newSurface->ReplaceGraphicsSurface();
671 //////////////////////////////
672 // TextureUploadRequest (phase #1)
673 //////////////////////////////
675 // Upload requested resources after resource context activated.
676 graphics.ActivateResourceContext();
678 const bool textureUploaded = mTextureUploadManager.ResourceUpload();
680 // Update & Render forcely if there exist some uploaded texture.
681 uploadOnly = textureUploaded ? false : uploadOnly;
683 const bool isRenderingToFbo = renderToFboEnabled && ((0u == frameCount) || (0u != frameCount % renderToFboInterval));
686 //////////////////////////////
688 //////////////////////////////
690 const uint32_t currentTime = static_cast<uint32_t>(currentFrameStartTime / NANOSECONDS_PER_MILLISECOND);
691 const uint32_t nextFrameTime = currentTime + static_cast<uint32_t>(mDefaultFrameDurationMilliseconds);
693 uint64_t noOfFramesSinceLastUpdate = 1;
694 float frameDelta = 0.0f;
697 if(mThreadMode == ThreadMode::RUN_IF_REQUESTED)
699 extraFramesDropped = 0;
700 while(timeSinceLastFrame >= mDefaultFrameDurationNanoseconds)
702 timeSinceLastFrame -= mDefaultFrameDurationNanoseconds;
703 extraFramesDropped++;
707 // If using the elapsed time, then calculate frameDelta as a multiple of mDefaultFrameDelta
708 noOfFramesSinceLastUpdate += extraFramesDropped;
710 frameDelta = mDefaultFrameDelta * noOfFramesSinceLastUpdate;
712 LOG_UPDATE_RENDER("timeSinceLastFrame(%llu) noOfFramesSinceLastUpdate(%u) frameDelta(%.6f)", timeSinceLastFrame, noOfFramesSinceLastUpdate, frameDelta);
714 Integration::UpdateStatus updateStatus;
716 AddPerformanceMarker(PerformanceInterface::UPDATE_START);
717 TRACE_UPDATE_RENDER_BEGIN("DALI_UPDATE");
718 mCore.Update(frameDelta,
725 TRACE_UPDATE_RENDER_END("DALI_UPDATE");
726 AddPerformanceMarker(PerformanceInterface::UPDATE_END);
728 unsigned int keepUpdatingStatus = updateStatus.KeepUpdating();
730 // Tell the event-thread to wake up (if asleep) and send a notification event to Core if required
731 if(updateStatus.NeedsNotification())
733 mNotificationTrigger.Trigger();
734 LOG_UPDATE_RENDER("Notification Triggered");
737 if(uploadOnly && (keepUpdatingStatus & Dali::Integration::KeepUpdating::STAGE_KEEP_RENDERING))
739 // Render forcely if there exist some keep rendering required.
743 // Optional logging of update/render status
744 mUpdateStatusLogger.Log(keepUpdatingStatus);
746 //////////////////////////////
748 //////////////////////////////
750 graphics.FrameStart();
751 mAdaptorInterfaces.GetDisplayConnectionInterface().ConsumeEvents();
753 if(mPreRenderCallback != NULL)
755 bool keepCallback = CallbackBase::ExecuteReturn<bool>(*mPreRenderCallback);
758 delete mPreRenderCallback;
759 mPreRenderCallback = NULL;
763 //////////////////////////////
764 // TextureUploadRequest (phase #2)
765 //////////////////////////////
767 // Upload requested resources after resource context activated.
768 graphics.ActivateResourceContext();
770 // Since uploadOnly value used at Update side, we should not change uploadOnly value now even some textures are uploaded.
771 mTextureUploadManager.ResourceUpload();
773 if(mFirstFrameAfterResume)
775 // mFirstFrameAfterResume is set to true when the thread is resumed
776 // Let graphics know the first frame after thread initialized or resumed.
777 graphics.SetFirstFrameAfterResume();
778 mFirstFrameAfterResume = FALSE;
781 Integration::RenderStatus renderStatus;
783 AddPerformanceMarker(PerformanceInterface::RENDER_START);
784 TRACE_UPDATE_RENDER_BEGIN("DALI_RENDER");
786 // Upload shared resources
787 TRACE_UPDATE_RENDER_BEGIN("DALI_PRE_RENDER");
788 mCore.PreRender(renderStatus, mForceClear);
789 TRACE_UPDATE_RENDER_END("DALI_PRE_RENDER");
791 if(!uploadOnly || surfaceResized)
793 // Go through each window
795 mAdaptorInterfaces.GetWindowContainerInterface(windows);
797 for(auto&& window : windows)
799 Dali::Integration::Scene scene = window->GetScene();
800 Dali::RenderSurfaceInterface* windowSurface = window->GetSurface();
802 if(scene && windowSurface)
804 TRACE_UPDATE_RENDER_SCOPE("DALI_RENDER_SCENE");
805 Integration::RenderStatus windowRenderStatus;
807 const uint32_t sceneSurfaceResized = scene.GetSurfaceRectChangedCount();
809 // clear previous frame damaged render items rects, buffer history is tracked on surface level
810 mDamagedRects.clear();
812 // Collect damage rects
813 mCore.PreRender(scene, mDamagedRects);
815 // Render off-screen frame buffers first if any
816 mCore.RenderScene(windowRenderStatus, scene, true);
818 Rect<int> clippingRect; // Empty for fbo rendering
820 // Switch to the context of the surface, merge damaged areas for previous frames
821 windowSurface->PreRender(sceneSurfaceResized > 0u, mDamagedRects, clippingRect); // Switch GL context
823 // Render the surface
824 mCore.RenderScene(windowRenderStatus, scene, false, clippingRect);
826 // Buffer swapping now happens when the surface render target is presented.
828 // If surface is resized, the surface resized count is decreased.
829 if(DALI_UNLIKELY(sceneSurfaceResized > 0u))
831 SurfaceResized(sceneSurfaceResized);
838 DALI_LOG_RELEASE_INFO("DALI Rendering skip (upload only)\n");
841 TRACE_UPDATE_RENDER_BEGIN("DALI_POST_RENDER");
844 graphics.PostRender();
848 TRACE_UPDATE_RENDER_END("DALI_POST_RENDER");
850 //////////////////////////////
852 //////////////////////////////
853 if(DALI_UNLIKELY(deletedSurface))
855 LOG_UPDATE_RENDER_TRACE_FMT("Deleting Surface");
857 deletedSurface->DestroySurface();
862 TRACE_UPDATE_RENDER_END("DALI_RENDER");
863 AddPerformanceMarker(PerformanceInterface::RENDER_END);
865 // if the memory pool interval is set and has elapsed, log the graphics memory pools
866 if(0 < memPoolInterval && memPoolInterval < lastFrameTime - lastMemPoolLogTime)
868 lastMemPoolLogTime = lastFrameTime;
869 graphics.LogMemoryPools();
874 // Trigger event thread to request Update/Render thread to sleep if update not required
875 if((Integration::KeepUpdating::NOT_REQUESTED == keepUpdatingStatus) && !renderStatus.NeedsUpdate())
877 mSleepTrigger->Trigger();
878 updateRequired = false;
879 LOG_UPDATE_RENDER("Sleep Triggered");
883 updateRequired = true;
886 //////////////////////////////
888 //////////////////////////////
890 extraFramesDropped = 0;
892 if(timeToSleepUntil == 0)
894 // If this is the first frame after the thread is initialized or resumed, we
895 // use the actual time the current frame starts from to calculate the time to
896 // sleep until the next frame.
897 timeToSleepUntil = currentFrameStartTime + mDefaultFrameDurationNanoseconds;
901 // Otherwise, always use the sleep-until time calculated in the last frame to
902 // calculate the time to sleep until the next frame. In this way, if there is
903 // any time gap between the current frame and the next frame, or if update or
904 // rendering in the current frame takes too much time so that the specified
905 // sleep-until time has already passed, it will try to keep the frames syncing
906 // by shortening the duration of the next frame.
907 timeToSleepUntil += mDefaultFrameDurationNanoseconds;
909 // Check the current time at the end of the frame
910 uint64_t currentFrameEndTime = 0;
911 TimeService::GetNanoseconds(currentFrameEndTime);
912 while(currentFrameEndTime > timeToSleepUntil + mDefaultFrameDurationNanoseconds)
914 // We are more than one frame behind already, so just drop the next frames
915 // until the sleep-until time is later than the current time so that we can
917 timeToSleepUntil += mDefaultFrameDurationNanoseconds;
918 extraFramesDropped++;
922 TRACE_UPDATE_RENDER_END("DALI_UPDATE_RENDER");
924 // Render to FBO is intended to measure fps above 60 so sleep is not wanted.
925 if(0u == renderToFboInterval)
927 TRACE_UPDATE_RENDER_SCOPE("DALI_UPDATE_RENDER_SLEEP");
928 // Sleep until at least the the default frame duration has elapsed. This will return immediately if the specified end-time has already passed.
929 TimeService::SleepUntil(timeToSleepUntil);
932 TRACE_UPDATE_RENDER_BEGIN("DALI_RENDER_THREAD_FINISH");
934 // Inform core of context destruction
935 mCore.ContextDestroyed();
938 mAdaptorInterfaces.GetWindowContainerInterface(windows);
941 for(auto&& window : windows)
943 Dali::RenderSurfaceInterface* surface = window->GetSurface();
944 surface->DestroySurface();
949 LOG_UPDATE_RENDER("THREAD DESTROYED");
951 TRACE_UPDATE_RENDER_END("DALI_RENDER_THREAD_FINISH");
953 // Uninstall the logging function
954 mEnvironmentOptions.UnInstallLogFunction();
957 bool CombinedUpdateRenderController::UpdateRenderReady(bool& useElapsedTime, bool updateRequired, uint64_t& timeToSleepUntil)
959 useElapsedTime = true;
961 ConditionalWait::ScopedLock updateLock(mUpdateRenderThreadWaitCondition);
962 while((!mUpdateRenderRunCount || // Should try to wait if event-thread has paused the Update/Render thread
963 (mUpdateRenderThreadCanSleep && !updateRequired && !mPendingRequestUpdate)) && // Ensure we wait if we're supposed to be sleeping AND do not require another update
964 !mDestroyUpdateRenderThread && // Ensure we don't wait if the update-render-thread is supposed to be destroyed
965 !mNewSurface && // Ensure we don't wait if we need to replace the surface
966 !mDeletedSurface && // Ensure we don't wait if we need to delete the surface
967 !mSurfaceResized) // Ensure we don't wait if we need to resize the surface
969 LOG_UPDATE_RENDER("WAIT: mUpdateRenderRunCount: %d", mUpdateRenderRunCount);
970 LOG_UPDATE_RENDER(" mUpdateRenderThreadCanSleep: %d, updateRequired: %d, mPendingRequestUpdate: %d", mUpdateRenderThreadCanSleep, updateRequired, mPendingRequestUpdate);
971 LOG_UPDATE_RENDER(" mDestroyUpdateRenderThread: %d", mDestroyUpdateRenderThread);
972 LOG_UPDATE_RENDER(" mNewSurface: %d", mNewSurface);
973 LOG_UPDATE_RENDER(" mDeletedSurface: %d", mDeletedSurface);
974 LOG_UPDATE_RENDER(" mSurfaceResized: %d", mSurfaceResized);
976 // Reset the time when the thread is waiting, so the sleep-until time for
977 // the first frame after resuming should be based on the actual start time
978 // of the first frame.
979 timeToSleepUntil = 0;
981 TRACE_UPDATE_RENDER_BEGIN("DALI_UPDATE_RENDER_THREAD_WAIT_CONDITION");
982 mUpdateRenderThreadWaitCondition.Wait(updateLock);
983 TRACE_UPDATE_RENDER_END("DALI_UPDATE_RENDER_THREAD_WAIT_CONDITION");
985 if(!mUseElapsedTimeAfterWait)
987 useElapsedTime = false;
991 LOG_COUNTER_UPDATE_RENDER("mUpdateRenderRunCount: %d", mUpdateRenderRunCount);
992 LOG_COUNTER_UPDATE_RENDER("mUpdateRenderThreadCanSleep: %d, updateRequired: %d, mPendingRequestUpdate: %d", mUpdateRenderThreadCanSleep, updateRequired, mPendingRequestUpdate);
993 LOG_COUNTER_UPDATE_RENDER("mDestroyUpdateRenderThread: %d", mDestroyUpdateRenderThread);
994 LOG_COUNTER_UPDATE_RENDER("mNewSurface: %d", mNewSurface);
995 LOG_COUNTER_UPDATE_RENDER("mDeletedSurface: %d", mDeletedSurface);
996 LOG_COUNTER_UPDATE_RENDER("mSurfaceResized: %d", mSurfaceResized);
998 mUseElapsedTimeAfterWait = FALSE;
999 mUpdateRenderThreadCanSleep = FALSE;
1000 mPendingRequestUpdate = FALSE;
1002 // If we've been asked to run Update/Render cycles a finite number of times then decrement so we wait after the
1003 // requested number of cycles
1004 if(mUpdateRenderRunCount > 0)
1006 --mUpdateRenderRunCount;
1009 // Keep the update-render thread alive if this thread is NOT to be destroyed
1010 return !mDestroyUpdateRenderThread;
1013 Dali::RenderSurfaceInterface* CombinedUpdateRenderController::ShouldSurfaceBeReplaced()
1015 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
1017 Dali::RenderSurfaceInterface* newSurface = mNewSurface;
1023 void CombinedUpdateRenderController::SurfaceReplaced()
1025 // Just increment the semaphore
1026 mSurfaceSemaphore.Release(1);
1029 Dali::RenderSurfaceInterface* CombinedUpdateRenderController::ShouldSurfaceBeDeleted()
1031 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
1033 Dali::RenderSurfaceInterface* deletedSurface = mDeletedSurface;
1034 mDeletedSurface = NULL;
1036 return deletedSurface;
1039 void CombinedUpdateRenderController::SurfaceDeleted()
1041 // Just increment the semaphore
1042 mSurfaceSemaphore.Release(1);
1045 void CombinedUpdateRenderController::SurfaceResized(uint32_t resizedCount)
1047 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
1049 if(mSurfaceResized >= resizedCount)
1051 mSurfaceResized -= resizedCount;
1055 mSurfaceResized = 0u;
1059 void CombinedUpdateRenderController::PreCompileShader(std::string vertexShader, std::string fragmentShader)
1061 GraphicsInterface& graphics = mAdaptorInterfaces.GetGraphicsInterface();
1063 Graphics::ShaderCreateInfo vertexShaderCreateInfo;
1064 vertexShaderCreateInfo.SetPipelineStage(Graphics::PipelineStage::VERTEX_SHADER);
1065 vertexShaderCreateInfo.SetSourceMode(Graphics::ShaderSourceMode::TEXT);
1066 const std::vector<char>& vertexShaderSrc = StringToVector(std::move(vertexShader));
1067 vertexShaderCreateInfo.SetSourceSize(vertexShaderSrc.size());
1068 vertexShaderCreateInfo.SetSourceData(static_cast<const void*>(vertexShaderSrc.data()));
1069 auto vertexGraphicsShader = graphics.GetController().CreateShader(vertexShaderCreateInfo, nullptr);
1071 Graphics::ShaderCreateInfo fragmentShaderCreateInfo;
1072 fragmentShaderCreateInfo.SetPipelineStage(Graphics::PipelineStage::FRAGMENT_SHADER);
1073 fragmentShaderCreateInfo.SetSourceMode(Graphics::ShaderSourceMode::TEXT);
1074 const std::vector<char>& fragmentShaderSrc = StringToVector(std::move(fragmentShader));
1075 fragmentShaderCreateInfo.SetSourceSize(fragmentShaderSrc.size());
1076 fragmentShaderCreateInfo.SetSourceData(static_cast<const void*>(fragmentShaderSrc.data()));
1077 auto fragmentGraphicsShader = graphics.GetController().CreateShader(fragmentShaderCreateInfo, nullptr);
1079 std::vector<Graphics::ShaderState> shaderStates{
1080 Graphics::ShaderState()
1081 .SetShader(*vertexGraphicsShader.get())
1082 .SetPipelineStage(Graphics::PipelineStage::VERTEX_SHADER),
1083 Graphics::ShaderState()
1084 .SetShader(*fragmentGraphicsShader.get())
1085 .SetPipelineStage(Graphics::PipelineStage::FRAGMENT_SHADER)};
1087 auto createInfo = Graphics::ProgramCreateInfo();
1088 createInfo.SetShaderState(shaderStates);
1090 auto graphicsProgram = graphics.GetController().CreateProgram(createInfo, nullptr);
1091 ShaderPreCompiler::Get().AddPreCompiledProgram(std::move(graphicsProgram));
1094 void CombinedUpdateRenderController::CancelPreCompile()
1096 if(mIsPreCompileCancelled == FALSE)
1098 mIsPreCompileCancelled = TRUE;
1099 ShaderPreCompiler::Get().Awake();
1103 ///////////////////////////////////////////////////////////////////////////////////////////////////
1105 ///////////////////////////////////////////////////////////////////////////////////////////////////
1107 void CombinedUpdateRenderController::NotifyThreadInitialised()
1109 // Just increment the semaphore
1110 mEventThreadSemaphore.Release(1);
1113 void CombinedUpdateRenderController::NotifyGraphicsInitialised()
1115 mGraphicsInitializeWait.Notify();
1118 void CombinedUpdateRenderController::AddPerformanceMarker(PerformanceInterface::MarkerType type)
1120 if(mPerformanceInterface)
1122 mPerformanceInterface->AddMarker(type);
1126 /////////////////////////////////////////////////////////////////////////////////////////////////
1127 // POST RENDERING: EVENT THREAD
1128 /////////////////////////////////////////////////////////////////////////////////////////////////
1130 void CombinedUpdateRenderController::PostRenderComplete()
1132 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
1133 mPostRendering = FALSE;
1135 mUpdateRenderThreadWaitCondition.Notify(lock);
1138 ///////////////////////////////////////////////////////////////////////////////////////////////////
1139 // POST RENDERING: RENDER THREAD
1140 ///////////////////////////////////////////////////////////////////////////////////////////////////
1142 void CombinedUpdateRenderController::PostRenderStarted()
1144 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
1145 mPostRendering = TRUE;
1148 void CombinedUpdateRenderController::PostRenderWaitForCompletion()
1150 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
1151 while(mPostRendering &&
1152 !mNewSurface && // We should NOT wait if we're replacing the surface
1153 !mDeletedSurface && // We should NOT wait if we're deleting the surface
1154 !mDestroyUpdateRenderThread)
1156 mUpdateRenderThreadWaitCondition.Wait(lock);
1160 } // namespace Adaptor
1162 } // namespace Internal