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 RawShaderData precompiledShader;
576 Integration::ShaderPrecompiler::Get().GetPrecompileShaderList(precompiledShader);
577 auto numberOfPrecomipledShader = precompiledShader.shaderCount;
578 for(int i= 0; i<numberOfPrecomipledShader; ++i)
580 auto vertexShader = std::string(graphics.GetController().GetGlAbstraction().GetVertexShaderPrefix() + precompiledShader.vertexPrefix[i].data() + precompiledShader.vertexShader.data());
581 auto fragmentShader = std::string(graphics.GetController().GetGlAbstraction().GetFragmentShaderPrefix() + precompiledShader.fragmentPrefix[i].data() + precompiledShader.fragmentShader.data());
582 mCore.PreCompileShader(vertexShader.data(), fragmentShader.data());
584 DALI_LOG_RELEASE_INFO("ShaderPrecompiler[ENABLE], shader :%d \n",numberOfPrecomipledShader);
588 DALI_LOG_RELEASE_INFO("ShaderPrecompiler[DISABLE] \n");
591 while(UpdateRenderReady(useElapsedTime, updateRequired, timeToSleepUntil))
593 LOG_UPDATE_RENDER_TRACE;
596 bool uploadOnly = mUploadWithoutRendering;
597 unsigned int surfaceResized = mSurfaceResized;
598 Dali::RenderSurfaceInterface* deletedSurface = ShouldSurfaceBeDeleted();
600 // Performance statistics are logged upon a VSYNC tick so use this point for a VSync marker
601 AddPerformanceMarker(PerformanceInterface::VSYNC);
603 uint64_t currentFrameStartTime = 0;
604 TimeService::GetNanoseconds(currentFrameStartTime);
606 uint64_t timeSinceLastFrame = currentFrameStartTime - lastFrameTime;
608 // Optional FPS Tracking when continuously rendering
609 if(useElapsedTime && mFpsTracker.Enabled())
611 float absoluteTimeSinceLastRender = timeSinceLastFrame * NANOSECONDS_TO_SECOND;
612 mFpsTracker.Track(absoluteTimeSinceLastRender);
615 lastFrameTime = currentFrameStartTime; // Store frame start time
617 //////////////////////////////
619 //////////////////////////////
621 Dali::RenderSurfaceInterface* newSurface = ShouldSurfaceBeReplaced();
622 if(DALI_UNLIKELY(newSurface))
624 LOG_UPDATE_RENDER_TRACE_FMT("Replacing Surface");
625 // This is designed for replacing pixmap surfaces, but should work for window as well
626 // we need to delete the surface and renderable (pixmap / window)
627 // Then create a new pixmap/window and new surface
628 // If the new surface has a different display connection, then the context will be lost
629 mAdaptorInterfaces.GetDisplayConnectionInterface().Initialize();
630 graphics.ActivateSurfaceContext(newSurface);
631 // TODO: ReplaceGraphicsSurface doesn't work, InitializeGraphics()
632 // already creates new surface window, the surface and the context.
633 // We probably don't need ReplaceGraphicsSurface at all.
634 // newSurface->ReplaceGraphicsSurface();
638 //////////////////////////////
639 // TextureUploadRequest (phase #1)
640 //////////////////////////////
642 // Upload requested resources after resource context activated.
643 graphics.ActivateResourceContext();
645 const bool textureUploaded = mTextureUploadManager.ResourceUpload();
647 // Update & Render forcely if there exist some uploaded texture.
648 uploadOnly = textureUploaded ? false : uploadOnly;
650 const bool isRenderingToFbo = renderToFboEnabled && ((0u == frameCount) || (0u != frameCount % renderToFboInterval));
653 //////////////////////////////
655 //////////////////////////////
657 const uint32_t currentTime = static_cast<uint32_t>(currentFrameStartTime / NANOSECONDS_PER_MILLISECOND);
658 const uint32_t nextFrameTime = currentTime + static_cast<uint32_t>(mDefaultFrameDurationMilliseconds);
660 uint64_t noOfFramesSinceLastUpdate = 1;
661 float frameDelta = 0.0f;
664 if(mThreadMode == ThreadMode::RUN_IF_REQUESTED)
666 extraFramesDropped = 0;
667 while(timeSinceLastFrame >= mDefaultFrameDurationNanoseconds)
669 timeSinceLastFrame -= mDefaultFrameDurationNanoseconds;
670 extraFramesDropped++;
674 // If using the elapsed time, then calculate frameDelta as a multiple of mDefaultFrameDelta
675 noOfFramesSinceLastUpdate += extraFramesDropped;
677 frameDelta = mDefaultFrameDelta * noOfFramesSinceLastUpdate;
679 LOG_UPDATE_RENDER("timeSinceLastFrame(%llu) noOfFramesSinceLastUpdate(%u) frameDelta(%.6f)", timeSinceLastFrame, noOfFramesSinceLastUpdate, frameDelta);
681 Integration::UpdateStatus updateStatus;
683 AddPerformanceMarker(PerformanceInterface::UPDATE_START);
684 TRACE_UPDATE_RENDER_BEGIN("DALI_UPDATE");
685 mCore.Update(frameDelta,
692 TRACE_UPDATE_RENDER_END("DALI_UPDATE");
693 AddPerformanceMarker(PerformanceInterface::UPDATE_END);
695 unsigned int keepUpdatingStatus = updateStatus.KeepUpdating();
697 // Tell the event-thread to wake up (if asleep) and send a notification event to Core if required
698 if(updateStatus.NeedsNotification())
700 mNotificationTrigger.Trigger();
701 LOG_UPDATE_RENDER("Notification Triggered");
704 // Optional logging of update/render status
705 mUpdateStatusLogger.Log(keepUpdatingStatus);
707 //////////////////////////////
709 //////////////////////////////
711 graphics.FrameStart();
712 mAdaptorInterfaces.GetDisplayConnectionInterface().ConsumeEvents();
714 if(mPreRenderCallback != NULL)
716 bool keepCallback = CallbackBase::ExecuteReturn<bool>(*mPreRenderCallback);
719 delete mPreRenderCallback;
720 mPreRenderCallback = NULL;
724 //////////////////////////////
725 // TextureUploadRequest (phase #2)
726 //////////////////////////////
728 // Upload requested resources after resource context activated.
729 graphics.ActivateResourceContext();
731 // Since uploadOnly value used at Update side, we should not change uploadOnly value now even some textures are uploaded.
732 mTextureUploadManager.ResourceUpload();
734 if(mFirstFrameAfterResume)
736 // mFirstFrameAfterResume is set to true when the thread is resumed
737 // Let graphics know the first frame after thread initialized or resumed.
738 graphics.SetFirstFrameAfterResume();
739 mFirstFrameAfterResume = FALSE;
742 Integration::RenderStatus renderStatus;
744 AddPerformanceMarker(PerformanceInterface::RENDER_START);
745 TRACE_UPDATE_RENDER_BEGIN("DALI_RENDER");
747 // Upload shared resources
748 TRACE_UPDATE_RENDER_BEGIN("DALI_PRE_RENDER");
749 mCore.PreRender(renderStatus, mForceClear);
750 TRACE_UPDATE_RENDER_END("DALI_PRE_RENDER");
752 if(!uploadOnly || surfaceResized)
754 // Go through each window
756 mAdaptorInterfaces.GetWindowContainerInterface(windows);
758 for(auto&& window : windows)
760 Dali::Integration::Scene scene = window->GetScene();
761 Dali::RenderSurfaceInterface* windowSurface = window->GetSurface();
763 if(scene && windowSurface)
765 TRACE_UPDATE_RENDER_BEGIN("DALI_RENDER_SCENE");
766 Integration::RenderStatus windowRenderStatus;
768 const bool sceneSurfaceResized = scene.IsSurfaceRectChanged();
770 // clear previous frame damaged render items rects, buffer history is tracked on surface level
771 mDamagedRects.clear();
773 // Collect damage rects
774 mCore.PreRender(scene, mDamagedRects);
776 // Render off-screen frame buffers first if any
777 mCore.RenderScene(windowRenderStatus, scene, true);
779 Rect<int> clippingRect; // Empty for fbo rendering
781 // Switch to the context of the surface, merge damaged areas for previous frames
782 windowSurface->PreRender(sceneSurfaceResized, mDamagedRects, clippingRect); // Switch GL context
784 // Render the surface
785 mCore.RenderScene(windowRenderStatus, scene, false, clippingRect);
787 // Buffer swapping now happens when the surface render target is presented.
789 // If surface is resized, the surface resized count is decreased.
790 if(DALI_UNLIKELY(sceneSurfaceResized))
794 TRACE_UPDATE_RENDER_END("DALI_RENDER_SCENE");
799 TRACE_UPDATE_RENDER_BEGIN("DALI_POST_RENDER");
802 graphics.PostRender();
806 TRACE_UPDATE_RENDER_END("DALI_POST_RENDER");
808 //////////////////////////////
810 //////////////////////////////
811 if(DALI_UNLIKELY(deletedSurface))
813 LOG_UPDATE_RENDER_TRACE_FMT("Deleting Surface");
815 deletedSurface->DestroySurface();
820 TRACE_UPDATE_RENDER_END("DALI_RENDER");
821 AddPerformanceMarker(PerformanceInterface::RENDER_END);
823 // if the memory pool interval is set and has elapsed, log the graphics memory pools
824 if(0 < memPoolInterval && memPoolInterval < lastFrameTime - lastMemPoolLogTime)
826 lastMemPoolLogTime = lastFrameTime;
827 graphics.LogMemoryPools();
832 // Trigger event thread to request Update/Render thread to sleep if update not required
833 if((Integration::KeepUpdating::NOT_REQUESTED == keepUpdatingStatus) && !renderStatus.NeedsUpdate())
835 mSleepTrigger->Trigger();
836 updateRequired = false;
837 LOG_UPDATE_RENDER("Sleep Triggered");
841 updateRequired = true;
844 //////////////////////////////
846 //////////////////////////////
848 extraFramesDropped = 0;
850 if(timeToSleepUntil == 0)
852 // If this is the first frame after the thread is initialized or resumed, we
853 // use the actual time the current frame starts from to calculate the time to
854 // sleep until the next frame.
855 timeToSleepUntil = currentFrameStartTime + mDefaultFrameDurationNanoseconds;
859 // Otherwise, always use the sleep-until time calculated in the last frame to
860 // calculate the time to sleep until the next frame. In this way, if there is
861 // any time gap between the current frame and the next frame, or if update or
862 // rendering in the current frame takes too much time so that the specified
863 // sleep-until time has already passed, it will try to keep the frames syncing
864 // by shortening the duration of the next frame.
865 timeToSleepUntil += mDefaultFrameDurationNanoseconds;
867 // Check the current time at the end of the frame
868 uint64_t currentFrameEndTime = 0;
869 TimeService::GetNanoseconds(currentFrameEndTime);
870 while(currentFrameEndTime > timeToSleepUntil + mDefaultFrameDurationNanoseconds)
872 // We are more than one frame behind already, so just drop the next frames
873 // until the sleep-until time is later than the current time so that we can
875 timeToSleepUntil += mDefaultFrameDurationNanoseconds;
876 extraFramesDropped++;
880 // Render to FBO is intended to measure fps above 60 so sleep is not wanted.
881 if(0u == renderToFboInterval)
883 // Sleep until at least the the default frame duration has elapsed. This will return immediately if the specified end-time has already passed.
884 TimeService::SleepUntil(timeToSleepUntil);
888 // Inform core of context destruction
889 mCore.ContextDestroyed();
892 mAdaptorInterfaces.GetWindowContainerInterface(windows);
895 for(auto&& window : windows)
897 Dali::RenderSurfaceInterface* surface = window->GetSurface();
898 surface->DestroySurface();
903 LOG_UPDATE_RENDER("THREAD DESTROYED");
905 // Uninstall the logging function
906 mEnvironmentOptions.UnInstallLogFunction();
909 bool CombinedUpdateRenderController::UpdateRenderReady(bool& useElapsedTime, bool updateRequired, uint64_t& timeToSleepUntil)
911 useElapsedTime = true;
913 ConditionalWait::ScopedLock updateLock(mUpdateRenderThreadWaitCondition);
914 while((!mUpdateRenderRunCount || // Should try to wait if event-thread has paused the Update/Render thread
915 (mUpdateRenderThreadCanSleep && !updateRequired && !mPendingRequestUpdate)) && // Ensure we wait if we're supposed to be sleeping AND do not require another update
916 !mDestroyUpdateRenderThread && // Ensure we don't wait if the update-render-thread is supposed to be destroyed
917 !mNewSurface && // Ensure we don't wait if we need to replace the surface
918 !mDeletedSurface && // Ensure we don't wait if we need to delete the surface
919 !mSurfaceResized) // Ensure we don't wait if we need to resize the surface
921 LOG_UPDATE_RENDER("WAIT: mUpdateRenderRunCount: %d", mUpdateRenderRunCount);
922 LOG_UPDATE_RENDER(" mUpdateRenderThreadCanSleep: %d, updateRequired: %d, mPendingRequestUpdate: %d", mUpdateRenderThreadCanSleep, updateRequired, mPendingRequestUpdate);
923 LOG_UPDATE_RENDER(" mDestroyUpdateRenderThread: %d", mDestroyUpdateRenderThread);
924 LOG_UPDATE_RENDER(" mNewSurface: %d", mNewSurface);
925 LOG_UPDATE_RENDER(" mDeletedSurface: %d", mDeletedSurface);
926 LOG_UPDATE_RENDER(" mSurfaceResized: %d", mSurfaceResized);
928 // Reset the time when the thread is waiting, so the sleep-until time for
929 // the first frame after resuming should be based on the actual start time
930 // of the first frame.
931 timeToSleepUntil = 0;
933 mUpdateRenderThreadWaitCondition.Wait(updateLock);
935 if(!mUseElapsedTimeAfterWait)
937 useElapsedTime = false;
941 LOG_COUNTER_UPDATE_RENDER("mUpdateRenderRunCount: %d", mUpdateRenderRunCount);
942 LOG_COUNTER_UPDATE_RENDER("mUpdateRenderThreadCanSleep: %d, updateRequired: %d, mPendingRequestUpdate: %d", mUpdateRenderThreadCanSleep, updateRequired, mPendingRequestUpdate);
943 LOG_COUNTER_UPDATE_RENDER("mDestroyUpdateRenderThread: %d", mDestroyUpdateRenderThread);
944 LOG_COUNTER_UPDATE_RENDER("mNewSurface: %d", mNewSurface);
945 LOG_COUNTER_UPDATE_RENDER("mDeletedSurface: %d", mDeletedSurface);
946 LOG_COUNTER_UPDATE_RENDER("mSurfaceResized: %d", mSurfaceResized);
948 mUseElapsedTimeAfterWait = FALSE;
949 mUpdateRenderThreadCanSleep = FALSE;
950 mPendingRequestUpdate = FALSE;
952 // If we've been asked to run Update/Render cycles a finite number of times then decrement so we wait after the
953 // requested number of cycles
954 if(mUpdateRenderRunCount > 0)
956 --mUpdateRenderRunCount;
959 // Keep the update-render thread alive if this thread is NOT to be destroyed
960 return !mDestroyUpdateRenderThread;
963 Dali::RenderSurfaceInterface* CombinedUpdateRenderController::ShouldSurfaceBeReplaced()
965 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
967 Dali::RenderSurfaceInterface* newSurface = mNewSurface;
973 void CombinedUpdateRenderController::SurfaceReplaced()
975 // Just increment the semaphore
976 mSurfaceSemaphore.Release(1);
979 Dali::RenderSurfaceInterface* CombinedUpdateRenderController::ShouldSurfaceBeDeleted()
981 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
983 Dali::RenderSurfaceInterface* deletedSurface = mDeletedSurface;
984 mDeletedSurface = NULL;
986 return deletedSurface;
989 void CombinedUpdateRenderController::SurfaceDeleted()
991 // Just increment the semaphore
992 mSurfaceSemaphore.Release(1);
995 void CombinedUpdateRenderController::SurfaceResized()
997 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
1004 ///////////////////////////////////////////////////////////////////////////////////////////////////
1006 ///////////////////////////////////////////////////////////////////////////////////////////////////
1008 void CombinedUpdateRenderController::NotifyThreadInitialised()
1010 // Just increment the semaphore
1011 mEventThreadSemaphore.Release(1);
1014 void CombinedUpdateRenderController::NotifyGraphicsInitialised()
1016 mGraphicsInitializeWait.Notify();
1019 void CombinedUpdateRenderController::AddPerformanceMarker(PerformanceInterface::MarkerType type)
1021 if(mPerformanceInterface)
1023 mPerformanceInterface->AddMarker(type);
1027 /////////////////////////////////////////////////////////////////////////////////////////////////
1028 // POST RENDERING: EVENT THREAD
1029 /////////////////////////////////////////////////////////////////////////////////////////////////
1031 void CombinedUpdateRenderController::PostRenderComplete()
1033 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
1034 mPostRendering = FALSE;
1035 mUpdateRenderThreadWaitCondition.Notify(lock);
1038 ///////////////////////////////////////////////////////////////////////////////////////////////////
1039 // POST RENDERING: RENDER THREAD
1040 ///////////////////////////////////////////////////////////////////////////////////////////////////
1042 void CombinedUpdateRenderController::PostRenderStarted()
1044 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
1045 mPostRendering = TRUE;
1048 void CombinedUpdateRenderController::PostRenderWaitForCompletion()
1050 ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
1051 while(mPostRendering &&
1052 !mNewSurface && // We should NOT wait if we're replacing the surface
1053 !mDeletedSurface && // We should NOT wait if we're deleting the surface
1054 !mDestroyUpdateRenderThread)
1056 mUpdateRenderThreadWaitCondition.Wait(lock);
1060 } // namespace Adaptor
1062 } // namespace Internal