Pipeline caching 16/253516/3
authorAdam Bialogonski <adam.b@samsung.com>
Fri, 12 Feb 2021 18:16:07 +0000 (18:16 +0000)
committerAdam Bialogonski <adam.b@samsung.com>
Fri, 19 Feb 2021 11:09:48 +0000 (11:09 +0000)
- naive pipeline caching
- returns unique_ptr to wrapper of pipeline rather than actual implementation
- still requires optimization inside the pipeline implementation

Change-Id: I53f31b08bc7174b35dc64e6fdc6166b4d9a038dc

dali/internal/graphics/gles-impl/egl-graphics-controller.cpp
dali/internal/graphics/gles-impl/egl-graphics-controller.h
dali/internal/graphics/gles-impl/file.list
dali/internal/graphics/gles-impl/gles-graphics-pipeline-cache.cpp [new file with mode: 0644]
dali/internal/graphics/gles-impl/gles-graphics-pipeline-cache.h [new file with mode: 0644]
dali/internal/graphics/gles-impl/gles-graphics-pipeline.cpp
dali/internal/graphics/gles-impl/gles-graphics-pipeline.h

index e296bee..32cc491 100644 (file)
@@ -151,9 +151,15 @@ EglGraphicsController::CreateBuffer(const BufferCreateInfo& bufferCreateInfo, Gr
   return NewObject<GLES::Buffer>(bufferCreateInfo, *this, std::move(oldBuffer));
 }
 
-Graphics::UniquePtr<Pipeline> EglGraphicsController::CreatePipeline(const PipelineCreateInfo& pipelineCreateInfo, Graphics::UniquePtr<Pipeline>&& oldPipeline)
+Graphics::UniquePtr<Pipeline> EglGraphicsController::CreatePipeline(const PipelineCreateInfo& pipelineCreateInfo, Graphics::UniquePtr<Graphics::Pipeline>&& oldPipeline)
 {
-  return NewObject<GLES::Pipeline>(pipelineCreateInfo, *this, std::move(oldPipeline));
+  // Create pipeline cache if needed
+  if(!mPipelineCache)
+  {
+    mPipelineCache = std::make_unique<GLES::PipelineCache>(*this);
+  }
+
+  return mPipelineCache->GetPipeline(pipelineCreateInfo, std::move(oldPipeline));
 }
 
 void EglGraphicsController::AddTexture(GLES::Texture& texture)
index 6a1cfa1..790b252 100644 (file)
@@ -25,6 +25,7 @@
 #include "gles-context.h"
 #include "gles-graphics-buffer.h"
 #include "gles-graphics-memory.h"
+#include "gles-graphics-pipeline-cache.h"
 #include "gles-graphics-texture.h"
 
 namespace Dali
