Adding texture conversion on upload 79/257779/1
authorDavid Steele <david.steele@samsung.com>
Fri, 30 Apr 2021 13:02:02 +0000 (14:02 +0100)
committerDavid Steele <david.steele@samsung.com>
Fri, 30 Apr 2021 13:40:29 +0000 (14:40 +0100)
Change-Id: I92173724636d535bbe550c45e5d1affb8b47b507

dali/internal/graphics/gles-impl/egl-graphics-controller.cpp
dali/internal/graphics/gles-impl/gles-graphics-texture.cpp
dali/internal/graphics/gles-impl/gles-graphics-texture.h

index 7ef6162..5546800 100644 (file)
@@ -560,22 +560,42 @@ void EglGraphicsController::ProcessTextureUpdateQueue()
 
     if(source.sourceType == Graphics::TextureUpdateSourceInfo::Type::MEMORY)
     {
-      // GPU memory must be already allocated (glTexImage2D())
+      // GPU memory must be already allocated.
+
+      // Check if it needs conversion
       auto*       texture    = static_cast<GLES::Texture*>(info.dstTexture);
       const auto& createInfo = texture->GetCreateInfo();
+      auto        srcFormat  = GLES::GLTextureFormatType(info.srcFormat).format;
+      auto        destFormat = GLES::GLTextureFormatType(createInfo.format).format;
+      auto        destType   = GLES::GLTextureFormatType(createInfo.format).type;
+
+      // From render-texture.cpp
+      const bool isSubImage(info.dstOffset2D.x != 0 || info.dstOffset2D.y != 0 ||
+                            info.srcExtent2D.width != (createInfo.size.width / (1 << info.level)) ||
+                            info.srcExtent2D.height != (createInfo.size.height / (1 << info.level)));
+
+      auto*                sourceBuffer = reinterpret_cast<uint8_t*>(source.memorySource.memory);
+      std::vector<uint8_t> tempBuffer;
+      if(mGlAbstraction->TextureRequiresConverting(srcFormat, destFormat, isSubImage))
+      {
+        // Convert RGB to RGBA if necessary.
+        texture->TryConvertPixelData(source.memorySource.memory, info.srcFormat, createInfo.format, info.srcSize, info.srcExtent2D.width, info.srcExtent2D.height, tempBuffer);
+        sourceBuffer = &tempBuffer[0];
+      }
 
       mGlAbstraction->PixelStorei(GL_UNPACK_ALIGNMENT, 1);
 
       mGlAbstraction->BindTexture(GL_TEXTURE_2D, texture->GetGLTexture());
+
       mGlAbstraction->TexSubImage2D(GL_TEXTURE_2D,
                                     info.level,
                                     info.dstOffset2D.x,
                                     info.dstOffset2D.y,
                                     info.srcExtent2D.width,
                                     info.srcExtent2D.height,
-                                    GLES::GLTextureFormatType(createInfo.format).format,
-                                    GLES::GLTextureFormatType(createInfo.format).type,
-                                    source.memorySource.memory);
+                                    destFormat,
+                                    destType,
+                                    sourceBuffer);
 
       // free staging memory
       free(source.memorySource.memory);
index cf91080..7e84495 100644 (file)
@@ -43,6 +43,49 @@ const int32_t DALI_MAGNIFY_DEFAULT = GL_LINEAR;
 
 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* pData, uint32_t sizeInBytes, uint32_t width, uint32_t height, uint32_t rowStride, void* pOutput)
+{
+  auto inData  = reinterpret_cast<const uint8_t*>(pData);
+  auto outData = reinterpret_cast<uint8_t*>(pOutput);
+  auto outIdx  = 0u;
+  for(auto i = 0u; i < sizeInBytes; i += 3)
+  {
+    outData[outIdx]     = inData[i];
+    outData[outIdx + 1] = inData[i + 1];
+    outData[outIdx + 2] = inData[i + 2];
+    outData[outIdx + 3] = 0xff;
+    outIdx += 4;
+  }
+}
+
+/**
+ * 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
+ */
+static const std::vector<ColorConversion> COLOR_CONVERSION_TABLE = {
+  {Format::R8G8B8_UNORM, Format::R8G8B8A8_UNORM, ConvertRGB32ToRGBA32, WriteRGB32ToRGBA32}};
+
+/**
+ * Constructor
+ */
 Texture::Texture(const Graphics::TextureCreateInfo& createInfo, Graphics::EglGraphicsController& controller)
 : TextureResource(createInfo, controller)
 {
@@ -156,7 +199,6 @@ bool Texture::InitializeTexture()
 
         // Clear staging buffer if there was any
         mStagingBuffer.clear();
-
         mTextureId = texture;
 
         // Default texture filtering (to be set later via command buffer binding)
@@ -258,4 +300,31 @@ 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 width, uint32_t height, std::vector<uint8_t>& outputBuffer)
+{
+  // No need to convert
+  if(srcFormat == destFormat)
+  {
+    return false;
+  }
+
+  auto it = std::find_if(COLOR_CONVERSION_TABLE.begin(), COLOR_CONVERSION_TABLE.end(), [&](auto& item) {
+    return item.srcFormat == srcFormat && item.destFormat == destFormat;
+  });
+
+  // No suitable format, return empty array
+  if(it == COLOR_CONVERSION_TABLE.end())
+  {
+    return false;
+  }
+  auto begin = reinterpret_cast<const uint8_t*>(pData);
+
+  outputBuffer = std::move(it->pConversionFunc(begin, sizeInBytes, width, height, 0u));
+  return !outputBuffer.empty();
+}
+
 } // namespace Dali::Graphics::GLES
index f094fe0..6bbe0f0 100644 (file)
@@ -97,15 +97,29 @@ public:
     return mGlTarget;
   }
 
-protected:
+  /**
+   * @param pData  Input data
+   * @param sizeInBytes Size of the input data in bytes
+   * @param width  Width of the output buffer
+   * @param height height of the output buffer
+   * @param outputBuffer The buffer to write to
+   * @return true if converted, or false otherwise
+   */
+  bool TryConvertPixelData(const void* pData, Graphics::Format srcFormat, Graphics::Format destFormat, uint32_t sizeInBytes, uint32_t width, uint32_t height, std::vector<uint8_t>& outputBuffer);
+
+  bool InitializeNativeImage();
+
+  bool InitializeTexture();
+
+  Format ValidateFormat(Format sourceFormat);
+
 private:
   std::vector<char> mStagingBuffer;
   uint32_t          mTextureId{0u};
   GLenum            mGlTarget{0u};
   void*             mGLOwnerContext{nullptr};
-  bool              InitializeNativeImage();
-  bool              InitializeTexture();
 };
+
 } // namespace Dali::Graphics::GLES
 
 #endif