/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
namespace Dali::Graphics::GLES
{
+struct ColorConversion
+{
+ Format srcFormat;
+ Format destFormat;
+ std::vector<uint8_t> (*pConversionFunc)(const void*, uint32_t, uint32_t, uint32_t, uint32_t);
+ void (*pConversionWriteFunc)(const void*, uint32_t, uint32_t, uint32_t, uint32_t, void*);
+};
+
+inline void WriteRGB32ToRGBA32(const void* __restrict__ pData, uint32_t sizeInBytes, uint32_t width, uint32_t height, uint32_t rowStride, void* __restrict__ pOutput)
+{
+ const uint8_t* __restrict__ inData = reinterpret_cast<const uint8_t*>(pData);
+ uint8_t* __restrict__ outData = reinterpret_cast<uint8_t*>(pOutput);
+ if(rowStride == 0u)
+ {
+ rowStride = width;
+ }
+ for(auto y = 0u; y < height; ++y)
+ {
+ auto inIdx = 0u;
+ auto outIdx = 0u;
+ for(auto x = 0u; x < width; ++x)
+ {
+ outData[outIdx] = inData[inIdx];
+ outData[outIdx + 1] = inData[inIdx + 1];
+ outData[outIdx + 2] = inData[inIdx + 2];
+ outData[outIdx + 3] = 0xff;
+ outIdx += 4;
+ inIdx += 3;
+ }
+ inData += rowStride * 3u;
+ outData += width * 4u;
+ }
+}
+
+/**
+ * Converts RGB to RGBA
+ */
+inline std::vector<uint8_t> ConvertRGB32ToRGBA32(const void* pData, uint32_t sizeInBytes, uint32_t width, uint32_t height, uint32_t rowStride)
+{
+ std::vector<uint8_t> rgbaBuffer{};
+ rgbaBuffer.resize(width * height * 4);
+ WriteRGB32ToRGBA32(pData, sizeInBytes, width, height, rowStride, &rgbaBuffer[0]);
+ return rgbaBuffer;
+}
+
+/**
+ * Format conversion table
+ */
+const std::vector<ColorConversion>& GetColorConversionTable()
+{
+ static const std::vector<ColorConversion> COLOR_CONVERSION_TABLE = {
+ {Format::R8G8B8_UNORM, Format::R8G8B8A8_UNORM, ConvertRGB32ToRGBA32, WriteRGB32ToRGBA32}};
+ return COLOR_CONVERSION_TABLE;
+}
+
+/**
+ * Constructor
+ */
Texture::Texture(const Graphics::TextureCreateInfo& createInfo, Graphics::EglGraphicsController& controller)
: TextureResource(createInfo, controller)
{
bool Texture::InitializeNativeImage()
{
- auto gl = mController.GetGL();
+ auto context = mController.GetCurrentContext();
+ auto gl = mController.GetGL();
GLuint texture{0};
- if(!gl)
+ if(!gl || !context)
{
// Do nothing during shutdown
return false;
if(created)
{
gl->GenTextures(1, &texture);
- gl->BindTexture(mGlTarget, texture);
+ context->BindTexture(mGlTarget, GetTextureTypeId(), texture);
gl->PixelStorei(GL_UNPACK_ALIGNMENT, 1); // We always use tightly packed data
// Apply default sampling parameters
- gl->TexParameteri(mGlTarget, GL_TEXTURE_MIN_FILTER, DALI_MINIFY_DEFAULT);
- gl->TexParameteri(mGlTarget, GL_TEXTURE_MAG_FILTER, DALI_MAGNIFY_DEFAULT);
- gl->TexParameteri(mGlTarget, GL_TEXTURE_WRAP_S, GL_WRAP_DEFAULT);
- gl->TexParameteri(mGlTarget, GL_TEXTURE_WRAP_T, GL_WRAP_DEFAULT);
+ SetSamplerParameter(GL_TEXTURE_MIN_FILTER, mDefaultSamplerState.minFilter, DALI_MINIFY_DEFAULT);
+ SetSamplerParameter(GL_TEXTURE_MAG_FILTER, mDefaultSamplerState.magFilter, DALI_MAGNIFY_DEFAULT);
+ SetSamplerParameter(GL_TEXTURE_WRAP_S, mDefaultSamplerState.wrapS, GL_WRAP_DEFAULT);
+ SetSamplerParameter(GL_TEXTURE_WRAP_T, mDefaultSamplerState.wrapT, GL_WRAP_DEFAULT);
// platform specific implementation decides on what GL extension to use
if(nativeImage->TargetTexture() != 0u)
bool Texture::InitializeTexture()
{
- auto gl = mController.GetGL();
- if(!gl)
+ auto context = mController.GetCurrentContext();
+ auto gl = mController.GetGL();
+ if(!gl || !context)
{
// Do nothing during shutdown
return false;
GLuint texture{0};
- mGlTarget = GLTextureTarget(mCreateInfo.textureType).target;
+ mGlTarget = GLTextureTarget(mCreateInfo.textureType).target;
+ mIsCompressed = Graphics::GLES::FormatCompression(mCreateInfo.format).compressed;
switch(mCreateInfo.textureType)
{
{
// Bind texture
gl->GenTextures(1, &texture);
- gl->BindTexture(GL_TEXTURE_2D, texture);
+ context->BindTexture(GL_TEXTURE_2D, GetTextureTypeId(), texture);
// Allocate memory for the texture
- gl->TexImage2D(GL_TEXTURE_2D,
- 0,
- format.format,
- mCreateInfo.size.width,
- mCreateInfo.size.height,
- 0,
- format.format,
- format.type,
- (mCreateInfo.data ? mStagingBuffer.data() : nullptr));
+ if(!mIsCompressed)
+ {
+ gl->TexImage2D(GL_TEXTURE_2D,
+ 0,
+ format.internalFormat,
+ mCreateInfo.size.width,
+ mCreateInfo.size.height,
+ 0,
+ format.format,
+ format.type,
+ (mCreateInfo.data ? mStagingBuffer.data() : nullptr));
+ }
+ else
+ {
+ gl->CompressedTexImage2D(GL_TEXTURE_2D,
+ 0,
+ format.internalFormat,
+ mCreateInfo.size.width,
+ mCreateInfo.size.height,
+ 0,
+ mCreateInfo.dataSize,
+ (mCreateInfo.data ? mStagingBuffer.data() : nullptr));
+ }
+
+ // Clear staging buffer if there was any
+ mStagingBuffer.clear();
+ mTextureId = texture;
+ // Default texture filtering (to be set later via command buffer binding)
+ SetSamplerParameter(GL_TEXTURE_MIN_FILTER, mDefaultSamplerState.minFilter, Graphics::GLES::GLSamplerFilterAndMipMapMode(Graphics::SamplerFilter::LINEAR, SamplerMipmapMode::NONE));
+ SetSamplerParameter(GL_TEXTURE_MAG_FILTER, mDefaultSamplerState.magFilter, Graphics::GLES::GLSamplerFilterAndMipMapMode(Graphics::SamplerFilter::LINEAR, SamplerMipmapMode::NONE));
+ SetSamplerParameter(GL_TEXTURE_WRAP_S, mDefaultSamplerState.wrapS, GL_WRAP_DEFAULT);
+ SetSamplerParameter(GL_TEXTURE_WRAP_T, mDefaultSamplerState.wrapT, GL_WRAP_DEFAULT);
+ }
+ break;
+ }
+ // Texture Cubemap
+ case Graphics::TextureType::TEXTURE_CUBEMAP:
+ {
+ Graphics::GLES::GLTextureFormatType format(mCreateInfo.format);
+
+ if(format.format && format.type)
+ {
+ // Bind texture
+ gl->GenTextures(1, &texture);
+ context->BindTexture(GL_TEXTURE_CUBE_MAP, GetTextureTypeId(), texture);
+ gl->PixelStorei(GL_UNPACK_ALIGNMENT, 1); // We always use tightly packed data
+
+ SetSamplerParameter(GL_TEXTURE_MIN_FILTER, mDefaultSamplerState.minFilter, Graphics::GLES::GLSamplerFilterAndMipMapMode(Graphics::SamplerFilter::LINEAR, SamplerMipmapMode::NONE));
+ SetSamplerParameter(GL_TEXTURE_MAG_FILTER, mDefaultSamplerState.magFilter, Graphics::GLES::GLSamplerFilterAndMipMapMode(Graphics::SamplerFilter::LINEAR, SamplerMipmapMode::NONE));
+ SetSamplerParameter(GL_TEXTURE_WRAP_S, mDefaultSamplerState.wrapS, GL_WRAP_DEFAULT);
+ SetSamplerParameter(GL_TEXTURE_WRAP_T, mDefaultSamplerState.wrapT, GL_WRAP_DEFAULT);
+
+ // Allocate memory for the texture
+ for(uint32_t i = 0; i < 6; ++i)
+ {
+ if(!mIsCompressed)
+ {
+ gl->TexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i,
+ 0,
+ format.internalFormat,
+ mCreateInfo.size.width,
+ mCreateInfo.size.height,
+ 0,
+ format.format,
+ format.type,
+ (mCreateInfo.data ? mStagingBuffer.data() : nullptr));
+ }
+ else
+ {
+ gl->CompressedTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i,
+ 0,
+ format.internalFormat,
+ mCreateInfo.size.width,
+ mCreateInfo.size.height,
+ 0,
+ mCreateInfo.dataSize,
+ (mCreateInfo.data ? mStagingBuffer.data() : nullptr));
+ }
+ }
// Clear staging buffer if there was any
mStagingBuffer.clear();
mTextureId = texture;
- // Default texture filtering (to be set later via command buffer binding)
- gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, Graphics::GLES::GLSamplerFilterAndMipMapMode(Graphics::SamplerFilter::LINEAR, SamplerMipmapMode::NONE));
- gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, Graphics::GLES::GLSamplerFilterAndMipMapMode(Graphics::SamplerFilter::LINEAR, SamplerMipmapMode::NONE));
+ SetSamplerParameter(GL_TEXTURE_WRAP_R, mDefaultSamplerState.wrapR, GL_WRAP_DEFAULT);
}
break;
}
void Texture::Bind(const TextureBinding& binding) const
{
- auto gl = mController.GetGL();
- if(!gl)
+ auto context = mController.GetCurrentContext();
+ auto gl = mController.GetGL();
+ if(!gl || !context)
{
// Do nothing during shutdown
return;
}
- gl->ActiveTexture(GL_TEXTURE0 + binding.binding);
- gl->BindTexture(mGlTarget, mTextureId);
+ context->ActiveTexture(binding.binding);
+ context->BindTexture(mGlTarget, GetTextureTypeId(), mTextureId);
// For GLES2 if there is a sampler set in the binding
if(binding.sampler)
{
- // Non-default.
- auto* sampler = static_cast<const GLES::Sampler*>(binding.sampler);
- const auto& samplerCreateInfo = sampler->GetCreateInfo();
+ const auto& samplerCreateInfo = static_cast<const GLES::Sampler*>(binding.sampler)->GetCreateInfo();
auto mipMapMode = samplerCreateInfo.mipMapMode;
- mipMapMode = Graphics::SamplerMipmapMode::NONE; // @todo Remove when mip-map generation is supported
- gl->TexParameteri(mGlTarget, GL_TEXTURE_MIN_FILTER, GLSamplerFilterAndMipMapMode(samplerCreateInfo.minFilter, mipMapMode).glFilter);
- gl->TexParameteri(mGlTarget, GL_TEXTURE_MAG_FILTER, GLSamplerFilter(samplerCreateInfo.magFilter).glFilter);
- gl->TexParameteri(mGlTarget, GL_TEXTURE_WRAP_S, GLAddressMode(samplerCreateInfo.addressModeU).texParameter);
- gl->TexParameteri(mGlTarget, GL_TEXTURE_WRAP_T, GLAddressMode(samplerCreateInfo.addressModeV).texParameter);
+ SetSamplerParameter(GL_TEXTURE_MIN_FILTER, mDefaultSamplerState.minFilter, GLSamplerFilterAndMipMapMode(samplerCreateInfo.minFilter, mipMapMode).glFilter);
+ SetSamplerParameter(GL_TEXTURE_MAG_FILTER, mDefaultSamplerState.magFilter, GLSamplerFilter(samplerCreateInfo.magFilter).glFilter);
+ SetSamplerParameter(GL_TEXTURE_WRAP_S, mDefaultSamplerState.wrapS, GLAddressMode(samplerCreateInfo.addressModeU).texParameter);
+ SetSamplerParameter(GL_TEXTURE_WRAP_T, mDefaultSamplerState.wrapT, GLAddressMode(samplerCreateInfo.addressModeV).texParameter);
+
if(mGlTarget == GL_TEXTURE_CUBE_MAP)
{
- gl->TexParameteri(mGlTarget, GL_TEXTURE_WRAP_R, GLAddressMode(samplerCreateInfo.addressModeW).texParameter);
+ SetSamplerParameter(GL_TEXTURE_WRAP_R, mDefaultSamplerState.wrapR, GLAddressMode(samplerCreateInfo.addressModeW).texParameter);
}
}
else
{
- gl->TexParameteri(mGlTarget, GL_TEXTURE_MIN_FILTER, DALI_MINIFY_DEFAULT);
- gl->TexParameteri(mGlTarget, GL_TEXTURE_MAG_FILTER, DALI_MAGNIFY_DEFAULT);
- gl->TexParameteri(mGlTarget, GL_TEXTURE_WRAP_S, GL_WRAP_DEFAULT);
- gl->TexParameteri(mGlTarget, GL_TEXTURE_WRAP_T, GL_WRAP_DEFAULT);
+ SetSamplerParameter(GL_TEXTURE_MIN_FILTER, mDefaultSamplerState.minFilter, DALI_MINIFY_DEFAULT);
+ SetSamplerParameter(GL_TEXTURE_MAG_FILTER, mDefaultSamplerState.magFilter, DALI_MAGNIFY_DEFAULT);
+ SetSamplerParameter(GL_TEXTURE_WRAP_S, mDefaultSamplerState.wrapS, GL_WRAP_DEFAULT);
+ SetSamplerParameter(GL_TEXTURE_WRAP_T, mDefaultSamplerState.wrapT, GL_WRAP_DEFAULT);
if(mGlTarget == GL_TEXTURE_CUBE_MAP)
{
- gl->TexParameteri(mGlTarget, GL_TEXTURE_WRAP_R, GL_WRAP_DEFAULT);
+ SetSamplerParameter(GL_TEXTURE_WRAP_R, mDefaultSamplerState.wrapR, GL_WRAP_DEFAULT);
}
}
+
+ if(mMaxMipMapLevel)
+ {
+ SetSamplerParameter(GL_TEXTURE_MAX_LEVEL, mDefaultSamplerState.maxLevel, mMaxMipMapLevel);
+ }
}
void Texture::Prepare()
}
}
+/**
+ * This function tests whether format is supported by the driver. If possible it applies
+ * format conversion to suitable supported pixel format.
+ */
+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)
+{
+ // No need to convert
+ if(srcFormat == destFormat)
+ {
+ return false;
+ }
+
+ auto it = std::find_if(GetColorConversionTable().begin(), GetColorConversionTable().end(), [&](auto& item) {
+ return item.srcFormat == srcFormat && item.destFormat == destFormat;
+ });
+
+ // No suitable format, return empty array
+ if(it == GetColorConversionTable().end())
+ {
+ return false;
+ }
+ auto begin = reinterpret_cast<const uint8_t*>(pData);
+
+ outputBuffer = std::move(it->pConversionFunc(begin, sizeInBytes, width, height, inStride));
+ return !outputBuffer.empty();
+}
+
+void Texture::SetSamplerParameter(uint32_t param, uint32_t& cacheValue, uint32_t value) const
+{
+ if(cacheValue != value)
+ {
+ auto gl = mController.GetGL();
+ gl->TexParameteri(mGlTarget, param, value);
+ cacheValue = value;
+ }
+}
+
} // namespace Dali::Graphics::GLES