@@ -41,7 +42,8 @@ namespace Graphics
 namespace GLES
 {
 class CommandBuffer;
-}
+class PipelineCache;
+} // namespace GLES
 
 /**
  * EGL Implementation of the graphics controller.
@@ -416,6 +418,8 @@ private:
   std::queue<TextureUpdateRequest> mTextureUpdateRequests;
 
   std::unique_ptr<GLES::Context> mContext{nullptr}; ///< Context object handling command buffers execution
+
+  std::unique_ptr<GLES::PipelineCache> mPipelineCache{nullptr}; ///< Internal pipeline cache
 };
 
 } // namespace Graphics
index 832ed14..6c30917 100644 (file)
@@ -13,6 +13,7 @@ SET( adaptor_graphics_gles_src_files ${adaptor_graphics_gles_src_files}
     ${adaptor_graphics_dir}/gles-impl/gles-graphics-sampler.cpp
     ${adaptor_graphics_dir}/gles-impl/gles-graphics-shader.cpp
     ${adaptor_graphics_dir}/gles-impl/gles-graphics-texture.cpp
+    ${adaptor_graphics_dir}/gles-impl/gles-graphics-pipeline-cache.cpp
     ${adaptor_graphics_dir}/gles-impl/gles-context.cpp
 )
 
diff --git a/dali/internal/graphics/gles-impl/gles-graphics-pipeline-cache.cpp b/dali/internal/graphics/gles-impl/gles-graphics-pipeline-cache.cpp
new file mode 100644 (file)
index 0000000..9e69c77
--- /dev/null
@@ -0,0 +1,156 @@
+/*
+ * Copyright (c) 2021 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// CLASS HEADER
+#include "gles-graphics-pipeline-cache.h"
+
+// INTERNAL INCLUDES
+#include "gles-graphics-pipeline.h"
+
+namespace Dali::Graphics::GLES
+{
+/**
+ * @brief Implementation of pipeline cache
+ */
+struct PipelineCache::Impl
+{
+  explicit Impl(EglGraphicsController& _controller)
+  : controller(_controller)
+  {
+  }
+
+  ~Impl() = default;
+
+  EglGraphicsController& controller;
+
+  std::vector<UniquePtr<PipelineImpl>> mPipelines;
+};
+
+PipelineCache::PipelineCache(EglGraphicsController& controller)
+{
+  mImpl = std::make_unique<Impl>(controller);
+}
+
+PipelineCache::~PipelineCache() = default;
+
+PipelineImpl* PipelineCache::FindPipelineImpl(const PipelineCreateInfo& info)
+{
+  // hashing by shaders and then states, shaders should use the same pointers
+  for(auto& pipeline : mImpl->mPipelines)
+  {
+    auto& cacheInfo = pipeline->GetCreateInfo();
+
+    if(!info.shaderState)
+    {
+      continue;
+    }
+
+    // TODO: Hash the structures. In GLES the order should not matter
+    //       (it matters now to keep it simple)
+    if(info.shaderState->size() == cacheInfo.shaderState->size())
+    {
+      if(memcmp(info.shaderState->data(), cacheInfo.shaderState->data(), sizeof(info.shaderState->size() * sizeof(ShaderState))) != 0)
+      {
+        continue; // early exit
+      }
+
+      // test null states
+      // TODO: create and store such bitmask when pipeline
+      //       is being constructed!
+      uint32_t infoBits =
+        (info.inputAssemblyState ? 1 << 0 : 0) |
+        (info.vertexInputState ? 1 << 1 : 0) |
+        (info.viewportState ? 1 << 2 : 0) |
+        (info.depthStencilState ? 1 << 3 : 0) |
+        (info.colorBlendState ? 1 << 4 : 0) |
+        (info.framebufferState ? 1 << 5 : 0) |
+        (info.rasterizationState ? 1 << 6 : 0) |
+        (info.basePipeline ? 1 << 7 : 0);
+
+      uint32_t cacheBits =
+        (cacheInfo.inputAssemblyState ? 1 << 0 : 0) |
+        (cacheInfo.vertexInputState ? 1 << 1 : 0) |
+        (cacheInfo.viewportState ? 1 << 2 : 0) |
+        (cacheInfo.depthStencilState ? 1 << 3 : 0) |
+        (cacheInfo.colorBlendState ? 1 << 4 : 0) |
+        (cacheInfo.framebufferState ? 1 << 5 : 0) |
+        (cacheInfo.rasterizationState ? 1 << 6 : 0) |
+        (cacheInfo.basePipeline ? 1 << 7 : 0);
+
+      if(cacheBits != infoBits)
+      {
+        continue; // early exit
+      }
+
+      // Now compare states
+      // TODO: hash the binary content on pipeline creation
+      // TODO: optimize by adding a lookup table storing size
+      //       of each field and generalizing the type (void*)
+
+      auto updateResult = [](const auto& lhs, const auto& rhs) {
+        if(!rhs) return 0;
+        return memcmp(lhs, rhs, sizeof(decltype(*rhs)));
+      };
+
+      auto result = 0u;
+      result |= updateResult(info.vertexInputState, cacheInfo.vertexInputState);
+      result |= updateResult(info.inputAssemblyState, cacheInfo.inputAssemblyState);
+      result |= updateResult(info.rasterizationState, cacheInfo.rasterizationState);
+      result |= updateResult(info.framebufferState, cacheInfo.framebufferState);
+      result |= updateResult(info.colorBlendState, cacheInfo.colorBlendState);
+      result |= updateResult(info.depthStencilState, cacheInfo.depthStencilState);
+      result |= updateResult(info.viewportState, cacheInfo.viewportState);
+
+      // early exit
+      if(!result)
+      {
+        continue;
+      }
+
+      // For now ignoring dynamic state mask and allocator
+
+      // Getting as far as here, we have found our pipeline impl
+      return pipeline.get();
+    }
+  }
+  return nullptr;
+}
+
+Graphics::UniquePtr<Graphics::Pipeline> PipelineCache::GetPipeline(const PipelineCreateInfo& pipelineCreateInfo, Graphics::UniquePtr<Graphics::Pipeline>&& oldPipeline)
+{
+  // create or get from cache
+  auto cachedPipeline = FindPipelineImpl(pipelineCreateInfo);
+
+  if(!cachedPipeline)
+  {
+    // create new pipeline
+    auto pipeline = MakeUnique<GLES::PipelineImpl>(pipelineCreateInfo, mImpl->controller);
+
+    cachedPipeline = pipeline.get();
+
+    // add it to cache
+    mImpl->mPipelines.emplace_back(std::move(pipeline));
+  }
+  else
+  {
+  }
+  // create new pipeline wrapper n
+  auto wrapper = MakeUnique<GLES::Pipeline>(*cachedPipeline);
+  return std::move(wrapper);
+}
+
+} // namespace Dali::Graphics::GLES
\ No newline at end of file
diff --git a/dali/internal/graphics/gles-impl/gles-graphics-pipeline-cache.h b/dali/internal/graphics/gles-impl/gles-graphics-pipeline-cache.h
new file mode 100644 (file)
index 0000000..f93e079
--- /dev/null
@@ -0,0 +1,79 @@
+#ifndef DALI_GRAPHICS_GLES_PIPELINE_CACHE_H
+#define DALI_GRAPHICS_GLES_PIPELINE_CACHE_H
+
+/*
+ * Copyright (c) 2021 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// EXTERNAL INCLUDES
+#include <dali/graphics-api/graphics-pipeline-create-info.h>
+#include <dali/graphics-api/graphics-pipeline.h>
+
+// INTERNAL INCLUDES
+#include "gles-graphics-resource.h"
+
+namespace Dali::Graphics
+{
+class EglGraphicsController;
+namespace GLES
+{
+class Pipeline;
+class PipelineImpl;
+
+/**
+ * @brief PipelineCache manages pipeline and program
+ * objects so there are no duplicates created.
+ */
+class PipelineCache
+{
+public:
+  /**
+   * @brief Constructor
+   */
+  explicit PipelineCache(EglGraphicsController& controller);
+
+  /**
+   * @brief Destructor
+   */
+  ~PipelineCache();
+
+  /**
+   * @brief Retrieves pipeline matching the spec
+   *
+   * Function returns either existing pipeline if one is found
+   * in the cache or creates new one.
+   *
+   * @param[in] pipelineCreateInfo Valid PipelineCreateInfo structure
+   * @param[in] oldPipeline previous pipeline object
+   * @return Pipeline object
+   */
+  Graphics::UniquePtr<Graphics::Pipeline> GetPipeline(const PipelineCreateInfo& pipelineCreateInfo, Graphics::UniquePtr<Graphics::Pipeline>&& oldPipeline);
+
+private:
+  /**
+   * @brief Finds pipeline implementation based on the spec
+   * @param[in] info Valid create info structure
+   * @return Returns pointer to pipeline or nullptr
+   */
+  PipelineImpl* FindPipelineImpl(const PipelineCreateInfo& info);
+
+private:
+  struct Impl;
+  std::unique_ptr<Impl> mImpl;
+};
+} // namespace GLES
+} // namespace Dali::Graphics
+#endif
index 2f31d6e..b747a19 100644 (file)
  *
  */
 
