[dali_2.3.21] Merge branch 'devel/master'
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / controls / gl-view / gl-view-render-thread.cpp
1 /*
2  * Copyright (c) 2023 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-toolkit/internal/controls/gl-view/gl-view-render-thread.h>
20
21 //EXTERNAL INCLUDES
22 #include <dali/devel-api/adaptor-framework/thread-settings.h>
23 #include <dali/integration-api/adaptor-framework/adaptor.h>
24 #include <dali/integration-api/debug.h>
25 #include <chrono>
26 #include <thread>
27
28 namespace Dali
29 {
30 namespace Toolkit
31 {
32 namespace Internal
33 {
34 namespace
35 {
36 constexpr unsigned int NANOSECONDS_PER_SECOND(1e+9);
37
38 // The following values will get calculated at compile time
39 constexpr float    DEFAULT_FRAME_DURATION_IN_SECONDS(1.0f / 60.0f);
40 constexpr uint64_t DEFAULT_FRAME_DURATION_IN_NANOSECONDS(DEFAULT_FRAME_DURATION_IN_SECONDS* NANOSECONDS_PER_SECOND);
41
42 } // namespace
43
44 GlViewRenderThread::GlViewRenderThread(Dali::NativeImageSourceQueuePtr queue)
45 : mLogFactory(Dali::Adaptor::Get().GetLogFactory()),
46   mTraceFactory(Dali::Adaptor::Get().GetTraceFactory()),
47   mSurfaceSize(1, 1),
48   mNativeImageSurface(),
49   mNativeImageQueue(queue),
50   mSurfaceSemaphore(1),
51   mGlInitCallback(nullptr),
52   mGlRenderFrameCallback(nullptr),
53   mGlTerminateCallback(nullptr),
54   mResizeCallback(nullptr),
55   mDepth(false),
56   mStencil(false),
57   mMSAA(0),
58   mGraphicsApiVersion(20),
59   mConditionalWait(),
60   mIsThreadStarted(0),
61   mIsThreadStopped(0),
62   mIsThreadPaused(0),
63   mIsRenderRequested(0),
64   mRenderingMode(0),
65   mIsSurfaceResized(0),
66   mDefaultFrameDurationNanoseconds(DEFAULT_FRAME_DURATION_IN_NANOSECONDS)
67 {
68   mNativeImageSurface = Dali::NativeImageSurface::New(mNativeImageQueue);
69
70   if(!mNativeImageSurface)
71   {
72     DALI_LOG_ERROR("Creating NativeImageSurface Failed, Could not start GlView Render Thread");
73   }
74 }
75
76 void GlViewRenderThread::RegisterGlCallbacks(CallbackBase* initCallback, CallbackBase* renderFrameCallback, CallbackBase* terminateCallback)
77 {
78   if(!mGlInitCallback && !mGlRenderFrameCallback && !mGlTerminateCallback)
79   {
80     mGlInitCallback        = std::unique_ptr<CallbackBase>(initCallback);
81     mGlRenderFrameCallback = std::unique_ptr<CallbackBase>(renderFrameCallback);
82     mGlTerminateCallback   = std::unique_ptr<CallbackBase>(terminateCallback);
83   }
84 }
85
86 void GlViewRenderThread::SetResizeCallback(CallbackBase* resizeCallback)
87 {
88   if(!mResizeCallback)
89   {
90     mResizeCallback = std::unique_ptr<CallbackBase>(resizeCallback);
91   }
92 }
93
94 bool GlViewRenderThread::SetGraphicsConfig(bool depth, bool stencil, int msaa, int version)
95 {
96   mDepth              = depth;
97   mStencil            = stencil;
98   mMSAA               = msaa;
99   mGraphicsApiVersion = version;
100
101   if(mNativeImageSurface)
102   {
103     return mNativeImageSurface->SetGraphicsConfig(mDepth, mStencil, mMSAA, mGraphicsApiVersion);
104   }
105
106   return false;
107 }
108
109 void GlViewRenderThread::SetOnDemandRenderMode(bool onDemand)
110 {
111   ConditionalWait::ScopedLock lock(mConditionalWait);
112   mRenderingMode = static_cast<unsigned int>(onDemand);
113   DALI_LOG_RELEASE_INFO("GlViewRenderThread::SetOnDemandRenderMode(): mRenderingMode: %d\n", mRenderingMode);
114   if(!onDemand && !mIsThreadPaused)
115   {
116     mConditionalWait.Notify(lock);
117   }
118 }
119
120 void GlViewRenderThread::SetSurfaceSize(Dali::Vector2 size)
121 {
122   //GlViewRenderThread::Run was already blocked in Internal::GlView::OnSizeSet
123   mSurfaceSize      = size;
124   mIsSurfaceResized = 1;
125 }
126
127 void GlViewRenderThread::RenderOnce()
128 {
129   //Notify GLRender thread.
130   Dali::ConditionalWait::ScopedLock lock(mConditionalWait);
131   mIsRenderRequested = 1;
132   mConditionalWait.Notify(lock);
133 }
134
135 void GlViewRenderThread::GetNanoSeconds(uint64_t& timeInNanoseconds)
136 {
137   // Get the time of a monotonic clock since its epoch.
138   auto epoch        = std::chrono::steady_clock::now().time_since_epoch();
139   auto duration     = std::chrono::duration_cast<std::chrono::nanoseconds>(epoch);
140   timeInNanoseconds = static_cast<uint64_t>(duration.count());
141 }
142
143 void GlViewRenderThread::SleepUntil(uint64_t timeInNanoseconds)
144 {
145   using Clock     = std::chrono::steady_clock;
146   using TimePoint = std::chrono::time_point<Clock>;
147
148   const Clock::duration duration = std::chrono::nanoseconds(timeInNanoseconds);
149   const TimePoint       timePoint(duration);
150
151   std::this_thread::sleep_until(timePoint);
152 }
153
154 void GlViewRenderThread::Run()
155 {
156   Dali::SetThreadName("GlViewRenderer");
157   mLogFactory.InstallLogFunction();
158   mTraceFactory.InstallTraceFunction();
159
160   int renderFrameResult = 0;
161
162   if(!mNativeImageSurface)
163   {
164     DALI_LOG_ERROR("NativeImageSurface is null, Could not start GlView Render Thread");
165     return;
166   }
167
168   AcquireSurface();
169   mNativeImageSurface->InitializeGraphics();
170   ReleaseSurface();
171
172   mNativeImageSurface->PreRender();
173   if(mGlInitCallback)
174   {
175     CallbackBase::Execute(*mGlInitCallback);
176   }
177
178   uint64_t timeToSleepUntil = 0;
179
180   while(RenderReady(timeToSleepUntil))
181   {
182     uint64_t currentFrameStartTime = 0;
183     GetNanoSeconds(currentFrameStartTime);
184
185     AcquireSurface();
186     mNativeImageSurface->PreRender();
187     if(mIsSurfaceResized)
188     {
189       if(mResizeCallback)
190       {
191         CallbackBase::Execute(*mResizeCallback, static_cast<int>(mSurfaceSize.x), static_cast<int>(mSurfaceSize.y));
192       }
193       mIsSurfaceResized = 0;
194     }
195
196     if(mNativeImageSurface->CanRender())
197     {
198       if(mGlRenderFrameCallback)
199       {
200         renderFrameResult = CallbackBase::ExecuteReturn<int>(*mGlRenderFrameCallback);
201         if(renderFrameResult)
202         {
203           mNativeImageSurface->PostRender();
204         }
205       }
206     }
207
208     ReleaseSurface();
209
210     if(timeToSleepUntil == 0)
211     {
212       timeToSleepUntil = currentFrameStartTime + mDefaultFrameDurationNanoseconds;
213     }
214     else
215     {
216       timeToSleepUntil += mDefaultFrameDurationNanoseconds;
217       uint64_t currentFrameEndTime = 0;
218       GetNanoSeconds(currentFrameEndTime);
219       while(currentFrameEndTime > timeToSleepUntil + mDefaultFrameDurationNanoseconds)
220       {
221         timeToSleepUntil += mDefaultFrameDurationNanoseconds;
222       }
223     }
224
225     SleepUntil(timeToSleepUntil);
226   }
227
228   if(mGlTerminateCallback)
229   {
230     CallbackBase::Execute(*mGlTerminateCallback);
231   }
232
233   mNativeImageSurface->TerminateGraphics();
234
235   return;
236 }
237
238 void GlViewRenderThread::Stop()
239 {
240   // Set to come out Render Thread out of waiting condition.
241   Dali::ConditionalWait::ScopedLock lock(mConditionalWait);
242   mIsThreadStopped = 1;
243   mIsThreadPaused  = 0;
244   mConditionalWait.Notify(lock);
245 }
246
247 void GlViewRenderThread::Pause()
248 {
249   //Notify GLRender thread, If actor visibility is change
250   Dali::ConditionalWait::ScopedLock lock(mConditionalWait);
251   mIsThreadPaused = 1;
252   mConditionalWait.Notify(lock);
253 }
254
255 void GlViewRenderThread::Resume()
256 {
257   Dali::ConditionalWait::ScopedLock lock(mConditionalWait);
258   if(!mIsThreadStarted)
259   {
260     Start();
261     mIsThreadStarted = 1;
262   }
263   mIsThreadPaused = 0;
264   mConditionalWait.Notify(lock);
265 }
266
267 bool GlViewRenderThread::RenderReady(uint64_t& timeToSleepUntil)
268 {
269   ConditionalWait::ScopedLock lock(mConditionalWait);
270   while((!mIsThreadStopped && mRenderingMode && !mIsRenderRequested) || mIsThreadPaused)
271   {
272     timeToSleepUntil = 0;
273     mConditionalWait.Wait(lock);
274   }
275
276   mIsRenderRequested = 0;
277   // Keep the update-render thread alive if this thread is NOT to be destroyed
278   return !mIsThreadStopped;
279 }
280
281 void GlViewRenderThread::AcquireSurface()
282 {
283   mSurfaceSemaphore.Acquire();
284 }
285
286 void GlViewRenderThread::ReleaseSurface()
287 {
288   mSurfaceSemaphore.Release(1);
289 }
290
291 GlViewRenderThread::~GlViewRenderThread()
292 {
293 }
294
295 } // namespace Internal
296 } // namespace Toolkit
297 } // namespace Dali