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