+// CLASS HEADER
 #include "gles-graphics-pipeline.h"
+
+// EXTERNAL INCLUDES
 #include <dali/integration-api/gl-abstraction.h>
 #include <dali/integration-api/gl-defines.h>
 #include <memory>
+
+// INTERNAL INCLUDES
 #include "egl-graphics-controller.h"
 #include "gles-graphics-shader.h"
 
@@ -27,10 +32,10 @@ namespace Dali::Graphics::GLES
 /**
  * Copy of pipeline state, can be also used for internal caching
  */
-struct Pipeline::PipelineState
+struct PipelineImpl::PipelineState
 {
-  PipelineState() = default;
-
+  PipelineState()  = default;
+  ~PipelineState() = default;
   ColorBlendState          colorBlendState;
   DepthStencilState        depthStencilState;
   std::vector<ShaderState> shaderState;
@@ -41,11 +46,11 @@ struct Pipeline::PipelineState
   InputAssemblyState       inputAssemblyState;
 };
 
-Pipeline::Pipeline(const Graphics::PipelineCreateInfo& createInfo, Graphics::EglGraphicsController& controller)
-: PipelineResource(createInfo, controller)
+PipelineImpl::PipelineImpl(const Graphics::PipelineCreateInfo& createInfo, Graphics::EglGraphicsController& controller)
+: mController(controller)
 {
   // the creation is deferred so it's needed to copy certain parts of the CreateInfo structure
-  mPipelineState = std::make_unique<Pipeline::PipelineState>();
+  mPipelineState = std::make_unique<PipelineImpl::PipelineState>();
 
   // Make copies of structured pass by pointers and replace
   // stored create info structure fields
@@ -59,7 +64,17 @@ Pipeline::Pipeline(const Graphics::PipelineCreateInfo& createInfo, Graphics::Egl
   CopyStateIfSet(createInfo.viewportState, mPipelineState->viewportState, &mCreateInfo.viewportState);
 }
 
-bool Pipeline::InitializeResource()
+const PipelineCreateInfo& PipelineImpl::GetCreateInfo() const
+{
+  return mCreateInfo;
+}
+
+auto& PipelineImpl::GetController() const
+{
+  return mController;
+}
+
+bool PipelineImpl::InitializeResource()
 {
   auto& gl   = *GetController().GetGL();
   mGlProgram = gl.CreateProgram();
@@ -90,7 +105,7 @@ bool Pipeline::InitializeResource()
   return true;
 }
 
-void Pipeline::DestroyResource()
+void PipelineImpl::DestroyResource()
 {
   if(mGlProgram)
   {
@@ -99,18 +114,18 @@ void Pipeline::DestroyResource()
   }
 }
 
-void Pipeline::DiscardResource()
+void PipelineImpl::DiscardResource()
 {
   // Pass program to discard queue
 }
 
-uint32_t Pipeline::GetGLProgram() const
+uint32_t PipelineImpl::GetGLProgram() const
 {
   return mGlProgram;
 }
 
 // FIXME: THIS FUNCTION IS NOT IN USE YET, REQUIRES PROPER PIPELINE
-void Pipeline::Bind(GLES::Pipeline* prevPipeline)
+void PipelineImpl::Bind(GLES::PipelineImpl* prevPipeline)
 {
   // Same pipeline to bind, nothing to do
   if(prevPipeline == this)
@@ -147,4 +162,33 @@ void Pipeline::Bind(GLES::Pipeline* prevPipeline)
   gl.UseProgram(program);
 }
 
+void PipelineImpl::Retain()
+{
+  ++mRefCount;
+}
+
+void PipelineImpl::Release()
+{
+  --mRefCount;
+}
+
+uint32_t PipelineImpl::GetRefCount() const
+{
+  return mRefCount;
+}
+
+PipelineImpl::~PipelineImpl() = default;
+
+// PIPELINE WRAPPER
+
+const PipelineCreateInfo& Pipeline::GetCreateInfo() const
+{
+  return mPipeline.GetCreateInfo();
+}
+
+EglGraphicsController& Pipeline::GetController() const
+{
+  return mPipeline.GetController();
+}
+
 } // namespace Dali::Graphics::GLES
