Shader support 09/254109/11
authorAdam Bialogonski <adam.b@samsung.com>
Thu, 25 Feb 2021 17:03:36 +0000 (17:03 +0000)
committerRichard Huang <r.huang@samsung.com>
Fri, 26 Feb 2021 20:18:13 +0000 (20:18 +0000)
- program implementation
- pipeline cache upgraded
- pipeline cache supports caching program
- shader reflection bound with Program
- added shader compilation and program linking

Change-Id: I53569d04848410b21150ca239b6f9b01015310b9

24 files changed:
automated-tests/src/dali-adaptor-internal/CMakeLists.txt
automated-tests/src/dali-adaptor/CMakeLists.txt
automated-tests/src/dali-adaptor/dali-test-suite-utils/test-graphics-controller.cpp
automated-tests/src/dali-adaptor/dali-test-suite-utils/test-graphics-controller.h
automated-tests/src/dali-adaptor/dali-test-suite-utils/test-graphics-pipeline.cpp
automated-tests/src/dali-adaptor/dali-test-suite-utils/test-graphics-pipeline.h
automated-tests/src/dali-adaptor/dali-test-suite-utils/test-graphics-program.cpp [new file with mode: 0644]
automated-tests/src/dali-adaptor/dali-test-suite-utils/test-graphics-program.h [new file with mode: 0644]
automated-tests/src/dali-graphics/CMakeLists.txt
automated-tests/src/dali-platform-abstraction/CMakeLists.txt
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-context.cpp
dali/internal/graphics/gles-impl/gles-graphics-pipeline-cache.cpp
dali/internal/graphics/gles-impl/gles-graphics-pipeline-cache.h
dali/internal/graphics/gles-impl/gles-graphics-pipeline.cpp
dali/internal/graphics/gles-impl/gles-graphics-pipeline.h
dali/internal/graphics/gles-impl/gles-graphics-program.cpp [new file with mode: 0644]
dali/internal/graphics/gles-impl/gles-graphics-program.h [new file with mode: 0644]
dali/internal/graphics/gles-impl/gles-graphics-reflection.cpp
dali/internal/graphics/gles-impl/gles-graphics-reflection.h
dali/internal/graphics/gles-impl/gles-graphics-shader.cpp
dali/internal/graphics/gles-impl/gles-graphics-shader.h

index 51f3cf7..7f3a34c 100644 (file)
@@ -34,6 +34,7 @@ LIST(APPEND TC_SOURCES
     ../dali-adaptor/dali-test-suite-utils/test-graphics-controller.cpp
     ../dali-adaptor/dali-test-suite-utils/test-graphics-texture.cpp
     ../dali-adaptor/dali-test-suite-utils/test-graphics-pipeline.cpp
+    ../dali-adaptor/dali-test-suite-utils/test-graphics-program.cpp
     ../dali-adaptor/dali-test-suite-utils/test-graphics-reflection.cpp
     ../dali-adaptor/dali-test-suite-utils/test-graphics-sampler.cpp
     ../dali-adaptor/dali-test-suite-utils/test-native-image.cpp
index b29d3ff..1039a3f 100644 (file)
@@ -32,6 +32,7 @@ LIST(APPEND TC_SOURCES
     dali-test-suite-utils/test-graphics-command-buffer.cpp
     dali-test-suite-utils/test-graphics-controller.cpp
     dali-test-suite-utils/test-graphics-pipeline.cpp
+    dali-test-suite-utils/test-graphics-program.cpp
     dali-test-suite-utils/test-graphics-reflection.cpp
     dali-test-suite-utils/test-graphics-texture.cpp
     dali-test-suite-utils/test-graphics-sampler.cpp
index 41d13c9..45fbfa3 100644 (file)
@@ -18,6 +18,7 @@
 
 #include "test-graphics-buffer.h"
 #include "test-graphics-command-buffer.h"
+#include "test-graphics-program.h"
 #include "test-graphics-reflection.h"
 #include "test-graphics-sampler.h"
 #include "test-graphics-texture.h"
@@ -705,6 +706,12 @@ Graphics::UniquePtr<Graphics::Pipeline> TestGraphicsController::CreatePipeline(c
   return std::make_unique<TestGraphicsPipeline>(mGl, pipelineCreateInfo);
 }
 
+Graphics::UniquePtr<Graphics::Program> TestGraphicsController::CreateProgram(const Graphics::ProgramCreateInfo& programCreateInfo, Graphics::UniquePtr<Graphics::Program>&& oldProgram)
+{
+  mCallStack.PushCall("CreateProgram", "");
+  return Graphics::MakeUnique<TestGraphicsProgram>(mGl, programCreateInfo, mVertexFormats);
+}
+
 Graphics::UniquePtr<Graphics::Shader> TestGraphicsController::CreateShader(const Graphics::ShaderCreateInfo& shaderCreateInfo, Graphics::UniquePtr<Graphics::Shader>&& oldShader)
 {
   mCallStack.PushCall("Controller::CreateShader", "");
@@ -767,12 +774,11 @@ const Graphics::TextureProperties& TestGraphicsController::GetTextureProperties(
   return textureProperties;
 }
 
-const Graphics::Reflection& TestGraphicsController::GetPipelineReflection(const Graphics::Pipeline& pipeline)
+const Graphics::Reflection& TestGraphicsController::GetProgramReflection(const Graphics::Program& program)
 {
-  static TestGraphicsReflection reflection(mGl);
-  mCallStack.PushCall("Controller::GetPipelineReflection", "");
+  mCallStack.PushCall("Controller::GetProgramReflection", "");
 
-  return reflection;
+  return static_cast<const TestGraphicsProgram*>(&program)->GetReflection();
 }
 
 bool TestGraphicsController::PipelineEquals(const Graphics::Pipeline& pipeline0, const Graphics::Pipeline& pipeline1) const
@@ -781,4 +787,10 @@ bool TestGraphicsController::PipelineEquals(const Graphics::Pipeline& pipeline0,
   return false;
 }
 
+bool TestGraphicsController::GetProgramParameter(Graphics::Program& program, uint32_t parameterId, void* outData)
+{
+  mCallStack.PushCall("Controller::GetProgramParameter", "");
+  return false;
+}
+
 } // namespace Dali
index bf2ba21..2ee3de4 100644 (file)
@@ -176,6 +176,14 @@ public:
   Graphics::UniquePtr<Graphics::Pipeline> CreatePipeline(const Graphics::PipelineCreateInfo& pipelineCreateInfo, Graphics::UniquePtr<Graphics::Pipeline>&& oldPipeline) override;
 
   /**
+   * @brief Creates new Program object
+   *
+   * @param[in] programCreateInfo The valid ProgramCreateInfo structure
+   * @return pointer to the Program object
+   */
+  Graphics::UniquePtr<Graphics::Program> CreateProgram(const Graphics::ProgramCreateInfo& programCreateInfo, Graphics::UniquePtr<Graphics::Program>&& oldProgram) override;
+
+  /**
    * @brief Creates new Shader object
    *
    * @param[in] shaderCreateInfo The valid ShaderCreateInfo structure
@@ -268,12 +276,12 @@ public:
   const Graphics::TextureProperties& GetTextureProperties(const Graphics::Texture& texture) override;
 
   /**
-   * @brief Returns the reflection of the given pipeline
+   * @brief Returns the reflection of the given program
    *
-   * @param[in] pipeline The pipeline
-   * @return The reflection of the pipeline
+   * @param[in] program The program
+   * @return The reflection of the program
    */
-  const Graphics::Reflection& GetPipelineReflection(const Graphics::Pipeline& pipeline) override;
+  const Graphics::Reflection& GetProgramReflection(const Graphics::Program& program) override;
 
   /**
    * @brief Tests whether two Pipelines are the same.
@@ -284,9 +292,33 @@ public:
    */
   bool PipelineEquals(const Graphics::Pipeline& pipeline0, const Graphics::Pipeline& pipeline1) const override;
 
+public: // Test Functions
+  void SetVertexFormats(Property::Array& vfs)
+  {
+    mVertexFormats = vfs;
+  }
+
+  void ClearSubmitStack()
+  {
+    mSubmitStack.clear();
+  }
+
+  /**
+   * @brief Retrieves program parameters
+   *
+   * This function can be used to retrieve data from internal implementation
+   *
+   * @param[in] program Valid program object
+   * @param[in] parameterId Integer parameter id
+   * @param[out] outData Pointer to output memory
+   * @return True on success
+   */
+  bool GetProgramParameter(Graphics::Program& program, uint32_t parameterId, void* outData) override;
+
 public:
-  mutable TraceCallStack mCallStack;
-  mutable TraceCallStack mCommandBufferCallStack;
+  mutable TraceCallStack                    mCallStack;
+  mutable TraceCallStack                    mCommandBufferCallStack;
+  mutable std::vector<Graphics::SubmitInfo> mSubmitStack;
 
   TestGlAbstraction              mGl;
   TestGlSyncAbstraction          mGlSyncAbstraction;
@@ -294,6 +326,8 @@ public:
 
   bool isDiscardQueueEmptyResult{true};
   bool isDrawOnResumeRequiredResult{true};
+
+  Property::Array mVertexFormats;
 };
 
 } // namespace Dali
