2151d5bc3921e75df4643f6bf87b1b956ee8d6b0
[platform/core/uifw/dali-adaptor.git] / dali / internal / graphics / gles-impl / gles-graphics-texture.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 "gles-graphics-texture.h"
20
21 // EXTERNAL INCLUDES
22 #include <dali/integration-api/debug.h>
23 #include <dali/integration-api/gl-abstraction.h>
24 #include <dali/integration-api/gl-defines.h>
25 #include <vector>
26
27 // INTERNAL INCLUDES
28 #include "egl-graphics-controller.h"
29 #include "gles-graphics-sampler.h"
30 #include "gles-graphics-types.h"
31
32 namespace
33 {
34 // These match the GL specification
35 //const int32_t GL_MINIFY_DEFAULT  = GL_NEAREST_MIPMAP_LINEAR;
36 //const int32_t GL_MAGNIFY_DEFAULT = GL_LINEAR;
37 const int32_t GL_WRAP_DEFAULT = GL_CLAMP_TO_EDGE;
38
39 // These are the Dali defaults
40 const int32_t DALI_MINIFY_DEFAULT  = GL_LINEAR;
41 const int32_t DALI_MAGNIFY_DEFAULT = GL_LINEAR;
42 } // namespace
43
44 namespace Dali::Graphics::GLES
45 {
46 struct ColorConversion
47 {
48   Format srcFormat;
49   Format destFormat;
50   std::vector<uint8_t> (*pConversionFunc)(const void*, uint32_t, uint32_t, uint32_t, uint32_t);
51   void (*pConversionWriteFunc)(const void*, uint32_t, uint32_t, uint32_t, uint32_t, void*);
52 };
53
54 inline void WriteRGB32ToRGBA32(const void* __restrict__ pData, uint32_t sizeInBytes, uint32_t width, uint32_t height, uint32_t rowStride, void* __restrict__ pOutput)
55 {
56   const uint8_t* __restrict__ inData = reinterpret_cast<const uint8_t*>(pData);
57   uint8_t* __restrict__ outData      = reinterpret_cast<uint8_t*>(pOutput);
58   if(rowStride == 0u)
59   {
60     rowStride = width;
61   }
62   for(auto y = 0u; y < height; ++y)
63   {
64     auto inIdx  = 0u;
65     auto outIdx = 0u;
66     for(auto x = 0u; x < width; ++x)
67     {
68       outData[outIdx]     = inData[inIdx];
69       outData[outIdx + 1] = inData[inIdx + 1];
70       outData[outIdx + 2] = inData[inIdx + 2];
71       outData[outIdx + 3] = 0xff;
72       outIdx += 4;
73       inIdx += 3;
74     }
75     inData += rowStride * 3u;
76     outData += width * 4u;
77   }
78 }
79
80 /**
81  * Converts RGB to RGBA
82  */
83 inline std::vector<uint8_t> ConvertRGB32ToRGBA32(const void* pData, uint32_t sizeInBytes, uint32_t width, uint32_t height, uint32_t rowStride)
84 {
85   std::vector<uint8_t> rgbaBuffer{};
86   rgbaBuffer.resize(width * height * 4);
87   WriteRGB32ToRGBA32(pData, sizeInBytes, width, height, rowStride, &rgbaBuffer[0]);
88   return rgbaBuffer;
89 }
90
91 /**
92  * Format conversion table
93  */
94 const std::vector<ColorConversion>& GetColorConversionTable()
95 {
96   static const std::vector<ColorConversion> COLOR_CONVERSION_TABLE = {
97     {Format::R8G8B8_UNORM, Format::R8G8B8A8_UNORM, ConvertRGB32ToRGBA32, WriteRGB32ToRGBA32}};
98   return COLOR_CONVERSION_TABLE;
99 }
100
101 /**
102  * Constructor
103  */
104 Texture::Texture(const Graphics::TextureCreateInfo& createInfo, Graphics::EglGraphicsController& controller)
105 : TextureResource(createInfo, controller)
106 {
107   // If there is any data, move it into staging buffer
108   if(mCreateInfo.data && mCreateInfo.dataSize)
109   {
110     mStagingBuffer.resize(size_t(mCreateInfo.dataSize));
111     std::copy(reinterpret_cast<char*>(mCreateInfo.data),
112               reinterpret_cast<char*>(mCreateInfo.data) + mCreateInfo.dataSize,
113               mStagingBuffer.begin());
114   }
115
116   // Add texture to the Resource queue
117   mController.AddTexture(*this);
118 }
119
120 bool Texture::InitializeResource()
121 {
122   if(mCreateInfo.nativeImagePtr)
123   {
124     return InitializeNativeImage();
125   }
126   return InitializeTexture();
127 }
128
129 bool Texture::InitializeNativeImage()
130 {
131   auto   context = mController.GetCurrentContext();
132   auto   gl      = mController.GetGL();
133   GLuint texture{0};
134
135   if(!gl || !context)
136   {
137     // Do nothing during shutdown
138     return false;
139   }
140
141   NativeImageInterfacePtr nativeImage = mCreateInfo.nativeImagePtr;
142   bool                    created     = nativeImage->CreateResource();
143   mGlTarget                           = nativeImage->GetTextureTarget();
144   if(created)
145   {
146     gl->GenTextures(1, &texture);
147     context->BindTexture(mGlTarget, GetTextureTypeId(), texture);
148
149     gl->PixelStorei(GL_UNPACK_ALIGNMENT, 1); // We always use tightly packed data
150
151     // Apply default sampling parameters
152     SetSamplerParameter(GL_TEXTURE_MIN_FILTER, mDefaultSamplerState.minFilter, DALI_MINIFY_DEFAULT);
153     SetSamplerParameter(GL_TEXTURE_MAG_FILTER, mDefaultSamplerState.magFilter, DALI_MAGNIFY_DEFAULT);
154     SetSamplerParameter(GL_TEXTURE_WRAP_S, mDefaultSamplerState.wrapS, GL_WRAP_DEFAULT);
155     SetSamplerParameter(GL_TEXTURE_WRAP_T, mDefaultSamplerState.wrapT, GL_WRAP_DEFAULT);
156
157     // platform specific implementation decides on what GL extension to use
158     if(nativeImage->TargetTexture() != 0u)
159     {
160       gl->DeleteTextures(1, &texture);
161       nativeImage->DestroyResource();
162       texture = 0u;
163       created = false;
164     }
165     else
166     {
167       mTextureId = texture;
168     }
169   }
170   else
171   {
172     DALI_LOG_ERROR("Native Image: InitializeNativeImage, CreateResource() failed\n");
173   }
174
175   return created; // WARNING! May be false! Needs handling! (Well, initialized on bind)
176 }
177
178 bool Texture::InitializeTexture()
179 {
180   auto context = mController.GetCurrentContext();
181   auto gl      = mController.GetGL();
182   if(!gl || !context)
183   {
184     // Do nothing during shutdown
185     return false;
186   }
187
188   GLuint texture{0};
189
190   mGlTarget     = GLTextureTarget(mCreateInfo.textureType).target;
191   mIsCompressed = Graphics::GLES::FormatCompression(mCreateInfo.format).compressed;
192
193   switch(mCreateInfo.textureType)
194   {
195     // Texture 2D
196     case Graphics::TextureType::TEXTURE_2D:
197     {
198       Graphics::GLES::GLTextureFormatType format(mCreateInfo.format);
199
200       // TODO: find better condition, with this test the L8 doesn't work
201       if(1) //format.format && format.type)
202       {
203         // Bind texture
204         gl->GenTextures(1, &texture);
205         context->BindTexture(GL_TEXTURE_2D, GetTextureTypeId(), texture);
206
207         if(mCreateInfo.allocationPolicy == Graphics::TextureAllocationPolicy::CREATION || mCreateInfo.data)
208         {
209           // Allocate memory for the texture
210           if(!mIsCompressed)
211           {
212             gl->TexImage2D(GL_TEXTURE_2D,
213                            0,
214                            format.internalFormat,
215                            mCreateInfo.size.width,
216                            mCreateInfo.size.height,
217                            0,
218                            format.format,
219                            format.type,
220                            (mCreateInfo.data ? mStagingBuffer.data() : nullptr));
221           }
222           else
223           {
224             gl->CompressedTexImage2D(GL_TEXTURE_2D,
225                                      0,
226                                      format.internalFormat,
227                                      mCreateInfo.size.width,
228                                      mCreateInfo.size.height,
229                                      0,
230                                      mCreateInfo.dataSize,
231                                      (mCreateInfo.data ? mStagingBuffer.data() : nullptr));
232           }
233         }
234
235         // Clear staging buffer if there was any
236         mStagingBuffer.clear();
237         mTextureId = texture;
238         // Default texture filtering (to be set later via command buffer binding)
239         SetSamplerParameter(GL_TEXTURE_MIN_FILTER, mDefaultSamplerState.minFilter, Graphics::GLES::GLSamplerFilterAndMipMapMode(Graphics::SamplerFilter::LINEAR, SamplerMipmapMode::NONE));
240         SetSamplerParameter(GL_TEXTURE_MAG_FILTER, mDefaultSamplerState.magFilter, Graphics::GLES::GLSamplerFilterAndMipMapMode(Graphics::SamplerFilter::LINEAR, SamplerMipmapMode::NONE));
241         SetSamplerParameter(GL_TEXTURE_WRAP_S, mDefaultSamplerState.wrapS, GL_WRAP_DEFAULT);
242         SetSamplerParameter(GL_TEXTURE_WRAP_T, mDefaultSamplerState.wrapT, GL_WRAP_DEFAULT);
243       }
244       break;
245     }
246     // Texture Cubemap
247     case Graphics::TextureType::TEXTURE_CUBEMAP:
248     {
249       Graphics::GLES::GLTextureFormatType format(mCreateInfo.format);
250
251       if(format.format && format.type)
252       {
253         // Bind texture
254         gl->GenTextures(1, &texture);
255         context->BindTexture(GL_TEXTURE_CUBE_MAP, GetTextureTypeId(), texture);
256         gl->PixelStorei(GL_UNPACK_ALIGNMENT, 1); // We always use tightly packed data
257
258         SetSamplerParameter(GL_TEXTURE_MIN_FILTER, mDefaultSamplerState.minFilter, Graphics::GLES::GLSamplerFilterAndMipMapMode(Graphics::SamplerFilter::LINEAR, SamplerMipmapMode::NONE));
259         SetSamplerParameter(GL_TEXTURE_MAG_FILTER, mDefaultSamplerState.magFilter, Graphics::GLES::GLSamplerFilterAndMipMapMode(Graphics::SamplerFilter::LINEAR, SamplerMipmapMode::NONE));
260         SetSamplerParameter(GL_TEXTURE_WRAP_S, mDefaultSamplerState.wrapS, GL_WRAP_DEFAULT);
261         SetSamplerParameter(GL_TEXTURE_WRAP_T, mDefaultSamplerState.wrapT, GL_WRAP_DEFAULT);
262
263         if(mCreateInfo.allocationPolicy == Graphics::TextureAllocationPolicy::CREATION || mCreateInfo.data)
264         {
265           // Allocate memory for the texture
266           for(uint32_t i = 0; i < 6; ++i)
267           {
268             if(!mIsCompressed)
269             {
270               gl->TexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i,
271                              0,
272                              format.internalFormat,
273                              mCreateInfo.size.width,
274                              mCreateInfo.size.height,
275                              0,
276                              format.format,
277                              format.type,
278                              (mCreateInfo.data ? mStagingBuffer.data() : nullptr));
279             }
280             else
281             {
282               gl->CompressedTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i,
283                                        0,
284                                        format.internalFormat,
285                                        mCreateInfo.size.width,
286                                        mCreateInfo.size.height,
287                                        0,
288                                        mCreateInfo.dataSize,
289                                        (mCreateInfo.data ? mStagingBuffer.data() : nullptr));
290             }
291           }
292         }
293
294         // Clear staging buffer if there was any
295         mStagingBuffer.clear();
296
297         mTextureId = texture;
298
299         SetSamplerParameter(GL_TEXTURE_WRAP_R, mDefaultSamplerState.wrapR, GL_WRAP_DEFAULT);
300       }
301       break;
302     }
303     default:
304     {
305       // nothing?
306     }
307   }
308   return true;
309 }
310
311 void Texture::DestroyResource()
312 {
313   auto gl = mController.GetGL();
314   if(!gl)
315   {
316     return;
317   }
318
319   // This is a proper destructor
320   if(mTextureId)
321   {
322     gl->DeleteTextures(1, &mTextureId);
323   }
324   if(mCreateInfo.nativeImagePtr)
325   {
326     mCreateInfo.nativeImagePtr->DestroyResource();
327   }
328 }
329
330 void Texture::DiscardResource()
331 {
332   mController.DiscardResource(this);
333 }
334
335 void Texture::Bind(const TextureBinding& binding) const
336 {
337   auto context = mController.GetCurrentContext();
338   auto gl      = mController.GetGL();
339   if(!gl || !context)
340   {
341     // Do nothing during shutdown
342     return;
343   }
344
345   context->ActiveTexture(binding.binding);
346   context->BindTexture(mGlTarget, GetTextureTypeId(), mTextureId);
347
348   // For GLES2 if there is a sampler set in the binding
349   if(binding.sampler)
350   {
351     const auto& samplerCreateInfo = static_cast<const GLES::Sampler*>(binding.sampler)->GetCreateInfo();
352
353     auto mipMapMode = samplerCreateInfo.mipMapMode;
354
355     // @todo : Should we always ignore mipmap mode when it is compressed, and never bind higher level mipmap?
356     if(mMaxMipMapLevel == 0u && mIsCompressed)
357     {
358       mipMapMode = Graphics::SamplerMipmapMode::NONE;
359     }
360
361     SetSamplerParameter(GL_TEXTURE_MIN_FILTER, mDefaultSamplerState.minFilter, GLSamplerFilterAndMipMapMode(samplerCreateInfo.minFilter, mipMapMode).glFilter);
362     SetSamplerParameter(GL_TEXTURE_MAG_FILTER, mDefaultSamplerState.magFilter, GLSamplerFilter(samplerCreateInfo.magFilter).glFilter);
363     SetSamplerParameter(GL_TEXTURE_WRAP_S, mDefaultSamplerState.wrapS, GLAddressMode(samplerCreateInfo.addressModeU).texParameter);
364     SetSamplerParameter(GL_TEXTURE_WRAP_T, mDefaultSamplerState.wrapT, GLAddressMode(samplerCreateInfo.addressModeV).texParameter);
365
366     if(mGlTarget == GL_TEXTURE_CUBE_MAP)
367     {
368       SetSamplerParameter(GL_TEXTURE_WRAP_R, mDefaultSamplerState.wrapR, GLAddressMode(samplerCreateInfo.addressModeW).texParameter);
369     }
370   }
371   else
372   {
373     SetSamplerParameter(GL_TEXTURE_MIN_FILTER, mDefaultSamplerState.minFilter, DALI_MINIFY_DEFAULT);
374     SetSamplerParameter(GL_TEXTURE_MAG_FILTER, mDefaultSamplerState.magFilter, DALI_MAGNIFY_DEFAULT);
375     SetSamplerParameter(GL_TEXTURE_WRAP_S, mDefaultSamplerState.wrapS, GL_WRAP_DEFAULT);
376     SetSamplerParameter(GL_TEXTURE_WRAP_T, mDefaultSamplerState.wrapT, GL_WRAP_DEFAULT);
377     if(mGlTarget == GL_TEXTURE_CUBE_MAP)
378     {
379       SetSamplerParameter(GL_TEXTURE_WRAP_R, mDefaultSamplerState.wrapR, GL_WRAP_DEFAULT);
380     }
381   }
382
383   if(mMaxMipMapLevel)
384   {
385     SetSamplerParameter(GL_TEXTURE_MAX_LEVEL, mDefaultSamplerState.maxLevel, mMaxMipMapLevel);
386   }
387 }
388
389 void Texture::Prepare()
390 {
391   NativeImageInterfacePtr nativeImage = mCreateInfo.nativeImagePtr;
392   if(nativeImage)
393   {
394     nativeImage->PrepareTexture();
395   }
396 }
397
398 /**
399  * This function tests whether format is supported by the driver. If possible it applies
400  * format conversion to suitable supported pixel format.
401  */
402 bool Texture::TryConvertPixelData(const void* pData, Graphics::Format srcFormat, Graphics::Format destFormat, uint32_t sizeInBytes, uint32_t inStride, uint32_t width, uint32_t height, std::vector<uint8_t>& outputBuffer)
403 {
404   // No need to convert
405   if(srcFormat == destFormat)
406   {
407     return false;
408   }
409
410   auto it = std::find_if(GetColorConversionTable().begin(), GetColorConversionTable().end(), [&](auto& item) {
411     return item.srcFormat == srcFormat && item.destFormat == destFormat;
412   });
413
414   // No suitable format, return empty array
415   if(it == GetColorConversionTable().end())
416   {
417     return false;
418   }
419   auto begin = reinterpret_cast<const uint8_t*>(pData);
420
421   outputBuffer = std::move(it->pConversionFunc(begin, sizeInBytes, width, height, inStride));
422   return !outputBuffer.empty();
423 }
424
425 void Texture::SetSamplerParameter(uint32_t param, uint32_t& cacheValue, uint32_t value) const
426 {
427   if(cacheValue != value)
428   {
429     auto gl = mController.GetGL();
430     gl->TexParameteri(mGlTarget, param, value);
431     cacheValue = value;
432   }
433 }
434
435 } // namespace Dali::Graphics::GLES