2 * Copyright (c) 2023 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 #include <dali/internal/adaptor/common/combined-update-render-controller.h>
22 #include <dali/integration-api/platform-abstraction.h>
23 #include <dali/integration-api/shader-precompiler.h>
26 #include "dali/public-api/common/dali-common.h"
29 #include <dali/integration-api/adaptor-framework/trigger-event-factory.h>
30 #include <dali/internal/adaptor/common/adaptor-internal-services.h>
31 #include <dali/internal/adaptor/common/combined-update-render-controller-debug.h>
32 #include <dali/internal/graphics/common/graphics-interface.h>
33 #include <dali/internal/graphics/gles/egl-graphics.h>
34 #include <dali/internal/system/common/environment-options.h>
35 #include <dali/internal/system/common/texture-upload-manager-impl.h>
36 #include <dali/internal/system/common/time-service.h>
37 #include <dali/internal/thread/common/thread-settings-impl.h>
38 #include <dali/internal/window-system/common/window-impl.h>
49 const unsigned int CREATED_THREAD_COUNT = 1u;
51 const int CONTINUOUS = -1;
54 const unsigned int TRUE = 1u;
55 const unsigned int FALSE = 0u;
57 const unsigned int MILLISECONDS_PER_SECOND(1e+3);
58 const float NANOSECONDS_TO_SECOND(1e-9f);
59 const unsigned int NANOSECONDS_PER_SECOND(1e+9);
60 const unsigned int NANOSECONDS_PER_MILLISECOND(1e+6);
62 // The following values will get calculated at compile time
63 const float DEFAULT_FRAME_DURATION_IN_SECONDS(1.0f / 60.0f);
64 const uint64_t DEFAULT_FRAME_DURATION_IN_MILLISECONDS(DEFAULT_FRAME_DURATION_IN_SECONDS* MILLISECONDS_PER_SECOND);
65 const uint64_t DEFAULT_FRAME_DURATION_IN_NANOSECONDS(DEFAULT_FRAME_DURATION_IN_SECONDS* NANOSECONDS_PER_SECOND);
68 * Handles the use case when an update-request is received JUST before we process a sleep-request. If we did not have an update-request count then
69 * there is a danger that, on the event-thread we could have:
70 * 1) An update-request where we do nothing as Update/Render thread still running.
71 * 2) Quickly followed by a sleep-request being handled where we pause the Update/Render Thread (even though we have an update to process).
73 * Using a counter means we increment the counter on an update-request, and decrement it on a sleep-request. This handles the above scenario because:
74 * 1) MAIN THREAD: Update Request: COUNTER = 1
75 * 2) UPDATE/RENDER THREAD: Do Update/Render, then no Updates required -> Sleep Trigger
76 * 3) MAIN THREAD: Update Request: COUNTER = 2
77 * 4) MAIN THREAD: Sleep Request: COUNTER = 1 -> We do not sleep just yet
79 * Also ensures we preserve battery life by only doing ONE update when the above use case is not triggered.
80 * 1) MAIN THREAD: Update Request: COUNTER = 1
81 * 2) UPDATE/RENDER THREAD: Do Update/Render, then no Updates required -> Sleep Trigger
82 * 3) MAIN THREAD: Sleep Request: COUNTER = 0 -> Go to sleep
84 const unsigned int MAXIMUM_UPDATE_REQUESTS = 2;
85 } // unnamed namespace
87 ///////////////////////////////////////////////////////////////////////////////////////////////////
89 ///////////////////////////////////////////////////////////////////////////////////////////////////
91 CombinedUpdateRenderController::CombinedUpdateRenderController(AdaptorInternalServices& adaptorInterfaces, const EnvironmentOptions& environmentOptions, ThreadMode threadMode)
92 : mFpsTracker(environmentOptions),
93 mUpdateStatusLogger(environmentOptions),
94 mEventThreadSemaphore(0),
96 mUpdateRenderThreadWaitCondition(),
97 mAdaptorInterfaces(adaptorInterfaces),
98 mPerformanceInterface(adaptorInterfaces.GetPerformanceInterface()),
99 mCore(adaptorInterfaces.GetCore()),
100 mEnvironmentOptions(environmentOptions),
101 mNotificationTrigger(adaptorInterfaces.GetProcessCoreEventsTrigger()),
103 mPreRenderCallback(NULL),
104 mTextureUploadManager(adaptorInterfaces.GetTextureUploadManager()),
105 mUpdateRenderThread(NULL),
106 mDefaultFrameDelta(0.0f),
107 mDefaultFrameDurationMilliseconds(0u),
108 mDefaultFrameDurationNanoseconds(0u),
109 mDefaultHalfFrameNanoseconds(0u),
110 mUpdateRequestCount(0u),
113 mThreadMode(threadMode),
114 mUpdateRenderRunCount(0),
115 mDestroyUpdateRenderThread(FALSE),
116 mUpdateRenderThreadCanSleep(FALSE),
117 mPendingRequestUpdate(FALSE),
118 mUseElapsedTimeAfterWait(FALSE),
120 mDeletedSurface(nullptr),
121 mPostRendering(FALSE),
124 mUploadWithoutRendering(FALSE),
125 mFirstFrameAfterResume(FALSE)
129 // Initialise frame delta/duration variables first
130 SetRenderRefreshRate(environmentOptions.GetRenderRefreshRate());
132 // Set the thread-synchronization interface on the render-surface
133 Dali::RenderSurfaceInterface* currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
136 currentSurface->SetThreadSynchronization(*this);
139 mSleepTrigger = TriggerEventFactory::CreateTriggerEvent(MakeCallback(this, &CombinedUpdateRenderController::ProcessSleepRequest), TriggerEventInterface::KEEP_ALIVE_AFTER_TRIGGER);
142 CombinedUpdateRenderController::~CombinedUpdateRenderController()
148 delete mPreRenderCallback;
149 delete mSleepTrigger;
152 void CombinedUpdateRenderController::Initialize()
156 // Ensure Update/Render Thread not already created
157 DALI_ASSERT_ALWAYS(!mUpdateRenderThread);
159 // Create Update/Render Thread
160 ConditionalWait::ScopedLock lock(mGraphicsInitializeWait);
161 mUpdateRenderThread = new pthread_t();
162 int error = pthread_create(mUpdateRenderThread, NULL, InternalUpdateRenderThreadEntryFunc, this);
163 DALI_ASSERT_ALWAYS(!error && "Return code from pthread_create() when creating UpdateRenderThread");
165 // The Update/Render thread will now run and initialise the graphics interface etc. and will then wait for Start to be called
166 // When this function returns, the application initialisation on the event thread should occur
169 void CombinedUpdateRenderController::Start()
173 DALI_ASSERT_ALWAYS(!mRunning && mUpdateRenderThread);
175 // Wait until all threads created in Initialise are up and running
176 for(unsigned int i = 0; i < CREATED_THREAD_COUNT; ++i)
178 mEventThreadSemaphore.Acquire();
183 LOG_EVENT("Startup Complete, starting Update/Render Thread");
185 RunUpdateRenderThread(CONTINUOUS, AnimationProgression::NONE, UpdateMode::NORMAL);
187 Dali::RenderSurfaceInterface* currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
190 currentSurface->StartRender();
193 DALI_LOG_RELEASE_INFO("CombinedUpdateRenderController::Start\n");
196 void CombinedUpdateRenderController::Pause()
202 PauseUpdateRenderThread();
204 AddPerformanceMarker(PerformanceInterface::PAUSED);
206 DALI_LOG_RELEASE_INFO("CombinedUpdateRenderController::Pause\n");
209 void CombinedUpdateRenderController::Resume()
213 if(!mRunning && IsUpdateRenderThreadPaused())
215 LOG_EVENT("Resuming");
217 RunUpdateRenderThread(CONTINUOUS, AnimationProgression::USE_ELAPSED_TIME, UpdateMode::NORMAL);
219 AddPerformanceMarker(PerformanceInterface::RESUME);
223 mFirstFrameAfterResume = TRUE;
225 DALI_LOG_RELEASE_INFO("CombinedUpdateRenderController::Resume\n");
229 DALI_LOG_RELEASE_INFO("CombinedUpdateRenderController::Resume: Already resumed [%d, %d, %d]\n", mRunning, mUpdateRenderRunCount, mUpdateRenderThreadCanSleep);
233 void CombinedUpdateRenderController::Stop()
237 // Stop Rendering and the Update/Render Thread
238 Dali::RenderSurfaceInterface* currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
241 currentSurface->StopRender();
244 StopUpdateRenderThread();
246 if(mUpdateRenderThread)
248 LOG_EVENT("Destroying UpdateRenderThread");
250 // wait for the thread to finish
251 pthread_join(*mUpdateRenderThread, NULL);
253 delete mUpdateRenderThread;
254 mUpdateRenderThread = NULL;
259 DALI_LOG_RELEASE_INFO("CombinedUpdateRenderController::Stop\n");
262 void CombinedUpdateRenderController::RequestUpdate()
266 // Increment the update-request count to the maximum
267 if(mUpdateRequestCount < MAXIMUM_UPDATE_REQUESTS)
269 ++mUpdateRequestCount;
272 if(mRunning && IsUpdateRenderThreadPaused())
274 LOG_EVENT("Processing");
276 RunUpdateRenderThread(CONTINUOUS, AnimationProgression::NONE, UpdateMode::NORMAL);
279 ConditionalWait::ScopedLock updateLock(mUpdateRenderThreadWaitCondition);
280 mPendingRequestUpdate = TRUE;
283 void CombinedUpdateRenderController::RequestUpdateOnce(UpdateMode updateMode)
285 // Increment the update-request count to the maximum
286 if(mUpdateRequestCount < MAXIMUM_UPDATE_REQUESTS)
288 ++mUpdateRequestCount;
291 if(IsUpdateRenderThreadPaused() || updateMode == UpdateMode::FORCE_RENDER)
295 // Run Update/Render once
296 RunUpdateRenderThread(ONCE, AnimationProgression::NONE, updateMode);
300 void CombinedUpdateRenderController::ReplaceSurface(Dali::RenderSurfaceInterface* newSurface)
304 if(mUpdateRenderThread)
306 // Set the ThreadSyncronizationInterface on the new surface
307 newSurface->SetThreadSynchronization(*this);
309 LOG_EVENT("Starting to replace the surface, event-thread blocked");
311 // Start replacing the surface.
313 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
314 mPostRendering = FALSE; // Clear the post-rendering flag as Update/Render thread will replace the surface now
315 mNewSurface = newSurface;
316 mUpdateRenderThreadWaitCondition.Notify(lock);
319 // Wait until the surface has been replaced
320 mSurfaceSemaphore.Acquire();
322 LOG_EVENT("Surface replaced, event-thread continuing");
326 void CombinedUpdateRenderController::DeleteSurface(Dali::RenderSurfaceInterface* surface)
330 if(mUpdateRenderThread)
332 LOG_EVENT("Starting to delete the surface, event-thread blocked");
334 // Start replacing the surface.
336 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
337 mPostRendering = FALSE; // Clear the post-rendering flag as Update/Render thread will delete the surface now
338 mDeletedSurface = surface;
339 mUpdateRenderThreadWaitCondition.Notify(lock);
342 // Wait until the surface has been deleted
343 mSurfaceSemaphore.Acquire();
345 LOG_EVENT("Surface deleted, event-thread continuing");
349 void CombinedUpdateRenderController::WaitForGraphicsInitialization()
351 ConditionalWait::ScopedLock lk(mGraphicsInitializeWait);
354 if(mUpdateRenderThread)
356 LOG_EVENT("Waiting for graphics initialisation, event-thread blocked");
358 // Wait until the graphics has been initialised
359 mGraphicsInitializeWait.Wait(lk);
361 LOG_EVENT("graphics initialised, event-thread continuing");
365 void CombinedUpdateRenderController::ResizeSurface()
369 LOG_EVENT("Resize the surface");
372 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
373 // Surface is resized and the surface resized count is increased.
375 mUpdateRenderThreadWaitCondition.Notify(lock);
379 void CombinedUpdateRenderController::SetRenderRefreshRate(unsigned int numberOfFramesPerRender)
381 // Not protected by lock, but written to rarely so not worth adding a lock when reading
382 mDefaultFrameDelta = numberOfFramesPerRender * DEFAULT_FRAME_DURATION_IN_SECONDS;
383 mDefaultFrameDurationMilliseconds = uint64_t(numberOfFramesPerRender) * DEFAULT_FRAME_DURATION_IN_MILLISECONDS;
384 mDefaultFrameDurationNanoseconds = uint64_t(numberOfFramesPerRender) * DEFAULT_FRAME_DURATION_IN_NANOSECONDS;
385 mDefaultHalfFrameNanoseconds = mDefaultFrameDurationNanoseconds / 2u;
387 LOG_EVENT("mDefaultFrameDelta(%.6f), mDefaultFrameDurationMilliseconds(%lld), mDefaultFrameDurationNanoseconds(%lld)", mDefaultFrameDelta, mDefaultFrameDurationMilliseconds, mDefaultFrameDurationNanoseconds);
390 void CombinedUpdateRenderController::SetPreRenderCallback(CallbackBase* callback)
393 LOG_EVENT("Set PreRender Callback");
395 ConditionalWait::ScopedLock updateLock(mUpdateRenderThreadWaitCondition);
396 if(mPreRenderCallback)
398 delete mPreRenderCallback;
400 mPreRenderCallback = callback;
403 void CombinedUpdateRenderController::AddSurface(Dali::RenderSurfaceInterface* surface)
406 LOG_EVENT("Surface is added");
407 if(mUpdateRenderThread)
409 // Set the ThreadSyncronizationInterface on the added surface
410 surface->SetThreadSynchronization(*this);
414 int32_t CombinedUpdateRenderController::GetThreadId() const
419 ///////////////////////////////////////////////////////////////////////////////////////////////////
421 ///////////////////////////////////////////////////////////////////////////////////////////////////
423 void CombinedUpdateRenderController::RunUpdateRenderThread(int numberOfCycles, AnimationProgression animationProgression, UpdateMode updateMode)
425 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
429 case ThreadMode::NORMAL:
431 mUpdateRenderRunCount = numberOfCycles;
432 mUseElapsedTimeAfterWait = (animationProgression == AnimationProgression::USE_ELAPSED_TIME);
435 case ThreadMode::RUN_IF_REQUESTED:
437 if(updateMode != UpdateMode::FORCE_RENDER)
439 // Render only if the update mode is FORCE_RENDER which means the application requests it.
440 // We don't want to awake the update thread.
444 mUpdateRenderRunCount++; // Increase the update request count
445 mUseElapsedTimeAfterWait = TRUE; // The elapsed time should be used. We want animations to proceed.
450 mUpdateRenderThreadCanSleep = FALSE;
451 mUploadWithoutRendering = (updateMode == UpdateMode::SKIP_RENDER);
452 LOG_COUNTER_EVENT("mUpdateRenderRunCount: %d, mUseElapsedTimeAfterWait: %d", mUpdateRenderRunCount, mUseElapsedTimeAfterWait);
453 mUpdateRenderThreadWaitCondition.Notify(lock);
456 void CombinedUpdateRenderController::PauseUpdateRenderThread()
458 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
459 mUpdateRenderRunCount = 0;
462 void CombinedUpdateRenderController::StopUpdateRenderThread()
464 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
465 mDestroyUpdateRenderThread = TRUE;
466 mUpdateRenderThreadWaitCondition.Notify(lock);
469 bool CombinedUpdateRenderController::IsUpdateRenderThreadPaused()
471 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
473 if(mThreadMode == ThreadMode::RUN_IF_REQUESTED)
475 return !mRunning || mUpdateRenderThreadCanSleep;
478 return (mUpdateRenderRunCount != CONTINUOUS) || // Report paused if NOT continuously running
479 mUpdateRenderThreadCanSleep; // Report paused if sleeping
482 void CombinedUpdateRenderController::ProcessSleepRequest()
486 // Decrement Update request count
487 if(mUpdateRequestCount > 0)
489 --mUpdateRequestCount;
492 // Can sleep if our update-request count is 0
493 // Update/Render thread can choose to carry on updating if it determines more update/renders are required
494 if(mUpdateRequestCount == 0)
496 LOG_EVENT("Going to sleep");
498 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
499 mUpdateRenderThreadCanSleep = TRUE;
503 ///////////////////////////////////////////////////////////////////////////////////////////////////
504 // UPDATE/RENDER THREAD
505 ///////////////////////////////////////////////////////////////////////////////////////////////////
507 void CombinedUpdateRenderController::UpdateRenderThread()
509 ThreadSettings::SetThreadName("RenderThread\0");
510 mThreadId = ThreadSettings::GetThreadId();
512 // Install a function for logging
513 mEnvironmentOptions.InstallLogFunction();
515 // Install a function for tracing
516 mEnvironmentOptions.InstallTraceFunction();
518 LOG_UPDATE_RENDER("THREAD CREATED");
520 // Initialize graphics
521 GraphicsInterface& graphics = mAdaptorInterfaces.GetGraphicsInterface();
522 graphics.Initialize();
524 Dali::DisplayConnection& displayConnection = mAdaptorInterfaces.GetDisplayConnectionInterface();
525 displayConnection.Initialize(); //@todo Move InitializeGraphics code into graphics implementation
527 // Setup graphics controller into upload manager.
528 GetImplementation(mTextureUploadManager).InitalizeGraphicsController(graphics.GetController());
530 NotifyGraphicsInitialised();
532 //@todo Vk swaps this around, but we need to support surfaceless context for multi-window
533 graphics.ConfigureSurface(mAdaptorInterfaces.GetRenderSurfaceInterface());
535 // Tell core it has a context
536 mCore.ContextCreated();
538 NotifyThreadInitialised();
540 // Initialize and create graphics resource for the shared context.
541 WindowContainer windows;
542 mAdaptorInterfaces.GetWindowContainerInterface(windows);
544 for(auto&& window : windows)
546 Dali::Integration::Scene scene = window->GetScene();
547 Dali::RenderSurfaceInterface* windowSurface = window->GetSurface();
549 if(scene && windowSurface)
551 windowSurface->InitializeGraphics();
556 uint64_t lastFrameTime;
557 TimeService::GetNanoseconds(lastFrameTime);
558 uint64_t lastMemPoolLogTime = lastFrameTime;
560 LOG_UPDATE_RENDER("THREAD INITIALISED");
562 bool useElapsedTime = true;
563 bool updateRequired = true;
564 uint64_t timeToSleepUntil = 0;
565 int extraFramesDropped = 0;
567 const uint64_t memPoolInterval = 1e9 * float(mEnvironmentOptions.GetMemoryPoolInterval());
569 const unsigned int renderToFboInterval = mEnvironmentOptions.GetRenderToFboInterval();
570 const bool renderToFboEnabled = 0u != renderToFboInterval;
571 unsigned int frameCount = 0u;
573 if(Integration::ShaderPrecompiler::Get().IsEnable())
575 std::vector<RawShaderData> precompiledShaderList;
576 Integration::ShaderPrecompiler::Get().GetPrecompileShaderList(precompiledShaderList);
577 DALI_LOG_RELEASE_INFO("ShaderPrecompiler[ENABLE], list size:%d \n",precompiledShaderList.size());
578 for(auto precompiledShader = precompiledShaderList.begin(); precompiledShader != precompiledShaderList.end(); ++precompiledShader)
580 auto numberOfPrecomipledShader = precompiledShader->shaderCount;
581 for(int i= 0; i<numberOfPrecomipledShader; ++i)
583 auto vertexShader = std::string(graphics.GetController().GetGlAbstraction().GetVertexShaderPrefix() + precompiledShader->vertexPrefix[i].data() + precompiledShader->vertexShader.data());
584 auto fragmentShader = std::string(graphics.GetController().GetGlAbstraction().GetFragmentShaderPrefix() + precompiledShader->fragmentPrefix[i].data() + precompiledShader->fragmentShader.data());
585 mCore.PreCompileShader(vertexShader.data(), fragmentShader.data());
587 DALI_LOG_RELEASE_INFO("ShaderPrecompiler[ENABLE], shader count :%d \n",numberOfPrecomipledShader);
592 DALI_LOG_RELEASE_INFO("ShaderPrecompiler[DISABLE] \n");
595 while(UpdateRenderReady(useElapsedTime, updateRequired, timeToSleepUntil))
597 LOG_UPDATE_RENDER_TRACE;
600 bool uploadOnly = mUploadWithoutRendering;
601 unsigned int surfaceResized = mSurfaceResized;
602 Dali::RenderSurfaceInterface* deletedSurface = ShouldSurfaceBeDeleted();
604 // Performance statistics are logged upon a VSYNC tick so use this point for a VSync marker
605 AddPerformanceMarker(PerformanceInterface::VSYNC);
607 uint64_t currentFrameStartTime = 0;
608 TimeService::GetNanoseconds(currentFrameStartTime);
610 uint64_t timeSinceLastFrame = currentFrameStartTime - lastFrameTime;
612 // Optional FPS Tracking when continuously rendering
613 if(useElapsedTime && mFpsTracker.Enabled())
615 float absoluteTimeSinceLastRender = timeSinceLastFrame * NANOSECONDS_TO_SECOND;
616 mFpsTracker.Track(absoluteTimeSinceLastRender);
619 lastFrameTime = currentFrameStartTime; // Store frame start time
621 //////////////////////////////
623 //////////////////////////////
625 Dali::RenderSurfaceInterface* newSurface = ShouldSurfaceBeReplaced();
626 if(DALI_UNLIKELY(newSurface))
628 LOG_UPDATE_RENDER_TRACE_FMT("Replacing Surface");
629 // This is designed for replacing pixmap surfaces, but should work for window as well
630 // we need to delete the surface and renderable (pixmap / window)
631 // Then create a new pixmap/window and new surface
632 // If the new surface has a different display connection, then the context will be lost
633 mAdaptorInterfaces.GetDisplayConnectionInterface().Initialize();
634 graphics.ActivateSurfaceContext(newSurface);
635 // TODO: ReplaceGraphicsSurface doesn't work, InitializeGraphics()
636 // already creates new surface window, the surface and the context.
637 // We probably don't need ReplaceGraphicsSurface at all.
638 // newSurface->ReplaceGraphicsSurface();
642 //////////////////////////////
643 // TextureUploadRequest (phase #1)
644 //////////////////////////////
646 // Upload requested resources after resource context activated.
647 graphics.ActivateResourceContext();
649 const bool textureUploaded = mTextureUploadManager.ResourceUpload();
651 // Update & Render forcely if there exist some uploaded texture.
652 uploadOnly = textureUploaded ? false : uploadOnly;
654 const bool isRenderingToFbo = renderToFboEnabled && ((0u == frameCount) || (0u != frameCount % renderToFboInterval));
657 //////////////////////////////
659 //////////////////////////////
661 const uint32_t currentTime = static_cast<uint32_t>(currentFrameStartTime / NANOSECONDS_PER_MILLISECOND);
662 const uint32_t nextFrameTime = currentTime + static_cast<uint32_t>(mDefaultFrameDurationMilliseconds);
664 uint64_t noOfFramesSinceLastUpdate = 1;
665 float frameDelta = 0.0f;
668 if(mThreadMode == ThreadMode::RUN_IF_REQUESTED)
670 extraFramesDropped = 0;
671 while(timeSinceLastFrame >= mDefaultFrameDurationNanoseconds)
673 timeSinceLastFrame -= mDefaultFrameDurationNanoseconds;
674 extraFramesDropped++;
678 // If using the elapsed time, then calculate frameDelta as a multiple of mDefaultFrameDelta
679 noOfFramesSinceLastUpdate += extraFramesDropped;
681 frameDelta = mDefaultFrameDelta * noOfFramesSinceLastUpdate;
683 LOG_UPDATE_RENDER("timeSinceLastFrame(%llu) noOfFramesSinceLastUpdate(%u) frameDelta(%.6f)", timeSinceLastFrame, noOfFramesSinceLastUpdate, frameDelta);
685 Integration::UpdateStatus updateStatus;
687 AddPerformanceMarker(PerformanceInterface::UPDATE_START);
688 TRACE_UPDATE_RENDER_BEGIN("DALI_UPDATE");
689 mCore.Update(frameDelta,
696 TRACE_UPDATE_RENDER_END("DALI_UPDATE");
697 AddPerformanceMarker(PerformanceInterface::UPDATE_END);
699 unsigned int keepUpdatingStatus = updateStatus.KeepUpdating();
701 // Tell the event-thread to wake up (if asleep) and send a notification event to Core if required
702 if(updateStatus.NeedsNotification())
704 mNotificationTrigger.Trigger();
705 LOG_UPDATE_RENDER("Notification Triggered");
708 // Optional logging of update/render status
709 mUpdateStatusLogger.Log(keepUpdatingStatus);
711 //////////////////////////////
713 //////////////////////////////
715 graphics.FrameStart();
716 mAdaptorInterfaces.GetDisplayConnectionInterface().ConsumeEvents();
718 if(mPreRenderCallback != NULL)
720 bool keepCallback = CallbackBase::ExecuteReturn<bool>(*mPreRenderCallback);
723 delete mPreRenderCallback;
724 mPreRenderCallback = NULL;
728 //////////////////////////////
729 // TextureUploadRequest (phase #2)
730 //////////////////////////////
732 // Upload requested resources after resource context activated.
733 graphics.ActivateResourceContext();
735 // Since uploadOnly value used at Update side, we should not change uploadOnly value now even some textures are uploaded.
736 mTextureUploadManager.ResourceUpload();
738 if(mFirstFrameAfterResume)
740 // mFirstFrameAfterResume is set to true when the thread is resumed
741 // Let graphics know the first frame after thread initialized or resumed.
742 graphics.SetFirstFrameAfterResume();
743 mFirstFrameAfterResume = FALSE;
746 Integration::RenderStatus renderStatus;
748 AddPerformanceMarker(PerformanceInterface::RENDER_START);
749 TRACE_UPDATE_RENDER_BEGIN("DALI_RENDER");
751 // Upload shared resources
752 TRACE_UPDATE_RENDER_BEGIN("DALI_PRE_RENDER");
753 mCore.PreRender(renderStatus, mForceClear);
754 TRACE_UPDATE_RENDER_END("DALI_PRE_RENDER");
756 if(!uploadOnly || surfaceResized)
758 // Go through each window
760 mAdaptorInterfaces.GetWindowContainerInterface(windows);
762 for(auto&& window : windows)
764 Dali::Integration::Scene scene = window->GetScene();
765 Dali::RenderSurfaceInterface* windowSurface = window->GetSurface();
767 if(scene && windowSurface)
769 TRACE_UPDATE_RENDER_BEGIN("DALI_RENDER_SCENE");
770 Integration::RenderStatus windowRenderStatus;
772 const bool sceneSurfaceResized = scene.IsSurfaceRectChanged();
774 // clear previous frame damaged render items rects, buffer history is tracked on surface level
775 mDamagedRects.clear();
777 // Collect damage rects
778 mCore.PreRender(scene, mDamagedRects);
780 // Render off-screen frame buffers first if any
781 mCore.RenderScene(windowRenderStatus, scene, true);
783 Rect<int> clippingRect; // Empty for fbo rendering
785 // Switch to the context of the surface, merge damaged areas for previous frames
786 windowSurface->PreRender(sceneSurfaceResized, mDamagedRects, clippingRect); // Switch GL context
788 // Render the surface
789 mCore.RenderScene(windowRenderStatus, scene, false, clippingRect);
791 // Buffer swapping now happens when the surface render target is presented.
793 // If surface is resized, the surface resized count is decreased.
794 if(DALI_UNLIKELY(sceneSurfaceResized))
798 TRACE_UPDATE_RENDER_END("DALI_RENDER_SCENE");
803 TRACE_UPDATE_RENDER_BEGIN("DALI_POST_RENDER");
806 graphics.PostRender();
810 TRACE_UPDATE_RENDER_END("DALI_POST_RENDER");
812 //////////////////////////////
814 //////////////////////////////
815 if(DALI_UNLIKELY(deletedSurface))
817 LOG_UPDATE_RENDER_TRACE_FMT("Deleting Surface");
819 deletedSurface->DestroySurface();
824 TRACE_UPDATE_RENDER_END("DALI_RENDER");
825 AddPerformanceMarker(PerformanceInterface::RENDER_END);
827 // if the memory pool interval is set and has elapsed, log the graphics memory pools
828 if(0 < memPoolInterval && memPoolInterval < lastFrameTime - lastMemPoolLogTime)
830 lastMemPoolLogTime = lastFrameTime;
831 graphics.LogMemoryPools();
836 // Trigger event thread to request Update/Render thread to sleep if update not required
837 if((Integration::KeepUpdating::NOT_REQUESTED == keepUpdatingStatus) && !renderStatus.NeedsUpdate())
839 mSleepTrigger->Trigger();
840 updateRequired = false;
841 LOG_UPDATE_RENDER("Sleep Triggered");
845 updateRequired = true;
848 //////////////////////////////
850 //////////////////////////////
852 extraFramesDropped = 0;
854 if(timeToSleepUntil == 0)
856 // If this is the first frame after the thread is initialized or resumed, we
857 // use the actual time the current frame starts from to calculate the time to
858 // sleep until the next frame.
859 timeToSleepUntil = currentFrameStartTime + mDefaultFrameDurationNanoseconds;
863 // Otherwise, always use the sleep-until time calculated in the last frame to
864 // calculate the time to sleep until the next frame. In this way, if there is
865 // any time gap between the current frame and the next frame, or if update or
866 // rendering in the current frame takes too much time so that the specified
867 // sleep-until time has already passed, it will try to keep the frames syncing
868 // by shortening the duration of the next frame.
869 timeToSleepUntil += mDefaultFrameDurationNanoseconds;
871 // Check the current time at the end of the frame
872 uint64_t currentFrameEndTime = 0;
873 TimeService::GetNanoseconds(currentFrameEndTime);
874 while(currentFrameEndTime > timeToSleepUntil + mDefaultFrameDurationNanoseconds)
876 // We are more than one frame behind already, so just drop the next frames
877 // until the sleep-until time is later than the current time so that we can
879 timeToSleepUntil += mDefaultFrameDurationNanoseconds;
880 extraFramesDropped++;
884 // Render to FBO is intended to measure fps above 60 so sleep is not wanted.
885 if(0u == renderToFboInterval)
887 // Sleep until at least the the default frame duration has elapsed. This will return immediately if the specified end-time has already passed.
888 TimeService::SleepUntil(timeToSleepUntil);
892 // Inform core of context destruction
893 mCore.ContextDestroyed();
896 mAdaptorInterfaces.GetWindowContainerInterface(windows);
899 for(auto&& window : windows)
901 Dali::RenderSurfaceInterface* surface = window->GetSurface();
902 surface->DestroySurface();
907 LOG_UPDATE_RENDER("THREAD DESTROYED");
909 // Uninstall the logging function
910 mEnvironmentOptions.UnInstallLogFunction();
913 bool CombinedUpdateRenderController::UpdateRenderReady(bool& useElapsedTime, bool updateRequired, uint64_t& timeToSleepUntil)
915 useElapsedTime = true;
917 ConditionalWait::ScopedLock updateLock(mUpdateRenderThreadWaitCondition);
918 while((!mUpdateRenderRunCount || // Should try to wait if event-thread has paused the Update/Render thread
919 (mUpdateRenderThreadCanSleep && !updateRequired && !mPendingRequestUpdate)) && // Ensure we wait if we're supposed to be sleeping AND do not require another update
920 !mDestroyUpdateRenderThread && // Ensure we don't wait if the update-render-thread is supposed to be destroyed
921 !mNewSurface && // Ensure we don't wait if we need to replace the surface
922 !mDeletedSurface && // Ensure we don't wait if we need to delete the surface
923 !mSurfaceResized) // Ensure we don't wait if we need to resize the surface
925 LOG_UPDATE_RENDER("WAIT: mUpdateRenderRunCount: %d", mUpdateRenderRunCount);
926 LOG_UPDATE_RENDER(" mUpdateRenderThreadCanSleep: %d, updateRequired: %d, mPendingRequestUpdate: %d", mUpdateRenderThreadCanSleep, updateRequired, mPendingRequestUpdate);
927 LOG_UPDATE_RENDER(" mDestroyUpdateRenderThread: %d", mDestroyUpdateRenderThread);
928 LOG_UPDATE_RENDER(" mNewSurface: %d", mNewSurface);
929 LOG_UPDATE_RENDER(" mDeletedSurface: %d", mDeletedSurface);
930 LOG_UPDATE_RENDER(" mSurfaceResized: %d", mSurfaceResized);
932 // Reset the time when the thread is waiting, so the sleep-until time for
933 // the first frame after resuming should be based on the actual start time
934 // of the first frame.
935 timeToSleepUntil = 0;
937 mUpdateRenderThreadWaitCondition.Wait(updateLock);
939 if(!mUseElapsedTimeAfterWait)
941 useElapsedTime = false;
945 LOG_COUNTER_UPDATE_RENDER("mUpdateRenderRunCount: %d", mUpdateRenderRunCount);
946 LOG_COUNTER_UPDATE_RENDER("mUpdateRenderThreadCanSleep: %d, updateRequired: %d, mPendingRequestUpdate: %d", mUpdateRenderThreadCanSleep, updateRequired, mPendingRequestUpdate);
947 LOG_COUNTER_UPDATE_RENDER("mDestroyUpdateRenderThread: %d", mDestroyUpdateRenderThread);
948 LOG_COUNTER_UPDATE_RENDER("mNewSurface: %d", mNewSurface);
949 LOG_COUNTER_UPDATE_RENDER("mDeletedSurface: %d", mDeletedSurface);
950 LOG_COUNTER_UPDATE_RENDER("mSurfaceResized: %d", mSurfaceResized);
952 mUseElapsedTimeAfterWait = FALSE;
953 mUpdateRenderThreadCanSleep = FALSE;
954 mPendingRequestUpdate = FALSE;
956 // If we've been asked to run Update/Render cycles a finite number of times then decrement so we wait after the
957 // requested number of cycles
958 if(mUpdateRenderRunCount > 0)
960 --mUpdateRenderRunCount;
963 // Keep the update-render thread alive if this thread is NOT to be destroyed
964 return !mDestroyUpdateRenderThread;
967 Dali::RenderSurfaceInterface* CombinedUpdateRenderController::ShouldSurfaceBeReplaced()
969 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
971 Dali::RenderSurfaceInterface* newSurface = mNewSurface;
977 void CombinedUpdateRenderController::SurfaceReplaced()
979 // Just increment the semaphore
980 mSurfaceSemaphore.Release(1);
983 Dali::RenderSurfaceInterface* CombinedUpdateRenderController::ShouldSurfaceBeDeleted()
985 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
987 Dali::RenderSurfaceInterface* deletedSurface = mDeletedSurface;
988 mDeletedSurface = NULL;
990 return deletedSurface;
993 void CombinedUpdateRenderController::SurfaceDeleted()
995 // Just increment the semaphore
996 mSurfaceSemaphore.Release(1);
999 void CombinedUpdateRenderController::SurfaceResized()
1001 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
1008 ///////////////////////////////////////////////////////////////////////////////////////////////////
1010 ///////////////////////////////////////////////////////////////////////////////////////////////////
1012 void CombinedUpdateRenderController::NotifyThreadInitialised()
1014 // Just increment the semaphore
1015 mEventThreadSemaphore.Release(1);
1018 void CombinedUpdateRenderController::NotifyGraphicsInitialised()
1020 mGraphicsInitializeWait.Notify();
1023 void CombinedUpdateRenderController::AddPerformanceMarker(PerformanceInterface::MarkerType type)
1025 if(mPerformanceInterface)
1027 mPerformanceInterface->AddMarker(type);
1031 /////////////////////////////////////////////////////////////////////////////////////////////////
1032 // POST RENDERING: EVENT THREAD
1033 /////////////////////////////////////////////////////////////////////////////////////////////////
1035 void CombinedUpdateRenderController::PostRenderComplete()
1037 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
1038 mPostRendering = FALSE;
1039 mUpdateRenderThreadWaitCondition.Notify(lock);
1042 ///////////////////////////////////////////////////////////////////////////////////////////////////
1043 // POST RENDERING: RENDER THREAD
1044 ///////////////////////////////////////////////////////////////////////////////////////////////////
1046 void CombinedUpdateRenderController::PostRenderStarted()
1048 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
1049 mPostRendering = TRUE;
1052 void CombinedUpdateRenderController::PostRenderWaitForCompletion()
1054 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
1055 while(mPostRendering &&
1056 !mNewSurface && // We should NOT wait if we're replacing the surface
1057 !mDeletedSurface && // We should NOT wait if we're deleting the surface
1058 !mDestroyUpdateRenderThread)
1060 mUpdateRenderThreadWaitCondition.Wait(lock);
1064 } // namespace Adaptor
1066 } // namespace Internal