Fixed static analysis errors in UBO code
[platform/core/uifw/dali-adaptor.git] / dali / internal / graphics / gles-impl / gles-context.cpp
index 1d981bc..569fdfc 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.
@@ -34,6 +34,7 @@
 #include <EGL/egl.h>
 #include <EGL/eglext.h>
 #include <map>
+#include <unordered_map>
 
 namespace Dali::Graphics::GLES
 {
@@ -53,24 +54,42 @@ 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)
   {
+    // 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();
     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);
@@ -120,6 +139,10 @@ struct Context::Impl
     memset(&mGlStateCache.mBoundTextureId, 0, sizeof(mGlStateCache.mBoundTextureId));
 
     mGlStateCache.mFrameBufferStateCache.Reset();
+
+    GLint maxTextures;
+    gl.GetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &maxTextures);
+    DALI_LOG_RELEASE_INFO("GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS: %d\n", maxTextures);
   }
 
   /**
@@ -206,11 +229,12 @@ 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
 
-  bool mGlContextCreated{false}; ///< True if the OpenGL context has been created
+  bool mGlContextCreated{false};    ///< True if the OpenGL context has been created
+  bool mVertexBuffersChanged{true}; ///< True if BindVertexBuffers changed any buffer bindings
 
   EGLContext mNativeDrawContext{0u}; ///< Native rendering EGL context compatible with window context
 
@@ -265,11 +289,16 @@ void Context::Flush(bool reset, const GLES::DrawCallDescriptor& drawCall, GLES::
     return;
   }
 
+  // If this draw uses a different pipeline _AND_ the pipeline has a different GL Program,
+  // Then bind the new program. Ensure vertex atrributes are set.
+
+  bool programChanged = false;
   if(mImpl->mNewPipeline && mImpl->mCurrentPipeline != mImpl->mNewPipeline)
   {
     if(!currentProgram || currentProgram->GetImplementation()->GetGlProgram() != newProgram->GetImplementation()->GetGlProgram())
     {
       mImpl->mNewPipeline->Bind(newProgram->GetImplementation()->GetGlProgram());
+      programChanged = true;
     }
 
     // Blend state
@@ -286,73 +315,118 @@ 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 back 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();
 
-    texture->Prepare(); // @todo also non-const.
-
-    if(binding.binding < samplers.size()) // binding maps to texture unit. (texture bindings should also be in binding order)
+    if(programChanged)
     {
-      // 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);
+      // @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)
+      {
+        ++currentSampler;
+        currentElement = 0;
+      }
     }
   }
 
-  // for each attribute bind vertices
-
   const auto& pipelineState    = mImpl->mNewPipeline ? mImpl->mNewPipeline->GetCreateInfo() : mImpl->mCurrentPipeline->GetCreateInfo();
   const auto& vertexInputState = pipelineState.vertexInputState;
 
-  if(hasGLES3)
-  {
-    mImpl->BindProgramVAO(static_cast<const GLES::Program*>(pipelineState.programState->program)->GetImplementation(), *vertexInputState);
-  }
-
-  for(const auto& attr : vertexInputState->attributes)
+  // for each attribute bind vertices, unless the pipeline+buffer is the same
+  if(programChanged || mImpl->mVertexBuffersChanged)
   {
-    // Enable location
-    if(!hasGLES3)
+    if(hasGLES3)
     {
-      mImpl->SetVertexAttributeLocation(attr.location, true);
+      mImpl->BindProgramVAO(static_cast<const GLES::Program*>(pipelineState.programState->program)->GetImplementation(), *vertexInputState);
     }
 
-    const auto& bufferSlot    = mImpl->mCurrentVertexBufferBindings[attr.binding];
-    const auto& bufferBinding = vertexInputState->bufferBindings[attr.binding];
+    for(const auto& attr : vertexInputState->attributes)
+    {
+      // Enable location
+      if(!hasGLES3)
+      {
+        mImpl->SetVertexAttributeLocation(attr.location, true);
+      }
+
+      const auto& bufferSlot    = mImpl->mCurrentVertexBufferBindings[attr.binding];
+      const auto& bufferBinding = vertexInputState->bufferBindings[attr.binding];
 
-    auto glesBuffer = bufferSlot.buffer->GetGLBuffer();
+      auto glesBuffer = bufferSlot.buffer->GetGLBuffer();
 
-    // Bind buffer
-    BindBuffer(GL_ARRAY_BUFFER, glesBuffer);
+      BindBuffer(GL_ARRAY_BUFFER, glesBuffer); // Cached
 
-    gl.VertexAttribPointer(attr.location,
-                           GLVertexFormat(attr.format).size,
-                           GLVertexFormat(attr.format).format,
-                           GL_FALSE,
-                           bufferBinding.stride,
-                           reinterpret_cast<void*>(attr.offset));
+      if(attr.format == VertexInputFormat::FLOAT ||
+         attr.format == VertexInputFormat::FVECTOR2 ||
+         attr.format == VertexInputFormat::FVECTOR3 ||
+         attr.format == VertexInputFormat::FVECTOR4)
+      {
+        gl.VertexAttribPointer(attr.location, // Not cached...
+                               GLVertexFormat(attr.format).size,
+                               GLVertexFormat(attr.format).format,
+                               GL_FALSE,
+                               bufferBinding.stride,
+                               reinterpret_cast<void*>(attr.offset));
+      }
+      else
+      {
+        gl.VertexAttribIPointer(attr.location,
+                                GLVertexFormat(attr.format).size,
+                                GLVertexFormat(attr.format).format,
+                                bufferBinding.stride,
+                                reinterpret_cast<void*>(attr.offset));
+      }
+
+      if(hasGLES3)
+      {
+        switch(bufferBinding.inputRate)
+        {
+          case Graphics::VertexInputRate::PER_VERTEX:
+          {
+            gl.VertexAttribDivisor(attr.location, 0);
+            break;
+          }
+          case Graphics::VertexInputRate::PER_INSTANCE:
+          {
+            //@todo Get actual instance rate...
+            gl.VertexAttribDivisor(attr.location, 1);
+            break;
+          }
+        }
+      }
+    }
   }
 
   // Resolve topology
   const auto& ia = pipelineState.inputAssemblyState;
 
-  // Bind uniforms
-
   // Resolve draw call
   switch(drawCall.type)
   {
@@ -367,9 +441,19 @@ void Context::Flush(bool reset, const GLES::DrawCallDescriptor& drawCall, GLES::
         mImpl->FlushVertexAttributeLocations();
       }
 
-      gl.DrawArrays(GLESTopology(ia->topology),
-                    drawCall.draw.firstVertex,
-                    drawCall.draw.vertexCount);
+      if(drawCall.draw.instanceCount == 0)
+      {
+        gl.DrawArrays(GLESTopology(ia->topology),
+                      drawCall.draw.firstVertex,
+                      drawCall.draw.vertexCount);
+      }
+      else
+      {
+        gl.DrawArraysInstanced(GLESTopology(ia->topology),
+                               drawCall.draw.firstVertex,
+                               drawCall.draw.vertexCount,
+                               drawCall.draw.instanceCount);
+      }
       break;
     }
     case DrawCallDescriptor::Type::DRAW_INDEXED:
@@ -388,10 +472,21 @@ void Context::Flush(bool reset, const GLES::DrawCallDescriptor& drawCall, GLES::
       }
 
       auto indexBufferFormat = GLIndexFormat(binding.format).format;
-      gl.DrawElements(GLESTopology(ia->topology),
-                      drawCall.drawIndexed.indexCount,
-                      indexBufferFormat,
-                      reinterpret_cast<void*>(binding.offset));
+      if(drawCall.drawIndexed.instanceCount == 0)
+      {
+        gl.DrawElements(GLESTopology(ia->topology),
+                        drawCall.drawIndexed.indexCount,
+                        indexBufferFormat,
+                        reinterpret_cast<void*>(binding.offset));
+      }
+      else
+      {
+        gl.DrawElementsInstanced(GLESTopology(ia->topology),
+                                 drawCall.drawIndexed.indexCount,
+                                 indexBufferFormat,
+                                 reinterpret_cast<void*>(binding.offset),
+                                 drawCall.drawIndexed.instanceCount);
+      }
       break;
     }
     case DrawCallDescriptor::Type::DRAW_INDEXED_INDIRECT:
@@ -434,9 +529,19 @@ void Context::BindVertexBuffers(const GLES::VertexBufferBindingDescriptor* bindi
     mImpl->mCurrentVertexBufferBindings.resize(count);
   }
   // Copy only set slots
-  std::copy_if(bindings, bindings + count, mImpl->mCurrentVertexBufferBindings.begin(), [](auto& item) {
-    return (nullptr != item.buffer);
-  });
+  mImpl->mVertexBuffersChanged = false;
+  auto toIter                  = mImpl->mCurrentVertexBufferBindings.begin();
+  for(auto fromIter = bindings, end = bindings + count; fromIter != end; ++fromIter)
+  {
+    if(fromIter->buffer != nullptr)
+    {
+      if(toIter->buffer != fromIter->buffer || toIter->offset != fromIter->offset)
+      {
+        mImpl->mVertexBuffersChanged = true;
+      }
+      *toIter++ = *fromIter;
+    }
+  }
 }
 
 void Context::BindIndexBuffer(const IndexBufferBindingDescriptor& indexBufferBinding)
@@ -459,9 +564,9 @@ void Context::BindUniformBuffers(const UniformBufferBindingDescriptor* uboBindin
     mImpl->mCurrentStandaloneUBOBinding = standaloneBindings;
   }
 
-  if(uboCount >= mImpl->mCurrentUBOBindings.size())
+  if(uboCount && uboCount > mImpl->mCurrentUBOBindings.size())
   {
-    mImpl->mCurrentUBOBindings.resize(uboCount + 1);
+    mImpl->mCurrentUBOBindings.resize(uboCount);
   }
 
   auto it = uboBindings;
@@ -471,6 +576,7 @@ void Context::BindUniformBuffers(const UniformBufferBindingDescriptor* uboBindin
     {
       mImpl->mCurrentUBOBindings[i] = *it;
     }
+    ++it;
   }
 }
 
@@ -600,6 +706,20 @@ void Context::ResolveUniformBuffers()
   {
     ResolveStandaloneUniforms();
   }
+  if(!mImpl->mCurrentUBOBindings.empty())
+  {
+    ResolveGpuUniformBuffers();
+  }
+}
+
+void Context::ResolveGpuUniformBuffers()
+{
+  auto& gl = *mImpl->mController.GetGL();
+  auto  i  = 0u;
+  for(auto& binding : mImpl->mCurrentUBOBindings)
+  {
+    gl.BindBufferRange(GL_UNIFORM_BUFFER, i++, binding.buffer->GetGLBuffer(), GLintptr(binding.offset), binding.dataSize);
+  }
 }
 
 void Context::ResolveStandaloneUniforms()
@@ -746,6 +866,7 @@ void Context::EndRenderPass(GLES::TextureDependencyChecker& dependencyChecker)
 void Context::ClearState()
 {
   mImpl->mCurrentTextureBindings.clear();
+  mImpl->mCurrentUBOBindings.clear();
 }
 
 void Context::ColorMask(bool enabled)
@@ -936,15 +1057,34 @@ void Context::GenerateMipmap(GLenum target)
   gl.GenerateMipmap(target);
 }
 
-void Context::BindBuffer(GLenum target, uint32_t bufferId)
+bool 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 false;
+      }
+      mImpl->mGlStateCache.mBoundArrayBufferId = bufferId;
+      break;
+    }
+    case GL_ELEMENT_ARRAY_BUFFER:
+    {
+      if(mImpl->mGlStateCache.mBoundElementArrayBufferId == bufferId)
+      {
+        return false;
+      }
+      mImpl->mGlStateCache.mBoundElementArrayBufferId = bufferId;
+      break;
+    }
   }
+
+  // Cache miss. Bind buffer.
+  auto& gl = *mImpl->mController.GetGL();
+  gl.BindBuffer(target, bufferId);
+  return true;
 }
 
 void Context::DrawBuffers(uint32_t count, const GLenum* buffers)
@@ -1009,6 +1149,34 @@ 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;
+            gl->DeleteVertexArrays(1, &vao);
+            if(mImpl->mProgramVAOCurrentState == vao)
+            {
+              mImpl->mProgramVAOCurrentState = 0u;
+            }
+          }
+          mImpl->mProgramVAOMap.erase(iter);
+        }
+      }
+    }
+  }
 }
 
 void Context::PrepareForNativeRendering()