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