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