index 0cc4194..d303d56 100644 (file)
@@ -26,8 +26,8 @@ TestGraphicsPipeline::TestGraphicsPipeline(TestGlAbstraction& gl, const Graphics
   if(createInfo.colorBlendState)
     colorBlendState = *createInfo.colorBlendState;
 
-  if(createInfo.shaderState)
-    shaderState = *createInfo.shaderState;
+  if(createInfo.programState)
+    programState = *createInfo.programState;
 
   if(createInfo.viewportState)
     viewportState = *createInfo.viewportState;
index 666e0d4..dc3a58d 100644 (file)
@@ -31,7 +31,7 @@ public:
   TestGlAbstraction& mGl;
 
   Graphics::ColorBlendState          colorBlendState;
-  std::vector<Graphics::ShaderState> shaderState;
+  Graphics::ProgramState             programState;
   Graphics::ViewportState            viewportState;
   Graphics::FramebufferState         framebufferState;
   Graphics::Pipeline                 basePipeline;
diff --git a/automated-tests/src/dali-adaptor/dali-test-suite-utils/test-graphics-program.cpp b/automated-tests/src/dali-adaptor/dali-test-suite-utils/test-graphics-program.cpp
new file mode 100644 (file)
index 0000000..5b8c02c
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * 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.
+ */
+
+#include "test-graphics-program.h"
+
+namespace Dali
+{
+TestGraphicsProgram::TestGraphicsProgram(TestGlAbstraction& gl, const Graphics::ProgramCreateInfo& createInfo, Property::Array& vertexFormats)
+: mGl(gl),
+  mCreateInfo(createInfo),
+  mReflection(gl)
+{
+}
+
+} // namespace Dali
diff --git a/automated-tests/src/dali-adaptor/dali-test-suite-utils/test-graphics-program.h b/automated-tests/src/dali-adaptor/dali-test-suite-utils/test-graphics-program.h
new file mode 100644 (file)
index 0000000..2bd04de
--- /dev/null
@@ -0,0 +1,53 @@
+#ifndef DALI_TEST_GRAPHICS_PROGRAM_H
+#define DALI_TEST_GRAPHICS_PROGRAM_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.
+ */
+
+#include <dali/graphics-api/graphics-program-create-info.h>
+#include <dali/graphics-api/graphics-program.h>
+#include "test-gl-abstraction.h"
+#include "test-graphics-reflection.h"
+
+namespace Dali
+{
+class TestGraphicsProgram : public Graphics::Program
+{
+public:
+  TestGraphicsProgram(TestGlAbstraction& gl, const Graphics::ProgramCreateInfo& createInfo, Property::Array& vertexFormats);
+
+  // For API
+  const TestGraphicsReflection& GetReflection() const
+  {
+    // Build a reflection
+    return mReflection;
+  }
+
+  // For tests
+  TestGraphicsReflection& GetProgamReflection()
+  {
+    return mReflection;
+  }
+
+public:
+  TestGlAbstraction&          mGl;
+  Graphics::ProgramCreateInfo mCreateInfo;
+  TestGraphicsReflection      mReflection;
+};
+
+} // namespace Dali
+
+#endif //DALI_TEST_GRAPHICS_PROGRAM_H
index e1c48a8..8e0a197 100644 (file)
@@ -20,6 +20,7 @@ LIST(APPEND TC_SOURCES
   ../dali-adaptor/dali-test-suite-utils/test-graphics-texture.cpp
   ../dali-adaptor/dali-test-suite-utils/test-graphics-sampler.cpp
   ../dali-adaptor/dali-test-suite-utils/test-graphics-pipeline.cpp
+  ../dali-adaptor/dali-test-suite-utils/test-graphics-program.cpp
   ../dali-adaptor/dali-test-suite-utils/test-graphics-reflection.cpp
   ../dali-adaptor/dali-test-suite-utils/test-native-image.cpp
   ../dali-adaptor/dali-test-suite-utils/test-platform-abstraction.cpp
index 3fdc341..2335408 100644 (file)
@@ -23,6 +23,7 @@ LIST(APPEND TC_SOURCES
     ../dali-adaptor/dali-test-suite-utils/test-graphics-texture.cpp
     ../dali-adaptor/dali-test-suite-utils/test-graphics-sampler.cpp
     ../dali-adaptor/dali-test-suite-utils/test-graphics-pipeline.cpp
+    ../dali-adaptor/dali-test-suite-utils/test-graphics-program.cpp
     ../dali-adaptor/dali-test-suite-utils/test-graphics-reflection.cpp
     ../dali-adaptor/dali-test-suite-utils/test-native-image.cpp
     ../dali-adaptor/dali-test-suite-utils/test-platform-abstraction.cpp
index ef507a7..86fa049 100644 (file)
@@ -26,6 +26,7 @@
 #include <dali/internal/graphics/gles-impl/gles-graphics-texture.h>
 #include <dali/internal/graphics/gles-impl/gles-graphics-types.h>
 #include <dali/public-api/common/dali-common.h>
+#include "gles-graphics-program.h"
 
 namespace Dali::Graphics
 {
@@ -152,11 +153,6 @@ EglGraphicsController::CreateBuffer(const BufferCreateInfo& bufferCreateInfo, Gr
   return NewObject<GLES::Buffer>(bufferCreateInfo, *this, std::move(oldBuffer));
 }
 
-Graphics::UniquePtr<Shader> EglGraphicsController::CreateShader(const ShaderCreateInfo& shaderCreateInfo, Graphics::UniquePtr<Shader>&& oldShader)
-{
-  return NewObject<GLES::Shader>(shaderCreateInfo, *this, std::move(oldShader));
-}
-
 Graphics::UniquePtr<Pipeline> EglGraphicsController::CreatePipeline(const PipelineCreateInfo& pipelineCreateInfo, Graphics::UniquePtr<Graphics::Pipeline>&& oldPipeline)
 {
   // Create pipeline cache if needed
@@ -168,6 +164,27 @@ Graphics::UniquePtr<Pipeline> EglGraphicsController::CreatePipeline(const Pipeli
   return mPipelineCache->GetPipeline(pipelineCreateInfo, std::move(oldPipeline));
 }
 
+Graphics::UniquePtr<Program> EglGraphicsController::CreateProgram(const ProgramCreateInfo& programCreateInfo, UniquePtr<Program>&& oldProgram)
+{
+  // Create program cache if needed
+  if(!mPipelineCache)
+  {
+    mPipelineCache = std::make_unique<GLES::PipelineCache>(*this);
+  }
+
+  return mPipelineCache->GetProgram(programCreateInfo, std::move(oldProgram));
+}
+
+Graphics::UniquePtr<Shader> EglGraphicsController::CreateShader(const ShaderCreateInfo& shaderCreateInfo, Graphics::UniquePtr<Shader>&& oldShader)
+{
+  return NewObject<GLES::Shader>(shaderCreateInfo, *this, std::move(oldShader));
+}
+
+const Graphics::Reflection& EglGraphicsController::GetProgramReflection(const Graphics::Program& program)
+{
+  return static_cast<const Graphics::GLES::Program*>(&program)->GetReflection();
+}
+
 void EglGraphicsController::AddTexture(GLES::Texture& texture)
 {
   // Assuming we are on the correct context
@@ -187,6 +204,12 @@ void EglGraphicsController::ProcessDiscardQueues()
 
   // Process buffers
   ProcessDiscardQueue<GLES::Buffer>(mDiscardBufferQueue);
+
+  // Process pipelines
+  ProcessDiscardQueue<GLES::Pipeline>(mDiscardPipelineQueue);
+
+  // Process programs
+  ProcessDiscardQueue<GLES::Program>(mDiscardProgramQueue);
 }
 
 void EglGraphicsController::ProcessCreateQueues()
@@ -355,4 +378,14 @@ Graphics::UniquePtr<Memory> EglGraphicsController::MapBufferRange(const MapBuffe
   return Graphics::UniquePtr<Memory>(new GLES::Memory(mapInfo, *this));
 }
 
+bool EglGraphicsController::GetProgramParameter(Graphics::Program& program, uint32_t parameterId, void* outData)
+{
+  return static_cast<GLES::Program*>(&program)->GetImplementation()->GetParameter(parameterId, outData);
+}
+
+GLES::PipelineCache& EglGraphicsController::GetPipelineCache() const
+{
+  return *mPipelineCache;
+}
+
 } // namespace Dali::Graphics
index c7d85c0..70f6d8c 100644 (file)
@@ -25,8 +25,8 @@
 #include "gles-context.h"
 #include "gles-graphics-buffer.h"
 #include "gles-graphics-memory.h"
-#include "gles-graphics-pipeline.h"
 #include "gles-graphics-pipeline-cache.h"
+#include "gles-graphics-pipeline.h"
 #include "gles-graphics-reflection.h"
 #include "gles-graphics-texture.h"
 
@@ -196,6 +196,11 @@ public:
   Graphics::UniquePtr<Pipeline> CreatePipeline(const PipelineCreateInfo& pipelineCreateInfo, Graphics::UniquePtr<Pipeline>&& oldPipeline) override;
 
   /**
+   * @copydoc Dali::Graphics::CreateProgram()
+   */
+  Graphics::UniquePtr<Program> CreateProgram(const ProgramCreateInfo& programCreateInfo, UniquePtr<Program>&& oldProgram) override;
+
+  /**
    * @copydoc Dali::Graphics::CreateShader()
    */
   Graphics::UniquePtr<Shader> CreateShader(const ShaderCreateInfo& shaderCreateInfo, Graphics::UniquePtr<Shader>&& oldShader) override;
@@ -264,11 +269,8 @@ public:
   /**
    * @copydoc Dali::Graphics::Controller::GetPipelineReflection()
    */
-  const Reflection& GetPipelineReflection(const Pipeline& pipeline) override
-  {
-    static GLES::Reflection dummy(*this);
-    return dummy;
-  }
+
+  [[nodiscard]] const Reflection& GetProgramReflection(const Graphics::Program& program) override;
 
   /**
    * @copydoc Dali::Graphics::PipelineEquals()
@@ -317,6 +319,30 @@ public:
   }
 
   /**
+   * @brief Pushes Program to the discard queue
+   *
+   * Function is called from the UniquePtr custom deleter.
+   *
+   * @param[in] program Pointer to the program
+   */
+  void DiscardResource(GLES::Program* program)
+  {
+    mDiscardProgramQueue.push(program);
+  }
+
+  /**
+   * @brief Pushes Pipeline to the discard queue
+   *
+   * Function is called from the UniquePtr custom deleter.
+   *
+   * @param[in] program Pointer to the pipeline
+   */
+  void DiscardResource(GLES::Pipeline* pipeline)
+  {
+    mDiscardPipelineQueue.push(pipeline);
+  }
+
+  /**
    * @brief Flushes all pending updates
    *
    * Function flushes all pending resource constructions,
@@ -330,11 +356,14 @@ public:
     // Process updates
     ProcessTextureUpdateQueue();
 
+    // Process main command queue
+    ProcessCommandQueues();
+
     // Process discards
     ProcessDiscardQueues();
 
-    // Process main command queue
-    ProcessCommandQueues();
+    // Flush pipeline cache to remove unused pipelines
+    GetPipelineCache().FlushCache();
   }
 
   // Test update to tick controller, usually it will run own thread
@@ -409,6 +438,26 @@ public:
    */
   void ProcessTextureUpdateQueue();
 
+  /**
+   * @brief Returns program custom parameter
+   *
+   * This function can be used as a backdoor in order to retrieve
+   * certain data out of implementation
+   *
+   * @param[in] program Valid Program object
+   * @param parameterId Integer id of parameter
+   * @param outData Output data
+   * @return True if parameter retrieved
+   */
+  bool GetProgramParameter(Graphics::Program& program, uint32_t parameterId, void* outData) override;
+
+  /**
+   * @brief Returns pipeline cache object
+   *
+   * @return Valid pipeline cache object
+   */
+  [[nodiscard]] GLES::PipelineCache& GetPipelineCache() const;
+
 private:
   Integration::GlAbstraction*              mGlAbstraction{nullptr};
   Integration::GlSyncAbstraction*          mGlSyncAbstraction{nullptr};
@@ -420,6 +469,10 @@ private:
   std::queue<GLES::Buffer*> mCreateBufferQueue;  ///< Create queue for buffer resource
   std::queue<GLES::Buffer*> mDiscardBufferQueue; ///< Discard queue for buffer resource
 
+  std::queue<GLES::Program*> mDiscardProgramQueue; ///< Discard queue for program resource
+
+  std::queue<GLES::Pipeline*> mDiscardPipelineQueue; ///< Discard queue of pipelines
+
   std::queue<GLES::CommandBuffer*> mCommandQueue; ///< we may have more in the future
 
   using TextureUpdateRequest = std::pair<TextureUpdateInfo, TextureUpdateSourceInfo>;
index e251745..6195634 100644 (file)
@@ -8,6 +8,7 @@ SET( adaptor_graphics_gles_src_files ${adaptor_graphics_gles_src_files}
     ${adaptor_graphics_dir}/gles-impl/gles-graphics-framebuffer.cpp
     ${adaptor_graphics_dir}/gles-impl/gles-graphics-memory.cpp
     ${adaptor_graphics_dir}/gles-impl/gles-graphics-pipeline.cpp
+    ${adaptor_graphics_dir}/gles-impl/gles-graphics-program.cpp
     ${adaptor_graphics_dir}/gles-impl/gles-graphics-reflection.cpp
     ${adaptor_graphics_dir}/gles-impl/gles-graphics-render-pass.cpp
     ${adaptor_graphics_dir}/gles-impl/gles-graphics-render-target.cpp
index 3d965ad..50eb479 100644 (file)
@@ -70,6 +70,7 @@ void Context::Flush(bool reset, const GLES::DrawCallDescriptor& drawCall)
     mImpl->mCurrentPipeline = mImpl->mNewPipeline;
     mImpl->mNewPipeline     = nullptr;
   }
+  mImpl->mCurrentPipeline->GetPipeline().Bind(nullptr);
 
   // Blend state
   ResolveBlendState();
index 9e69c77..4b178a2 100644 (file)
  *
  */
 
-// CLASS HEADER
 #include "gles-graphics-pipeline-cache.h"
-
-// INTERNAL INCLUDES
+#include <algorithm>
 #include "gles-graphics-pipeline.h"
+#include "gles-graphics-program.h"
 
 namespace Dali::Graphics::GLES
 {
 /**
- * @brief Implementation of pipeline cache
+ * @brief custom delete function for cached object
+ */
+template<class T>
+struct CachedObjectDeleter
+{
+  CachedObjectDeleter() = default;
+
+  void operator()(T* object)
+  {
+    // Discard resource (add it to discard queue)
+    object->DiscardResource();
+  }
+};
+
+/**
+ * @brief The order of states being stored in the cache and mask
+ */
+enum class StateLookupIndex : uint32_t
+{
+  COLOR_BLEND_STATE_BIT    = 0,
+  VIEWPORT_STATE_BIT       = 1,
+  FRAMEBUFFER_STATE_BIT    = 2,
+  BASE_PIPELINE_STATE_BIT  = 3,
+  DEPTH_STENCIL_STATE_BIT  = 4,
+  RASTERIZATION_STATE_BIT  = 5,
+  VERTEX_INPUT_STATE_BIT   = 6,
+  INPUT_ASSEMBLY_STATE_BIT = 7,
+  MAX_STATE                = 8
+};
+
+/**
+ * Helper float compare function
+ */
+static bool cmpf(float A, float B, float epsilon = 0.005f)
+{
+  return (fabs(A - B) < epsilon);
+}
+
+/**
+ * Helper operators
+ */
+static bool operator==(const Graphics::Viewport& lhs, const Graphics::Viewport& rhs)
+{
+  return cmpf(lhs.x, rhs.x) &&
+         cmpf(lhs.y, rhs.y) &&
+         cmpf(lhs.width, rhs.width) &&
+         cmpf(lhs.height, rhs.height) &&
+         cmpf(lhs.minDepth, rhs.minDepth) &&
+         cmpf(lhs.maxDepth, rhs.maxDepth);
+}
+
+static bool operator==(const Graphics::Rect2D& lhs, const Graphics::Rect2D& rhs)
+{
+  return cmpf(lhs.x, rhs.x) &&
+         cmpf(lhs.y, rhs.y) &&
+         cmpf(lhs.width, rhs.width) &&
+         cmpf(lhs.height, rhs.height);
+}
+
+static bool operator==(const Graphics::StencilOpState& lhs, const Graphics::StencilOpState& rhs)
+{
+  return lhs.failOp == rhs.failOp &&
+         lhs.passOp == rhs.passOp &&
+         lhs.depthFailOp == rhs.depthFailOp &&
+         lhs.compareOp == rhs.compareOp &&
+         lhs.compareMask == rhs.compareMask &&
+         lhs.writeMask == rhs.writeMask &&
+         lhs.reference == rhs.reference;
+}
+
+static bool
+operator==(const Dali::Graphics::VertexInputState::Attribute& lhs,
+           const Dali::Graphics::VertexInputState::Attribute& rhs)
+{
+  return lhs.location == rhs.location &&
+         lhs.binding == rhs.binding &&
+         lhs.offset == rhs.offset &&
+         lhs.format == rhs.format;
+}
+
+static bool
+operator==(const Dali::Graphics::VertexInputState::Binding& lhs, const Dali::Graphics::VertexInputState::Binding& rhs)
+{
+  return lhs.stride == rhs.stride &&
+         lhs.inputRate == rhs.inputRate;
+}
+
+using PipelineStateCompateFunctionType = bool(const Graphics::PipelineCreateInfo*,
+                                              const Graphics::PipelineCreateInfo*);
+
+static std::vector<PipelineStateCompateFunctionType*> STATE_COMPARE_FUNC_TABLE{};
+
+/**
+ * @brief Initialises compare function lookup table
+ */
+void InitialiseStateCompareLookupTable()
+{
+  STATE_COMPARE_FUNC_TABLE = {
+    [](const auto* lhs, const auto* rhs) -> bool // colorBlendState
+    {
+      const auto& lcb = *lhs->colorBlendState;
+      const auto& rcb = *rhs->colorBlendState;
+      return lcb.logicOpEnable == rcb.logicOpEnable &&
+             lcb.logicOp == rcb.logicOp &&
+             cmpf(lcb.blendConstants[0], rcb.blendConstants[0]) &&
+             cmpf(lcb.blendConstants[1], rcb.blendConstants[1]) &&
+             cmpf(lcb.blendConstants[2], rcb.blendConstants[2]) &&
+             cmpf(lcb.blendConstants[3], rcb.blendConstants[3]) &&
+             lcb.blendEnable == rcb.blendEnable &&
+             lcb.srcColorBlendFactor == rcb.srcColorBlendFactor &&
+             lcb.dstColorBlendFactor == rcb.dstColorBlendFactor &&
+             lcb.colorBlendOp == rcb.colorBlendOp &&
+             lcb.srcAlphaBlendFactor == rcb.srcAlphaBlendFactor &&
+             lcb.dstAlphaBlendFactor == rcb.dstAlphaBlendFactor &&
+             lcb.alphaBlendOp == rcb.alphaBlendOp &&
+             lcb.colorComponentWriteBits == rcb.colorComponentWriteBits;
+    },
+    [](const auto* lhs, const auto* rhs) -> bool // viewport state
+    {
+      const auto& lvp = *lhs->viewportState;
+      const auto& rvp = *rhs->viewportState;
+      return lvp.viewport == rvp.viewport &&
+             lvp.scissor == rvp.scissor &&
+             lvp.scissorTestEnable == rvp.scissorTestEnable;
+    },
+    [](const auto* lhs, const auto* rhs) -> bool // framebufferState
+    {
+      const auto& lfb = *lhs->framebufferState;
+      const auto& rfb = *rhs->framebufferState;
+      return lfb.framebuffer == rfb.framebuffer;
+    },
+    [](const auto* lhs, const auto* rhs) -> bool // basePipeline
+    {
+      return lhs->basePipeline == rhs->basePipeline;
+    },
+    [](const auto* lhs, const auto* rhs) -> bool // depthStencilState
+    {
+      const auto& lds = *lhs->depthStencilState;
+      const auto& rds = *rhs->depthStencilState;
+      return lds.depthTestEnable == rds.depthTestEnable &&
+             lds.depthWriteEnable == rds.depthWriteEnable &&
+             lds.depthCompareOp == rds.depthCompareOp &&
+             lds.stencilTestEnable == rds.stencilTestEnable &&
+             lds.front == rds.front &&
+             lds.back == rds.back;
+    },
+    [](const auto* lhs, const auto* rhs) -> bool // rasterizationState
+    {
+      const auto& lrs = *lhs->rasterizationState;
+      const auto& rrs = *rhs->rasterizationState;
+      return lrs.cullMode == rrs.cullMode &&
+             lrs.polygonMode == rrs.polygonMode &&
+             lrs.frontFace == rrs.frontFace;
+    },
+    [](const auto* lhs, const auto* rhs) -> bool // vertexInputState
+    {
+      const auto& lvi = *lhs->vertexInputState;
+      const auto& rvi = *rhs->vertexInputState;
+      return lvi.bufferBindings.size() == rvi.bufferBindings.size() &&
+             lvi.attributes.size() == rvi.attributes.size() &&
+             std::equal(lvi.bufferBindings.begin(), lvi.bufferBindings.end(), rvi.bufferBindings.begin(), [](const auto& lhs, const auto& rhs) {
+               return operator==(lhs, rhs);
+             }) &&
+             std::equal(lvi.attributes.begin(), lvi.attributes.end(), rvi.attributes.begin(), [](const auto& lhs, const auto& rhs) {
+               return operator==(lhs, rhs);
+             });
+    },
+    [](const auto* lhs, const auto* rhs) -> bool // inputAssemblyState
+    {
+      const auto& lia = *lhs->inputAssemblyState;
+      const auto& ria = *rhs->inputAssemblyState;
+      return lia.topology == ria.topology &&
+             lia.primitiveRestartEnable == ria.primitiveRestartEnable;
+    },
+  };
+}
+
+/**
+ * @brief Helper function calculating the bitmask of set states
+ *
+ * @param[in] info Valid PipelineCreateInfo structure
+ * @return bitmask of set states
+ */
+inline uint32_t GetStateBitmask(const PipelineCreateInfo& info)
+{
+  uint32_t mask{0u};
+  mask |= bool(info.colorBlendState) << int(StateLookupIndex::COLOR_BLEND_STATE_BIT);
+  mask |= bool(info.viewportState) << int(StateLookupIndex::VIEWPORT_STATE_BIT);
+  mask |= bool(info.framebufferState) << int(StateLookupIndex::FRAMEBUFFER_STATE_BIT);
+  mask |= bool(info.basePipeline) << int(StateLookupIndex::BASE_PIPELINE_STATE_BIT);
+  mask |= bool(info.depthStencilState) << int(StateLookupIndex::DEPTH_STENCIL_STATE_BIT);
+  mask |= bool(info.rasterizationState) << int(StateLookupIndex::RASTERIZATION_STATE_BIT);
+  mask |= bool(info.vertexInputState) << int(StateLookupIndex::VERTEX_INPUT_STATE_BIT);
+  mask |= bool(info.inputAssemblyState) << int(StateLookupIndex::INPUT_ASSEMBLY_STATE_BIT);
+  return mask;
+}
+
+/**
+ * @brief Implementation of cache
  */
 struct PipelineCache::Impl
 {
+  /**
+   * @brief Constructor
+   */
   explicit Impl(EglGraphicsController& _controller)
   : controller(_controller)
   {
+    // Initialise lookup table
+    InitialiseStateCompareLookupTable();
   }
 
+  /**
+   * @brief destructor
+   */
   ~Impl() = default;
 
-  EglGraphicsController& controller;
+  /**
+   * @brief Structure describes a single cache entry
+   */
+  struct CacheEntry
+  {
+    CacheEntry() = default;
+
+    CacheEntry(UniquePtr<PipelineImpl>&& _pipeline, uint32_t _bitmask)
+    : pipeline(std::move(_pipeline)),
+      stateBitmask(_bitmask)
+    {
+    }
+
+    ~CacheEntry() = default;
+
+    CacheEntry(CacheEntry&&) = default;
+    CacheEntry& operator=(CacheEntry&&) = default;
+
+    UniquePtr<PipelineImpl> pipeline{nullptr};
+    uint32_t                stateBitmask{0u};
+  };
 
-  std::vector<UniquePtr<PipelineImpl>> mPipelines;
+  EglGraphicsController&  controller;
+  std::vector<CacheEntry> entries;
+
+  /**
+   * @brief Sorted array of shaders used to create program
+   */
+  struct ProgramCacheEntry
+  {
+    // sorted array of shaders
+    std::vector<const Shader*> shaders;
+    UniquePtr<ProgramImpl>     program{nullptr};
+  };
+
+  std::vector<ProgramCacheEntry> programEntries;
 };
 
 PipelineCache::PipelineCache(EglGraphicsController& controller)
@@ -49,108 +287,180 @@ 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 bitmask = GetStateBitmask(info);
+
+  for(auto& entry : mImpl->entries)
   {
+    auto& pipeline  = entry.pipeline;
     auto& cacheInfo = pipeline->GetCreateInfo();
-
-    if(!info.shaderState)
+    if(!info.programState)
     {
       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())
+    // Check whether the program is the same
+    if(info.programState->program)
     {
-      if(memcmp(info.shaderState->data(), cacheInfo.shaderState->data(), sizeof(info.shaderState->size() * sizeof(ShaderState))) != 0)
+      const auto& lhsProgram = *static_cast<const GLES::Program*>(info.programState->program);
+      const auto& rhsProgram = *static_cast<const GLES::Program*>(cacheInfo.programState->program);
+      if(lhsProgram != rhsProgram)
       {
-        continue; // early exit
+        continue;
       }
 
-      // 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)
+      // Test whether set states bitmask matches
+      if(entry.stateBitmask != bitmask)
       {
-        continue; // early exit
+        continue;
       }
 
-      // 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)
+      // Now test only for states that are set
+      auto i = 0;
+      for(i = 0; i < int(StateLookupIndex::MAX_STATE); ++i)
       {
-        continue;
+        // Test only set states
+        if((entry.stateBitmask & (1 << i)))
+        {
+          if(!STATE_COMPARE_FUNC_TABLE[i](&info, &cacheInfo))
+          {
+            break;
+          }
+        }
       }
 
-      // For now ignoring dynamic state mask and allocator
-
+      // TODO: For now ignoring dynamic state mask and allocator
       // Getting as far as here, we have found our pipeline impl
-      return pipeline.get();
+      if(i == int(StateLookupIndex::MAX_STATE))
+      {
+        return pipeline.get();
+      }
+    }
+  }
+  return nullptr;
+}
+
+ProgramImpl* PipelineCache::FindProgramImpl(const ProgramCreateInfo& info)
+{
+  if(mImpl->programEntries.empty())
+  {
+    return nullptr;
+  }
+
+  // assert if no shaders given
+  std::vector<const Shader*> shaders;
+  shaders.reserve(info.shaderState->size());
+
+  for(auto& state : *info.shaderState)
+  {
+    shaders.push_back(state.shader);
+  }
+
+  // sort
+  std::sort(shaders.begin(), shaders.end());
+
+  for(auto& item : mImpl->programEntries)
+  {
+    if(item.shaders.size() != shaders.size())
+    {
+      continue;
+    }
+
+    int k = shaders.size();
+    while(--k >= 0 && item.shaders[k] == shaders[k])
+      ;
+
+    if(k < 0)
+    {
+      return item.program.get();
     }
   }
   return nullptr;
 }
 
-Graphics::UniquePtr<Graphics::Pipeline> PipelineCache::GetPipeline(const PipelineCreateInfo& pipelineCreateInfo, Graphics::UniquePtr<Graphics::Pipeline>&& oldPipeline)
+Graphics::UniquePtr<Graphics::Pipeline> PipelineCache::GetPipeline(const PipelineCreateInfo&                 pipelineCreateInfo,
+                                                                   Graphics::UniquePtr<Graphics::Pipeline>&& oldPipeline)
 {
-  // create or get from cache
   auto cachedPipeline = FindPipelineImpl(pipelineCreateInfo);
 
+  // Return same pointer if nothing changed
+  if(oldPipeline && *static_cast<GLES::Pipeline*>(oldPipeline.get()) == cachedPipeline)
+  {
+    return std::move(oldPipeline);
+  }
+
   if(!cachedPipeline)
   {
     // create new pipeline
-    auto pipeline = MakeUnique<GLES::PipelineImpl>(pipelineCreateInfo, mImpl->controller);
+    auto pipeline = MakeUnique<GLES::PipelineImpl>(pipelineCreateInfo, mImpl->controller, *this);
 
     cachedPipeline = pipeline.get();
 
     // add it to cache
-    mImpl->mPipelines.emplace_back(std::move(pipeline));
+    mImpl->entries.emplace_back(std::move(pipeline), GetStateBitmask(pipelineCreateInfo));
   }
-  else
+
+  auto wrapper = MakeUnique<GLES::Pipeline, CachedObjectDeleter<GLES::Pipeline>>(*cachedPipeline);
+  return std::move(wrapper);
+}
+
+Graphics::UniquePtr<Graphics::Program> PipelineCache::GetProgram(const ProgramCreateInfo&                 programCreateInfo,
+                                                                 Graphics::UniquePtr<Graphics::Program>&& oldProgram)
+{
+  ProgramImpl* cachedProgram = FindProgramImpl(programCreateInfo);
+
+  // Return same pointer if nothing changed
+  if(oldProgram && *static_cast<GLES::Program*>(oldProgram.get()) == cachedProgram)
   {
+    return std::move(oldProgram);
   }
-  // create new pipeline wrapper n
-  auto wrapper = MakeUnique<GLES::Pipeline>(*cachedPipeline);
+
+  if(!cachedProgram)
+  {
+    // create new pipeline
+    auto program = MakeUnique<GLES::ProgramImpl>(programCreateInfo, mImpl->controller);
+
+    program->Create();
+
+    cachedProgram = program.get();
+
+    // add it to cache
+    mImpl->programEntries.emplace_back();
+    auto& item   = mImpl->programEntries.back();
+    item.program = std::move(program);
+    for(auto& state : *programCreateInfo.shaderState)
+    {
+      item.shaders.push_back(state.shader);
+    }
+
+    std::sort(item.shaders.begin(), item.shaders.end());
+  }
+
+  auto wrapper = MakeUnique<GLES::Program, CachedObjectDeleter<GLES::Program>>(cachedProgram);
   return std::move(wrapper);
 }
 
+void PipelineCache::FlushCache()
+{
+  decltype(mImpl->entries) newEntries;
+  newEntries.reserve(mImpl->entries.size());
+
+  for(auto& entry : mImpl->entries)
+  {
+    // Move items which are still in use into the new array
+    if(entry.pipeline->GetRefCount() != 0)
+    {
+      newEntries.emplace_back(std::move(entry));
+    }
+  }
+
+  // Move temporary array in place of stored cache
+  // Unused pipelines will be deleted automatically
+  mImpl->entries = std::move(newEntries);
+
+  // TODO: program cache may require similar action. However,
+  //       since there is always one wrapper for Program object
+  //       kept in the pipeline, then death of pipeline will result
+  //       killing the program (if program isn't in use anymore)
+}
+
 } // namespace Dali::Graphics::GLES
\ No newline at end of file
index f93e079..9c1a16b 100644 (file)
@@ -21,6 +21,8 @@
 // EXTERNAL INCLUDES
 #include <dali/graphics-api/graphics-pipeline-create-info.h>
 #include <dali/graphics-api/graphics-pipeline.h>
+#include <dali/graphics-api/graphics-program-create-info.h>
+#include <dali/graphics-api/graphics-program.h>
 
 // INTERNAL INCLUDES
 #include "gles-graphics-resource.h"
@@ -32,7 +34,8 @@ namespace GLES
 {
 class Pipeline;
 class PipelineImpl;
-
+class Program;
+class ProgramImpl;
 /**
  * @brief PipelineCache manages pipeline and program
  * objects so there are no duplicates created.
@@ -62,6 +65,26 @@ public:
    */
   Graphics::UniquePtr<Graphics::Pipeline> GetPipeline(const PipelineCreateInfo& pipelineCreateInfo, Graphics::UniquePtr<Graphics::Pipeline>&& oldPipeline);
 
+  /**
+   * @brief Retrieves program matching the spec
+   *
+   * Function returns either existing program if one is found
+   * in the cache or creates new one.
+   *
+   * @param[in] programCreateInfo Valid ProgramCreateInfo structure
+   * @param[in] oldProgram previous program object
+   * @return Program object
+   */
+  Graphics::UniquePtr<Graphics::Program> GetProgram(const ProgramCreateInfo& pipelineCreateInfo, Graphics::UniquePtr<Graphics::Program>&& oldProgram);
+
+  /**
+   * @brief Flushes pipeline and program cache
+   *
+   * Removes cached items when they are no longer needed. This function
+   * should be called at the very end of Controller render loop iteration.
+   */
+  void FlushCache();
+
 private:
   /**
    * @brief Finds pipeline implementation based on the spec
@@ -70,6 +93,13 @@ private:
    */
   PipelineImpl* FindPipelineImpl(const PipelineCreateInfo& info);
 
+  /**
+ * @brief Finds program implementation based on the spec
+ * @param[in] info Valid create info structure
+ * @return Returns pointer to program or nullptr
+ */
+  ProgramImpl* FindProgramImpl(const ProgramCreateInfo& info);
+
 private:
   struct Impl;
   std::unique_ptr<Impl> mImpl;
index dbebefb..8cc9491 100644 (file)
 #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"
-
-namespace
-{
-struct LegacyProgram : Dali::Graphics::ExtensionCreateInfo
-{
-  uint32_t programId;
-};
-} // namespace
+#include "gles-graphics-pipeline-cache.h"
+#include "gles-graphics-program.h"
 
 namespace Dali::Graphics::GLES
 {
@@ -44,19 +36,25 @@ struct PipelineImpl::PipelineState
 {
   PipelineState()  = default;
   ~PipelineState() = default;
-  ColorBlendState          colorBlendState;
-  DepthStencilState        depthStencilState;
-  std::vector<ShaderState> shaderState;
-  ViewportState            viewportState;
-  FramebufferState         framebufferState;
-  RasterizationState       rasterizationState;
-  VertexInputState         vertexInputState;
-  InputAssemblyState       inputAssemblyState;
+
+  // for maintaining correct lifecycle, the owned program
+  // wrapper must be created
+  UniquePtr<Program> program;
+
+  ColorBlendState    colorBlendState;
+  DepthStencilState  depthStencilState;
+  ProgramState       programState;
+  ViewportState      viewportState;
+  FramebufferState   framebufferState;
+  RasterizationState rasterizationState;
+  VertexInputState   vertexInputState;
+  InputAssemblyState inputAssemblyState;
+
+  PipelineCache* pipelineCache{};
 };
 
-PipelineImpl::PipelineImpl(const Graphics::PipelineCreateInfo& createInfo, Graphics::EglGraphicsController& controller)
-: mController(controller),
-  mReflection(controller)
+PipelineImpl::PipelineImpl(const Graphics::PipelineCreateInfo& createInfo, Graphics::EglGraphicsController& controller, PipelineCache& pipelineCache)
+: mController(controller)
 {
   // the creation is deferred so it's needed to copy certain parts of the CreateInfo structure
   mPipelineState = std::make_unique<PipelineImpl::PipelineState>();
@@ -66,31 +64,22 @@ PipelineImpl::PipelineImpl(const Graphics::PipelineCreateInfo& createInfo, Graph
   CopyStateIfSet(createInfo.inputAssemblyState, mPipelineState->inputAssemblyState, &mCreateInfo.inputAssemblyState);
   CopyStateIfSet(createInfo.vertexInputState, mPipelineState->vertexInputState, &mCreateInfo.vertexInputState);
   CopyStateIfSet(createInfo.rasterizationState, mPipelineState->rasterizationState, &mCreateInfo.rasterizationState);
+  CopyStateIfSet(createInfo.programState, mPipelineState->programState, &mCreateInfo.programState);
   CopyStateIfSet(createInfo.framebufferState, mPipelineState->framebufferState, &mCreateInfo.framebufferState);
   CopyStateIfSet(createInfo.colorBlendState, mPipelineState->colorBlendState, &mCreateInfo.colorBlendState);
   CopyStateIfSet(createInfo.depthStencilState, mPipelineState->depthStencilState, &mCreateInfo.depthStencilState);
-  CopyStateIfSet(createInfo.shaderState, mPipelineState->shaderState, &mCreateInfo.shaderState);
+  CopyStateIfSet(createInfo.programState, mPipelineState->programState, &mCreateInfo.programState);
   CopyStateIfSet(createInfo.viewportState, mPipelineState->viewportState, &mCreateInfo.viewportState);
 
-  if(createInfo.nextExtension)
-  {
-    LegacyProgram* legacyProgram = static_cast<LegacyProgram*>(createInfo.nextExtension);
-    mGlProgram                   = legacyProgram->programId;
-    mReflection.SetGlProgram(mGlProgram);
-    printf("GLES::Pipeline: mProgramId: %u\n", mGlProgram);
-  }
+  // This program doesn't need custom deleter
+  auto programImpl        = static_cast<const GLES::Program*>(createInfo.programState->program)->GetImplementation();
+  mPipelineState->program = MakeUnique<GLES::Program>(programImpl);
 
-  const std::vector<Graphics::ShaderState>* shaderStates = mCreateInfo.shaderState;
-  if(shaderStates)
-  {
-    printf("GLES::Pipeline: shaderStates %p, shaderStates size: %lu\n", shaderStates, shaderStates->size());
+  // To make sure the program is alive as long as the pipeline is!
+  mCreateInfo.programState->program = mPipelineState->program.get();
 
-    std::for_each(shaderStates->begin(), shaderStates->end(), [](Graphics::ShaderState shaderState) {
-      const Graphics::Shader* shader        = shaderState.shader;
-      Graphics::PipelineStage pipelineStage = shaderState.pipelineStage;
-      printf("GLES::Pipeline: shader: %p, pipelineStage: %d\n", shader, static_cast<int>(pipelineStage));
-    });
-  }
+  // Set pipeline cache
+  mPipelineState->pipelineCache = &pipelineCache;
 }
 
 const PipelineCreateInfo& PipelineImpl::GetCreateInfo() const
@@ -103,92 +92,11 @@ auto& PipelineImpl::GetController() const
   return mController;
 }
 
-bool PipelineImpl::InitializeResource()
-{
-  auto& gl = *GetController().GetGL();
-  //  mGlProgram = gl.CreateProgram();
-
-  for(auto& shader : mPipelineState->shaderState)
-  {
-    // TODO: Compile shader (shouldn't compile if already did so)
-    const auto glesShader = static_cast<const GLES::Shader*>(shader.shader);
-    if(glesShader->Compile())
-    {
-      // Attach shader
-      gl.AttachShader(mGlProgram, glesShader->GetGLShader());
-    }
-    else
-    {
-      // failure
-      gl.DeleteProgram(mGlProgram);
-      return false;
-    }
-  }
-
-  // Link program
-  gl.LinkProgram(mGlProgram);
-
-  // Check for errors
-  // TODO:
-
-  return true;
-}
-
-void PipelineImpl::DestroyResource()
-{
-  if(mGlProgram)
-  {
-    auto& gl = *GetController().GetGL();
-    gl.DeleteProgram(mGlProgram);
-  }
-}
-
-void PipelineImpl::DiscardResource()
-{
-  // Pass program to discard queue
-}
-
-uint32_t PipelineImpl::GetGLProgram() const
-{
-  return mGlProgram;
-}
-
-// FIXME: THIS FUNCTION IS NOT IN USE YET, REQUIRES PROPER PIPELINE
 void PipelineImpl::Bind(GLES::PipelineImpl* prevPipeline)
 {
-  // Same pipeline to bind, nothing to do
-  if(prevPipeline == this)
-  {
-    return;
-  }
-
-  auto& gl = *GetController().GetGL();
-
-  // ------------------ VIEWPORT
-
-  const auto& viewportState    = mPipelineState->viewportState;
-  auto        setViewportState = [this, &gl, viewportState]() {
-    if(viewportState.scissorTestEnable)
-    {
-      gl.Enable(GL_SCISSOR_TEST);
-      const auto& scissor = mPipelineState->viewportState.scissor;
-      gl.Scissor(scissor.x, scissor.y, scissor.width, scissor.height);
-    }
-    else
-    {
-      gl.Disable(GL_SCISSOR_TEST);
-      const auto& scissor = mPipelineState->viewportState.scissor;
-      gl.Scissor(scissor.x, scissor.y, scissor.width, scissor.height);
-    }
-  };
-
-  // Resolve viewport/scissor state change
-  ExecuteStateChange(setViewportState, prevPipeline->GetCreateInfo().viewportState, &mPipelineState->viewportState);
-
-  // ---------------------- PROGRAM
-  auto program = mGlProgram;
-
-  gl.UseProgram(program);
+  auto& gl        = *GetController().GetGL();
+  auto  glProgram = static_cast<const GLES::Program*>(GetCreateInfo().programState->program)->GetImplementation()->GetGlProgram();
+  gl.UseProgram(glProgram);
 }
 
 void PipelineImpl::Retain()
@@ -220,4 +128,19 @@ EglGraphicsController& Pipeline::GetController() const
   return mPipeline.GetController();
 }
 
+Pipeline::~Pipeline()
+{
+  // decrease refcount
+  if(mPipeline.GetRefCount())
+  {
+    mPipeline.Release();
+  }
+}
+
+void Pipeline::DiscardResource()
+{
+  // Send pipeline to discard queue if refcount is 0
+  GetController().DiscardResource(this);
+}
+
 } // namespace Dali::Graphics::GLES
index e8b734f..fca74fa 100644 (file)
 
 namespace Dali::Graphics::GLES
 {
-using PipelineResource = Resource<Graphics::Pipeline, Graphics::PipelineCreateInfo>;
+class PipelineCache;
 
 /**
- * @brief PipelineWrapper is the object
- * returned to the client-side
+ * @brief PipelineImpl is the implementation of Pipeline
+ *
+ * PipelineImpl is owned by the pipeline cache. The client-side
+ * will receive Graphics::Pipeline objects which are only
+ * wrappers for this implementation. The lifecycle of
+ * PipelineImpl is managed by the PipelineCache.
  */
 class PipelineImpl
 {
@@ -42,8 +46,9 @@ public:
    * @brief Constructor
    * @param[in] createInfo valid TextureCreateInfo structure
    * @param[in] controller Reference to the Controller
+   * @param[in] pipelineCache Reference to valid pipeline cache
    */
-  PipelineImpl(const Graphics::PipelineCreateInfo& createInfo, Graphics::EglGraphicsController& controller);
+  PipelineImpl(const Graphics::PipelineCreateInfo& createInfo, Graphics::EglGraphicsController& controller, PipelineCache& pipelineCache);
 
   /**
    * @brief Destructor
@@ -51,29 +56,6 @@ public:
   ~PipelineImpl();
 
   /**
-   * @brief Destroys all the low-level resources used by the class
-   */
-  void DestroyResource();
-
-  /**
-   * @brief Initializes low-level resources
-   *
-   * @return Tron success
-   */
-  bool InitializeResource();
-
-  /**
-   * @brief Discards object
-   */
-  void DiscardResource();
-
-  /**
-   * @brief returns GL program id
-   * @return GL program id
-   */
-  [[nodiscard]] uint32_t GetGLProgram() const;
-
-  /**
    * @brief Binds pipeline
    *
    * Binds Pipeline by binding GL program and flushing state.
@@ -86,37 +68,35 @@ public:
   void Bind(GLES::PipelineImpl* prevPipeline);
 
   /**
-   * Executes state change function if condition met
+   * @brief Increases ref count
    */
-  template<typename FUNC, typename STATE>
-  void ExecuteStateChange(FUNC& func, const STATE* prevPipelineState, const STATE* thisPipelineState)
-  {
-    if(!prevPipelineState)
-    {
-      func();
-    }
-    else
-    {
-      // binary test and execute when different
-      if(memcmp(prevPipelineState, thisPipelineState, sizeof(STATE)) != 0)
-      {
-        func();
-      }
-    }
-  }
-
   void Retain();
 
+  /**
+   * @brief Decreases ref count
+   */
   void Release();
 
+  /**
+   * @brief Retrieves ref count
+   * @return Refcount value
+   */
   [[nodiscard]] uint32_t GetRefCount() const;
 
+  /**
+   * @brief Returns PipelineCreateInfo structure
+   *
+   * @return PipelineCreateInfo structure
+   */
   [[nodiscard]] const PipelineCreateInfo& GetCreateInfo() const;
 
+  /**
+   * @brief Returns controller
+   *
+   * @return Reference to the Controller
+   */
   [[nodiscard]] auto& GetController() const;
 
-  Graphics::GLES::Reflection& GetReflection();
-
 private:
   /**
    * @brief Helper function. Copies state if pointer is set
@@ -146,30 +126,29 @@ private:
     }
   }
 
-  // Pipeline state is stored as a copy of create info
-  // data.
+  // Pipeline state is store locally so any assigned pointers on a
+  // client-side may go safely out of scope.
   struct PipelineState;
   std::unique_ptr<PipelineState> mPipelineState;
 
   EglGraphicsController& mController;
   PipelineCreateInfo     mCreateInfo;
 
-  Graphics::GLES::Reflection mReflection;
-
-  uint32_t mGlProgram{0u};
-
   uint32_t mRefCount{0u};
 };
 
 /**
- * @brief Pipeline class wraps a unique pipeline object
- *
+ * @brief Pipeline class wraps the PipelineImpl
  */
 class Pipeline : public Graphics::Pipeline
 {
 public:
   Pipeline() = delete;
 
+  /**
+   * @brief Constructor
+   * @param pipeline Pipeline implementation
+   */
   explicit Pipeline(GLES::PipelineImpl& pipeline)
   : mPipeline(pipeline)
   {
@@ -177,21 +156,57 @@ public:
     mPipeline.Retain();
   }
 
-  ~Pipeline() override
-  {
-    // decrease refcount
-    mPipeline.Release();
-  }
+  /**
+   * @brief Destructor
+   */
+  ~Pipeline() override;
 
+  /**
+   * @brief Returns pipeline implementation
+   *
+   * @return Valid pipeline implementation
+   */
   [[nodiscard]] auto& GetPipeline() const
   {
     return mPipeline;
   }
 
+  /**
+   * @brief Returns create info structure
+   *
+   * @return Valid create info structure
+   */
   [[nodiscard]] const PipelineCreateInfo& GetCreateInfo() const;
 
+  /**
+   * @brief Returns controller
+   *
+   * @return reference to Controller
+   */
   [[nodiscard]] EglGraphicsController& GetController() const;
 
+  bool operator==(const PipelineImpl* impl) const
+  {
+    return &mPipeline == impl;
+  }
+
+  /**
+   * @brief Run by UniquePtr to discard resource
+   */
+  void DiscardResource();
+
+  /**
+   * @brief Destroy resource
+   *
+   * Despite this class doesn't inherit Resource it must provide
+   * (so it won't duplicate same data) same set of functions
+   * so it can work with resource management functions of Controller.
+   */
+  void DestroyResource()
+  {
+    // Nothing to do here
+  }
+
 private:
   GLES::PipelineImpl& mPipeline;
 };
diff --git a/dali/internal/graphics/gles-impl/gles-graphics-program.cpp b/dali/internal/graphics/gles-impl/gles-graphics-program.cpp
new file mode 100644 (file)
index 0000000..2e28eca
--- /dev/null
@@ -0,0 +1,192 @@
+/*
+ * 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-program.h"
+
+// INTERNAL HEADERS
+#include "egl-graphics-controller.h"
+#include "gles-graphics-reflection.h"
+#include "gles-graphics-shader.h"
+
+// EXTERNAL HEADERS
+#include <dali/integration-api/gl-abstraction.h>
+#include <dali/integration-api/gl-defines.h>
+
+namespace Dali::Graphics::GLES
+{
+struct ProgramImpl::Impl
+{
+  explicit Impl(EglGraphicsController& _controller, const ProgramCreateInfo& info)
+  : controller(_controller)
+  {
+    createInfo = info;
+    if(info.shaderState)
+    {
+      createInfo.shaderState = new std::vector<ShaderState>(*info.shaderState);
+    }
+  }
+
+  ~Impl()
+  {
+    delete createInfo.shaderState;
+  }
+
+  EglGraphicsController& controller;
+  ProgramCreateInfo      createInfo;
+  uint32_t               glProgram{};
+  uint32_t               refCount{0u};
+
+  std::unique_ptr<GLES::Reflection> reflection{nullptr};
+};
+
+ProgramImpl::ProgramImpl(const Graphics::ProgramCreateInfo& createInfo, Graphics::EglGraphicsController& controller)
+{
+  // Create implementation
+  mImpl = std::make_unique<Impl>(controller, createInfo);
+
+  // Build reflection
+  mImpl->reflection = std::make_unique<GLES::Reflection>(*this, controller);
+}
+
+ProgramImpl::~ProgramImpl() = default;
+
+bool ProgramImpl::Destroy()
+{
+  if(mImpl->glProgram)
+  {
+    auto& gl = *mImpl->controller.GetGL();
+    gl.DeleteProgram(mImpl->glProgram);
+    return true;
+  }
+  return false;
+}
+
+bool ProgramImpl::Create()
+{
+  // Create and link new program
+  auto& gl      = *mImpl->controller.GetGL();
+  auto  program = gl.CreateProgram();
+
+  const auto& info = mImpl->createInfo;
+  for(const auto& state : *info.shaderState)
+  {
+    const auto* shader = static_cast<const GLES::Shader*>(state.shader);
+
+    // Compile shader first (ignored when compiled)
+    shader->Compile();
+
+    gl.AttachShader(program, shader->GetGLShader());
+  }
+  gl.LinkProgram(program);
+
+  GLint status{0};
+  gl.GetProgramiv(program, GL_LINK_STATUS, &status);
+  if(status != GL_TRUE)
+  {
+    char    output[4096];
+    GLsizei size{0u};
+    gl.GetProgramInfoLog(program, 4096, &size, output);
+
+    // log on error
+    // TODO: un-printf-it
+    printf("Log: %s\n", output);
+    gl.DeleteProgram(program);
+    return false;
+  }
+
+  mImpl->glProgram = program;
+
+  // Initialize reflection
+  mImpl->reflection->BuildUniformReflection();
+  mImpl->reflection->BuildVertexAttributeReflection();
+  mImpl->reflection->BuildUniformBlockReflection();
+
+  return true;
+}
+
+uint32_t ProgramImpl::GetGlProgram() const
+{
+  return mImpl->glProgram;
+}
+
+uint32_t ProgramImpl::Retain()
+{
+  return ++mImpl->refCount;
+}
+
+uint32_t ProgramImpl::Release()
+{
+  return --mImpl->refCount;
+}
+
+const Graphics::Reflection& ProgramImpl::GetReflection() const
+{
+  return *mImpl->reflection;
+}
+
+bool ProgramImpl::GetParameter(uint32_t parameterId, void* out)
+{
+  if(parameterId == 1) // a magic number to access program id
+  {
+    *reinterpret_cast<decltype(&mImpl->glProgram)>(out) = mImpl->glProgram;
+    return true;
+  }
+  return false;
+}
+
+EglGraphicsController& ProgramImpl::GetController() const
+{
+  return mImpl->controller;
+}
+
+const ProgramCreateInfo& ProgramImpl::GetCreateInfo() const
+{
+  return mImpl->createInfo;
+}
+
+Program::~Program()
+{
+  // Destroy GL resources of implementation. This should happen
+  // only if there's no more pipelines using this program so
+  // it is safe to do it in the destructor
+  if(!mProgram->Release())
+  {
+    mProgram->Destroy();
+  }
+}
+
+const Graphics::Reflection& Program::GetReflection() const
+{
+  return mProgram->GetReflection();
+}
+
+EglGraphicsController& Program::GetController() const
+{
+  return GetImplementation()->GetController();
+}
+
+const ProgramCreateInfo& Program::GetCreateInfo() const
+{
+  return GetImplementation()->GetCreateInfo();
+}
+
+void Program::DiscardResource()
+{
+  GetController().DiscardResource(this);
+}
+
+}; // namespace Dali::Graphics::GLES
diff --git a/dali/internal/graphics/gles-impl/gles-graphics-program.h b/dali/internal/graphics/gles-impl/gles-graphics-program.h
new file mode 100644 (file)
index 0000000..a080012
--- /dev/null
@@ -0,0 +1,225 @@
+#ifndef DALI_GRAPHICS_GLES_PROGRAM_H
+#define DALI_GRAPHICS_GLES_PROGRAM_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-program-create-info.h>
+#include <dali/graphics-api/graphics-program.h>
+
+// INTERNAL INCLUDES
+#include "gles-graphics-resource.h"
+
+namespace Dali::Graphics
+{
+class Reflection;
+namespace GLES
+{
+/**
+ * @brief Program implementation
+ *
+ * Program implementation is owned only by the PipelineCache
+ *
+ * Like pipeline, it's created and managed by the PipelineCache
+ */
+class ProgramImpl
+{
+public:
+  /**
+   * @brief Constructor
+   *
+   * @param[in] createInfo  Valid create info structure
+   * @param[in] controller Valid reference to the controller object
+   */
+  ProgramImpl(const Graphics::ProgramCreateInfo& createInfo, Graphics::EglGraphicsController& controller);
+
+  /**
+   * @brief Destructor
+   */
+  ~ProgramImpl();
+
+  /**
+   * @brief Destroys GL resources associated with the Program
+   *
+   * @return True on success
+   */
+  bool Destroy();
+
+  /**
+   * @brief Creates GL resource for this Program
+   *
+   * @return True on success
+   */
+  bool Create();
+
+  /**
+   * @brief Returns GL program id
+   *
+   * @return GL program id
+   */
+  [[nodiscard]] uint32_t GetGlProgram() const;
+
+  /**
+   * @brief Increases ref count
+   *
+   * @return new refcount
+   */
+  uint32_t Retain();
+
+  /**
+   * @brief Decreases ref count
+   *
+   * @return New refcount
+   */
+  uint32_t Release();
+
+  /**
+   * @brief Returns reflection
+   *
+   * @return Valid reflection associated with the Program
+   */
+  [[nodiscard]] const Graphics::Reflection& GetReflection() const;
+
+  /**
+   * @brief Returns controller
+   *
+   * @return Valid Controller object
+   */
+  [[nodiscard]] EglGraphicsController& GetController() const;
+
+  /**
+   * @brief Returns create info structure
+   *
+   * @return Reference to valid create info structure
+   */
+  [[nodiscard]] const ProgramCreateInfo& GetCreateInfo() const;
+
+  /**
+   * @brief Returns parameter value specified by parameterId
+   *
+   * This function can be used as a backdoor into the implementation
+   * used to retrieve internal data.
+   *
+   * @param[in] parameterId Integer parameter id
+   * @param[out] out Pointer to write to
+   *
+   * @return True on success
+   */
+  bool GetParameter(uint32_t parameterId, void* out);
+
+private:
+  friend class Program;
+
+  struct Impl;
+  std::unique_ptr<Impl> mImpl;
+};
+
+///////////////////////////////////////////////////////////////
+
+/**
+ * @brief Wrapper for the pipeline implementation
+ *
+ * This object is returned back to the client-side
+ */
+class Program : public Graphics::Program
+{
+public:
+  /**
+   * @brief Constructor
+   *
+   * @param[in] impl Pointer to valid implementation
+   */
+  explicit Program(ProgramImpl* impl)
+  : mProgram(impl)
+  {
+    mProgram->Retain();
+  }
+
+  /**
+   * @brief Destructor
+   */
+  ~Program() override;
+
+  /**
+   * @brief Returns reference to the Reflection object
+
+   * @return Reflection
+   */
+  [[nodiscard]] const Graphics::Reflection& GetReflection() const;
+
+  /**
+   * @brief Retrieves internal program implementation
+   *
+   * @return Valid pointer to the ProgramImpl object
+   */
+  [[nodiscard]] ProgramImpl* GetImplementation() const
+  {
+    return mProgram;
+  }
+
+  /**
+   * @brief Returns controller
+   *
+   * @return controller
+   */
+  [[nodiscard]] EglGraphicsController& GetController() const;
+
+  /**
+   * @brief Returns create info structure
+   *
+   * @return create info structure
+   */
+  [[nodiscard]] const ProgramCreateInfo& GetCreateInfo() const;
+
+  bool operator==(const GLES::Program& program) const
+  {
+    return (program.mProgram == mProgram);
+  }
+
+  bool operator==(const GLES::ProgramImpl* programImpl) const
+  {
+    return (programImpl == mProgram);
+  }
+
+  bool operator!=(const GLES::Program& program) const
+  {
+    return (program.mProgram != mProgram);
+  }
+
+  /**
+   * @brief Run by UniquePtr to discard resource
+   */
+  void DiscardResource();
+
+  /**
+   * @brief Destroying GL resources
+   *
+   * This function is kept for compatibility with Resource<> class
+   * so can the object can be use with templated functions.
+   */
+  void DestroyResource()
+  {
+    // nothing to do here
+  }
+
+private:
+  ProgramImpl* mProgram{nullptr};
+};
+} // namespace GLES
+} // namespace Dali::Graphics
+
+#endif //DALI_GRAPHICS_PROGRAM_H
index 4450557..2668ba2 100644 (file)
@@ -25,6 +25,8 @@
 #include <GLES3/gl3.h>
 #include <GLES3/gl31.h>
 
+#include "gles-graphics-program.h"
+
 #include <iostream>
 
 namespace
@@ -48,7 +50,7 @@ Dali::Graphics::VertexInputAttributeFormat GetVertexAttributeTypeFormat(GLenum t
   }
 }
 
-int GetGLDataTypeSize(GLenum type)
+uint32_t GetGLDataTypeSize(GLenum type)
 {
   // There are many more types than what are covered here, but
   // they are not supported in dali.
@@ -87,33 +89,29 @@ bool SortByLocation(Dali::Graphics::UniformInfo a, Dali::Graphics::UniformInfo b
 
 } // namespace
 
-namespace Dali
-{
-namespace Graphics
-{
-namespace GLES
+namespace Dali::Graphics::GLES
 {
-Reflection::Reflection(Graphics::EglGraphicsController& controller)
+Reflection::Reflection(GLES::ProgramImpl& program, Graphics::EglGraphicsController& controller)
 : Graphics::Reflection(),
   mController(controller),
-  mGlProgram(0u)
+  mProgram(program)
 {
 }
 
-Reflection::~Reflection()
-{
-}
+Reflection::~Reflection() = default;
 
 void Reflection::BuildVertexAttributeReflection()
 {
+  auto glProgram = mProgram.GetGlProgram();
+
   int    written, size, location, maxLength, nAttribs;
   GLenum type;
   char*  name;
 
   auto gl = mController.GetGL();
 
-  gl->GetProgramiv(mGlProgram, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, &maxLength);
-  gl->GetProgramiv(mGlProgram, GL_ACTIVE_ATTRIBUTES, &nAttribs);
+  gl->GetProgramiv(glProgram, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, &maxLength);
+  gl->GetProgramiv(glProgram, GL_ACTIVE_ATTRIBUTES, &nAttribs);
 
   mVertexInputAttributes.clear();
   mVertexInputAttributes.resize(nAttribs);
@@ -121,8 +119,8 @@ void Reflection::BuildVertexAttributeReflection()
   name = new GLchar[maxLength];
   for(int i = 0; i < nAttribs; i++)
   {
-    gl->GetActiveAttrib(mGlProgram, i, maxLength, &written, &size, &type, name);
-    location = gl->GetAttribLocation(mGlProgram, name);
+    gl->GetActiveAttrib(glProgram, i, maxLength, &written, &size, &type, name);
+    location = gl->GetAttribLocation(glProgram, name);
 
     AttributeInfo attributeInfo;
     attributeInfo.location = location;
@@ -136,6 +134,8 @@ void Reflection::BuildVertexAttributeReflection()
 
 void Reflection::BuildUniformReflection()
 {
+  auto glProgram = mProgram.GetGlProgram();
+
   int   maxLen;
   char* name;
 
@@ -143,8 +143,8 @@ void Reflection::BuildUniformReflection()
 
   auto gl = mController.GetGL();
 
-  gl->GetProgramiv(mGlProgram, GL_ACTIVE_UNIFORM_MAX_LENGTH, &maxLen);
-  gl->GetProgramiv(mGlProgram, GL_ACTIVE_UNIFORMS, &numUniforms);
+  gl->GetProgramiv(glProgram, GL_ACTIVE_UNIFORM_MAX_LENGTH, &maxLen);
+  gl->GetProgramiv(glProgram, GL_ACTIVE_UNIFORMS, &numUniforms);
 
   mUniformBlocks.clear();
   mDefaultUniformBlock.members.clear();
@@ -152,20 +152,19 @@ void Reflection::BuildUniformReflection()
 
   name = new char[maxLen];
 
-  int maxUniformLocations;
-  gl->GetProgramiv(mGlProgram, GL_MAX_UNIFORM_LOCATIONS, &maxUniformLocations);
+  using UniformLocationSizePair = std::pair<uint32_t, uint32_t>;
+  std::vector<UniformLocationSizePair> uniformSizes;
 
-  std::vector<int> uniformSize;
-  uniformSize.reserve(maxUniformLocations);
+  uniformSizes.reserve(numUniforms);
 
   for(int i = 0; i < numUniforms; ++i)
   {
     int    size;
     GLenum type;
     int    written;
-    gl->GetActiveUniform(mGlProgram, i, maxLen, &written, &size, &type, name);
-    int location          = gl->GetUniformLocation(mGlProgram, name);
-    uniformSize[location] = GetGLDataTypeSize(type);
+    gl->GetActiveUniform(glProgram, i, maxLen, &written, &size, &type, name);
+    int location = gl->GetUniformLocation(glProgram, name);
+    uniformSizes.push_back(std::make_pair(location, GetGLDataTypeSize(type)));
 
     Dali::Graphics::UniformInfo uniformInfo;
     uniformInfo.name         = name;
@@ -198,10 +197,21 @@ void Reflection::BuildUniformReflection()
   // Calculate the uniform offset
   for(unsigned int i = 0; i < mDefaultUniformBlock.members.size(); ++i)
   {
-    mDefaultUniformBlock.members[i].offset = i == 0 ? 0 : mDefaultUniformBlock.members[i - 1].offset + uniformSize[mDefaultUniformBlock.members[i - 1].location];
+    if(i == 0)
+    {
+      mDefaultUniformBlock.members[i].offset = 0;
+    }
+    else
+    {
+      uint32_t previousUniformLocation       = mDefaultUniformBlock.members[i - 1].location;
+      auto     previousUniform               = std::find_if(uniformSizes.begin(), uniformSizes.end(), [&previousUniformLocation](const UniformLocationSizePair& iter) { return iter.first == previousUniformLocation; });
+      mDefaultUniformBlock.members[i].offset = mDefaultUniformBlock.members[i - 1].offset + previousUniform->second;
+    }
   }
 
-  mDefaultUniformBlock.size = mDefaultUniformBlock.members.back().offset + uniformSize[mDefaultUniformBlock.members.back().location];
+  uint32_t lastUniformLocation = mDefaultUniformBlock.members.back().location;
+  auto     lastUniform         = std::find_if(uniformSizes.begin(), uniformSizes.end(), [&lastUniformLocation](const UniformLocationSizePair& iter) { return iter.first == lastUniformLocation; });
+  mDefaultUniformBlock.size    = mDefaultUniformBlock.members.back().offset + lastUniform->second;
 
   mUniformBlocks.push_back(mDefaultUniformBlock);
 
@@ -211,16 +221,16 @@ void Reflection::BuildUniformReflection()
 // TODO: Maybe this is not needed if uniform block is not support by dali shaders?
 void Reflection::BuildUniformBlockReflection()
 {
-  auto gl = mController.GetGL();
-
-  int numUniformBlocks = 0;
-  gl->GetProgramiv(mGlProgram, GL_ACTIVE_UNIFORM_BLOCKS, &numUniformBlocks);
+  auto gl               = mController.GetGL();
+  auto glProgram        = mProgram.GetGlProgram();
+  int  numUniformBlocks = 0;
+  gl->GetProgramiv(glProgram, GL_ACTIVE_UNIFORM_BLOCKS, &numUniformBlocks);
 
   mUniformBlocks.clear();
   mUniformBlocks.resize(numUniformBlocks);
 
   int uniformBlockMaxLength = 0;
-  gl->GetProgramiv(mGlProgram, GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH, &uniformBlockMaxLength);
+  gl->GetProgramiv(glProgram, GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH, &uniformBlockMaxLength);
 
   char* uniformBlockName = new char[uniformBlockMaxLength];
   for(int i = 0; i < numUniformBlocks; i++)
@@ -228,9 +238,9 @@ void Reflection::BuildUniformBlockReflection()
     int length;
     int blockBinding;
     int blockDataSize;
-    gl->GetActiveUniformBlockName(mGlProgram, i, uniformBlockMaxLength, &length, uniformBlockName);
-    gl->GetActiveUniformBlockiv(mGlProgram, i, GL_UNIFORM_BLOCK_BINDING, &blockBinding);
-    gl->GetActiveUniformBlockiv(mGlProgram, i, GL_UNIFORM_BLOCK_DATA_SIZE, &blockDataSize);
+    gl->GetActiveUniformBlockName(glProgram, i, uniformBlockMaxLength, &length, uniformBlockName);
+    gl->GetActiveUniformBlockiv(glProgram, i, GL_UNIFORM_BLOCK_BINDING, &blockBinding);
+    gl->GetActiveUniformBlockiv(glProgram, i, GL_UNIFORM_BLOCK_DATA_SIZE, &blockDataSize);
 
     Dali::Graphics::UniformBlockInfo uniformBlockInfo;
     uniformBlockInfo.name    = uniformBlockName;
@@ -238,12 +248,12 @@ void Reflection::BuildUniformBlockReflection()
     uniformBlockInfo.binding = blockBinding;
 
     int nUnis;
-    gl->GetActiveUniformBlockiv(mGlProgram, i, GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS, &nUnis);
+    gl->GetActiveUniformBlockiv(glProgram, i, GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS, &nUnis);
     int* unifIndexes = new GLint[nUnis];
-    gl->GetActiveUniformBlockiv(mGlProgram, i, GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES, unifIndexes);
+    gl->GetActiveUniformBlockiv(glProgram, i, GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES, unifIndexes);
     char* uniformName{};
     int   maxUniLen;
-    gl->GetProgramiv(mGlProgram, GL_ACTIVE_UNIFORM_MAX_LENGTH, &maxUniLen);
+    gl->GetProgramiv(glProgram, GL_ACTIVE_UNIFORM_MAX_LENGTH, &maxUniLen);
 
     for(int unif = 0; unif < nUnis; ++unif)
     {
@@ -251,8 +261,8 @@ void Reflection::BuildUniformBlockReflection()
       int    size;
       GLenum type;
 
-      gl->GetActiveUniform(mGlProgram, uniIndex, maxUniLen, &length, &size, &type, uniformName);
-      int location = gl->GetUniformLocation(mGlProgram, uniformName);
+      gl->GetActiveUniform(glProgram, uniIndex, maxUniLen, &length, &size, &type, uniformName);
+      int location = gl->GetUniformLocation(glProgram, uniformName);
 
       Dali::Graphics::UniformInfo uniform;
       uniform.name     = uniformName;
@@ -472,14 +482,4 @@ Graphics::ShaderLanguage Reflection::GetLanguage() const
   return Graphics::ShaderLanguage::GLSL_3_2;
 }
 
-void Reflection::SetGlProgram(uint32_t glProgram)
-{
-  mGlProgram = glProgram;
-
-  BuildVertexAttributeReflection();
-  BuildUniformReflection();
-}
-
-} // namespace GLES
-} // namespace Graphics
-} // namespace Dali
+} // namespace Dali::Graphics::GLES
index 1e07a01..b06fb24 100644 (file)
 #include <dali/graphics-api/graphics-types.h>
 #include <dali/integration-api/gl-abstraction.h>
 
-namespace Dali
-{
-namespace Graphics
+namespace Dali::Graphics
 {
 class EglGraphicsController;
 
 namespace GLES
 {
+class ProgramImpl;
 constexpr uint32_t ERROR_ATTRIBUTE_NOT_FOUND(-1u);
 
 /**
@@ -46,8 +45,9 @@ constexpr uint32_t ERROR_ATTRIBUTE_NOT_FOUND(-1u);
 class Reflection : public Dali::Graphics::Reflection
 {
 public:
-  Reflection(Graphics::EglGraphicsController& controller);
-  virtual ~Reflection();
+  explicit Reflection(GLES::ProgramImpl& program, Graphics::EglGraphicsController& controller);
+
+  ~Reflection() override;
 
   // not copyable
   Reflection(const Reflection&) = delete;
@@ -59,7 +59,7 @@ public:
    * @param [in] name The name of vertex attribute
    * @return The index of the vertex attribute in the shader
    */
-  uint32_t GetVertexAttributeLocation(const std::string& name) const;
+  [[nodiscard]] uint32_t GetVertexAttributeLocation(const std::string& name) const override;
 
   /**
    * @brief Gets the format of a vertex attribute.
@@ -67,7 +67,7 @@ public:
    * @param [in] location The location of vertex attribute
    * @return The format of a vertex attribute
    */
-  Dali::Graphics::VertexInputAttributeFormat GetVertexAttributeFormat(uint32_t location) const;
+  [[nodiscard]] Dali::Graphics::VertexInputAttributeFormat GetVertexAttributeFormat(uint32_t location) const override;
 
   /**
    * @brief Gets the name of a vertex attribute.
@@ -75,14 +75,14 @@ public:
    * @param [in] location The location of vertex attribute
    * @return The name of the vertex attribute
    */
-  std::string GetVertexAttributeName(uint32_t location) const;
+  [[nodiscard]] std::string GetVertexAttributeName(uint32_t location) const override;
 
   /**
    * @brief Gets the locations of all the vertex attribute in the shader.
    *
    * @return A vector of the locations of all the vertex attributes in the shader
    */
-  std::vector<uint32_t> GetVertexAttributeLocations() const;
+  [[nodiscard]] std::vector<uint32_t> GetVertexAttributeLocations() const override;
 
   // Uniform blocks
 
@@ -91,7 +91,7 @@ public:
    *
    * @return The number of uniform blocks
    */
-  uint32_t GetUniformBlockCount() const;
+  [[nodiscard]] uint32_t GetUniformBlockCount() const override;
 
   /**
    * @brief Gets the binding point to which the uniform block with the given index is binded.
@@ -99,7 +99,7 @@ public:
    * @param [in] index The index of the uniform block
    * @return The binding point
    */
-  uint32_t GetUniformBlockBinding(uint32_t index) const;
+  [[nodiscard]] uint32_t GetUniformBlockBinding(uint32_t index) const override;
 
   /**
    * @brief Gets the size of the uniform block with the given index.
@@ -107,7 +107,7 @@ public:
    * @param [in] index The index of the uniform block
    * @return The size of the uniform block
    */
-  uint32_t GetUniformBlockSize(uint32_t index) const;
+  [[nodiscard]] uint32_t GetUniformBlockSize(uint32_t index) const override;
 
   /**
    * @brief Retrieves the information of the uniform block with the given index.
@@ -118,14 +118,14 @@ public:
    * @param [out] out A structure that contains the information of the uniform block
    * @return Whether the uniform block exists or not
    */
-  bool GetUniformBlock(uint32_t index, Dali::Graphics::UniformBlockInfo& out) const;
+  bool GetUniformBlock(uint32_t index, Dali::Graphics::UniformBlockInfo& out) const override;
 
   /**
    * @brief Gets the binding points of all the uniform blocks in the shader.
    *
    * @return A vector of binding points
    */
-  std::vector<uint32_t> GetUniformBlockLocations() const;
+  [[nodiscard]] std::vector<uint32_t> GetUniformBlockLocations() const override;
 
   /**
    * @brief Gets the name of uniform block with the given index.
@@ -133,7 +133,7 @@ public:
    * @param [in] blockIndex The index of the uniform block
    * @return The name of the uniform block
    */
-  std::string GetUniformBlockName(uint32_t blockIndex) const;
+  [[nodiscard]] std::string GetUniformBlockName(uint32_t blockIndex) const override;
 
   /**
    * @brief Gets the number of uniforms in the uniform block with the given index.
@@ -141,7 +141,7 @@ public:
    * @param [in] blockIndex The index of the uniform block
    * @return The number of uniforms in the uniform block
    */
-  uint32_t GetUniformBlockMemberCount(uint32_t blockIndex) const;
+  [[nodiscard]] uint32_t GetUniformBlockMemberCount(uint32_t blockIndex) const override;
 
   /**
    * @brief Gets the name of the uniform in the given location within the uniform block.
@@ -150,7 +150,7 @@ public:
    * @param [in] memberLocation The location of the uniform within the uniform block
    * @return The name of the uniform
    */
-  std::string GetUniformBlockMemberName(uint32_t blockIndex, uint32_t memberLocation) const;
+  [[nodiscard]] std::string GetUniformBlockMemberName(uint32_t blockIndex, uint32_t memberLocation) const override;
 
   /**
    * @brief Gets the byte offset of the uniform in the given location within the uniform block.
@@ -159,7 +159,7 @@ public:
    * @param [in] memberLocation The location of the uniform within the uniform block
    * @return The byte offset of the uniform
    */
-  uint32_t GetUniformBlockMemberOffset(uint32_t blockIndex, uint32_t memberLocation) const;
+  [[nodiscard]] uint32_t GetUniformBlockMemberOffset(uint32_t blockIndex, uint32_t memberLocation) const override;
 
   // Named uniforms
 
@@ -170,7 +170,7 @@ public:
    * @param [out] out The information of the uniform
    * @return Whether the uniform exists or not
    */
-  bool GetNamedUniform(const std::string& name, Dali::Graphics::UniformInfo& out) const;
+  [[nodiscard]] bool GetNamedUniform(const std::string& name, Dali::Graphics::UniformInfo& out) const override;
 
   // Sampler
 
@@ -179,7 +179,7 @@ public:
    *
    * @return A vector of the sampler uniforms
    */
-  std::vector<Dali::Graphics::UniformInfo> GetSamplers() const;
+  [[nodiscard]] std::vector<Dali::Graphics::UniformInfo> GetSamplers() const override;
 
   // Language
 
@@ -188,18 +188,9 @@ public:
    *
    * @return The language of the shader
    */
-  Graphics::ShaderLanguage GetLanguage() const;
-
-  // Other
+  [[nodiscard]] Graphics::ShaderLanguage GetLanguage() const override;
 
-  /**
-   * @brief Set the program object whose reflection to be built
-   *
-   * @param [in] glProgram The program object
-   */
-  void SetGlProgram(uint32_t glProgram);
-
-private:
+public:
   /**
    * @brief Build the reflection of vertex attributes
    */
@@ -221,7 +212,7 @@ protected:
 
 private:
   Graphics::EglGraphicsController& mController; ///< The Graphics controller
-  uint32_t                         mGlProgram;  ///< The GL program object
+  GLES::ProgramImpl&               mProgram;    ///< The Program object
 
   struct AttributeInfo
   {
@@ -237,7 +228,6 @@ private:
 };
 
 } // namespace GLES
-} // namespace Graphics
-} // namespace Dali
+} // namespace Dali::Graphics
 
 #endif // DALI_GRAPHICS_GLES_REFLECTION_H
index 1bbfe92..04a1239 100644 (file)
  *
  */
 
+// CLASS HEADER
 #include "gles-graphics-shader.h"
-#include <dali/integration-api/gl-abstraction.h>
-#include <vector>
-#include "egl-graphics-controller.h"
 
-#include <GLES3/gl3.h>
+// INTERNAL INCLUDES
+#include "egl-graphics-controller.h"
 
-namespace Dali
+namespace Dali::Graphics::GLES
 {
-namespace Graphics
-{
-namespace GLES
+struct Shader::Impl
 {
+  Impl()  = default;
+  ~Impl() = default;
+
+  std::vector<char> source{};
+  uint32_t          glShader{};
+};
+
 Shader::Shader(const Graphics::ShaderCreateInfo& createInfo, Graphics::EglGraphicsController& controller)
 : ShaderResource(createInfo, controller)
 {
-  if(mCreateInfo.sourceData && mCreateInfo.sourceSize)
+  // push shader to the create queue
+  mImpl = std::make_unique<Impl>();
+
+  // Make a copy of source code
+  mImpl->source.resize(createInfo.sourceSize);
+  std::copy(reinterpret_cast<const char*>(mCreateInfo.sourceData),
+            reinterpret_cast<const char*>(mCreateInfo.sourceData) + mCreateInfo.sourceSize,
+            mImpl->source.begin());
+
+  // Substitute pointer
+  mCreateInfo.sourceData = mImpl->source.data();
+}
+
+bool Shader::Compile() const
+{
+  auto& gl = *GetController().GetGL();
+  if(!mImpl->glShader)
   {
-    printf("GLES::Shader: stage: %d, sourceMode: %d, size: %u, source:\n\n%s\n", (int)mCreateInfo.pipelineStage, (int)mCreateInfo.sourceMode, mCreateInfo.sourceSize, static_cast<const char*>(mCreateInfo.sourceData));
+    GLenum pipelineStage{0u};
+    switch(GetCreateInfo().pipelineStage)
+    {
+      case Graphics::PipelineStage::TOP_OF_PIPELINE:
+      {
+        break;
+      }
+      case Graphics::PipelineStage::VERTEX_SHADER:
+      {
+        pipelineStage = GL_VERTEX_SHADER;
+        break;
+      }
+      case Graphics::PipelineStage::GEOMETRY_SHADER:
+      {
+        break;
+      }
+      case Graphics::PipelineStage::FRAGMENT_SHADER:
+      {
+        pipelineStage = GL_FRAGMENT_SHADER;
+        break;
+      }
+      case Graphics::PipelineStage::COMPUTE_SHADER:
+      {
+        break;
+      }
+      case Graphics::PipelineStage::TESSELATION_CONTROL:
+      {
+        break;
+      }
+      case Graphics::PipelineStage::TESSELATION_EVALUATION:
+      {
+        break;
+      }
+      case Graphics::PipelineStage::BOTTOM_OF_PIPELINE:
+      {
+        break;
+      }
+    }
+
+    if(pipelineStage)
+    {
+      auto       shader = gl.CreateShader(pipelineStage);
+      const auto src    = reinterpret_cast<const char*>(GetCreateInfo().sourceData);
+      GLint      size   = GetCreateInfo().sourceSize;
+      gl.ShaderSource(shader, 1, const_cast<const char**>(&src), &size);
+      gl.CompileShader(shader);
+
+      GLint status{0};
+      gl.GetShaderiv(shader, GL_COMPILE_STATUS, &status);
+      if(status != GL_TRUE)
+      {
+        char    output[4096];
+        GLsizei size{0u};
+        gl.GetShaderInfoLog(shader, 4096, &size, output);
+        printf("Code: %s\n", reinterpret_cast<const char*>(GetCreateInfo().sourceData));
+        printf("Log: %s\n", output);
+        gl.DeleteShader(shader);
+        return false;
+      }
+
+      // TODO: check error
+      mImpl->glShader = shader;
+    }
+    return true;
   }
+  return true;
+}
+
+uint32_t Shader::GetGLShader() const
+{
+  return mImpl->glShader;
 }
 
-} // namespace GLES
-} // namespace Graphics
-} // namespace Dali
+} // namespace Dali::Graphics::GLES
\ No newline at end of file
index f5802af..d96223f 100644 (file)
@@ -67,11 +67,7 @@ public:
    * @brief Compiles shader
    * @return
    */
-  bool Compile() const
-  {
-    mGlShader = 0;
-    return true;
-  }
+  bool Compile() const;
 
   /**
    * @brief Called when UniquePtr<> on client-side dies
@@ -81,13 +77,11 @@ public:
     // TODO: Implement moving to the discard queue
   }
 
-  uint32_t GetGLShader() const
-  {
-    return mGlShader;
-  }
+  uint32_t GetGLShader() const;
 
 private:
-  mutable uint32_t mGlShader;
+  struct Impl;
+  std::unique_ptr<Impl> mImpl;
 };
 
 } // namespace Dali::Graphics::GLES