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