Fix related to eglImage memory
[platform/core/uifw/dali-adaptor.git] / dali / internal / imaging / tizen / native-image-source-impl-tizen.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 // CLASS HEADER
19 #include <dali/internal/imaging/tizen/native-image-source-impl-tizen.h>
20
21 // EXTERNAL INCLUDES
22 #include <dali/integration-api/debug.h>
23 #include <dali/integration-api/gl-defines.h>
24 #include <tbm_surface_internal.h>
25 #include <cstring>
26
27 // INTERNAL INCLUDES
28 #include <dali/integration-api/adaptor-framework/render-surface-interface.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
33 namespace Dali
34 {
35 namespace Internal
36 {
37 namespace Adaptor
38 {
39 namespace
40 {
41 const char* SAMPLER_TYPE = "samplerExternalOES";
42
43 // clang-format off
44 tbm_format FORMATS_BLENDING_REQUIRED[] = {
45   TBM_FORMAT_ARGB4444, TBM_FORMAT_ABGR4444,
46   TBM_FORMAT_RGBA4444, TBM_FORMAT_BGRA4444,
47   TBM_FORMAT_RGBX5551, TBM_FORMAT_BGRX5551,
48   TBM_FORMAT_ARGB1555, TBM_FORMAT_ABGR1555,
49   TBM_FORMAT_RGBA5551, TBM_FORMAT_BGRA5551,
50   TBM_FORMAT_ARGB8888, TBM_FORMAT_ABGR8888,
51   TBM_FORMAT_RGBA8888, TBM_FORMAT_BGRA8888,
52   TBM_FORMAT_ARGB2101010, TBM_FORMAT_ABGR2101010,
53   TBM_FORMAT_RGBA1010102, TBM_FORMAT_BGRA1010102
54 };
55 // clang-format on
56
57 const int NUM_FORMATS_BLENDING_REQUIRED = 18;
58
59 } // namespace
60
61 using Dali::Integration::PixelBuffer;
62
63 NativeImageSourceTizen* NativeImageSourceTizen::New(uint32_t width, uint32_t height, Dali::NativeImageSource::ColorDepth depth, Any nativeImageSource)
64 {
65   NativeImageSourceTizen* image = new NativeImageSourceTizen(width, height, depth, nativeImageSource);
66   DALI_ASSERT_DEBUG(image && "NativeImageSource allocation failed.");
67
68   if(image)
69   {
70     image->Initialize();
71   }
72
73   return image;
74 }
75
76 NativeImageSourceTizen::NativeImageSourceTizen(uint32_t width, uint32_t height, Dali::NativeImageSource::ColorDepth depth, Any nativeImageSource)
77 : mCustomFragmentPrefix(),
78   mWidth(width),
79   mHeight(height),
80   mOwnTbmSurface(false),
81   mTbmSurface(NULL),
82   mTbmFormat(0),
83   mBlendingRequired(false),
84   mColorDepth(depth),
85   mEglImageKHR(NULL),
86   mEglGraphics(NULL),
87   mEglImageExtensions(NULL),
88   mSetSource(false),
89   mMutex(),
90   mIsBufferAcquired(false)
91 {
92   DALI_ASSERT_ALWAYS(Adaptor::IsAvailable());
93
94   GraphicsInterface* graphics = &(Adaptor::GetImplementation(Adaptor::Get()).GetGraphicsInterface());
95   mEglGraphics                = static_cast<EglGraphics*>(graphics);
96
97   mCustomFragmentPrefix = mEglGraphics->GetEglImageExtensionString();
98
99   mTbmSurface = GetSurfaceFromAny(nativeImageSource);
100
101   if(mTbmSurface != NULL)
102   {
103     tbm_surface_internal_ref(mTbmSurface);
104     mBlendingRequired = CheckBlending(tbm_surface_get_format(mTbmSurface));
105     mWidth            = tbm_surface_get_width(mTbmSurface);
106     mHeight           = tbm_surface_get_height(mTbmSurface);
107   }
108 }
109
110 void NativeImageSourceTizen::Initialize()
111 {
112   if(mTbmSurface != NULL || mWidth == 0 || mHeight == 0)
113   {
114     return;
115   }
116
117   tbm_format format = TBM_FORMAT_RGB888;
118   int        depth  = 0;
119
120   switch(mColorDepth)
121   {
122     case Dali::NativeImageSource::COLOR_DEPTH_DEFAULT:
123     {
124       format = TBM_FORMAT_ARGB8888;
125       depth  = 32;
126       break;
127     }
128     case Dali::NativeImageSource::COLOR_DEPTH_8:
129     {
130       format = TBM_FORMAT_C8;
131       depth  = 8;
132       break;
133     }
134     case Dali::NativeImageSource::COLOR_DEPTH_16:
135     {
136       format = TBM_FORMAT_RGB565;
137       depth  = 16;
138       break;
139     }
140     case Dali::NativeImageSource::COLOR_DEPTH_24:
141     {
142       format = TBM_FORMAT_RGB888;
143       depth  = 24;
144       break;
145     }
146     case Dali::NativeImageSource::COLOR_DEPTH_32:
147     {
148       format = TBM_FORMAT_ARGB8888;
149       depth  = 32;
150       break;
151     }
152     default:
153     {
154       DALI_LOG_WARNING("Wrong color depth.\n");
155       return;
156     }
157   }
158
159   // set whether blending is required according to pixel format based on the depth
160   /* default pixel format is RGB888
161      If depth = 8, Pixel::A8;
162      If depth = 16, Pixel::RGB565;
163      If depth = 32, Pixel::RGBA8888 */
164   mBlendingRequired = (depth == 32 || depth == 8);
165
166   mTbmSurface    = tbm_surface_create(mWidth, mHeight, format);
167   mOwnTbmSurface = true;
168 }
169
170 tbm_surface_h NativeImageSourceTizen::GetSurfaceFromAny(Any source) const
171 {
172   if(source.Empty())
173   {
174     return NULL;
175   }
176
177   if(source.GetType() == typeid(tbm_surface_h))
178   {
179     return AnyCast<tbm_surface_h>(source);
180   }
181   else
182   {
183     return NULL;
184   }
185 }
186
187 void NativeImageSourceTizen::DestroySurface()
188 {
189   if(mTbmSurface)
190   {
191     if(mIsBufferAcquired)
192     {
193       ReleaseBuffer();
194     }
195     if(mOwnTbmSurface)
196     {
197       if(tbm_surface_destroy(mTbmSurface) != TBM_SURFACE_ERROR_NONE)
198       {
199         DALI_LOG_ERROR("Failed to destroy tbm_surface\n");
200       }
201     }
202     else
203     {
204       tbm_surface_internal_unref(mTbmSurface);
205     }
206   }
207 }
208
209 NativeImageSourceTizen::~NativeImageSourceTizen()
210 {
211   DestroySurface();
212 }
213
214 Any NativeImageSourceTizen::GetNativeImageSource() const
215 {
216   return Any(mTbmSurface);
217 }
218
219 bool NativeImageSourceTizen::GetPixels(std::vector<unsigned char>& pixbuf, unsigned& width, unsigned& height, Pixel::Format& pixelFormat) const
220 {
221   Dali::Mutex::ScopedLock lock(mMutex);
222   if(mTbmSurface != NULL)
223   {
224     tbm_surface_info_s surface_info;
225
226     if(tbm_surface_map(mTbmSurface, TBM_SURF_OPTION_READ, &surface_info) != TBM_SURFACE_ERROR_NONE)
227     {
228       DALI_LOG_ERROR("Fail to map tbm_surface\n");
229
230       width  = 0;
231       height = 0;
232
233       return false;
234     }
235
236     tbm_format     format = surface_info.format;
237     uint32_t       stride = surface_info.planes[0].stride;
238     unsigned char* ptr    = surface_info.planes[0].ptr;
239
240     width  = mWidth;
241     height = mHeight;
242     size_t lineSize;
243     size_t offset;
244     size_t cOffset;
245
246     switch(format)
247     {
248       case TBM_FORMAT_RGB888:
249       {
250         lineSize    = width * 3;
251         pixelFormat = Pixel::RGB888;
252         pixbuf.resize(lineSize * height);
253         unsigned char* bufptr = &pixbuf[0];
254
255         for(unsigned int r = 0; r < height; ++r, bufptr += lineSize)
256         {
257           for(unsigned int c = 0; c < width; ++c)
258           {
259             cOffset                 = c * 3;
260             offset                  = cOffset + r * stride;
261             *(bufptr + cOffset)     = ptr[offset + 2];
262             *(bufptr + cOffset + 1) = ptr[offset + 1];
263             *(bufptr + cOffset + 2) = ptr[offset];
264           }
265         }
266         break;
267       }
268       case TBM_FORMAT_RGBA8888:
269       {
270         lineSize    = width * 4;
271         pixelFormat = Pixel::RGBA8888;
272         pixbuf.resize(lineSize * height);
273         unsigned char* bufptr = &pixbuf[0];
274
275         for(unsigned int r = 0; r < height; ++r, bufptr += lineSize)
276         {
277           for(unsigned int c = 0; c < width; ++c)
278           {
279             cOffset                 = c * 4;
280             offset                  = cOffset + r * stride;
281             *(bufptr + cOffset)     = ptr[offset + 3];
282             *(bufptr + cOffset + 1) = ptr[offset + 2];
283             *(bufptr + cOffset + 2) = ptr[offset + 1];
284             *(bufptr + cOffset + 3) = ptr[offset];
285           }
286         }
287         break;
288       }
289       case TBM_FORMAT_ARGB8888:
290       {
291         lineSize    = width * 4;
292         pixelFormat = Pixel::RGBA8888;
293         pixbuf.resize(lineSize * height);
294         unsigned char* bufptr = &pixbuf[0];
295
296         for(unsigned int r = 0; r < height; ++r, bufptr += lineSize)
297         {
298           for(unsigned int c = 0; c < width; ++c)
299           {
300             cOffset                 = c * 4;
301             offset                  = cOffset + r * stride;
302             *(bufptr + cOffset)     = ptr[offset + 2];
303             *(bufptr + cOffset + 1) = ptr[offset + 1];
304             *(bufptr + cOffset + 2) = ptr[offset];
305             *(bufptr + cOffset + 3) = ptr[offset + 3];
306           }
307         }
308         break;
309       }
310       default:
311       {
312         DALI_ASSERT_ALWAYS(0 && "Tbm surface has unsupported pixel format.\n");
313
314         return false;
315       }
316     }
317
318     if(tbm_surface_unmap(mTbmSurface) != TBM_SURFACE_ERROR_NONE)
319     {
320       DALI_LOG_ERROR("Fail to unmap tbm_surface\n");
321     }
322
323     return true;
324   }
325
326   DALI_LOG_WARNING("TBM surface does not exist.\n");
327
328   width  = 0;
329   height = 0;
330
331   return false;
332 }
333
334 void NativeImageSourceTizen::SetSource(Any source)
335 {
336   Dali::Mutex::ScopedLock lock(mMutex);
337
338   DestroySurface();
339
340   mOwnTbmSurface = false;
341   mTbmSurface    = GetSurfaceFromAny(source);
342
343   if(mTbmSurface != NULL)
344   {
345     mSetSource = true;
346     tbm_surface_internal_ref(mTbmSurface);
347     mBlendingRequired = CheckBlending(tbm_surface_get_format(mTbmSurface));
348     mWidth            = tbm_surface_get_width(mTbmSurface);
349     mHeight           = tbm_surface_get_height(mTbmSurface);
350   }
351 }
352
353 bool NativeImageSourceTizen::IsColorDepthSupported(Dali::NativeImageSource::ColorDepth colorDepth)
354 {
355   uint32_t*  formats;
356   uint32_t   formatNum;
357   tbm_format format = TBM_FORMAT_RGB888;
358
359   switch(colorDepth)
360   {
361     case Dali::NativeImageSource::COLOR_DEPTH_DEFAULT:
362     {
363       format = TBM_FORMAT_ARGB8888;
364       break;
365     }
366     case Dali::NativeImageSource::COLOR_DEPTH_8:
367     {
368       format = TBM_FORMAT_C8;
369       break;
370     }
371     case Dali::NativeImageSource::COLOR_DEPTH_16:
372     {
373       format = TBM_FORMAT_RGB565;
374       break;
375     }
376     case Dali::NativeImageSource::COLOR_DEPTH_24:
377     {
378       format = TBM_FORMAT_RGB888;
379       break;
380     }
381     case Dali::NativeImageSource::COLOR_DEPTH_32:
382     {
383       format = TBM_FORMAT_ARGB8888;
384       break;
385     }
386   }
387
388   if(tbm_surface_query_formats(&formats, &formatNum))
389   {
390     for(unsigned int i = 0; i < formatNum; i++)
391     {
392       if(formats[i] == format)
393       {
394         free(formats);
395         return true;
396       }
397     }
398   }
399
400   free(formats);
401   return false;
402 }
403
404 bool NativeImageSourceTizen::CreateResource()
405 {
406   // If an EGL image exists, use it as it is without creating it.
407   if(mEglImageKHR != NULL)
408   {
409     return true;
410   }
411
412   // casting from an unsigned int to a void *, which should then be cast back
413   // to an unsigned int in the driver.
414   EGLClientBuffer eglBuffer = reinterpret_cast<EGLClientBuffer>(mTbmSurface);
415   if(!eglBuffer || !tbm_surface_internal_is_valid(mTbmSurface))
416   {
417     return false;
418   }
419
420   mEglImageExtensions = mEglGraphics->GetImageExtensions();
421   DALI_ASSERT_DEBUG(mEglImageExtensions);
422
423   mEglImageKHR = mEglImageExtensions->CreateImageKHR(eglBuffer);
424
425   return mEglImageKHR != NULL;
426 }
427
428 void NativeImageSourceTizen::DestroyResource()
429 {
430   Dali::Mutex::ScopedLock lock(mMutex);
431   if(mEglImageKHR)
432   {
433     mEglImageExtensions->DestroyImageKHR(mEglImageKHR);
434
435     mEglImageKHR = NULL;
436   }
437 }
438
439 uint32_t NativeImageSourceTizen::TargetTexture()
440 {
441   mEglImageExtensions->TargetTextureKHR(mEglImageKHR);
442
443   return 0;
444 }
445
446 void NativeImageSourceTizen::PrepareTexture()
447 {
448   Dali::Mutex::ScopedLock lock(mMutex);
449   if(mSetSource)
450   {
451     // Destroy previous eglImage because use for new one.
452     // if mEglImageKHR is not to be NULL here, it will not be updated with a new eglImage.
453     mEglImageExtensions->DestroyImageKHR(mEglImageKHR);
454     mEglImageKHR = NULL;
455
456     if(CreateResource())
457     {
458       TargetTexture();
459     }
460
461     mSetSource = false;
462   }
463 }
464
465 const char* NativeImageSourceTizen::GetCustomFragmentPrefix() const
466 {
467   return mCustomFragmentPrefix;
468 }
469
470 const char* NativeImageSourceTizen::GetCustomSamplerTypename() const
471 {
472   return SAMPLER_TYPE;
473 }
474
475 int NativeImageSourceTizen::GetTextureTarget() const
476 {
477   return GL_TEXTURE_EXTERNAL_OES;
478 }
479
480 Any NativeImageSourceTizen::GetNativeImageHandle() const
481 {
482   return GetNativeImageSource();
483 }
484
485 bool NativeImageSourceTizen::SourceChanged() const
486 {
487   return false;
488 }
489
490 bool NativeImageSourceTizen::CheckBlending(tbm_format format)
491 {
492   if(mTbmFormat != format)
493   {
494     for(int i = 0; i < NUM_FORMATS_BLENDING_REQUIRED; ++i)
495     {
496       if(format == FORMATS_BLENDING_REQUIRED[i])
497       {
498         mBlendingRequired = true;
499         break;
500       }
501     }
502     mTbmFormat = format;
503   }
504
505   return mBlendingRequired;
506 }
507
508 uint8_t* NativeImageSourceTizen::AcquireBuffer(uint16_t& width, uint16_t& height, uint16_t& stride)
509 {
510   Dali::Mutex::ScopedLock lock(mMutex);
511   if(mTbmSurface != NULL)
512   {
513     tbm_surface_info_s info;
514
515     if(tbm_surface_map(mTbmSurface, TBM_SURF_OPTION_READ | TBM_SURF_OPTION_WRITE, &info) != TBM_SURFACE_ERROR_NONE)
516     {
517       DALI_LOG_ERROR("Fail to map tbm_surface\n");
518
519       width  = 0;
520       height = 0;
521
522       return NULL;
523     }
524     tbm_surface_internal_ref(mTbmSurface);
525     mIsBufferAcquired = true;
526
527     stride = info.planes[0].stride;
528     width  = mWidth;
529     height = mHeight;
530
531     return info.planes[0].ptr;
532   }
533   return NULL;
534 }
535
536 bool NativeImageSourceTizen::ReleaseBuffer()
537 {
538   Dali::Mutex::ScopedLock lock(mMutex);
539   bool                    ret = false;
540   if(mTbmSurface != NULL)
541   {
542     ret = (tbm_surface_unmap(mTbmSurface) == TBM_SURFACE_ERROR_NONE);
543     if(!ret)
544     {
545       DALI_LOG_ERROR("Fail to unmap tbm_surface\n");
546     }
547     tbm_surface_internal_unref(mTbmSurface);
548     mIsBufferAcquired = false;
549   }
550   return ret;
551 }
552
553 } // namespace Adaptor
554
555 } // namespace Internal
556
557 } // namespace Dali