Merge branch 'devel/master' into tizen
[platform/core/uifw/dali-adaptor.git] / dali / internal / window-system / common / gl-window-render-thread.cpp
1 /*
2  * Copyright (c) 2021 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 // EXTERNAL INCLUDES
19 #include <dali/devel-api/adaptor-framework/thread-settings.h>
20
21 // INTERNAL INCLUDES
22 #include <dali/internal/adaptor/common/adaptor-impl.h>
23 #include <dali/internal/system/common/time-service.h>
24 #include <dali/internal/window-system/common/gl-window-render-thread.h>
25
26 namespace Dali
27 {
28 namespace Internal
29 {
30 namespace Adaptor
31 {
32 namespace
33 {
34 constexpr unsigned int NANOSECONDS_PER_SECOND(1e+9);
35
36 // The following values will get calculated at compile time
37 constexpr float    DEFAULT_FRAME_DURATION_IN_SECONDS(1.0f / 60.0f);
38 constexpr uint64_t DEFAULT_FRAME_DURATION_IN_NANOSECONDS(DEFAULT_FRAME_DURATION_IN_SECONDS* NANOSECONDS_PER_SECOND);
39 constexpr uint64_t REFRESH_RATE(1u);
40
41 constexpr int MINIMUM_DIMENSION_CHANGE(1);
42 } // namespace
43
44 GlWindowRenderThread::GlWindowRenderThread(PositionSize positionSize, ColorDepth colorDepth)
45 : mGraphics(nullptr),
46   mWindowBase(nullptr),
47   mWindowRotationTrigger(),
48   mLogFactory(Dali::Adaptor::Get().GetLogFactory()),
49   mPositionSize(positionSize),
50   mColorDepth(colorDepth),
51   mGLInitCallback(),
52   mGLRenderFrameCallback(),
53   mGLTerminateCallback(),
54   mEGLSurface(nullptr),
55   mEGLContext(nullptr),
56   mDepth(false),
57   mStencil(false),
58   mIsEGLInitialize(false),
59   mGLESVersion(30), //Default GLES version 30
60   mMSAA(0),
61   mWindowRotationAngle(0),
62   mScreenRotationAngle(0),
63   mRenderThreadWaitCondition(),
64   mDestroyRenderThread(0),
65   mPauseRenderThread(0),
66   mRenderingMode(0),
67   mRequestRenderOnce(0),
68   mSurfaceStatus(0),
69   mPostRendering(0),
70   mDefaultFrameDurationNanoseconds(REFRESH_RATE * DEFAULT_FRAME_DURATION_IN_NANOSECONDS)
71 {
72 }
73
74 GlWindowRenderThread::~GlWindowRenderThread()
75 {
76 }
77
78 void GlWindowRenderThread::SetGraphicsInterface(GraphicsInterface* graphics)
79 {
80   mGraphics = graphics;
81 }
82
83 void GlWindowRenderThread::SetWindowBase(WindowBase* windowBase)
84 {
85   mWindowBase = windowBase;
86 }
87
88 void GlWindowRenderThread::SetEglConfig(bool depth, bool stencil, int msaa, int version)
89 {
90   mDepth       = depth;
91   mStencil     = stencil;
92   mMSAA        = msaa;
93   mGLESVersion = version;
94 }
95
96 void GlWindowRenderThread::Pause()
97 {
98   ConditionalWait::ScopedLock lock(mRenderThreadWaitCondition);
99   mPauseRenderThread = 1;
100   DALI_LOG_RELEASE_INFO("GlWindowRenderThread::Pause()\n");
101 }
102
103 void GlWindowRenderThread::Resume()
104 {
105   ConditionalWait::ScopedLock lock(mRenderThreadWaitCondition);
106   mPauseRenderThread = 0;
107   DALI_LOG_RELEASE_INFO("GlWindowRenderThread::Resume()\n");
108   mRenderThreadWaitCondition.Notify(lock);
109 }
110
111 void GlWindowRenderThread::Stop()
112 {
113   ConditionalWait::ScopedLock lock(mRenderThreadWaitCondition);
114   mDestroyRenderThread = 1;
115   DALI_LOG_RELEASE_INFO("GlWindowRenderThread::Stop()\n");
116   mRenderThreadWaitCondition.Notify(lock);
117 }
118
119 void GlWindowRenderThread::RegisterGlCallback(CallbackBase* initCallback,
120                                               CallbackBase* renderFrameCallback,
121                                               CallbackBase* terminateCallback)
122 {
123   mGLInitCallback        = std::unique_ptr<CallbackBase>(initCallback);
124   mGLRenderFrameCallback = std::unique_ptr<CallbackBase>(renderFrameCallback);
125   mGLTerminateCallback   = std::unique_ptr<CallbackBase>(terminateCallback);
126 }
127
128 void GlWindowRenderThread::SetOnDemandRenderMode(bool onDemand)
129 {
130   ConditionalWait::ScopedLock lock(mRenderThreadWaitCondition);
131   mRenderingMode = static_cast<unsigned int>(onDemand);
132   DALI_LOG_RELEASE_INFO("GlWindowRenderThread::SetOnDemandRenderMode(): mRenderingMode: %d\n", mRenderingMode);
133   if(!onDemand)
134   {
135     mRenderThreadWaitCondition.Notify(lock);
136   }
137 }
138
139 void GlWindowRenderThread::RenderOnce()
140 {
141   // Most of all, this function is called in event thread
142   ConditionalWait::ScopedLock lock(mRenderThreadWaitCondition);
143   mRequestRenderOnce = 1;
144   mRenderThreadWaitCondition.Notify(lock);
145 }
146
147 void GlWindowRenderThread::RequestWindowResize(int width, int height)
148 {
149   ConditionalWait::ScopedLock lock(mRenderThreadWaitCondition);
150   // Check resizing
151   if((fabs(width - mPositionSize.width) > MINIMUM_DIMENSION_CHANGE) ||
152      (fabs(height - mPositionSize.height) > MINIMUM_DIMENSION_CHANGE))
153   {
154     mSurfaceStatus |= static_cast<unsigned int>(SurfaceStatus::RESIZED); // Set bit for window resized
155     mPositionSize.width  = width;
156     mPositionSize.height = height;
157
158     DALI_LOG_RELEASE_INFO("GlWindowRenderThread::RequestWindowResize(), width:%d, height:%d\n", width, height);
159     mRenderThreadWaitCondition.Notify(lock);
160   }
161 }
162
163 void GlWindowRenderThread::RequestWindowRotate(int windowAngle)
164 {
165   if(!mWindowRotationTrigger)
166   {
167     mWindowRotationTrigger = std::unique_ptr<TriggerEventInterface>(TriggerEventFactory::CreateTriggerEvent(MakeCallback(this, &GlWindowRenderThread::WindowRotationCompleted),
168                                                                                                             TriggerEventInterface::KEEP_ALIVE_AFTER_TRIGGER));
169   }
170
171   ConditionalWait::ScopedLock lock(mRenderThreadWaitCondition);
172   if(mWindowRotationAngle != windowAngle)
173   {
174     mSurfaceStatus |= static_cast<unsigned int>(SurfaceStatus::WINDOW_ROTATED); // Set bit for window rotation
175     mWindowRotationAngle = windowAngle;
176     DALI_LOG_RELEASE_INFO("GlWindowRenderThread::RequestWindowRotate(): %d\n", windowAngle);
177     mRenderThreadWaitCondition.Notify(lock);
178   }
179 }
180
181 void GlWindowRenderThread::RequestScreenRotate(int screenAngle)
182 {
183   ConditionalWait::ScopedLock lock(mRenderThreadWaitCondition);
184   if(mScreenRotationAngle != screenAngle)
185   {
186     mSurfaceStatus |= static_cast<unsigned int>(SurfaceStatus::SCREEN_ROTATED); // Set bit for screen rotation
187     mScreenRotationAngle = screenAngle;
188     DALI_LOG_RELEASE_INFO("GlWindowRenderThread::RequestScreenRotate(): %d\n", screenAngle);
189     mRenderThreadWaitCondition.Notify(lock);
190   }
191 }
192
193 void GlWindowRenderThread::WindowRotationCompleted()
194 {
195   mWindowBase->WindowRotationCompleted(mWindowRotationAngle, mPositionSize.width, mPositionSize.height);
196
197   PostRenderFinish();
198 }
199
200 unsigned int GlWindowRenderThread::GetSurfaceStatus(int& windowRotationAngle, int& screenRotationAngle)
201 {
202   ConditionalWait::ScopedLock lock(mRenderThreadWaitCondition);
203
204   // Get the surface status and reset that.
205   unsigned int status = mSurfaceStatus;
206   mSurfaceStatus      = static_cast<unsigned int>(SurfaceStatus::NO_CHANGED);
207
208   windowRotationAngle = mWindowRotationAngle;
209   screenRotationAngle = mScreenRotationAngle;
210
211   return status;
212 }
213
214 void GlWindowRenderThread::Run()
215 {
216   Dali::SetThreadName("GlWindowRenderThread");
217   mLogFactory.InstallLogFunction();
218
219   int          renderFrameResult = 0;
220   unsigned int isSurfaceChanged  = 0;
221   bool         isWindowResized = false, isWindowRotated = false, isScreenRotated = false;
222   int          windowRotationAngle = 0, screenRotationAngle = 0, totalAngle = 0;
223   EglGraphics* eglGraphics = static_cast<EglGraphics*>(mGraphics);
224
225   Internal::Adaptor::EglImplementation& eglImpl = eglGraphics->GetEglImplementation();
226
227   InitializeGraphics(eglGraphics);
228
229   eglImpl.MakeContextCurrent(mEGLSurface, mEGLContext);
230
231   if(mGLInitCallback)
232   {
233     CallbackBase::Execute(*mGLInitCallback);
234   }
235
236   uint64_t timeToSleepUntil = 0;
237
238   while(RenderReady(timeToSleepUntil))
239   {
240     uint64_t currentFrameStartTime = 0;
241     TimeService::GetNanoseconds(currentFrameStartTime);
242
243     if(mGLRenderFrameCallback)
244     {
245       // PreRender
246       isSurfaceChanged = GetSurfaceStatus(windowRotationAngle, screenRotationAngle);
247       if(DALI_UNLIKELY(isSurfaceChanged))
248       {
249         isWindowResized = (isSurfaceChanged & static_cast<unsigned int>(SurfaceStatus::RESIZED)) ? true : false;
250         isWindowRotated = (isSurfaceChanged & static_cast<unsigned int>(SurfaceStatus::WINDOW_ROTATED)) ? true : false;
251         isScreenRotated = (isSurfaceChanged & static_cast<unsigned int>(SurfaceStatus::SCREEN_ROTATED)) ? true : false;
252         totalAngle      = (windowRotationAngle + screenRotationAngle) % 360;
253
254         if(isWindowRotated || isScreenRotated)
255         {
256           mWindowBase->SetEglWindowBufferTransform(totalAngle);
257           if(isWindowRotated)
258           {
259             mWindowBase->SetEglWindowTransform(windowRotationAngle);
260           }
261         }
262
263         if(isWindowResized)
264         {
265           Dali::PositionSize positionSize;
266           positionSize.x = mPositionSize.x;
267           positionSize.y = mPositionSize.y;
268           if(totalAngle == 0 || totalAngle == 180)
269           {
270             positionSize.width  = mPositionSize.width;
271             positionSize.height = mPositionSize.height;
272           }
273           else
274           {
275             positionSize.width  = mPositionSize.height;
276             positionSize.height = mPositionSize.width;
277           }
278           mWindowBase->ResizeEglWindow(positionSize);
279         }
280       }
281
282       // Render
283       renderFrameResult = CallbackBase::ExecuteReturn<int>(*mGLRenderFrameCallback);
284
285       // PostRender
286       if(DALI_UNLIKELY(isWindowRotated))
287       {
288         PostRenderStart();
289
290         mWindowRotationTrigger->Trigger();
291
292         PostRenderWaitForFinished();
293         isWindowRotated = false;
294       }
295
296       // buffer commit
297       if(renderFrameResult)
298       {
299         eglImpl.SwapBuffers(mEGLSurface);
300       }
301     }
302     renderFrameResult = 0;
303
304     if(timeToSleepUntil == 0)
305     {
306       timeToSleepUntil = currentFrameStartTime + mDefaultFrameDurationNanoseconds;
307     }
308     else
309     {
310       timeToSleepUntil += mDefaultFrameDurationNanoseconds;
311       uint64_t currentFrameEndTime = 0;
312       TimeService::GetNanoseconds(currentFrameEndTime);
313       while(currentFrameEndTime > timeToSleepUntil + mDefaultFrameDurationNanoseconds)
314       {
315         timeToSleepUntil += mDefaultFrameDurationNanoseconds;
316       }
317     }
318
319     TimeService::SleepUntil(timeToSleepUntil);
320   }
321
322   if(mGLTerminateCallback)
323   {
324     CallbackBase::Execute(*mGLTerminateCallback);
325   }
326
327   if(mIsEGLInitialize)
328   {
329     EglGraphics*                          eglGraphics = static_cast<EglGraphics*>(mGraphics);
330     Internal::Adaptor::EglImplementation& eglImpl     = eglGraphics->GetEglImplementation();
331
332     if(mEGLSurface)
333     {
334       eglImpl.DestroySurface(mEGLSurface);
335       mEGLSurface = nullptr;
336     }
337
338     if(mEGLContext)
339     {
340       eglImpl.DestroyContext(mEGLContext);
341       mEGLContext = nullptr;
342     }
343
344     eglImpl.TerminateGles();
345   }
346 }
347
348 void GlWindowRenderThread::InitializeGraphics(EglGraphics* eglGraphics)
349 {
350   mIsEGLInitialize = true;
351
352   Internal::Adaptor::EglImplementation& eglImpl = eglGraphics->GetEglImplementation();
353   eglImpl.SetGlesVersion(mGLESVersion);
354
355   if(eglImpl.ChooseConfig(true, mColorDepth) == false)
356   {
357     if(mGLESVersion == 30)
358     {
359       DALI_LOG_RELEASE_INFO("InitializeGraphics: Fail to choose config with GLES30, retry with GLES20\n");
360       eglImpl.SetGlesVersion(20);
361       mGLESVersion = 20;
362       if(eglImpl.ChooseConfig(true, mColorDepth) == false)
363       {
364         DALI_LOG_ERROR("InitializeGraphics: Fail to choose config with GLES20");
365         return;
366       }
367     }
368     else
369     {
370       DALI_LOG_ERROR("InitializeGraphics: Fail to choose config with GLES20");
371       return;
372     }
373   }
374   eglImpl.CreateWindowContext(mEGLContext);
375
376   // Create the EGL window
377   EGLNativeWindowType window = mWindowBase->CreateEglWindow(mPositionSize.width, mPositionSize.height);
378   mEGLSurface                = eglImpl.CreateSurfaceWindow(window, mColorDepth);
379 }
380
381 bool GlWindowRenderThread::RenderReady(uint64_t& timeToSleepUntil)
382 {
383   ConditionalWait::ScopedLock updateLock(mRenderThreadWaitCondition);
384   while((!mDestroyRenderThread && mRenderingMode && !mRequestRenderOnce && !mSurfaceStatus) || mPauseRenderThread)
385   {
386     timeToSleepUntil = 0;
387     mRenderThreadWaitCondition.Wait(updateLock);
388   }
389
390   mRequestRenderOnce = 0;
391   // Keep the update-render thread alive if this thread is NOT to be destroyed
392   return !mDestroyRenderThread;
393 }
394
395 void GlWindowRenderThread::PostRenderStart()
396 {
397   ConditionalWait::ScopedLock lock(mRenderThreadWaitCondition);
398   mPostRendering = false;
399 }
400
401 void GlWindowRenderThread::PostRenderFinish()
402 {
403   ConditionalWait::ScopedLock lock(mRenderThreadWaitCondition);
404   mPostRendering = true;
405   mRenderThreadWaitCondition.Notify(lock);
406 }
407
408 void GlWindowRenderThread::PostRenderWaitForFinished()
409 {
410   ConditionalWait::ScopedLock lock(mRenderThreadWaitCondition);
411   while(!mPostRendering && !mDestroyRenderThread)
412   {
413     mRenderThreadWaitCondition.Wait(lock);
414   }
415 }
416
417 } // namespace Adaptor
418
419 } // namespace Internal
420
421 } // namespace Dali