Revert "Make another ConditionalWait for post-rendearing"
[platform/core/uifw/dali-adaptor.git] / dali / internal / adaptor / common / combined-update-render-controller.cpp
1 /*
2  * Copyright (c) 2022 Samsung Electronics Co., Ltd.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  *
16  */
17
18 // CLASS HEADER
19 #include <dali/internal/adaptor/common/combined-update-render-controller.h>
20
21 // EXTERNAL INCLUDES
22 #include <dali/integration-api/platform-abstraction.h>
23 #include <errno.h>
24 #include <unistd.h>
25 #include "dali/public-api/common/dali-common.h"
26
27 // INTERNAL INCLUDES
28 #include <dali/integration-api/adaptor-framework/trigger-event-factory.h>
29 #include <dali/internal/adaptor/common/adaptor-internal-services.h>
30 #include <dali/internal/adaptor/common/combined-update-render-controller-debug.h>
31 #include <dali/internal/graphics/common/graphics-interface.h>
32 #include <dali/internal/graphics/gles/egl-graphics.h>
33 #include <dali/internal/system/common/environment-options.h>
34 #include <dali/internal/system/common/time-service.h>
35 #include <dali/internal/thread/common/thread-settings-impl.h>
36 #include <dali/internal/window-system/common/window-impl.h>
37
38 namespace Dali
39 {
40 namespace Internal
41 {
42 namespace Adaptor
43 {
44 namespace
45 {
46 const unsigned int CREATED_THREAD_COUNT = 1u;
47
48 const int CONTINUOUS = -1;
49 const int ONCE       = 1;
50
51 const unsigned int TRUE  = 1u;
52 const unsigned int FALSE = 0u;
53
54 const unsigned int MILLISECONDS_PER_SECOND(1e+3);
55 const float        NANOSECONDS_TO_SECOND(1e-9f);
56 const unsigned int NANOSECONDS_PER_SECOND(1e+9);
57 const unsigned int NANOSECONDS_PER_MILLISECOND(1e+6);
58
59 // The following values will get calculated at compile time
60 const float    DEFAULT_FRAME_DURATION_IN_SECONDS(1.0f / 60.0f);
61 const uint64_t DEFAULT_FRAME_DURATION_IN_MILLISECONDS(DEFAULT_FRAME_DURATION_IN_SECONDS* MILLISECONDS_PER_SECOND);
62 const uint64_t DEFAULT_FRAME_DURATION_IN_NANOSECONDS(DEFAULT_FRAME_DURATION_IN_SECONDS* NANOSECONDS_PER_SECOND);
63
64 /**
65  * 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
66  * there is a danger that, on the event-thread we could have:
67  *  1) An update-request where we do nothing as Update/Render thread still running.
68  *  2) Quickly followed by a sleep-request being handled where we pause the Update/Render Thread (even though we have an update to process).
69  *
70  * 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:
71  *  1) MAIN THREAD:           Update Request: COUNTER = 1
72  *  2) UPDATE/RENDER THREAD:  Do Update/Render, then no Updates required -> Sleep Trigger
73  *  3) MAIN THREAD:           Update Request: COUNTER = 2
74  *  4) MAIN THREAD:           Sleep Request:  COUNTER = 1 -> We do not sleep just yet
75  *
76  * Also ensures we preserve battery life by only doing ONE update when the above use case is not triggered.
77  *  1) MAIN THREAD:           Update Request: COUNTER = 1
78  *  2) UPDATE/RENDER THREAD:  Do Update/Render, then no Updates required -> Sleep Trigger
79  *  3) MAIN THREAD:           Sleep Request:  COUNTER = 0 -> Go to sleep
80  */
81 const unsigned int MAXIMUM_UPDATE_REQUESTS = 2;
82 } // unnamed namespace
83
84 ///////////////////////////////////////////////////////////////////////////////////////////////////
85 // EVENT THREAD
86 ///////////////////////////////////////////////////////////////////////////////////////////////////
87
88 CombinedUpdateRenderController::CombinedUpdateRenderController(AdaptorInternalServices& adaptorInterfaces, const EnvironmentOptions& environmentOptions, ThreadMode threadMode)
89 : mFpsTracker(environmentOptions),
90   mUpdateStatusLogger(environmentOptions),
91   mEventThreadSemaphore(0),
92   mSurfaceSemaphore(0),
93   mUpdateRenderThreadWaitCondition(),
94   mAdaptorInterfaces(adaptorInterfaces),
95   mPerformanceInterface(adaptorInterfaces.GetPerformanceInterface()),
96   mCore(adaptorInterfaces.GetCore()),
97   mEnvironmentOptions(environmentOptions),
98   mNotificationTrigger(adaptorInterfaces.GetProcessCoreEventsTrigger()),
99   mSleepTrigger(NULL),
100   mPreRenderCallback(NULL),
101   mUpdateRenderThread(NULL),
102   mDefaultFrameDelta(0.0f),
103   mDefaultFrameDurationMilliseconds(0u),
104   mDefaultFrameDurationNanoseconds(0u),
105   mDefaultHalfFrameNanoseconds(0u),
106   mUpdateRequestCount(0u),
107   mRunning(FALSE),
108   mThreadId(0),
109   mThreadMode(threadMode),
110   mUpdateRenderRunCount(0),
111   mDestroyUpdateRenderThread(FALSE),
112   mUpdateRenderThreadCanSleep(FALSE),
113   mPendingRequestUpdate(FALSE),
114   mUseElapsedTimeAfterWait(FALSE),
115   mNewSurface(NULL),
116   mDeletedSurface(nullptr),
117   mPostRendering(FALSE),
118   mSurfaceResized(0),
119   mForceClear(FALSE),
120   mUploadWithoutRendering(FALSE),
121   mFirstFrameAfterResume(FALSE)
122 {
123   LOG_EVENT_TRACE;
124
125   // Initialise frame delta/duration variables first
126   SetRenderRefreshRate(environmentOptions.GetRenderRefreshRate());
127
128   // Set the thread-synchronization interface on the render-surface
129   Dali::RenderSurfaceInterface* currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
130   if(currentSurface)
131   {
132     currentSurface->SetThreadSynchronization(*this);
133   }
134
135   mSleepTrigger = TriggerEventFactory::CreateTriggerEvent(MakeCallback(this, &CombinedUpdateRenderController::ProcessSleepRequest), TriggerEventInterface::KEEP_ALIVE_AFTER_TRIGGER);
136 }
137
138 CombinedUpdateRenderController::~CombinedUpdateRenderController()
139 {
140   LOG_EVENT_TRACE;
141
142   Stop();
143
144   delete mPreRenderCallback;
145   delete mSleepTrigger;
146 }
147
148 void CombinedUpdateRenderController::Initialize()
149 {
150   LOG_EVENT_TRACE;
151
152   // Ensure Update/Render Thread not already created
153   DALI_ASSERT_ALWAYS(!mUpdateRenderThread);
154
155   // Create Update/Render Thread
156   ConditionalWait::ScopedLock lock(mGraphicsInitializeWait);
157   mUpdateRenderThread = new pthread_t();
158   int error           = pthread_create(mUpdateRenderThread, NULL, InternalUpdateRenderThreadEntryFunc, this);
159   DALI_ASSERT_ALWAYS(!error && "Return code from pthread_create() when creating UpdateRenderThread");
160
161   // The Update/Render thread will now run and initialise the graphics interface etc. and will then wait for Start to be called
162   // When this function returns, the application initialisation on the event thread should occur
163 }
164
165 void CombinedUpdateRenderController::Start()
166 {
167   LOG_EVENT_TRACE;
168
169   DALI_ASSERT_ALWAYS(!mRunning && mUpdateRenderThread);
170
171   // Wait until all threads created in Initialise are up and running
172   for(unsigned int i = 0; i < CREATED_THREAD_COUNT; ++i)
173   {
174     mEventThreadSemaphore.Acquire();
175   }
176
177   mRunning = TRUE;
178
179   LOG_EVENT("Startup Complete, starting Update/Render Thread");
180
181   RunUpdateRenderThread(CONTINUOUS, AnimationProgression::NONE, UpdateMode::NORMAL);
182
183   Dali::RenderSurfaceInterface* currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
184   if(currentSurface)
185   {
186     currentSurface->StartRender();
187   }
188
189   DALI_LOG_RELEASE_INFO("CombinedUpdateRenderController::Start\n");
190 }
191
192 void CombinedUpdateRenderController::Pause()
193 {
194   LOG_EVENT_TRACE;
195
196   mRunning = FALSE;
197
198   PauseUpdateRenderThread();
199
200   AddPerformanceMarker(PerformanceInterface::PAUSED);
201
202   DALI_LOG_RELEASE_INFO("CombinedUpdateRenderController::Pause\n");
203 }
204
205 void CombinedUpdateRenderController::Resume()
206 {
207   LOG_EVENT_TRACE;
208
209   if(!mRunning && IsUpdateRenderThreadPaused())
210   {
211     LOG_EVENT("Resuming");
212
213     RunUpdateRenderThread(CONTINUOUS, AnimationProgression::USE_ELAPSED_TIME, UpdateMode::NORMAL);
214
215     AddPerformanceMarker(PerformanceInterface::RESUME);
216
217     mRunning               = TRUE;
218     mForceClear            = TRUE;
219     mFirstFrameAfterResume = TRUE;
220
221     DALI_LOG_RELEASE_INFO("CombinedUpdateRenderController::Resume\n");
222   }
223   else
224   {
225     DALI_LOG_RELEASE_INFO("CombinedUpdateRenderController::Resume: Already resumed [%d, %d, %d]\n", mRunning, mUpdateRenderRunCount, mUpdateRenderThreadCanSleep);
226   }
227 }
228
229 void CombinedUpdateRenderController::Stop()
230 {
231   LOG_EVENT_TRACE;
232
233   // Stop Rendering and the Update/Render Thread
234   Dali::RenderSurfaceInterface* currentSurface = mAdaptorInterfaces.GetRenderSurfaceInterface();
235   if(currentSurface)
236   {
237     currentSurface->StopRender();
238   }
239
240   StopUpdateRenderThread();
241
242   if(mUpdateRenderThread)
243   {
244     LOG_EVENT("Destroying UpdateRenderThread");
245
246     // wait for the thread to finish
247     pthread_join(*mUpdateRenderThread, NULL);
248
249     delete mUpdateRenderThread;
250     mUpdateRenderThread = NULL;
251   }
252
253   mRunning = FALSE;
254
255   DALI_LOG_RELEASE_INFO("CombinedUpdateRenderController::Stop\n");
256 }
257
258 void CombinedUpdateRenderController::RequestUpdate()
259 {
260   LOG_EVENT_TRACE;
261
262   // Increment the update-request count to the maximum
263   if(mUpdateRequestCount < MAXIMUM_UPDATE_REQUESTS)
264   {
265     ++mUpdateRequestCount;
266   }
267
268   if(mRunning && IsUpdateRenderThreadPaused())
269   {
270     LOG_EVENT("Processing");
271
272     RunUpdateRenderThread(CONTINUOUS, AnimationProgression::NONE, UpdateMode::NORMAL);
273   }
274
275   ConditionalWait::ScopedLock updateLock(mUpdateRenderThreadWaitCondition);
276   mPendingRequestUpdate = TRUE;
277 }
278
279 void CombinedUpdateRenderController::RequestUpdateOnce(UpdateMode updateMode)
280 {
281   // Increment the update-request count to the maximum
282   if(mUpdateRequestCount < MAXIMUM_UPDATE_REQUESTS)
283   {
284     ++mUpdateRequestCount;
285   }
286
287   if(IsUpdateRenderThreadPaused() || updateMode == UpdateMode::FORCE_RENDER)
288   {
289     LOG_EVENT_TRACE;
290
291     // Run Update/Render once
292     RunUpdateRenderThread(ONCE, AnimationProgression::NONE, updateMode);
293   }
294 }
295
296 void CombinedUpdateRenderController::ReplaceSurface(Dali::RenderSurfaceInterface* newSurface)
297 {
298   LOG_EVENT_TRACE;
299
300   if(mUpdateRenderThread)
301   {
302     // Set the ThreadSyncronizationInterface on the new surface
303     newSurface->SetThreadSynchronization(*this);
304
305     LOG_EVENT("Starting to replace the surface, event-thread blocked");
306
307     // Start replacing the surface.
308     {
309       ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
310       mPostRendering = FALSE; // Clear the post-rendering flag as Update/Render thread will replace the surface now
311       mNewSurface    = newSurface;
312       mUpdateRenderThreadWaitCondition.Notify(lock);
313     }
314
315     // Wait until the surface has been replaced
316     mSurfaceSemaphore.Acquire();
317
318     LOG_EVENT("Surface replaced, event-thread continuing");
319   }
320 }
321
322 void CombinedUpdateRenderController::DeleteSurface(Dali::RenderSurfaceInterface* surface)
323 {
324   LOG_EVENT_TRACE;
325
326   if(mUpdateRenderThread)
327   {
328     LOG_EVENT("Starting to delete the surface, event-thread blocked");
329
330     // Start replacing the surface.
331     {
332       ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
333       mPostRendering  = FALSE; // Clear the post-rendering flag as Update/Render thread will delete the surface now
334       mDeletedSurface = surface;
335       mUpdateRenderThreadWaitCondition.Notify(lock);
336     }
337
338     // Wait until the surface has been deleted
339     mSurfaceSemaphore.Acquire();
340
341     LOG_EVENT("Surface deleted, event-thread continuing");
342   }
343 }
344
345 void CombinedUpdateRenderController::WaitForGraphicsInitialization()
346 {
347   ConditionalWait::ScopedLock lk(mGraphicsInitializeWait);
348   LOG_EVENT_TRACE;
349
350   if(mUpdateRenderThread)
351   {
352     LOG_EVENT("Waiting for graphics initialisation, event-thread blocked");
353
354     // Wait until the graphics has been initialised
355     mGraphicsInitializeWait.Wait(lk);
356
357     LOG_EVENT("graphics initialised, event-thread continuing");
358   }
359 }
360
361 void CombinedUpdateRenderController::ResizeSurface()
362 {
363   LOG_EVENT_TRACE;
364
365   LOG_EVENT("Resize the surface");
366
367   {
368     ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
369     mPostRendering = FALSE; // Clear the post-rendering flag as Update/Render thread will resize the surface now
370     // Surface is resized and the surface resized count is increased.
371     mSurfaceResized++;
372     mUpdateRenderThreadWaitCondition.Notify(lock);
373   }
374 }
375
376 void CombinedUpdateRenderController::SetRenderRefreshRate(unsigned int numberOfFramesPerRender)
377 {
378   // Not protected by lock, but written to rarely so not worth adding a lock when reading
379   mDefaultFrameDelta                = numberOfFramesPerRender * DEFAULT_FRAME_DURATION_IN_SECONDS;
380   mDefaultFrameDurationMilliseconds = uint64_t(numberOfFramesPerRender) * DEFAULT_FRAME_DURATION_IN_MILLISECONDS;
381   mDefaultFrameDurationNanoseconds  = uint64_t(numberOfFramesPerRender) * DEFAULT_FRAME_DURATION_IN_NANOSECONDS;
382   mDefaultHalfFrameNanoseconds      = mDefaultFrameDurationNanoseconds / 2u;
383
384   LOG_EVENT("mDefaultFrameDelta(%.6f), mDefaultFrameDurationMilliseconds(%lld), mDefaultFrameDurationNanoseconds(%lld)", mDefaultFrameDelta, mDefaultFrameDurationMilliseconds, mDefaultFrameDurationNanoseconds);
385 }
386
387 void CombinedUpdateRenderController::SetPreRenderCallback(CallbackBase* callback)
388 {
389   LOG_EVENT_TRACE;
390   LOG_EVENT("Set PreRender Callback");
391
392   ConditionalWait::ScopedLock updateLock(mUpdateRenderThreadWaitCondition);
393   if(mPreRenderCallback)
394   {
395     delete mPreRenderCallback;
396   }
397   mPreRenderCallback = callback;
398 }
399
400 void CombinedUpdateRenderController::AddSurface(Dali::RenderSurfaceInterface* surface)
401 {
402   LOG_EVENT_TRACE;
403   LOG_EVENT("Surface is added");
404   if(mUpdateRenderThread)
405   {
406     // Set the ThreadSyncronizationInterface on the added surface
407     surface->SetThreadSynchronization(*this);
408   }
409 }
410
411 int32_t CombinedUpdateRenderController::GetThreadId() const
412 {
413   return mThreadId;
414 }
415
416 ///////////////////////////////////////////////////////////////////////////////////////////////////
417 // EVENT THREAD
418 ///////////////////////////////////////////////////////////////////////////////////////////////////
419
420 void CombinedUpdateRenderController::RunUpdateRenderThread(int numberOfCycles, AnimationProgression animationProgression, UpdateMode updateMode)
421 {
422   ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
423
424   switch(mThreadMode)
425   {
426     case ThreadMode::NORMAL:
427     {
428       mUpdateRenderRunCount    = numberOfCycles;
429       mUseElapsedTimeAfterWait = (animationProgression == AnimationProgression::USE_ELAPSED_TIME);
430       break;
431     }
432     case ThreadMode::RUN_IF_REQUESTED:
433     {
434       if(updateMode != UpdateMode::FORCE_RENDER)
435       {
436         // Render only if the update mode is FORCE_RENDER which means the application requests it.
437         // We don't want to awake the update thread.
438         return;
439       }
440
441       mUpdateRenderRunCount++;         // Increase the update request count
442       mUseElapsedTimeAfterWait = TRUE; // The elapsed time should be used. We want animations to proceed.
443       break;
444     }
445   }
446
447   mUpdateRenderThreadCanSleep = FALSE;
448   mUploadWithoutRendering     = (updateMode == UpdateMode::SKIP_RENDER);
449   LOG_COUNTER_EVENT("mUpdateRenderRunCount: %d, mUseElapsedTimeAfterWait: %d", mUpdateRenderRunCount, mUseElapsedTimeAfterWait);
450   mUpdateRenderThreadWaitCondition.Notify(lock);
451 }
452
453 void CombinedUpdateRenderController::PauseUpdateRenderThread()
454 {
455   ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
456   mUpdateRenderRunCount = 0;
457 }
458
459 void CombinedUpdateRenderController::StopUpdateRenderThread()
460 {
461   ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
462   mDestroyUpdateRenderThread = TRUE;
463   mUpdateRenderThreadWaitCondition.Notify(lock);
464 }
465
466 bool CombinedUpdateRenderController::IsUpdateRenderThreadPaused()
467 {
468   ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
469
470   if(mThreadMode == ThreadMode::RUN_IF_REQUESTED)
471   {
472     return !mRunning || mUpdateRenderThreadCanSleep;
473   }
474
475   return (mUpdateRenderRunCount != CONTINUOUS) || // Report paused if NOT continuously running
476          mUpdateRenderThreadCanSleep;             // Report paused if sleeping
477 }
478
479 void CombinedUpdateRenderController::ProcessSleepRequest()
480 {
481   LOG_EVENT_TRACE;
482
483   // Decrement Update request count
484   if(mUpdateRequestCount > 0)
485   {
486     --mUpdateRequestCount;
487   }
488
489   // Can sleep if our update-request count is 0
490   // Update/Render thread can choose to carry on updating if it determines more update/renders are required
491   if(mUpdateRequestCount == 0)
492   {
493     LOG_EVENT("Going to sleep");
494
495     ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
496     mUpdateRenderThreadCanSleep = TRUE;
497   }
498 }
499
500 ///////////////////////////////////////////////////////////////////////////////////////////////////
501 // UPDATE/RENDER THREAD
502 ///////////////////////////////////////////////////////////////////////////////////////////////////
503
504 void CombinedUpdateRenderController::UpdateRenderThread()
505 {
506   ThreadSettings::SetThreadName("RenderThread\0");
507   mThreadId = ThreadSettings::GetThreadId();
508
509   // Install a function for logging
510   mEnvironmentOptions.InstallLogFunction();
511
512   // Install a function for tracing
513   mEnvironmentOptions.InstallTraceFunction();
514
515   LOG_UPDATE_RENDER("THREAD CREATED");
516
517   // Initialize graphics
518   GraphicsInterface& graphics = mAdaptorInterfaces.GetGraphicsInterface();
519   graphics.Initialize();
520
521   Dali::DisplayConnection& displayConnection = mAdaptorInterfaces.GetDisplayConnectionInterface();
522   displayConnection.Initialize(); //@todo Move InitializeGraphics code into graphics implementation
523
524   NotifyGraphicsInitialised();
525
526   //@todo Vk swaps this around, but we need to support surfaceless context for multi-window
527   graphics.ConfigureSurface(mAdaptorInterfaces.GetRenderSurfaceInterface());
528
529   // Tell core it has a context
530   mCore.ContextCreated();
531
532   NotifyThreadInitialised();
533
534   // Update time
535   uint64_t lastFrameTime;
536   TimeService::GetNanoseconds(lastFrameTime);
537   uint64_t lastMemPoolLogTime = lastFrameTime;
538
539   LOG_UPDATE_RENDER("THREAD INITIALISED");
540
541   bool     useElapsedTime     = true;
542   bool     updateRequired     = true;
543   uint64_t timeToSleepUntil   = 0;
544   int      extraFramesDropped = 0;
545
546   const uint64_t memPoolInterval = 1e9 * float(mEnvironmentOptions.GetMemoryPoolInterval());
547
548   const unsigned int renderToFboInterval = mEnvironmentOptions.GetRenderToFboInterval();
549   const bool         renderToFboEnabled  = 0u != renderToFboInterval;
550   unsigned int       frameCount          = 0u;
551
552   while(UpdateRenderReady(useElapsedTime, updateRequired, timeToSleepUntil))
553   {
554     LOG_UPDATE_RENDER_TRACE;
555
556     // For thread safe
557     bool                          uploadOnly     = mUploadWithoutRendering;
558     unsigned int                  surfaceResized = mSurfaceResized;
559     Dali::RenderSurfaceInterface* deletedSurface = ShouldSurfaceBeDeleted();
560
561     // Performance statistics are logged upon a VSYNC tick so use this point for a VSync marker
562     AddPerformanceMarker(PerformanceInterface::VSYNC);
563
564     uint64_t currentFrameStartTime = 0;
565     TimeService::GetNanoseconds(currentFrameStartTime);
566
567     uint64_t timeSinceLastFrame = currentFrameStartTime - lastFrameTime;
568
569     // Optional FPS Tracking when continuously rendering
570     if(useElapsedTime && mFpsTracker.Enabled())
571     {
572       float absoluteTimeSinceLastRender = timeSinceLastFrame * NANOSECONDS_TO_SECOND;
573       mFpsTracker.Track(absoluteTimeSinceLastRender);
574     }
575
576     lastFrameTime = currentFrameStartTime; // Store frame start time
577
578     //////////////////////////////
579     // REPLACE SURFACE
580     //////////////////////////////
581
582     Dali::RenderSurfaceInterface* newSurface = ShouldSurfaceBeReplaced();
583     if(DALI_UNLIKELY(newSurface))
584     {
585       LOG_UPDATE_RENDER_TRACE_FMT("Replacing Surface");
586       // This is designed for replacing pixmap surfaces, but should work for window as well
587       // we need to delete the surface and renderable (pixmap / window)
588       // Then create a new pixmap/window and new surface
589       // If the new surface has a different display connection, then the context will be lost
590       mAdaptorInterfaces.GetDisplayConnectionInterface().Initialize();
591       graphics.ActivateSurfaceContext(newSurface);
592       // TODO: ReplaceGraphicsSurface doesn't work, InitializeGraphics()
593       // already creates new surface window, the surface and the context.
594       // We probably don't need ReplaceGraphicsSurface at all.
595       // newSurface->ReplaceGraphicsSurface();
596       SurfaceReplaced();
597     }
598
599     const bool isRenderingToFbo = renderToFboEnabled && ((0u == frameCount) || (0u != frameCount % renderToFboInterval));
600     ++frameCount;
601
602     //////////////////////////////
603     // UPDATE
604     //////////////////////////////
605
606     const uint32_t currentTime   = static_cast<uint32_t>(currentFrameStartTime / NANOSECONDS_PER_MILLISECOND);
607     const uint32_t nextFrameTime = currentTime + static_cast<uint32_t>(mDefaultFrameDurationMilliseconds);
608
609     uint64_t noOfFramesSinceLastUpdate = 1;
610     float    frameDelta                = 0.0f;
611     if(useElapsedTime)
612     {
613       if(mThreadMode == ThreadMode::RUN_IF_REQUESTED)
614       {
615         extraFramesDropped = 0;
616         while(timeSinceLastFrame >= mDefaultFrameDurationNanoseconds)
617         {
618           timeSinceLastFrame -= mDefaultFrameDurationNanoseconds;
619           extraFramesDropped++;
620         }
621       }
622
623       // If using the elapsed time, then calculate frameDelta as a multiple of mDefaultFrameDelta
624       noOfFramesSinceLastUpdate += extraFramesDropped;
625
626       frameDelta = mDefaultFrameDelta * noOfFramesSinceLastUpdate;
627     }
628     LOG_UPDATE_RENDER("timeSinceLastFrame(%llu) noOfFramesSinceLastUpdate(%u) frameDelta(%.6f)", timeSinceLastFrame, noOfFramesSinceLastUpdate, frameDelta);
629
630     Integration::UpdateStatus updateStatus;
631
632     AddPerformanceMarker(PerformanceInterface::UPDATE_START);
633     TRACE_UPDATE_RENDER_BEGIN("DALI_UPDATE");
634     mCore.Update(frameDelta,
635                  currentTime,
636                  nextFrameTime,
637                  updateStatus,
638                  renderToFboEnabled,
639                  isRenderingToFbo,
640                  uploadOnly);
641     TRACE_UPDATE_RENDER_END("DALI_UPDATE");
642     AddPerformanceMarker(PerformanceInterface::UPDATE_END);
643
644     unsigned int keepUpdatingStatus = updateStatus.KeepUpdating();
645
646     // Tell the event-thread to wake up (if asleep) and send a notification event to Core if required
647     if(updateStatus.NeedsNotification())
648     {
649       mNotificationTrigger.Trigger();
650       LOG_UPDATE_RENDER("Notification Triggered");
651     }
652
653     // Optional logging of update/render status
654     mUpdateStatusLogger.Log(keepUpdatingStatus);
655
656     //////////////////////////////
657     // RENDER
658     //////////////////////////////
659
660     graphics.FrameStart();
661     mAdaptorInterfaces.GetDisplayConnectionInterface().ConsumeEvents();
662
663     if(mPreRenderCallback != NULL)
664     {
665       bool keepCallback = CallbackBase::ExecuteReturn<bool>(*mPreRenderCallback);
666       if(!keepCallback)
667       {
668         delete mPreRenderCallback;
669         mPreRenderCallback = NULL;
670       }
671     }
672
673     graphics.ActivateResourceContext();
674
675     if(mFirstFrameAfterResume)
676     {
677       // mFirstFrameAfterResume is set to true when the thread is resumed
678       // Let graphics know the first frame after thread initialized or resumed.
679       graphics.SetFirstFrameAfterResume();
680       mFirstFrameAfterResume = FALSE;
681     }
682
683     Integration::RenderStatus renderStatus;
684
685     AddPerformanceMarker(PerformanceInterface::RENDER_START);
686     TRACE_UPDATE_RENDER_BEGIN("DALI_RENDER");
687
688     // Upload shared resources
689     mCore.PreRender(renderStatus, mForceClear);
690
691     if(!uploadOnly || surfaceResized)
692     {
693       // Go through each window
694       WindowContainer windows;
695       mAdaptorInterfaces.GetWindowContainerInterface(windows);
696
697       for(auto&& window : windows)
698       {
699         Dali::Integration::Scene      scene         = window->GetScene();
700         Dali::RenderSurfaceInterface* windowSurface = window->GetSurface();
701
702         if(scene && windowSurface)
703         {
704           Integration::RenderStatus windowRenderStatus;
705
706           const bool sceneSurfaceResized = scene.IsSurfaceRectChanged();
707
708           // clear previous frame damaged render items rects, buffer history is tracked on surface level
709           mDamagedRects.clear();
710
711           // Collect damage rects
712           mCore.PreRender(scene, mDamagedRects);
713
714           // Render off-screen frame buffers first if any
715           mCore.RenderScene(windowRenderStatus, scene, true);
716
717           Rect<int> clippingRect; // Empty for fbo rendering
718
719           // Switch to the context of the surface, merge damaged areas for previous frames
720           windowSurface->PreRender(sceneSurfaceResized, mDamagedRects, clippingRect); // Switch GL context
721
722           // Render the surface
723           mCore.RenderScene(windowRenderStatus, scene, false, clippingRect);
724
725           // Buffer swapping now happens when the surface render target is presented.
726
727           // If surface is resized, the surface resized count is decreased.
728           if(DALI_UNLIKELY(sceneSurfaceResized))
729           {
730             SurfaceResized();
731           }
732         }
733       }
734     }
735
736     if(!uploadOnly)
737     {
738       graphics.PostRender();
739     }
740
741     mCore.PostRender();
742
743     //////////////////////////////
744     // DELETE SURFACE
745     //////////////////////////////
746     if(DALI_UNLIKELY(deletedSurface))
747     {
748       LOG_UPDATE_RENDER_TRACE_FMT("Deleting Surface");
749
750       deletedSurface->DestroySurface();
751
752       SurfaceDeleted();
753     }
754
755     TRACE_UPDATE_RENDER_END("DALI_RENDER");
756     AddPerformanceMarker(PerformanceInterface::RENDER_END);
757
758     // if the memory pool interval is set and has elapsed, log the graphics memory pools
759     if(0 < memPoolInterval && memPoolInterval < lastFrameTime - lastMemPoolLogTime)
760     {
761       lastMemPoolLogTime = lastFrameTime;
762       graphics.LogMemoryPools();
763     }
764
765     mForceClear = false;
766
767     // Trigger event thread to request Update/Render thread to sleep if update not required
768     if((Integration::KeepUpdating::NOT_REQUESTED == keepUpdatingStatus) && !renderStatus.NeedsUpdate())
769     {
770       mSleepTrigger->Trigger();
771       updateRequired = false;
772       LOG_UPDATE_RENDER("Sleep Triggered");
773     }
774     else
775     {
776       updateRequired = true;
777     }
778
779     //////////////////////////////
780     // FRAME TIME
781     //////////////////////////////
782
783     extraFramesDropped = 0;
784
785     if(timeToSleepUntil == 0)
786     {
787       // If this is the first frame after the thread is initialized or resumed, we
788       // use the actual time the current frame starts from to calculate the time to
789       // sleep until the next frame.
790       timeToSleepUntil = currentFrameStartTime + mDefaultFrameDurationNanoseconds;
791     }
792     else
793     {
794       // Otherwise, always use the sleep-until time calculated in the last frame to
795       // calculate the time to sleep until the next frame. In this way, if there is
796       // any time gap between the current frame and the next frame, or if update or
797       // rendering in the current frame takes too much time so that the specified
798       // sleep-until time has already passed, it will try to keep the frames syncing
799       // by shortening the duration of the next frame.
800       timeToSleepUntil += mDefaultFrameDurationNanoseconds;
801
802       // Check the current time at the end of the frame
803       uint64_t currentFrameEndTime = 0;
804       TimeService::GetNanoseconds(currentFrameEndTime);
805       while(currentFrameEndTime > timeToSleepUntil + mDefaultFrameDurationNanoseconds)
806       {
807         // We are more than one frame behind already, so just drop the next frames
808         // until the sleep-until time is later than the current time so that we can
809         // catch up.
810         timeToSleepUntil += mDefaultFrameDurationNanoseconds;
811         extraFramesDropped++;
812       }
813     }
814
815     // Render to FBO is intended to measure fps above 60 so sleep is not wanted.
816     if(0u == renderToFboInterval)
817     {
818       // Sleep until at least the the default frame duration has elapsed. This will return immediately if the specified end-time has already passed.
819       TimeService::SleepUntil(timeToSleepUntil);
820     }
821   }
822
823   // Inform core of context destruction
824   mCore.ContextDestroyed();
825
826   WindowContainer windows;
827   mAdaptorInterfaces.GetWindowContainerInterface(windows);
828
829   // Destroy surfaces
830   for(auto&& window : windows)
831   {
832     Dali::RenderSurfaceInterface* surface = window->GetSurface();
833     surface->DestroySurface();
834   }
835
836   graphics.Shutdown();
837
838   LOG_UPDATE_RENDER("THREAD DESTROYED");
839
840   // Uninstall the logging function
841   mEnvironmentOptions.UnInstallLogFunction();
842 }
843
844 bool CombinedUpdateRenderController::UpdateRenderReady(bool& useElapsedTime, bool updateRequired, uint64_t& timeToSleepUntil)
845 {
846   useElapsedTime = true;
847
848   ConditionalWait::ScopedLock updateLock(mUpdateRenderThreadWaitCondition);
849   while((!mUpdateRenderRunCount ||                                                      // Should try to wait if event-thread has paused the Update/Render thread
850          (mUpdateRenderThreadCanSleep && !updateRequired && !mPendingRequestUpdate)) && // Ensure we wait if we're supposed to be sleeping AND do not require another update
851         !mDestroyUpdateRenderThread &&                                                  // Ensure we don't wait if the update-render-thread is supposed to be destroyed
852         !mNewSurface &&                                                                 // Ensure we don't wait if we need to replace the surface
853         !mDeletedSurface &&                                                             // Ensure we don't wait if we need to delete the surface
854         !mSurfaceResized)                                                               // Ensure we don't wait if we need to resize the surface
855   {
856     LOG_UPDATE_RENDER("WAIT: mUpdateRenderRunCount:       %d", mUpdateRenderRunCount);
857     LOG_UPDATE_RENDER("      mUpdateRenderThreadCanSleep: %d, updateRequired: %d, mPendingRequestUpdate: %d", mUpdateRenderThreadCanSleep, updateRequired, mPendingRequestUpdate);
858     LOG_UPDATE_RENDER("      mDestroyUpdateRenderThread:  %d", mDestroyUpdateRenderThread);
859     LOG_UPDATE_RENDER("      mNewSurface:                 %d", mNewSurface);
860     LOG_UPDATE_RENDER("      mDeletedSurface:             %d", mDeletedSurface);
861     LOG_UPDATE_RENDER("      mSurfaceResized:             %d", mSurfaceResized);
862
863     // Reset the time when the thread is waiting, so the sleep-until time for
864     // the first frame after resuming should be based on the actual start time
865     // of the first frame.
866     timeToSleepUntil = 0;
867
868     mUpdateRenderThreadWaitCondition.Wait(updateLock);
869
870     if(!mUseElapsedTimeAfterWait)
871     {
872       useElapsedTime = false;
873     }
874   }
875
876   LOG_COUNTER_UPDATE_RENDER("mUpdateRenderRunCount:       %d", mUpdateRenderRunCount);
877   LOG_COUNTER_UPDATE_RENDER("mUpdateRenderThreadCanSleep: %d, updateRequired: %d, mPendingRequestUpdate: %d", mUpdateRenderThreadCanSleep, updateRequired, mPendingRequestUpdate);
878   LOG_COUNTER_UPDATE_RENDER("mDestroyUpdateRenderThread:  %d", mDestroyUpdateRenderThread);
879   LOG_COUNTER_UPDATE_RENDER("mNewSurface:                 %d", mNewSurface);
880   LOG_COUNTER_UPDATE_RENDER("mDeletedSurface:             %d", mDeletedSurface);
881   LOG_COUNTER_UPDATE_RENDER("mSurfaceResized:             %d", mSurfaceResized);
882
883   mUseElapsedTimeAfterWait    = FALSE;
884   mUpdateRenderThreadCanSleep = FALSE;
885   mPendingRequestUpdate       = FALSE;
886
887   // If we've been asked to run Update/Render cycles a finite number of times then decrement so we wait after the
888   // requested number of cycles
889   if(mUpdateRenderRunCount > 0)
890   {
891     --mUpdateRenderRunCount;
892   }
893
894   // Keep the update-render thread alive if this thread is NOT to be destroyed
895   return !mDestroyUpdateRenderThread;
896 }
897
898 Dali::RenderSurfaceInterface* CombinedUpdateRenderController::ShouldSurfaceBeReplaced()
899 {
900   ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
901
902   Dali::RenderSurfaceInterface* newSurface = mNewSurface;
903   mNewSurface                              = NULL;
904
905   return newSurface;
906 }
907
908 void CombinedUpdateRenderController::SurfaceReplaced()
909 {
910   // Just increment the semaphore
911   mSurfaceSemaphore.Release(1);
912 }
913
914 Dali::RenderSurfaceInterface* CombinedUpdateRenderController::ShouldSurfaceBeDeleted()
915 {
916   ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
917
918   Dali::RenderSurfaceInterface* deletedSurface = mDeletedSurface;
919   mDeletedSurface                              = NULL;
920
921   return deletedSurface;
922 }
923
924 void CombinedUpdateRenderController::SurfaceDeleted()
925 {
926   // Just increment the semaphore
927   mSurfaceSemaphore.Release(1);
928 }
929
930 void CombinedUpdateRenderController::SurfaceResized()
931 {
932   ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
933   if(mSurfaceResized)
934   {
935     mSurfaceResized--;
936   }
937 }
938
939 ///////////////////////////////////////////////////////////////////////////////////////////////////
940 // ALL THREADS
941 ///////////////////////////////////////////////////////////////////////////////////////////////////
942
943 void CombinedUpdateRenderController::NotifyThreadInitialised()
944 {
945   // Just increment the semaphore
946   mEventThreadSemaphore.Release(1);
947 }
948
949 void CombinedUpdateRenderController::NotifyGraphicsInitialised()
950 {
951   mGraphicsInitializeWait.Notify();
952 }
953
954 void CombinedUpdateRenderController::AddPerformanceMarker(PerformanceInterface::MarkerType type)
955 {
956   if(mPerformanceInterface)
957   {
958     mPerformanceInterface->AddMarker(type);
959   }
960 }
961
962 /////////////////////////////////////////////////////////////////////////////////////////////////
963 // POST RENDERING: EVENT THREAD
964 /////////////////////////////////////////////////////////////////////////////////////////////////
965
966 void CombinedUpdateRenderController::PostRenderComplete()
967 {
968   ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
969   mPostRendering = FALSE;
970   mUpdateRenderThreadWaitCondition.Notify(lock);
971 }
972
973 ///////////////////////////////////////////////////////////////////////////////////////////////////
974 // POST RENDERING: RENDER THREAD
975 ///////////////////////////////////////////////////////////////////////////////////////////////////
976
977 void CombinedUpdateRenderController::PostRenderStarted()
978 {
979   ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
980   mPostRendering = TRUE;
981 }
982
983 void CombinedUpdateRenderController::PostRenderWaitForCompletion()
984 {
985   ConditionalWait::ScopedLock lock(mUpdateRenderThreadWaitCondition);
986   while(mPostRendering &&
987         !mNewSurface &&     // We should NOT wait if we're replacing the surface
988         !mDeletedSurface && // We should NOT wait if we're deleting the surface
989         !mDestroyUpdateRenderThread)
990   {
991     mUpdateRenderThreadWaitCondition.Wait(lock);
992   }
993 }
994
995 } // namespace Adaptor
996
997 } // namespace Internal
998
999 } // namespace Dali