\ No newline at end of file
index 0f202e1..64dad5a 100644 (file)
@@ -30,7 +30,11 @@ namespace Dali::Graphics::GLES
 {
 using PipelineResource = Resource<Graphics::Pipeline, Graphics::PipelineCreateInfo>;
 
-class Pipeline : public PipelineResource
+/**
+ * @brief PipelineWrapper is the object
+ * returned to the client-side
+ */
+class PipelineImpl
 {
 public:
   /**
@@ -38,24 +42,29 @@ public:
    * @param[in] createInfo valid TextureCreateInfo structure
    * @param[in] controller Reference to the Controller
    */
-  Pipeline(const Graphics::PipelineCreateInfo& createInfo, Graphics::EglGraphicsController& controller);
+  PipelineImpl(const Graphics::PipelineCreateInfo& createInfo, Graphics::EglGraphicsController& controller);
+
+  /**
+   * @brief Destructor
+   */
+  ~PipelineImpl();
 
   /**
    * @brief Destroys all the low-level resources used by the class
    */
-  void DestroyResource() override;
+  void DestroyResource();
 
   /**
    * @brief Initializes low-level resources
    *
    * @return Tron success
    */
-  bool InitializeResource() override;
+  bool InitializeResource();
 
   /**
    * @brief Discards object
    */
-  void DiscardResource() override;
+  void DiscardResource();
 
   /**
    * @brief returns GL program id
@@ -73,7 +82,7 @@ public:
    *
    * @param[in] prevPipeline previous pipeline
    */
-  void Bind(GLES::Pipeline* prevPipeline);
+  void Bind(GLES::PipelineImpl* prevPipeline);
 
   /**
    * Executes state change function if condition met
@@ -95,6 +104,16 @@ public:
     }
   }
 
+  void Retain();
+
+  void Release();
+
+  [[nodiscard]] uint32_t GetRefCount() const;
+
+  [[nodiscard]] const PipelineCreateInfo& GetCreateInfo() const;
+
+  [[nodiscard]] auto& GetController() const;
+
 private:
   /**
    * @brief Helper function. Copies state if pointer is set
@@ -127,9 +146,49 @@ private:
   // Pipeline state is stored as a copy of create info
   // data.
   struct PipelineState;
-  std::unique_ptr<PipelineState> mPipelineState{};
+  std::unique_ptr<PipelineState> mPipelineState;
+
+  EglGraphicsController& mController;
+  PipelineCreateInfo     mCreateInfo;
 
   uint32_t mGlProgram{0u};
+
+  uint32_t mRefCount{0u};
+};
+
+/**
+ * @brief Pipeline class wraps a unique pipeline object
+ *
+ */
+class Pipeline : public Graphics::Pipeline
+{
+public:
+  Pipeline() = delete;
+
+  explicit Pipeline(GLES::PipelineImpl& pipeline)
+  : mPipeline(pipeline)
+  {
+    // increase refcount
+    mPipeline.Retain();
+  }
+
+  ~Pipeline() override
+  {
+    // decrease refcount
+    mPipeline.Release();
+  }
+
+  [[nodiscard]] auto& GetPipeline() const
+  {
+    return mPipeline;
+  }
+
+  [[nodiscard]] const PipelineCreateInfo& GetCreateInfo() const;
+
+  [[nodiscard]] EglGraphicsController& GetController() const;
+
+private:
+  GLES::PipelineImpl& mPipeline;
 };
 
 } // namespace Dali::Graphics::GLES