2 * Copyright (c) 2023 Samsung Electronics Co., Ltd.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 #include "gles-graphics-texture.h"
22 #include <dali/integration-api/debug.h>
23 #include <dali/integration-api/gl-abstraction.h>
24 #include <dali/integration-api/gl-defines.h>
28 #include "egl-graphics-controller.h"
29 #include "gles-graphics-sampler.h"
30 #include "gles-graphics-types.h"
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;
39 // These are the Dali defaults
40 const int32_t DALI_MINIFY_DEFAULT = GL_LINEAR;
41 const int32_t DALI_MAGNIFY_DEFAULT = GL_LINEAR;
44 namespace Dali::Graphics::GLES
46 struct ColorConversion
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*);
54 inline void WriteRGB32ToRGBA32(const void* __restrict__ pData, uint32_t sizeInBytes, uint32_t width, uint32_t height, uint32_t rowStride, void* __restrict__ pOutput)
56 const uint8_t* __restrict__ inData = reinterpret_cast<const uint8_t*>(pData);
57 uint8_t* __restrict__ outData = reinterpret_cast<uint8_t*>(pOutput);
62 for(auto y = 0u; y < height; ++y)
66 for(auto x = 0u; x < width; ++x)
68 outData[outIdx] = inData[inIdx];
69 outData[outIdx + 1] = inData[inIdx + 1];
70 outData[outIdx + 2] = inData[inIdx + 2];
71 outData[outIdx + 3] = 0xff;
75 inData += rowStride * 3u;
76 outData += width * 4u;
81 * Converts RGB to RGBA
83 inline std::vector<uint8_t> ConvertRGB32ToRGBA32(const void* pData, uint32_t sizeInBytes, uint32_t width, uint32_t height, uint32_t rowStride)
85 std::vector<uint8_t> rgbaBuffer{};
86 rgbaBuffer.resize(width * height * 4);
87 WriteRGB32ToRGBA32(pData, sizeInBytes, width, height, rowStride, &rgbaBuffer[0]);
92 * Format conversion table
94 const std::vector<ColorConversion>& GetColorConversionTable()
96 static const std::vector<ColorConversion> COLOR_CONVERSION_TABLE = {
97 {Format::R8G8B8_UNORM, Format::R8G8B8A8_UNORM, ConvertRGB32ToRGBA32, WriteRGB32ToRGBA32}};
98 return COLOR_CONVERSION_TABLE;
104 Texture::Texture(const Graphics::TextureCreateInfo& createInfo, Graphics::EglGraphicsController& controller)
105 : TextureResource(createInfo, controller)
107 // If there is any data, move it into staging buffer
108 if(mCreateInfo.data && mCreateInfo.dataSize)
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());
116 // Add texture to the Resource queue
117 mController.AddTexture(*this);
120 bool Texture::InitializeResource()
122 if(mCreateInfo.nativeImagePtr)
124 return InitializeNativeImage();
126 return InitializeTexture();
129 bool Texture::InitializeNativeImage()
131 auto context = mController.GetCurrentContext();
132 auto gl = mController.GetGL();
137 // Do nothing during shutdown
141 NativeImageInterfacePtr nativeImage = mCreateInfo.nativeImagePtr;
142 bool created = nativeImage->CreateResource();
143 mGlTarget = nativeImage->GetTextureTarget();
146 gl->GenTextures(1, &texture);
147 context->BindTexture(mGlTarget, GetTextureTypeId(), texture);
149 gl->PixelStorei(GL_UNPACK_ALIGNMENT, 1); // We always use tightly packed data
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);
157 // platform specific implementation decides on what GL extension to use
158 if(nativeImage->TargetTexture() != 0u)
160 gl->DeleteTextures(1, &texture);
161 nativeImage->DestroyResource();
167 mTextureId = texture;
172 DALI_LOG_ERROR("Native Image: InitializeNativeImage, CreateResource() failed\n");
175 return created; // WARNING! May be false! Needs handling! (Well, initialized on bind)
178 bool Texture::InitializeTexture()
180 auto context = mController.GetCurrentContext();
181 auto gl = mController.GetGL();
184 // Do nothing during shutdown
190 mGlTarget = GLTextureTarget(mCreateInfo.textureType).target;
191 mIsCompressed = Graphics::GLES::FormatCompression(mCreateInfo.format).compressed;
193 switch(mCreateInfo.textureType)
196 case Graphics::TextureType::TEXTURE_2D:
198 Graphics::GLES::GLTextureFormatType format(mCreateInfo.format);
200 // TODO: find better condition, with this test the L8 doesn't work
201 if(1) //format.format && format.type)
204 gl->GenTextures(1, &texture);
205 context->BindTexture(GL_TEXTURE_2D, GetTextureTypeId(), texture);
207 if(mCreateInfo.allocationPolicy == Graphics::TextureAllocationPolicy::CREATION || mCreateInfo.data)
209 // Allocate memory for the texture
212 gl->TexImage2D(GL_TEXTURE_2D,
214 format.internalFormat,
215 mCreateInfo.size.width,
216 mCreateInfo.size.height,
220 (mCreateInfo.data ? mStagingBuffer.data() : nullptr));
224 gl->CompressedTexImage2D(GL_TEXTURE_2D,
226 format.internalFormat,
227 mCreateInfo.size.width,
228 mCreateInfo.size.height,
230 mCreateInfo.dataSize,
231 (mCreateInfo.data ? mStagingBuffer.data() : nullptr));
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);
247 case Graphics::TextureType::TEXTURE_CUBEMAP:
249 Graphics::GLES::GLTextureFormatType format(mCreateInfo.format);
251 if(format.format && format.type)
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
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);
263 if(mCreateInfo.allocationPolicy == Graphics::TextureAllocationPolicy::CREATION || mCreateInfo.data)
265 // Allocate memory for the texture
266 for(uint32_t i = 0; i < 6; ++i)
270 gl->TexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i,
272 format.internalFormat,
273 mCreateInfo.size.width,
274 mCreateInfo.size.height,
278 (mCreateInfo.data ? mStagingBuffer.data() : nullptr));
282 gl->CompressedTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i,
284 format.internalFormat,
285 mCreateInfo.size.width,
286 mCreateInfo.size.height,
288 mCreateInfo.dataSize,
289 (mCreateInfo.data ? mStagingBuffer.data() : nullptr));
294 // Clear staging buffer if there was any
295 mStagingBuffer.clear();
297 mTextureId = texture;
299 SetSamplerParameter(GL_TEXTURE_WRAP_R, mDefaultSamplerState.wrapR, GL_WRAP_DEFAULT);
311 void Texture::DestroyResource()
313 auto gl = mController.GetGL();
319 // This is a proper destructor
322 gl->DeleteTextures(1, &mTextureId);
324 if(mCreateInfo.nativeImagePtr)
326 mCreateInfo.nativeImagePtr->DestroyResource();
330 void Texture::DiscardResource()
332 mController.DiscardResource(this);
335 void Texture::Bind(const TextureBinding& binding) const
337 auto context = mController.GetCurrentContext();
338 auto gl = mController.GetGL();
341 // Do nothing during shutdown
345 context->ActiveTexture(binding.binding);
346 context->BindTexture(mGlTarget, GetTextureTypeId(), mTextureId);
348 // For GLES2 if there is a sampler set in the binding
351 const auto& samplerCreateInfo = static_cast<const GLES::Sampler*>(binding.sampler)->GetCreateInfo();
353 auto mipMapMode = samplerCreateInfo.mipMapMode;
355 // @todo : Should we always ignore mipmap mode when it is compressed, and never bind higher level mipmap?
356 if(mMaxMipMapLevel == 0u && mIsCompressed)
358 mipMapMode = Graphics::SamplerMipmapMode::NONE;
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);
366 if(mGlTarget == GL_TEXTURE_CUBE_MAP)
368 SetSamplerParameter(GL_TEXTURE_WRAP_R, mDefaultSamplerState.wrapR, GLAddressMode(samplerCreateInfo.addressModeW).texParameter);
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)
379 SetSamplerParameter(GL_TEXTURE_WRAP_R, mDefaultSamplerState.wrapR, GL_WRAP_DEFAULT);
385 SetSamplerParameter(GL_TEXTURE_MAX_LEVEL, mDefaultSamplerState.maxLevel, mMaxMipMapLevel);
389 void Texture::Prepare()
391 NativeImageInterfacePtr nativeImage = mCreateInfo.nativeImagePtr;
394 nativeImage->PrepareTexture();
399 * This function tests whether format is supported by the driver. If possible it applies
400 * format conversion to suitable supported pixel format.
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)
404 // No need to convert
405 if(srcFormat == destFormat)
410 auto it = std::find_if(GetColorConversionTable().begin(), GetColorConversionTable().end(), [&](auto& item) {
411 return item.srcFormat == srcFormat && item.destFormat == destFormat;
414 // No suitable format, return empty array
415 if(it == GetColorConversionTable().end())
419 auto begin = reinterpret_cast<const uint8_t*>(pData);
421 outputBuffer = std::move(it->pConversionFunc(begin, sizeInBytes, width, height, inStride));
422 return !outputBuffer.empty();
425 void Texture::SetSamplerParameter(uint32_t param, uint32_t& cacheValue, uint32_t value) const
427 if(cacheValue != value)
429 auto gl = mController.GetGL();
430 gl->TexParameteri(mGlTarget, param, value);
435 } // namespace Dali::Graphics::GLES