[Tizen] Support to get raw pixel informations of framebuffer for old driver device
[platform/core/uifw/dali-adaptor.git] / dali / internal / graphics / gles-impl / gles-context.cpp
index f2962a0..e38c3cd 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022 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.
@@ -17,6 +17,7 @@
 
 #include "gles-context.h"
 #include <dali/integration-api/adaptor-framework/render-surface-interface.h>
+#include <dali/integration-api/debug.h>
 #include <dali/integration-api/gl-abstraction.h>
 #include <dali/integration-api/gl-defines.h>
 #include <dali/internal/graphics/common/graphics-interface.h>
@@ -33,6 +34,7 @@
 #include <EGL/egl.h>
 #include <EGL/eglext.h>
 #include <map>
+#include <unordered_map>
 
 namespace Dali::Graphics::GLES
 {
@@ -52,24 +54,49 @@ struct Context::Impl
    * that VertexInputState has been set correctly for the pipeline.
    *
    */
-  void BindProgramVAO(GLES::ProgramImpl* program, const VertexInputState& vertexInputState)
+  void BindProgramVAO(const GLES::ProgramImpl* program, const VertexInputState& vertexInputState)
   {
-    auto& gl   = *mController.GetGL();
-    auto  iter = mProgramVAOMap.find(program);
+    // Calculate attributes location hash unordered.
+    std::size_t hash = 0;
+    for(const auto& attr : vertexInputState.attributes)
+    {
+      hash ^= std::hash<uint32_t>{}(attr.location);
+    }
+
+    auto& gl = *mController.GetGL();
+
+    if(DALI_UNLIKELY(!mDiscardedVAOList.empty()))
+    {
+      gl.DeleteVertexArrays(static_cast<Dali::GLsizei>(mDiscardedVAOList.size()), mDiscardedVAOList.data());
+      mDiscardedVAOList.clear();
+    }
+
+    auto iter = mProgramVAOMap.find(program);
     if(iter != mProgramVAOMap.end())
     {
-      if(mProgramVAOCurrentState != iter->second)
+      auto attributeIter = iter->second.find(hash);
+      if(attributeIter != iter->second.end())
       {
-        mProgramVAOCurrentState = iter->second;
-        gl.BindVertexArray(iter->second);
+        if(mProgramVAOCurrentState != attributeIter->second)
+        {
+          mProgramVAOCurrentState = attributeIter->second;
+          gl.BindVertexArray(attributeIter->second);
+
+          // Binding VAO seems to reset the index buffer binding so the cache must be reset
+          mGlStateCache.mBoundElementArrayBufferId = 0;
+        }
+        return;
       }
-      return;
     }
 
     uint32_t vao;
     gl.GenVertexArrays(1, &vao);
     gl.BindVertexArray(vao);
-    mProgramVAOMap[program] = vao;
+
+    // Binding VAO seems to reset the index buffer binding so the cache must be reset
+    mGlStateCache.mBoundElementArrayBufferId = 0;
+
+    mProgramVAOMap[program][hash] = vao;
     for(const auto& attr : vertexInputState.attributes)
     {
       gl.EnableVertexAttribArray(attr.location);
@@ -205,9 +232,10 @@ struct Context::Impl
   const GLES::RenderPass*   mCurrentRenderPass{nullptr};
 
   // Each context must have own VAOs as they cannot be shared
-  std::map<GLES::ProgramImpl*, uint32_t> mProgramVAOMap;              ///< GL program-VAO map
-  uint32_t                               mProgramVAOCurrentState{0u}; ///< Currently bound VAO
-  GLStateCache                           mGlStateCache{};             ///< GL status cache
+  std::unordered_map<const GLES::ProgramImpl*, std::map<std::size_t, uint32_t>> mProgramVAOMap;              ///< GL program-VAO map
+  uint32_t                                                                      mProgramVAOCurrentState{0u}; ///< Currently bound VAO
+  GLStateCache                                                                  mGlStateCache{};             ///< GL status cache
+  std::vector<Dali::GLuint>                                                     mDiscardedVAOList{};
 
   bool mGlContextCreated{false}; ///< True if the OpenGL context has been created
 
@@ -257,6 +285,13 @@ void Context::Flush(bool reset, const GLES::DrawCallDescriptor& drawCall, GLES::
     newProgram = static_cast<const GLES::Program*>(mImpl->mNewPipeline->GetCreateInfo().programState->program);
   }
 
+  if(!currentProgram && !newProgram)
+  {
+    // Early out if we have no program for this pipeline.
+    DALI_LOG_ERROR("No program defined for pipeline\n");
+    return;
+  }
+
   if(mImpl->mNewPipeline && mImpl->mCurrentPipeline != mImpl->mNewPipeline)
   {
     if(!currentProgram || currentProgram->GetImplementation()->GetGlProgram() != newProgram->GetImplementation()->GetGlProgram())
@@ -278,31 +313,42 @@ void Context::Flush(bool reset, const GLES::DrawCallDescriptor& drawCall, GLES::
   // Map binding# to sampler location
   const auto& reflection = !newProgram ? currentProgram->GetReflection() : newProgram->GetReflection();
   const auto& samplers   = reflection.GetSamplers();
+
+  uint32_t currentSampler = 0;
+  uint32_t currentElement = 0;
+
+  // @warning Assume that binding.binding is strictly linear in the same order as mCurrentTextureBindings
+  // elements. This avoids having to sort the bindings.
   for(const auto& binding : mImpl->mCurrentTextureBindings)
   {
+    if(currentSampler >= samplers.size())
+    {
+      // Don't bind more textures than there are active samplers.
+      break;
+    }
+
     auto texture = const_cast<GLES::Texture*>(static_cast<const GLES::Texture*>(binding.texture));
 
     // Texture may not have been initialized yet...(tbm_surface timing issue?)
     if(!texture->GetGLTexture())
     {
-      // Attempt to reinitialize
-      // @todo need to put this somewhere else where it isn't const.
-      // Maybe post it bac/k on end of initialize queue if initialization fails?
       texture->InitializeResource();
     }
 
     // Warning, this may cause glWaitSync to occur on the GPU.
     dependencyChecker.CheckNeedsSync(this, texture);
-
     texture->Bind(binding);
-
-    texture->Prepare(); // @todo also non-const.
-
-    if(binding.binding < samplers.size()) // binding maps to texture unit. (texture bindings should also be in binding order)
+    texture->Prepare();
+
+    // @warning Assume that location of array elements is sequential.
+    // @warning GL does not guarantee this, but in practice, it is.
+    gl.Uniform1i(samplers[currentSampler].location + currentElement,
+                 samplers[currentSampler].offset + currentElement);
+    ++currentElement;
+    if(currentElement >= samplers[currentSampler].elementCount)
     {
-      // Offset is set to the lexical offset within the frag shader, map it to the texture unit
-      // @todo Explicitly set the texture unit through the graphics interface
-      gl.Uniform1i(samplers[binding.binding].location, samplers[binding.binding].offset);
+      ++currentSampler;
+      currentElement = 0;
     }
   }
 
@@ -359,7 +405,6 @@ void Context::Flush(bool reset, const GLES::DrawCallDescriptor& drawCall, GLES::
         mImpl->FlushVertexAttributeLocations();
       }
 
-      //@todo Wait if textures need syncing
       gl.DrawArrays(GLESTopology(ia->topology),
                     drawCall.draw.firstVertex,
                     drawCall.draw.vertexCount);
@@ -720,6 +765,11 @@ void Context::EndRenderPass(GLES::TextureDependencyChecker& dependencyChecker)
       auto& gl = *mImpl->mController.GetGL();
       gl.Flush();
 
+      if(framebuffer->CaptureRequested())
+      {
+        framebuffer->DrawRenderedBuffer();
+      }
+
       /* @todo Full dependency checking would need to store textures in Begin, and create
        * fence objects here; but we're going to draw all fbos on shared context in serial,
        * so no real need (yet). Might want to consider ensuring order of render passes,
@@ -931,13 +981,31 @@ void Context::GenerateMipmap(GLenum target)
 
 void Context::BindBuffer(GLenum target, uint32_t bufferId)
 {
-  if(mImpl->mGlStateCache.mBoundArrayBufferId != bufferId)
+  switch(target)
   {
-    mImpl->mGlStateCache.mBoundArrayBufferId = bufferId;
-
-    auto& gl = *mImpl->mController.GetGL();
-    gl.BindBuffer(target, bufferId);
+    case GL_ARRAY_BUFFER:
+    {
+      if(mImpl->mGlStateCache.mBoundArrayBufferId == bufferId)
+      {
+        return;
+      }
+      mImpl->mGlStateCache.mBoundArrayBufferId = bufferId;
+      break;
+    }
+    case GL_ELEMENT_ARRAY_BUFFER:
+    {
+      if(mImpl->mGlStateCache.mBoundElementArrayBufferId == bufferId)
+      {
+        return;
+      }
+      mImpl->mGlStateCache.mBoundElementArrayBufferId = bufferId;
+      break;
+    }
   }
+
+  // Cache miss. Bind buffer.
+  auto& gl = *mImpl->mController.GetGL();
+  gl.BindBuffer(target, bufferId);
 }
 
 void Context::DrawBuffers(uint32_t count, const GLenum* buffers)
@@ -1002,6 +1070,40 @@ void Context::InvalidateCachedPipeline(GLES::Pipeline* pipeline)
   {
     mImpl->mCurrentPipeline = nullptr;
   }
+
+  // Remove cached VAO map
+  auto* gl = mImpl->mController.GetGL();
+  if(gl)
+  {
+    const auto* program = pipeline->GetCreateInfo().programState->program;
+    if(program)
+    {
+      const auto* programImpl = static_cast<const GLES::Program*>(program)->GetImplementation();
+      if(programImpl)
+      {
+        auto iter = mImpl->mProgramVAOMap.find(programImpl);
+        if(iter != mImpl->mProgramVAOMap.end())
+        {
+          for(auto& attributeHashPair : iter->second)
+          {
+            auto vao = attributeHashPair.second;
+
+            // Do not delete vao now. (Since Context might not be current.)
+            mImpl->mDiscardedVAOList.emplace_back(vao);
+            if(mImpl->mProgramVAOCurrentState == vao)
+            {
+              mImpl->mProgramVAOCurrentState = 0u;
+            }
+          }
+
+          // Clear cached Vertex buffer.
+          mImpl->mCurrentVertexBufferBindings.clear();
+
+          mImpl->mProgramVAOMap.erase(iter);
+        }
+      }
+    }
+  }
 }
 
 void Context::PrepareForNativeRendering()
@@ -1024,13 +1126,20 @@ void Context::PrepareForNativeRendering()
   if(!mImpl->mNativeDrawContext)
   {
     EGLint configId{0u};
-    EGLint size{0u};
-    eglGetConfigs(display, nullptr, 0, &size);
-    std::vector<EGLConfig> configs;
-    configs.resize(size);
-    eglGetConfigs(display, configs.data(), configs.size(), &size);
+    eglQueryContext(display, mImpl->mController.GetSharedContext(), EGL_CONFIG_ID, &configId);
 
-    eglQueryContext(display, context, EGL_CONFIG_ID, &configId);
+    EGLint configAttribs[3];
+    configAttribs[0] = EGL_CONFIG_ID;
+    configAttribs[1] = configId;
+    configAttribs[2] = EGL_NONE;
+
+    EGLConfig config;
+    EGLint    numConfigs;
+    if(eglChooseConfig(display, configAttribs, &config, 1, &numConfigs) != EGL_TRUE)
+    {
+      DALI_LOG_ERROR("eglChooseConfig failed!\n");
+      return;
+    }
 
     auto version = int(mImpl->mController.GetGLESVersion());
 
@@ -1041,7 +1150,12 @@ void Context::PrepareForNativeRendering()
     attribs.push_back(version % 10);
     attribs.push_back(EGL_NONE);
 
-    mImpl->mNativeDrawContext = eglCreateContext(display, configs[configId], mImpl->mController.GetSharedContext(), attribs.data());
+    mImpl->mNativeDrawContext = eglCreateContext(display, config, mImpl->mController.GetSharedContext(), attribs.data());
+    if(mImpl->mNativeDrawContext == EGL_NO_CONTEXT)
+    {
+      DALI_LOG_ERROR("eglCreateContext failed!\n");
+      return;
+    }
   }
 
   eglMakeCurrent(display, drawSurface, readSurface, mImpl->mNativeDrawContext);