[dali_2.3.28] Merge branch 'devel/master'
[platform/core/uifw/dali-adaptor.git] / dali / internal / imaging / tizen / native-image-source-queue-impl-tizen.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/imaging/tizen/native-image-source-queue-impl-tizen.h>
20
21 // EXTERNAL INCLUDES
22 #include <dali/devel-api/common/stage.h>
23 #include <dali/integration-api/debug.h>
24 #include <dali/integration-api/gl-defines.h>
25 #include <tbm_surface_internal.h>
26
27 // INTERNAL INCLUDES
28 #include <dali/devel-api/adaptor-framework/environment-variable.h>
29 #include <dali/internal/adaptor/common/adaptor-impl.h>
30 #include <dali/internal/graphics/common/egl-image-extensions.h>
31 #include <dali/internal/graphics/gles/egl-graphics.h>
32 #include <dali/internal/system/common/environment-variables.h>
33
34 namespace Dali
35 {
36 namespace Internal
37 {
38 namespace Adaptor
39 {
40 namespace
41 {
42 // clang-format off
43 int FORMATS_BLENDING_REQUIRED[] = {
44   TBM_FORMAT_ARGB4444, TBM_FORMAT_ABGR4444,
45   TBM_FORMAT_RGBA4444, TBM_FORMAT_BGRA4444,
46   TBM_FORMAT_RGBX5551, TBM_FORMAT_BGRX5551,
47   TBM_FORMAT_ARGB1555, TBM_FORMAT_ABGR1555,
48   TBM_FORMAT_RGBA5551, TBM_FORMAT_BGRA5551,
49   TBM_FORMAT_ARGB8888, TBM_FORMAT_ABGR8888,
50   TBM_FORMAT_RGBA8888, TBM_FORMAT_BGRA8888,
51   TBM_FORMAT_ARGB2101010, TBM_FORMAT_ABGR2101010,
52   TBM_FORMAT_RGBA1010102, TBM_FORMAT_BGRA1010102
53 };
54 // clang-format on
55
56 const char* SAMPLER_TYPE = "samplerExternalOES";
57
58 constexpr int32_t NUM_FORMATS_BLENDING_REQUIRED = 18;
59
60 constexpr int32_t DEFAULT_TBM_SURFACE_QUEUE_SIZE = 3u;
61
62 int32_t GetTbmSurfaceQueueSize()
63 {
64   static auto    queueSizeString = EnvironmentVariable::GetEnvironmentVariable(DALI_ENV_TBM_SURFACE_QUEUE_SIZE);
65   static int32_t queueSize       = queueSizeString ? std::atoi(queueSizeString) : DEFAULT_TBM_SURFACE_QUEUE_SIZE;
66   return queueSize;
67 }
68
69 } // namespace
70
71 NativeImageSourceQueueTizen* NativeImageSourceQueueTizen::New(uint32_t queueCount, uint32_t width, uint32_t height, Dali::NativeImageSourceQueue::ColorFormat colorFormat, Any nativeImageSourceQueue)
72 {
73   NativeImageSourceQueueTizen* image = new NativeImageSourceQueueTizen(queueCount, width, height, colorFormat, nativeImageSourceQueue);
74   DALI_ASSERT_DEBUG(image && "NativeImageSourceQueueTizen allocation failed.");
75
76   if(image)
77   {
78     image->Initialize(colorFormat);
79   }
80
81   return image;
82 }
83
84 NativeImageSourceQueueTizen::NativeImageSourceQueueTizen(uint32_t queueCount, uint32_t width, uint32_t height, Dali::NativeImageSourceQueue::ColorFormat colorFormat, Any nativeImageSourceQueue)
85 : mMutex(),
86   mQueueCount(queueCount),
87   mWidth(width),
88   mHeight(height),
89   mTbmQueue(NULL),
90   mConsumeSurface(NULL),
91   mEglImages(),
92   mBuffers(),
93   mEglGraphics(NULL),
94   mEglImageExtensions(NULL),
95   mOwnTbmQueue(false),
96   mBlendingRequired(false),
97   mIsResized(false),
98   mFreeRequest(false)
99 {
100   DALI_ASSERT_ALWAYS(Dali::Stage::IsCoreThread() && "Core is not installed. Might call this API from worker thread?");
101
102   GraphicsInterface* graphics = &(Adaptor::GetImplementation(Adaptor::Get()).GetGraphicsInterface());
103   mEglGraphics                = static_cast<EglGraphics*>(graphics);
104
105   mTbmQueue = GetSurfaceFromAny(nativeImageSourceQueue);
106
107   if(mTbmQueue != NULL)
108   {
109     mBlendingRequired = CheckBlending(tbm_surface_queue_get_format(mTbmQueue));
110     mQueueCount       = tbm_surface_queue_get_size(mTbmQueue);
111     mWidth            = tbm_surface_queue_get_width(mTbmQueue);
112     mHeight           = tbm_surface_queue_get_height(mTbmQueue);
113   }
114 }
115
116 NativeImageSourceQueueTizen::~NativeImageSourceQueueTizen()
117 {
118   if(mOwnTbmQueue)
119   {
120     if(mTbmQueue != NULL)
121     {
122       tbm_surface_queue_destroy(mTbmQueue);
123     }
124   }
125 }
126
127 void NativeImageSourceQueueTizen::Initialize(Dali::NativeImageSourceQueue::ColorFormat colorFormat)
128 {
129   if(mWidth == 0 || mHeight == 0)
130   {
131     return;
132   }
133
134   if(mTbmQueue == NULL)
135   {
136     int tbmFormat = TBM_FORMAT_ARGB8888;
137
138     switch(colorFormat)
139     {
140       case Dali::NativeImageSourceQueue::ColorFormat::RGBA8888: // TODO : Implement me after other codes fixed.
141       case Dali::NativeImageSourceQueue::ColorFormat::BGRA8888:
142       {
143         tbmFormat         = TBM_FORMAT_ARGB8888;
144         mBlendingRequired = true;
145         break;
146       }
147       case Dali::NativeImageSourceQueue::ColorFormat::RGBX8888: // TODO : Implement me after other codes fixed.
148       case Dali::NativeImageSourceQueue::ColorFormat::BGRX8888:
149       {
150         tbmFormat         = TBM_FORMAT_XRGB8888;
151         mBlendingRequired = false;
152         break;
153       }
154       case Dali::NativeImageSourceQueue::ColorFormat::RGB888: // TODO : Implement me after other codes fixed.
155       case Dali::NativeImageSourceQueue::ColorFormat::BGR888:
156       {
157         tbmFormat         = TBM_FORMAT_RGB888;
158         mBlendingRequired = false;
159         break;
160       }
161       default:
162       {
163         DALI_LOG_WARNING("Wrong color format.\n");
164         return;
165       }
166     }
167
168     if(mQueueCount == 0)
169     {
170       mQueueCount = GetTbmSurfaceQueueSize();
171     }
172
173     mTbmQueue = tbm_surface_queue_create(mQueueCount, mWidth, mHeight, tbmFormat, 0);
174     if(!mTbmQueue)
175     {
176       DALI_LOG_ERROR("NativeImageSourceQueueTizen::Initialize: tbm_surface_queue_create is failed! [%p]\n", mTbmQueue);
177       return;
178     }
179
180     mOwnTbmQueue = true;
181   }
182 }
183
184 tbm_surface_queue_h NativeImageSourceQueueTizen::GetSurfaceFromAny(Any source) const
185 {
186   if(source.Empty())
187   {
188     return NULL;
189   }
190
191   if(source.GetType() == typeid(tbm_surface_queue_h))
192   {
193     return AnyCast<tbm_surface_queue_h>(source);
194   }
195   else
196   {
197     return NULL;
198   }
199 }
200
201 Any NativeImageSourceQueueTizen::GetNativeImageSourceQueue() const
202 {
203   return Any(mTbmQueue);
204 }
205
206 void NativeImageSourceQueueTizen::SetSize(uint32_t width, uint32_t height)
207 {
208   Dali::Mutex::ScopedLock lock(mMutex);
209
210   if(mWidth == width && mHeight == height)
211   {
212     return;
213   }
214
215   tbm_surface_queue_reset(mTbmQueue, width, height, tbm_surface_queue_get_format(mTbmQueue));
216
217   mWidth     = width;
218   mHeight    = height;
219   mIsResized = true;
220 }
221
222 void NativeImageSourceQueueTizen::IgnoreSourceImage()
223 {
224   Dali::Mutex::ScopedLock lock(mMutex);
225   tbm_surface_h           surface;
226
227   if(tbm_surface_queue_can_acquire(mTbmQueue, 0))
228   {
229     if(tbm_surface_queue_acquire(mTbmQueue, &surface) != TBM_SURFACE_QUEUE_ERROR_NONE)
230     {
231       DALI_LOG_ERROR("NativeImageSourceQueueTizen::IgnoreSourceImage: Failed to aquire a tbm_surface\n");
232       return;
233     }
234
235     if(tbm_surface_internal_is_valid(surface))
236     {
237       tbm_surface_queue_release(mTbmQueue, surface);
238     }
239   }
240 }
241
242 bool NativeImageSourceQueueTizen::CanDequeueBuffer()
243 {
244   Dali::Mutex::ScopedLock lock(mMutex);
245   if(tbm_surface_queue_can_dequeue(mTbmQueue, 0))
246   {
247     return true;
248   }
249   return false;
250 }
251
252 uint8_t* NativeImageSourceQueueTizen::DequeueBuffer(uint32_t& width, uint32_t& height, uint32_t& stride)
253 {
254   Dali::Mutex::ScopedLock lock(mMutex);
255   if(mTbmQueue == NULL)
256   {
257     DALI_LOG_ERROR("TbmQueue is NULL");
258     return NULL;
259   }
260
261   tbm_surface_h tbmSurface;
262   if(tbm_surface_queue_dequeue(mTbmQueue, &tbmSurface) != TBM_SURFACE_QUEUE_ERROR_NONE)
263   {
264     DALI_LOG_ERROR("Failed to dequeue a tbm_surface [%p]\n", tbmSurface);
265     return NULL;
266   }
267
268   tbm_surface_info_s info;
269   int                ret = tbm_surface_map(tbmSurface, TBM_OPTION_WRITE, &info);
270   if(ret != TBM_SURFACE_ERROR_NONE)
271   {
272     DALI_LOG_ERROR("tbm_surface_map is failed! [%d] [%p]\n", ret, tbmSurface);
273     tbm_surface_queue_cancel_dequeue(mTbmQueue, tbmSurface);
274     return NULL;
275   }
276
277   uint8_t* buffer = info.planes[0].ptr;
278   if(!buffer)
279   {
280     DALI_LOG_ERROR("tbm buffer pointer is null! [%p]\n", tbmSurface);
281     tbm_surface_unmap(tbmSurface);
282     tbm_surface_queue_cancel_dequeue(mTbmQueue, tbmSurface);
283     return NULL;
284   }
285
286   tbm_surface_internal_ref(tbmSurface);
287
288   stride = info.planes[0].stride;
289   width  = mWidth;
290   height = mHeight;
291
292   // Push the buffer
293   mBuffers.insert({buffer, tbmSurface});
294   return buffer;
295 }
296
297 bool NativeImageSourceQueueTizen::EnqueueBuffer(uint8_t* buffer)
298 {
299   Dali::Mutex::ScopedLock lock(mMutex);
300   auto                    bufferInstance = mBuffers.find(buffer);
301   if(bufferInstance != mBuffers.end())
302   {
303     tbm_surface_internal_unref((*bufferInstance).second);
304     tbm_surface_unmap((*bufferInstance).second);
305     tbm_surface_queue_enqueue(mTbmQueue, (*bufferInstance).second);
306     mBuffers.erase(bufferInstance);
307     return true;
308   }
309   return false;
310 }
311
312 void NativeImageSourceQueueTizen::FreeReleasedBuffers()
313 {
314   Dali::Mutex::ScopedLock lock(mMutex);
315   mFreeRequest = true;
316 }
317
318 bool NativeImageSourceQueueTizen::CreateResource()
319 {
320   mEglImageExtensions = mEglGraphics->GetImageExtensions();
321   DALI_ASSERT_DEBUG(mEglImageExtensions);
322
323   return true;
324 }
325
326 void NativeImageSourceQueueTizen::DestroyResource()
327 {
328   Dali::Mutex::ScopedLock lock(mMutex);
329
330   ResetEglImageList(true);
331 }
332
333 uint32_t NativeImageSourceQueueTizen::TargetTexture()
334 {
335   return 0;
336 }
337
338 void NativeImageSourceQueueTizen::PrepareTexture()
339 {
340   Dali::Mutex::ScopedLock lock(mMutex);
341
342   bool updated = false;
343
344   do
345   {
346     tbm_surface_h oldSurface = mConsumeSurface;
347
348     if(tbm_surface_queue_can_acquire(mTbmQueue, 0))
349     {
350       if(tbm_surface_queue_acquire(mTbmQueue, &mConsumeSurface) != TBM_SURFACE_QUEUE_ERROR_NONE)
351       {
352         DALI_LOG_ERROR("Failed to aquire a tbm_surface\n");
353         return;
354       }
355
356       if(oldSurface)
357       {
358         if(tbm_surface_internal_is_valid(oldSurface))
359         {
360           tbm_surface_queue_release(mTbmQueue, oldSurface);
361         }
362       }
363       updated = true;
364     }
365     else
366     {
367       break;
368     }
369   } while(mFreeRequest); // Get the last one if buffer free was requested
370
371   if(updated)
372   {
373     if(mIsResized)
374     {
375       ResetEglImageList(false);
376       mIsResized = false;
377     }
378
379     if(mConsumeSurface)
380     {
381       auto iter = mEglImages.find(mConsumeSurface);
382       if(iter == mEglImages.end())
383       {
384         // Push the surface
385         tbm_surface_internal_ref(mConsumeSurface);
386
387         void* eglImageKHR = mEglImageExtensions->CreateImageKHR(reinterpret_cast<EGLClientBuffer>(mConsumeSurface));
388         mEglImageExtensions->TargetTextureKHR(eglImageKHR);
389
390         mEglImages.insert({mConsumeSurface, eglImageKHR});
391       }
392       else
393       {
394         mEglImageExtensions->TargetTextureKHR(iter->second);
395       }
396     }
397   }
398
399   if(mFreeRequest)
400   {
401     // Destroy all egl images which is not mConsumeSurface.
402     for(auto iter = mEglImages.begin(); iter != mEglImages.end();)
403     {
404       if(iter->first == mConsumeSurface)
405       {
406         ++iter;
407       }
408       else
409       {
410         mEglImageExtensions->DestroyImageKHR(iter->second);
411         tbm_surface_internal_unref(iter->first);
412
413         iter = mEglImages.erase(iter);
414       }
415     }
416
417     tbm_surface_queue_free_flush(mTbmQueue);
418     mFreeRequest = false;
419   }
420 }
421
422 bool NativeImageSourceQueueTizen::ApplyNativeFragmentShader(std::string& shader)
423 {
424   return mEglGraphics->ApplyNativeFragmentShader(shader, SAMPLER_TYPE);
425 }
426
427 const char* NativeImageSourceQueueTizen::GetCustomSamplerTypename() const
428 {
429   return SAMPLER_TYPE;
430 }
431
432 int NativeImageSourceQueueTizen::GetTextureTarget() const
433 {
434   return GL_TEXTURE_EXTERNAL_OES;
435 }
436
437 Any NativeImageSourceQueueTizen::GetNativeImageHandle() const
438 {
439   return nullptr;
440 }
441
442 bool NativeImageSourceQueueTizen::SourceChanged() const
443 {
444   return true;
445 }
446
447 void NativeImageSourceQueueTizen::ResetEglImageList(bool releaseConsumeSurface)
448 {
449   // When Tbm surface queue is reset(resized), the surface acquired before reset() is still valid, not the others.
450   // We can still use the acquired surface so that we will release it as the oldSurface in PrepareTexture() when the next surface is ready.
451   if(releaseConsumeSurface && mConsumeSurface)
452   {
453     if(tbm_surface_internal_is_valid(mConsumeSurface))
454     {
455       tbm_surface_queue_release(mTbmQueue, mConsumeSurface);
456     }
457     mConsumeSurface = NULL;
458   }
459
460   for(auto&& iter : mEglImages)
461   {
462     mEglImageExtensions->DestroyImageKHR(iter.second);
463
464     tbm_surface_internal_unref(iter.first);
465   }
466   mEglImages.clear();
467 }
468
469 bool NativeImageSourceQueueTizen::CheckBlending(int format)
470 {
471   for(int32_t i = 0; i < NUM_FORMATS_BLENDING_REQUIRED; ++i)
472   {
473     if(format == FORMATS_BLENDING_REQUIRED[i])
474     {
475       return true;
476     }
477   }
478
479   return false;
480 }
481
482 } // namespace Adaptor
483
484 } // namespace Internal
485
486 } // namespace Dali