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),
119 mIsQuitedPreCompile(FALSE),
121 mDeletedSurface(nullptr),
122 mPostRendering(FALSE),
125 mUploadWithoutRendering(FALSE),
126 mFirstFrameAfterResume(FALSE)
130 // Initialise frame delta/duration variables first
131 SetRenderRefreshRate(environmentOptions.GetRenderRefreshRate());
133 // Set the thread-synchronization interface on the render-surface
134 Dali::RenderSurfaceInterface* currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
137 currentSurface->SetThreadSynchronization(*this);
140 mSleepTrigger = TriggerEventFactory::CreateTriggerEvent(MakeCallback(this, &CombinedUpdateRenderController::ProcessSleepRequest), TriggerEventInterface::KEEP_ALIVE_AFTER_TRIGGER);
143 CombinedUpdateRenderController::~CombinedUpdateRenderController()
149 delete mPreRenderCallback;
150 delete mSleepTrigger;
153 void CombinedUpdateRenderController::Initialize()
157 // Ensure Update/Render Thread not already created
158 DALI_ASSERT_ALWAYS(!mUpdateRenderThread);
160 // Create Update/Render Thread
161 ConditionalWait::ScopedLock lock(mGraphicsInitializeWait);
162 mUpdateRenderThread = new pthread_t();
163 int error = pthread_create(mUpdateRenderThread, NULL, InternalUpdateRenderThreadEntryFunc, this);
164 DALI_ASSERT_ALWAYS(!error && "Return code from pthread_create() when creating UpdateRenderThread");
166 // The Update/Render thread will now run and initialise the graphics interface etc. and will then wait for Start to be called
167 // When this function returns, the application initialisation on the event thread should occur
170 void CombinedUpdateRenderController::Start()
174 DALI_ASSERT_ALWAYS(!mRunning && mUpdateRenderThread);
176 // Wait until all threads created in Initialise are up and running
177 for(unsigned int i = 0; i < CREATED_THREAD_COUNT; ++i)
179 mEventThreadSemaphore.Acquire();
184 LOG_EVENT("Startup Complete, starting Update/Render Thread");
186 RunUpdateRenderThread(CONTINUOUS, AnimationProgression::NONE, UpdateMode::NORMAL);
188 Dali::RenderSurfaceInterface* currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
191 currentSurface->StartRender();
194 DALI_LOG_RELEASE_INFO("CombinedUpdateRenderController::Start\n");
197 void CombinedUpdateRenderController::Pause()
203 PauseUpdateRenderThread();
205 AddPerformanceMarker(PerformanceInterface::PAUSED);
207 DALI_LOG_RELEASE_INFO("CombinedUpdateRenderController::Pause\n");
210 void CombinedUpdateRenderController::Resume()
214 if(!mRunning && IsUpdateRenderThreadPaused())
216 LOG_EVENT("Resuming");
218 RunUpdateRenderThread(CONTINUOUS, AnimationProgression::USE_ELAPSED_TIME, UpdateMode::NORMAL);
220 AddPerformanceMarker(PerformanceInterface::RESUME);
224 mFirstFrameAfterResume = TRUE;
226 DALI_LOG_RELEASE_INFO("CombinedUpdateRenderController::Resume\n");
230 DALI_LOG_RELEASE_INFO("CombinedUpdateRenderController::Resume: Already resumed [%d, %d, %d]\n", mRunning, mUpdateRenderRunCount, mUpdateRenderThreadCanSleep);
234 void CombinedUpdateRenderController::Stop()
238 // Stop Rendering and the Update/Render Thread
239 Dali::RenderSurfaceInterface* currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
242 currentSurface->StopRender();
245 StopUpdateRenderThread();
247 if(mUpdateRenderThread)
249 LOG_EVENT("Destroying UpdateRenderThread");
251 // wait for the thread to finish
252 pthread_join(*mUpdateRenderThread, NULL);
254 delete mUpdateRenderThread;
255 mUpdateRenderThread = NULL;
260 DALI_LOG_RELEASE_INFO("CombinedUpdateRenderController::Stop\n");
263 void CombinedUpdateRenderController::RequestUpdate()
267 // Increment the update-request count to the maximum
268 if(mUpdateRequestCount < MAXIMUM_UPDATE_REQUESTS)
270 ++mUpdateRequestCount;
273 if(mRunning && IsUpdateRenderThreadPaused())
275 LOG_EVENT("Processing");
277 RunUpdateRenderThread(CONTINUOUS, AnimationProgression::NONE, UpdateMode::NORMAL);
280 ConditionalWait::ScopedLock updateLock(mUpdateRenderThreadWaitCondition);
281 mPendingRequestUpdate = TRUE;
284 void CombinedUpdateRenderController::RequestUpdateOnce(UpdateMode updateMode)
286 // Increment the update-request count to the maximum
287 if(mUpdateRequestCount < MAXIMUM_UPDATE_REQUESTS)
289 ++mUpdateRequestCount;
292 if(IsUpdateRenderThreadPaused() || updateMode == UpdateMode::FORCE_RENDER)
296 // Run Update/Render once
297 RunUpdateRenderThread(ONCE, AnimationProgression::NONE, updateMode);
301 void CombinedUpdateRenderController::ReplaceSurface(Dali::RenderSurfaceInterface* newSurface)
305 if(mUpdateRenderThread)
307 // Set the ThreadSyncronizationInterface on the new surface
308 newSurface->SetThreadSynchronization(*this);
310 LOG_EVENT("Starting to replace the surface, event-thread blocked");
312 // Start replacing the surface.
314 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
315 mPostRendering = FALSE; // Clear the post-rendering flag as Update/Render thread will replace the surface now
316 mNewSurface = newSurface;
317 if(mIsQuitedPreCompile == FALSE)
319 mIsQuitedPreCompile = TRUE;
320 Integration::ShaderPrecompiler::Get().StopPrecompile();
322 mUpdateRenderThreadWaitCondition.Notify(lock);
325 // Wait until the surface has been replaced
326 mSurfaceSemaphore.Acquire();
328 LOG_EVENT("Surface replaced, event-thread continuing");
332 void CombinedUpdateRenderController::DeleteSurface(Dali::RenderSurfaceInterface* surface)
336 if(mUpdateRenderThread)
338 LOG_EVENT("Starting to delete the surface, event-thread blocked");
340 // Start replacing the surface.
342 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
343 mPostRendering = FALSE; // Clear the post-rendering flag as Update/Render thread will delete the surface now
344 mDeletedSurface = surface;
345 if(mIsQuitedPreCompile == FALSE)
347 mIsQuitedPreCompile = TRUE;
348 Integration::ShaderPrecompiler::Get().StopPrecompile();
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.
386 if(mIsQuitedPreCompile == FALSE)
388 mIsQuitedPreCompile = TRUE;
389 Integration::ShaderPrecompiler::Get().StopPrecompile();
391 mUpdateRenderThreadWaitCondition.Notify(lock);
395 void CombinedUpdateRenderController::SetRenderRefreshRate(unsigned int numberOfFramesPerRender)
397 // Not protected by lock, but written to rarely so not worth adding a lock when reading
398 mDefaultFrameDelta = numberOfFramesPerRender * DEFAULT_FRAME_DURATION_IN_SECONDS;
399 mDefaultFrameDurationMilliseconds = uint64_t(numberOfFramesPerRender) * DEFAULT_FRAME_DURATION_IN_MILLISECONDS;
400 mDefaultFrameDurationNanoseconds = uint64_t(numberOfFramesPerRender) * DEFAULT_FRAME_DURATION_IN_NANOSECONDS;
401 mDefaultHalfFrameNanoseconds = mDefaultFrameDurationNanoseconds / 2u;
403 LOG_EVENT("mDefaultFrameDelta(%.6f), mDefaultFrameDurationMilliseconds(%lld), mDefaultFrameDurationNanoseconds(%lld)", mDefaultFrameDelta, mDefaultFrameDurationMilliseconds, mDefaultFrameDurationNanoseconds);
406 void CombinedUpdateRenderController::SetPreRenderCallback(CallbackBase* callback)
409 LOG_EVENT("Set PreRender Callback");
411 ConditionalWait::ScopedLock updateLock(mUpdateRenderThreadWaitCondition);
412 if(mPreRenderCallback)
414 delete mPreRenderCallback;
416 mPreRenderCallback = callback;
419 void CombinedUpdateRenderController::AddSurface(Dali::RenderSurfaceInterface* surface)
422 LOG_EVENT("Surface is added");
423 if(mUpdateRenderThread)
425 // Set the ThreadSyncronizationInterface on the added surface
426 surface->SetThreadSynchronization(*this);
430 int32_t CombinedUpdateRenderController::GetThreadId() const
435 ///////////////////////////////////////////////////////////////////////////////////////////////////
437 ///////////////////////////////////////////////////////////////////////////////////////////////////
439 void CombinedUpdateRenderController::RunUpdateRenderThread(int numberOfCycles, AnimationProgression animationProgression, UpdateMode updateMode)
441 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
445 case ThreadMode::NORMAL:
447 mUpdateRenderRunCount = numberOfCycles;
448 mUseElapsedTimeAfterWait = (animationProgression == AnimationProgression::USE_ELAPSED_TIME);
451 case ThreadMode::RUN_IF_REQUESTED:
453 if(updateMode != UpdateMode::FORCE_RENDER)
455 // Render only if the update mode is FORCE_RENDER which means the application requests it.
456 // We don't want to awake the update thread.
460 mUpdateRenderRunCount++; // Increase the update request count
461 mUseElapsedTimeAfterWait = TRUE; // The elapsed time should be used. We want animations to proceed.
466 mUpdateRenderThreadCanSleep = FALSE;
467 mUploadWithoutRendering = (updateMode == UpdateMode::SKIP_RENDER);
468 LOG_COUNTER_EVENT("mUpdateRenderRunCount: %d, mUseElapsedTimeAfterWait: %d", mUpdateRenderRunCount, mUseElapsedTimeAfterWait);
469 if(mIsQuitedPreCompile == FALSE)
471 mIsQuitedPreCompile = TRUE;
472 Integration::ShaderPrecompiler::Get().StopPrecompile();
474 mUpdateRenderThreadWaitCondition.Notify(lock);
477 void CombinedUpdateRenderController::PauseUpdateRenderThread()
479 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
480 mUpdateRenderRunCount = 0;
483 void CombinedUpdateRenderController::StopUpdateRenderThread()
485 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
486 mDestroyUpdateRenderThread = TRUE;
487 if(mIsQuitedPreCompile == FALSE)
489 mIsQuitedPreCompile = TRUE;
490 Integration::ShaderPrecompiler::Get().StopPrecompile();
492 mUpdateRenderThreadWaitCondition.Notify(lock);
495 bool CombinedUpdateRenderController::IsUpdateRenderThreadPaused()
497 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
499 if(mThreadMode == ThreadMode::RUN_IF_REQUESTED)
501 return !mRunning || mUpdateRenderThreadCanSleep;
504 return (mUpdateRenderRunCount != CONTINUOUS) || // Report paused if NOT continuously running
505 mUpdateRenderThreadCanSleep; // Report paused if sleeping
508 void CombinedUpdateRenderController::ProcessSleepRequest()
512 // Decrement Update request count
513 if(mUpdateRequestCount > 0)
515 --mUpdateRequestCount;
518 // Can sleep if our update-request count is 0
519 // Update/Render thread can choose to carry on updating if it determines more update/renders are required
520 if(mUpdateRequestCount == 0)
522 LOG_EVENT("Going to sleep");
524 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
525 mUpdateRenderThreadCanSleep = TRUE;
529 ///////////////////////////////////////////////////////////////////////////////////////////////////
530 // UPDATE/RENDER THREAD
531 ///////////////////////////////////////////////////////////////////////////////////////////////////
533 void CombinedUpdateRenderController::UpdateRenderThread()
535 ThreadSettings::SetThreadName("RenderThread\0");
536 mThreadId = ThreadSettings::GetThreadId();
538 // Install a function for logging
539 mEnvironmentOptions.InstallLogFunction();
541 // Install a function for tracing
542 mEnvironmentOptions.InstallTraceFunction();
544 LOG_UPDATE_RENDER("THREAD CREATED");
546 // Initialize graphics
547 GraphicsInterface& graphics = mAdaptorInterfaces.GetGraphicsInterface();
548 graphics.Initialize();
550 Dali::DisplayConnection& displayConnection = mAdaptorInterfaces.GetDisplayConnectionInterface();
551 displayConnection.Initialize(); //@todo Move InitializeGraphics code into graphics implementation
553 // Setup graphics controller into upload manager.
554 GetImplementation(mTextureUploadManager).InitalizeGraphicsController(graphics.GetController());
556 NotifyGraphicsInitialised();
558 //@todo Vk swaps this around, but we need to support surfaceless context for multi-window
559 graphics.ConfigureSurface(mAdaptorInterfaces.GetRenderSurfaceInterface());
561 // Tell core it has a context
562 mCore.ContextCreated();
564 NotifyThreadInitialised();
566 // Initialize and create graphics resource for the shared context.
567 WindowContainer windows;
568 mAdaptorInterfaces.GetWindowContainerInterface(windows);
570 for(auto&& window : windows)
572 Dali::Integration::Scene scene = window->GetScene();
573 Dali::RenderSurfaceInterface* windowSurface = window->GetSurface();
575 if(scene && windowSurface)
577 windowSurface->InitializeGraphics();
582 uint64_t lastFrameTime;
583 TimeService::GetNanoseconds(lastFrameTime);
584 uint64_t lastMemPoolLogTime = lastFrameTime;
586 LOG_UPDATE_RENDER("THREAD INITIALISED");
588 bool useElapsedTime = true;
589 bool updateRequired = true;
590 uint64_t timeToSleepUntil = 0;
591 int extraFramesDropped = 0;
593 const uint64_t memPoolInterval = 1e9 * float(mEnvironmentOptions.GetMemoryPoolInterval());
595 const unsigned int renderToFboInterval = mEnvironmentOptions.GetRenderToFboInterval();
596 const bool renderToFboEnabled = 0u != renderToFboInterval;
597 unsigned int frameCount = 0u;
599 if(!mDestroyUpdateRenderThread)
601 Integration::ShaderPrecompiler::Get().WaitPrecompileList();
602 if(Integration::ShaderPrecompiler::Get().IsEnable())
604 std::vector<RawShaderData> precompiledShaderList;
605 Integration::ShaderPrecompiler::Get().GetPrecompileShaderList(precompiledShaderList);
606 DALI_LOG_RELEASE_INFO("ShaderPrecompiler[ENABLE], list size:%d \n", precompiledShaderList.size());
607 for(auto precompiledShader = precompiledShaderList.begin(); precompiledShader != precompiledShaderList.end(); ++precompiledShader)
609 if(mIsQuitedPreCompile == TRUE)
611 Integration::ShaderPrecompiler::Get().StopPrecompile();
612 DALI_LOG_RELEASE_INFO("ShaderPrecompiler[ENABLE], but stop precompile");
616 auto numberOfPrecomipledShader = precompiledShader->shaderCount;
617 for(int i = 0; i < numberOfPrecomipledShader; ++i)
619 auto vertexShader = std::string(graphics.GetController().GetGlAbstraction().GetVertexShaderPrefix() + precompiledShader->vertexPrefix[i].data() + precompiledShader->vertexShader.data());
620 auto fragmentShader = std::string(graphics.GetController().GetGlAbstraction().GetFragmentShaderPrefix() + precompiledShader->fragmentPrefix[i].data() + precompiledShader->fragmentShader.data());
621 mCore.PreCompileShader(vertexShader.data(), fragmentShader.data());
623 DALI_LOG_RELEASE_INFO("ShaderPrecompiler[ENABLE], shader count :%d \n", numberOfPrecomipledShader);
628 DALI_LOG_RELEASE_INFO("ShaderPrecompiler[DISABLE] \n");
632 while(UpdateRenderReady(useElapsedTime, updateRequired, timeToSleepUntil))
634 LOG_UPDATE_RENDER_TRACE;
637 bool uploadOnly = mUploadWithoutRendering;
638 unsigned int surfaceResized = mSurfaceResized;
639 Dali::RenderSurfaceInterface* deletedSurface = ShouldSurfaceBeDeleted();
641 // Performance statistics are logged upon a VSYNC tick so use this point for a VSync marker
642 AddPerformanceMarker(PerformanceInterface::VSYNC);
644 uint64_t currentFrameStartTime = 0;
645 TimeService::GetNanoseconds(currentFrameStartTime);
647 uint64_t timeSinceLastFrame = currentFrameStartTime - lastFrameTime;
649 // Optional FPS Tracking when continuously rendering
650 if(useElapsedTime && mFpsTracker.Enabled())
652 float absoluteTimeSinceLastRender = timeSinceLastFrame * NANOSECONDS_TO_SECOND;
653 mFpsTracker.Track(absoluteTimeSinceLastRender);
656 lastFrameTime = currentFrameStartTime; // Store frame start time
658 //////////////////////////////
660 //////////////////////////////
662 Dali::RenderSurfaceInterface* newSurface = ShouldSurfaceBeReplaced();
663 if(DALI_UNLIKELY(newSurface))
665 LOG_UPDATE_RENDER_TRACE_FMT("Replacing Surface");
666 // This is designed for replacing pixmap surfaces, but should work for window as well
667 // we need to delete the surface and renderable (pixmap / window)
668 // Then create a new pixmap/window and new surface
669 // If the new surface has a different display connection, then the context will be lost
670 mAdaptorInterfaces.GetDisplayConnectionInterface().Initialize();
671 graphics.ActivateSurfaceContext(newSurface);
672 // TODO: ReplaceGraphicsSurface doesn't work, InitializeGraphics()
673 // already creates new surface window, the surface and the context.
674 // We probably don't need ReplaceGraphicsSurface at all.
675 // newSurface->ReplaceGraphicsSurface();
679 //////////////////////////////
680 // TextureUploadRequest (phase #1)
681 //////////////////////////////
683 // Upload requested resources after resource context activated.
684 graphics.ActivateResourceContext();
686 const bool textureUploaded = mTextureUploadManager.ResourceUpload();
688 // Update & Render forcely if there exist some uploaded texture.
689 uploadOnly = textureUploaded ? false : uploadOnly;
691 const bool isRenderingToFbo = renderToFboEnabled && ((0u == frameCount) || (0u != frameCount % renderToFboInterval));
694 //////////////////////////////
696 //////////////////////////////
698 const uint32_t currentTime = static_cast<uint32_t>(currentFrameStartTime / NANOSECONDS_PER_MILLISECOND);
699 const uint32_t nextFrameTime = currentTime + static_cast<uint32_t>(mDefaultFrameDurationMilliseconds);
701 uint64_t noOfFramesSinceLastUpdate = 1;
702 float frameDelta = 0.0f;
705 if(mThreadMode == ThreadMode::RUN_IF_REQUESTED)
707 extraFramesDropped = 0;
708 while(timeSinceLastFrame >= mDefaultFrameDurationNanoseconds)
710 timeSinceLastFrame -= mDefaultFrameDurationNanoseconds;
711 extraFramesDropped++;
715 // If using the elapsed time, then calculate frameDelta as a multiple of mDefaultFrameDelta
716 noOfFramesSinceLastUpdate += extraFramesDropped;
718 frameDelta = mDefaultFrameDelta * noOfFramesSinceLastUpdate;
720 LOG_UPDATE_RENDER("timeSinceLastFrame(%llu) noOfFramesSinceLastUpdate(%u) frameDelta(%.6f)", timeSinceLastFrame, noOfFramesSinceLastUpdate, frameDelta);
722 Integration::UpdateStatus updateStatus;
724 AddPerformanceMarker(PerformanceInterface::UPDATE_START);
725 TRACE_UPDATE_RENDER_BEGIN("DALI_UPDATE");
726 mCore.Update(frameDelta,
733 TRACE_UPDATE_RENDER_END("DALI_UPDATE");
734 AddPerformanceMarker(PerformanceInterface::UPDATE_END);
736 unsigned int keepUpdatingStatus = updateStatus.KeepUpdating();
738 // Tell the event-thread to wake up (if asleep) and send a notification event to Core if required
739 if(updateStatus.NeedsNotification())
741 mNotificationTrigger.Trigger();
742 LOG_UPDATE_RENDER("Notification Triggered");
745 // Optional logging of update/render status
746 mUpdateStatusLogger.Log(keepUpdatingStatus);
748 //////////////////////////////
750 //////////////////////////////
752 graphics.FrameStart();
753 mAdaptorInterfaces.GetDisplayConnectionInterface().ConsumeEvents();
755 if(mPreRenderCallback != NULL)
757 bool keepCallback = CallbackBase::ExecuteReturn<bool>(*mPreRenderCallback);
760 delete mPreRenderCallback;
761 mPreRenderCallback = NULL;
765 //////////////////////////////
766 // TextureUploadRequest (phase #2)
767 //////////////////////////////
769 // Upload requested resources after resource context activated.
770 graphics.ActivateResourceContext();
772 // Since uploadOnly value used at Update side, we should not change uploadOnly value now even some textures are uploaded.
773 mTextureUploadManager.ResourceUpload();
775 if(mFirstFrameAfterResume)
777 // mFirstFrameAfterResume is set to true when the thread is resumed
778 // Let graphics know the first frame after thread initialized or resumed.
779 graphics.SetFirstFrameAfterResume();
780 mFirstFrameAfterResume = FALSE;
783 Integration::RenderStatus renderStatus;
785 AddPerformanceMarker(PerformanceInterface::RENDER_START);
786 TRACE_UPDATE_RENDER_BEGIN("DALI_RENDER");
788 // Upload shared resources
789 TRACE_UPDATE_RENDER_BEGIN("DALI_PRE_RENDER");
790 mCore.PreRender(renderStatus, mForceClear);
791 TRACE_UPDATE_RENDER_END("DALI_PRE_RENDER");
793 if(!uploadOnly || surfaceResized)
795 // Go through each window
797 mAdaptorInterfaces.GetWindowContainerInterface(windows);
799 for(auto&& window : windows)
801 Dali::Integration::Scene scene = window->GetScene();
802 Dali::RenderSurfaceInterface* windowSurface = window->GetSurface();
804 if(scene && windowSurface)
806 TRACE_UPDATE_RENDER_BEGIN("DALI_RENDER_SCENE");
807 Integration::RenderStatus windowRenderStatus;
809 const bool sceneSurfaceResized = scene.IsSurfaceRectChanged();
811 // clear previous frame damaged render items rects, buffer history is tracked on surface level
812 mDamagedRects.clear();
814 // Collect damage rects
815 mCore.PreRender(scene, mDamagedRects);
817 // Render off-screen frame buffers first if any
818 mCore.RenderScene(windowRenderStatus, scene, true);
820 Rect<int> clippingRect; // Empty for fbo rendering
822 // Switch to the context of the surface, merge damaged areas for previous frames
823 windowSurface->PreRender(sceneSurfaceResized, mDamagedRects, clippingRect); // Switch GL context
825 // Render the surface
826 mCore.RenderScene(windowRenderStatus, scene, false, clippingRect);
828 // Buffer swapping now happens when the surface render target is presented.
830 // If surface is resized, the surface resized count is decreased.
831 if(DALI_UNLIKELY(sceneSurfaceResized))
835 TRACE_UPDATE_RENDER_END("DALI_RENDER_SCENE");
840 TRACE_UPDATE_RENDER_BEGIN("DALI_POST_RENDER");
843 graphics.PostRender();
847 TRACE_UPDATE_RENDER_END("DALI_POST_RENDER");
849 //////////////////////////////
851 //////////////////////////////
852 if(DALI_UNLIKELY(deletedSurface))
854 LOG_UPDATE_RENDER_TRACE_FMT("Deleting Surface");
856 deletedSurface->DestroySurface();
861 TRACE_UPDATE_RENDER_END("DALI_RENDER");
862 AddPerformanceMarker(PerformanceInterface::RENDER_END);
864 // if the memory pool interval is set and has elapsed, log the graphics memory pools
865 if(0 < memPoolInterval && memPoolInterval < lastFrameTime - lastMemPoolLogTime)
867 lastMemPoolLogTime = lastFrameTime;
868 graphics.LogMemoryPools();
873 // Trigger event thread to request Update/Render thread to sleep if update not required
874 if((Integration::KeepUpdating::NOT_REQUESTED == keepUpdatingStatus) && !renderStatus.NeedsUpdate())
876 mSleepTrigger->Trigger();
877 updateRequired = false;
878 LOG_UPDATE_RENDER("Sleep Triggered");
882 updateRequired = true;
885 //////////////////////////////
887 //////////////////////////////
889 extraFramesDropped = 0;
891 if(timeToSleepUntil == 0)
893 // If this is the first frame after the thread is initialized or resumed, we
894 // use the actual time the current frame starts from to calculate the time to
895 // sleep until the next frame.
896 timeToSleepUntil = currentFrameStartTime + mDefaultFrameDurationNanoseconds;
900 // Otherwise, always use the sleep-until time calculated in the last frame to
901 // calculate the time to sleep until the next frame. In this way, if there is
902 // any time gap between the current frame and the next frame, or if update or
903 // rendering in the current frame takes too much time so that the specified
904 // sleep-until time has already passed, it will try to keep the frames syncing
905 // by shortening the duration of the next frame.
906 timeToSleepUntil += mDefaultFrameDurationNanoseconds;
908 // Check the current time at the end of the frame
909 uint64_t currentFrameEndTime = 0;
910 TimeService::GetNanoseconds(currentFrameEndTime);
911 while(currentFrameEndTime > timeToSleepUntil + mDefaultFrameDurationNanoseconds)
913 // We are more than one frame behind already, so just drop the next frames
914 // until the sleep-until time is later than the current time so that we can
916 timeToSleepUntil += mDefaultFrameDurationNanoseconds;
917 extraFramesDropped++;
921 // Render to FBO is intended to measure fps above 60 so sleep is not wanted.
922 if(0u == renderToFboInterval)
924 // Sleep until at least the the default frame duration has elapsed. This will return immediately if the specified end-time has already passed.
925 TimeService::SleepUntil(timeToSleepUntil);
929 // Inform core of context destruction
930 mCore.ContextDestroyed();
933 mAdaptorInterfaces.GetWindowContainerInterface(windows);
936 for(auto&& window : windows)
938 Dali::RenderSurfaceInterface* surface = window->GetSurface();
939 surface->DestroySurface();
944 LOG_UPDATE_RENDER("THREAD DESTROYED");
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 mUpdateRenderThreadWaitCondition.Wait(updateLock);
976 if(!mUseElapsedTimeAfterWait)
978 useElapsedTime = false;
982 LOG_COUNTER_UPDATE_RENDER("mUpdateRenderRunCount: %d", mUpdateRenderRunCount);
983 LOG_COUNTER_UPDATE_RENDER("mUpdateRenderThreadCanSleep: %d, updateRequired: %d, mPendingRequestUpdate: %d", mUpdateRenderThreadCanSleep, updateRequired, mPendingRequestUpdate);
984 LOG_COUNTER_UPDATE_RENDER("mDestroyUpdateRenderThread: %d", mDestroyUpdateRenderThread);
985 LOG_COUNTER_UPDATE_RENDER("mNewSurface: %d", mNewSurface);
986 LOG_COUNTER_UPDATE_RENDER("mDeletedSurface: %d", mDeletedSurface);
987 LOG_COUNTER_UPDATE_RENDER("mSurfaceResized: %d", mSurfaceResized);
989 mUseElapsedTimeAfterWait = FALSE;
990 mUpdateRenderThreadCanSleep = FALSE;
991 mPendingRequestUpdate = FALSE;
993 // If we've been asked to run Update/Render cycles a finite number of times then decrement so we wait after the
994 // requested number of cycles
995 if(mUpdateRenderRunCount > 0)
997 --mUpdateRenderRunCount;
1000 // Keep the update-render thread alive if this thread is NOT to be destroyed
1001 return !mDestroyUpdateRenderThread;
1004 Dali::RenderSurfaceInterface* CombinedUpdateRenderController::ShouldSurfaceBeReplaced()
1006 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
1008 Dali::RenderSurfaceInterface* newSurface = mNewSurface;
1014 void CombinedUpdateRenderController::SurfaceReplaced()
1016 // Just increment the semaphore
1017 mSurfaceSemaphore.Release(1);
1020 Dali::RenderSurfaceInterface* CombinedUpdateRenderController::ShouldSurfaceBeDeleted()
1022 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
1024 Dali::RenderSurfaceInterface* deletedSurface = mDeletedSurface;
1025 mDeletedSurface = NULL;
1027 return deletedSurface;
1030 void CombinedUpdateRenderController::SurfaceDeleted()
1032 // Just increment the semaphore
1033 mSurfaceSemaphore.Release(1);
1036 void CombinedUpdateRenderController::SurfaceResized()
1038 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
1045 ///////////////////////////////////////////////////////////////////////////////////////////////////
1047 ///////////////////////////////////////////////////////////////////////////////////////////////////
1049 void CombinedUpdateRenderController::NotifyThreadInitialised()
1051 // Just increment the semaphore
1052 mEventThreadSemaphore.Release(1);
1055 void CombinedUpdateRenderController::NotifyGraphicsInitialised()
1057 mGraphicsInitializeWait.Notify();
1060 void CombinedUpdateRenderController::AddPerformanceMarker(PerformanceInterface::MarkerType type)
1062 if(mPerformanceInterface)
1064 mPerformanceInterface->AddMarker(type);
1068 /////////////////////////////////////////////////////////////////////////////////////////////////
1069 // POST RENDERING: EVENT THREAD
1070 /////////////////////////////////////////////////////////////////////////////////////////////////
1072 void CombinedUpdateRenderController::PostRenderComplete()
1074 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
1075 mPostRendering = FALSE;
1076 if(mIsQuitedPreCompile == FALSE)
1078 mIsQuitedPreCompile = TRUE;
1079 Integration::ShaderPrecompiler::Get().StopPrecompile();
1081 mUpdateRenderThreadWaitCondition.Notify(lock);
1084 ///////////////////////////////////////////////////////////////////////////////////////////////////
1085 // POST RENDERING: RENDER THREAD
1086 ///////////////////////////////////////////////////////////////////////////////////////////////////
1088 void CombinedUpdateRenderController::PostRenderStarted()
1090 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
1091 mPostRendering = TRUE;
1094 void CombinedUpdateRenderController::PostRenderWaitForCompletion()
1096 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
1097 while(mPostRendering &&
1098 !mNewSurface && // We should NOT wait if we're replacing the surface
1099 !mDeletedSurface && // We should NOT wait if we're deleting the surface
1100 !mDestroyUpdateRenderThread)
1102 mUpdateRenderThreadWaitCondition.Wait(lock);
1106 } // namespace Adaptor
1108 } // namespace Internal