Merge "Added shader support to pipeline cache" into devel/master
authorAdeel Kazmi <adeel.kazmi@samsung.com>
Fri, 17 Nov 2023 23:03:13 +0000 (23:03 +0000)
committerGerrit Code Review <gerrit@review>
Fri, 17 Nov 2023 23:03:13 +0000 (23:03 +0000)
automated-tests/src/dali-adaptor/dali-test-suite-utils/test-gl-abstraction.h
automated-tests/src/dali-adaptor/dali-test-suite-utils/test-graphics-application.cpp
automated-tests/src/dali-graphics/CMakeLists.txt
automated-tests/src/dali-graphics/utc-Dali-GraphicsProgram.cpp
dali/internal/graphics/gles-impl/egl-graphics-controller.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-program.cpp
dali/internal/graphics/gles-impl/gles-graphics-program.h
dali/internal/graphics/gles-impl/gles-graphics-shader.cpp
dali/internal/graphics/gles-impl/gles-graphics-shader.h

index 2ca98dc..3f04a6b 100644 (file)
@@ -1041,7 +1041,32 @@ public:
 
     for(const auto& uniform : mActiveUniforms)
     {
-      GetUniformLocation(program, uniform.name.c_str());
+      std::string name = uniform.name;
+      if(uniform.size <= 1)
+      {
+        GetUniformLocation(program, name.c_str());
+      }
+      else
+      {
+        // Convert single active uniform from "uBlah[0]" or "uStruct[0].element" to N versions of the same
+        std::string suffix;
+        auto        iter = name.find("["); // Search for index operator
+        if(iter != std::string::npos)
+        {
+          name = uniform.name.substr(0, iter); // Strip off index operator
+          iter = uniform.name.find("]");
+          if(iter != std::string::npos && iter + 1 != uniform.name.length())
+          {
+            suffix = uniform.name.substr(iter + 1);
+          }
+        }
+        for(int i = 0; i < uniform.size; ++i)
+        {
+          std::stringstream nss;
+          nss << name << "[" << i << "]" << suffix;
+          GetUniformLocation(program, nss.str().c_str()); // Generate N uniforms in the uniform map
+        }
+      }
     }
 
     for(const auto& uniform : mCustomUniformData)
index 3e8e171..f4de9af 100644 (file)
@@ -98,6 +98,7 @@ void TestGraphicsApplication::InitializeCore()
 
 TestGraphicsApplication::~TestGraphicsApplication()
 {
+  mGraphicsController.Shutdown();
   Dali::Integration::Log::UninstallLogFunction();
   delete mCore;
 }
index 45ca6cb..1dee934 100644 (file)
@@ -40,8 +40,6 @@ PKG_CHECK_MODULES(${CAPI_LIB} REQUIRED
     dali2-core
     dali2-adaptor
     glesv2
-    ecore
-    ecore-x
 )
 
 ADD_COMPILE_OPTIONS( -O0 -ggdb --coverage -Wall -Werror )
index 9677500..c2c3e12 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -36,6 +36,18 @@ void utc_dali_program_cleanup(void)
 namespace
 {
 const std::string VERT_SHADER_SOURCE = "myVertShaderSource";
+
+const std::string VERT_SHADER_SOURCE2 =
+  "\n"
+  "in vec3 aPosition;\n"
+  "in vec3 aTexCoord;\n"
+  "out vec2 vTexCoord;\n"
+  "main()\n"
+  "{\n"
+  "  gl_Position=aPosition;\n"
+  "  vTexCoord = aTexCoord;\n"
+  "}\n";
+
 const std::string FRAG_SHADER_SOURCE =
   "\n"
   "uniform sampler2D sAlbedo;\n"
@@ -48,9 +60,20 @@ const std::string FRAG_SHADER_SOURCE =
   "{\n"
   "  gl_fragColor = texture2d(sAlbedo, vTexCoord) + lightDirection*texture2d(sNormals, vTexCoord);\n"
   "}\n";
+
+const std::string FRAG_SHADER_SOURCE2 =
+  "\n"
+  "uniform sampler2D sTextures[4];\n"
+  "uniform mediump vec3 lightDirection;\n"
+  "in mediump vec2 vTexCoord;\n"
+  "main()\n"
+  "{\n"
+  "  gl_fragColor = texture2d(sTextures[0], vTexCoord) + lightDirection*texture2d(sTextures[2], vTexCoord);\n"
+  "}\n";
+
 } //anonymous namespace
 
-int UtcDaliGraphicsProgram(void)
+int UtcDaliGraphicsProgram01(void)
 {
   TestGraphicsApplication app;
   tet_infoline("UtcDaliProgram - check that right sampler uniforms are bound for textures");
@@ -97,3 +120,160 @@ int UtcDaliGraphicsProgram(void)
 
   END_TEST;
 }
+
+int UtcDaliGraphicsProgram02(void)
+{
+  TestGraphicsApplication app;
+  tet_infoline("UtcDaliProgram - check that sampler arrays are handled and bound to textures");
+
+  Texture normals        = CreateTexture(TextureType::TEXTURE_2D, Pixel::RGBA8888, 16u, 16u);
+  Texture metalroughness = CreateTexture(TextureType::TEXTURE_2D, Pixel::RGBA8888, 16u, 16u);
+  Texture ao             = CreateTexture(TextureType::TEXTURE_2D, Pixel::RGBA8888, 16u, 16u);
+  Texture albedo         = CreateTexture(TextureType::TEXTURE_2D, Pixel::RGBA8888, 16u, 16u);
+
+  TextureSet textureSet = TextureSet::New();
+  textureSet.SetTexture(0, albedo);
+  textureSet.SetTexture(1, metalroughness);
+  textureSet.SetTexture(2, normals);
+  textureSet.SetTexture(3, ao);
+
+  Actor actor = CreateRenderableActor2(textureSet, VERT_SHADER_SOURCE, FRAG_SHADER_SOURCE2);
+  app.GetScene().Add(actor);
+
+  auto& gl             = app.GetGlAbstraction();
+  auto& glUniformTrace = gl.GetSetUniformTrace();
+  glUniformTrace.Enable(true);
+  glUniformTrace.EnableLogging(true);
+  gl.GetShaderTrace().Enable(true);
+  gl.GetShaderTrace().EnableLogging(true);
+
+  std::vector<ActiveUniform> activeUniforms{
+    {"uLightDir", GL_FLOAT_VEC4, 1},
+    {"sTextures[0]", GL_SAMPLER_2D, 4}}; // Array of 4 samplers
+  gl.SetActiveUniforms(activeUniforms);
+
+  app.SendNotification();
+  app.Render(16); // The above actor will get rendered and drawn once.
+
+  // Check what uniform values were set:
+  int value;
+  DALI_TEST_CHECK(gl.GetUniformValue("sTextures[0]", value)); // First in frag shader
+  DALI_TEST_EQUALS(value, 0, TEST_LOCATION);
+  DALI_TEST_CHECK(gl.GetUniformValue("sTextures[3]", value)); // 4th
+  DALI_TEST_EQUALS(value, 3, TEST_LOCATION);
+  DALI_TEST_CHECK(gl.GetUniformValue("sTextures[2]", value)); // 3rd
+  DALI_TEST_EQUALS(value, 2, TEST_LOCATION);
+  DALI_TEST_CHECK(gl.GetUniformValue("sTextures[1]", value)); // 2nd
+  DALI_TEST_EQUALS(value, 1, TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcDaliGraphicsShaderNew(void)
+{
+  TestGraphicsApplication app;
+  tet_infoline("UtcDaliProgram - check that multiple shaders from same source only create 1 program");
+
+  Texture diffuse = CreateTexture(TextureType::TEXTURE_2D, Pixel::RGBA8888, 16u, 16u);
+
+  // Creates 3 Dali::Shaders
+  Actor actor1 = CreateRenderableActor(diffuse, VERT_SHADER_SOURCE, FRAG_SHADER_SOURCE);
+  Actor actor2 = CreateRenderableActor(diffuse, VERT_SHADER_SOURCE, FRAG_SHADER_SOURCE);
+  Actor actor3 = CreateRenderableActor(diffuse, VERT_SHADER_SOURCE, FRAG_SHADER_SOURCE);
+
+  app.GetScene().Add(actor1);
+  app.GetScene().Add(actor2);
+  app.GetScene().Add(actor3);
+
+  auto& gl            = app.GetGlAbstraction();
+  auto& glShaderTrace = gl.GetShaderTrace();
+  glShaderTrace.Enable(true);
+  glShaderTrace.EnableLogging(true);
+
+  app.SendNotification();
+  app.Render(16); // The above actors will get rendered and drawn once, only 2 shaders should be created
+
+  DALI_TEST_EQUALS(glShaderTrace.CountMethod("CreateShader"), 2, TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcDaliGraphicsShaderNew02(void)
+{
+  TestGraphicsApplication app;
+  tet_infoline("UtcDaliProgram - check that mixed up multiple shaders from same source don't create dups");
+
+  Texture diffuse = CreateTexture(TextureType::TEXTURE_2D, Pixel::RGBA8888, 16u, 16u);
+
+  // Creates 3 Dali::Shaders
+  Actor actor1 = CreateRenderableActor(diffuse, VERT_SHADER_SOURCE, FRAG_SHADER_SOURCE);
+  Actor actor2 = CreateRenderableActor(diffuse, VERT_SHADER_SOURCE2, FRAG_SHADER_SOURCE2);
+  Actor actor3 = CreateRenderableActor(diffuse, VERT_SHADER_SOURCE, FRAG_SHADER_SOURCE2);
+  Actor actor4 = CreateRenderableActor(diffuse, VERT_SHADER_SOURCE2, FRAG_SHADER_SOURCE);
+
+  app.GetScene().Add(actor1);
+  app.GetScene().Add(actor2);
+  app.GetScene().Add(actor3);
+  app.GetScene().Add(actor4);
+
+  auto& gl            = app.GetGlAbstraction();
+  auto& glShaderTrace = gl.GetShaderTrace();
+  glShaderTrace.Enable(true);
+  glShaderTrace.EnableLogging(true);
+
+  app.SendNotification();
+  app.Render(16); // The above actors will get rendered and drawn once, only 2 shaders should be created
+
+  // Should only be 4 shaders, not 8.
+  DALI_TEST_EQUALS(glShaderTrace.CountMethod("CreateShader"), 4, TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcDaliGraphicsShaderFlush(void)
+{
+  TestGraphicsApplication app;
+  tet_infoline("UtcDaliProgram - check that unused shaders are flushed");
+
+  Texture diffuse       = CreateTexture(TextureType::TEXTURE_2D, Pixel::RGBA8888, 16u, 16u);
+  auto&   gl            = app.GetGlAbstraction();
+  auto&   glShaderTrace = gl.GetShaderTrace();
+  glShaderTrace.Enable(true);
+  glShaderTrace.EnableLogging(true);
+
+  {
+    // Creates 3 Dali::Shaders
+    Actor actor1 = CreateRenderableActor(diffuse, VERT_SHADER_SOURCE, FRAG_SHADER_SOURCE);
+    Actor actor2 = CreateRenderableActor(diffuse, VERT_SHADER_SOURCE2, FRAG_SHADER_SOURCE2);
+    Actor actor3 = CreateRenderableActor(diffuse, VERT_SHADER_SOURCE, FRAG_SHADER_SOURCE2);
+    Actor actor4 = CreateRenderableActor(diffuse, VERT_SHADER_SOURCE2, FRAG_SHADER_SOURCE);
+
+    app.GetScene().Add(actor1);
+    app.GetScene().Add(actor2);
+    app.GetScene().Add(actor3);
+    app.GetScene().Add(actor4);
+
+    app.SendNotification();
+    app.Render(16); // The above actors will get rendered and drawn once
+
+    // Should only be 4 shaders, not 8.
+    DALI_TEST_EQUALS(glShaderTrace.CountMethod("CreateShader"), 4, TEST_LOCATION);
+
+    UnparentAndReset(actor1);
+    UnparentAndReset(actor2);
+    UnparentAndReset(actor3);
+    UnparentAndReset(actor4);
+  }
+
+  for(int i = 0; i < 1199; ++i) // 3 flushes per frame
+  {
+    app.SendNotification();
+    app.Render(16);
+    DALI_TEST_EQUALS(glShaderTrace.CountMethod("DeleteShader"), 0, TEST_LOCATION);
+  }
+  app.SendNotification();
+  app.Render(16);
+  DALI_TEST_EQUALS(glShaderTrace.CountMethod("DeleteShader"), 4, TEST_LOCATION);
+
+  END_TEST;
+}
index 505201c..b211897 100644 (file)
@@ -298,7 +298,7 @@ Graphics::UniquePtr<Pipeline> EglGraphicsController::CreatePipeline(
 Graphics::UniquePtr<Program> EglGraphicsController::CreateProgram(
   const ProgramCreateInfo& programCreateInfo, UniquePtr<Program>&& oldProgram)
 {
-  // Create program cache if needed
+  // Create pipeline cache if needed
   if(!mPipelineCache)
   {
     mPipelineCache = std::make_unique<GLES::PipelineCache>(*this);
@@ -309,7 +309,12 @@ Graphics::UniquePtr<Program> EglGraphicsController::CreateProgram(
 
 Graphics::UniquePtr<Shader> EglGraphicsController::CreateShader(const ShaderCreateInfo& shaderCreateInfo, Graphics::UniquePtr<Shader>&& oldShader)
 {
-  return NewObject<GLES::Shader>(shaderCreateInfo, *this, std::move(oldShader));
+  // Create pipeline cache if needed
+  if(!mPipelineCache)
+  {
+    mPipelineCache = std::make_unique<GLES::PipelineCache>(*this);
+  }
+  return mPipelineCache->GetShader(shaderCreateInfo, std::move(oldShader));
 }
 
 Graphics::UniquePtr<Sampler> EglGraphicsController::CreateSampler(const SamplerCreateInfo& samplerCreateInfo, Graphics::UniquePtr<Sampler>&& oldSampler)
index 7f14016..b567a27 100644 (file)
 #include "gles-graphics-pipeline.h"
 #include "gles-graphics-program.h"
 
+namespace
+{
+constexpr uint32_t CACHE_CLEAN_FLUSH_COUNT = 3600u; // 60fps * 60sec / ~3 flushes per frame
+}
+
 namespace Dali::Graphics::GLES
 {
 /**
@@ -247,7 +252,8 @@ struct PipelineCache::Impl
   explicit Impl(EglGraphicsController& _controller)
   : controller(_controller),
     pipelineEntriesFlushRequired(false),
-    programEntriesFlushRequired(false)
+    programEntriesFlushRequired(false),
+    shaderEntriesFlushRequired(false)
   {
     // Initialise lookup table
     InitialiseStateCompareLookupTable();
@@ -287,9 +293,6 @@ struct PipelineCache::Impl
     uint32_t                stateBitmask{0u};
   };
 
-  EglGraphicsController&  controller;
-  std::vector<CacheEntry> entries;
-
   /**
    * @brief Sorted array of shaders used to create program
    */
@@ -300,10 +303,19 @@ struct PipelineCache::Impl
     UniquePtr<ProgramImpl>               program{nullptr};
   };
 
+  struct ShaderCacheEntry
+  {
+    UniquePtr<ShaderImpl> shaderImpl{nullptr};
+  };
+
+  EglGraphicsController&         controller;
+  std::vector<CacheEntry>        entries;
   std::vector<ProgramCacheEntry> programEntries;
+  std::vector<ShaderCacheEntry>  shaderEntries;
 
   bool pipelineEntriesFlushRequired : 1;
   bool programEntriesFlushRequired : 1;
+  bool shaderEntriesFlushRequired : 1;
 };
 
 PipelineCache::PipelineCache(EglGraphicsController& controller)
@@ -467,6 +479,53 @@ Graphics::UniquePtr<Graphics::Program> PipelineCache::GetProgram(const ProgramCr
   return std::move(wrapper);
 }
 
+ShaderImpl* PipelineCache::FindShaderImpl(const ShaderCreateInfo& shaderCreateInfo)
+{
+  if(!mImpl->shaderEntries.empty())
+  {
+    for(auto& item : mImpl->shaderEntries)
+    {
+      auto& itemInfo = item.shaderImpl->GetCreateInfo();
+      if(itemInfo.pipelineStage != shaderCreateInfo.pipelineStage ||
+         itemInfo.shaderlanguage != shaderCreateInfo.shaderlanguage ||
+         itemInfo.sourceMode != shaderCreateInfo.sourceMode ||
+         itemInfo.sourceSize != shaderCreateInfo.sourceSize)
+      {
+        continue;
+      }
+
+      if(memcmp(itemInfo.sourceData, shaderCreateInfo.sourceData, itemInfo.sourceSize) == 0)
+      {
+        return item.shaderImpl.get();
+      }
+    }
+  }
+  return nullptr;
+}
+
+Graphics::UniquePtr<Graphics::Shader> PipelineCache::GetShader(const ShaderCreateInfo&                 shaderCreateInfo,
+                                                               Graphics::UniquePtr<Graphics::Shader>&& oldShader)
+{
+  ShaderImpl* cachedShader = FindShaderImpl(shaderCreateInfo);
+
+  // Return same pointer if nothing changed
+  if(oldShader && *static_cast<GLES::Shader*>(oldShader.get()) == cachedShader)
+  {
+    return std::move(oldShader);
+  }
+
+  if(!cachedShader)
+  {
+    auto shader  = MakeUnique<GLES::ShaderImpl>(shaderCreateInfo, mImpl->controller);
+    cachedShader = shader.get();
+
+    mImpl->shaderEntries.emplace_back();
+    mImpl->shaderEntries.back().shaderImpl = std::move(shader);
+  }
+  auto wrapper = MakeUnique<GLES::Shader, CachedObjectDeleter<GLES::Shader>>(cachedShader);
+  return std::move(wrapper);
+}
+
 void PipelineCache::FlushCache()
 {
   if(mImpl->pipelineEntriesFlushRequired)
@@ -509,6 +568,39 @@ void PipelineCache::FlushCache()
 
     mImpl->programEntriesFlushRequired = false;
   }
+
+  if(mImpl->shaderEntriesFlushRequired)
+  {
+    // There is at least 1 unused shader
+    mImpl->shaderEntriesFlushRequired = false;
+    bool deleteRequired{false};
+    for(auto& entry : mImpl->shaderEntries)
+    {
+      if(entry.shaderImpl->GetRefCount() == 0)
+      {
+        mImpl->shaderEntriesFlushRequired = true;
+        auto frameCount                   = entry.shaderImpl->IncreaseFlushCount();
+        if(frameCount > CACHE_CLEAN_FLUSH_COUNT)
+        {
+          deleteRequired = true;
+        }
+      }
+    }
+    if(deleteRequired)
+    {
+      decltype(mImpl->shaderEntries) newShaderEntries;
+      newShaderEntries.reserve(mImpl->shaderEntries.size());
+      for(auto& entry : mImpl->shaderEntries)
+      {
+        if(entry.shaderImpl->GetRefCount() > 0 ||
+           entry.shaderImpl->GetFlushCount() <= CACHE_CLEAN_FLUSH_COUNT)
+        {
+          newShaderEntries.emplace_back(std::move(entry));
+        }
+      }
+      mImpl->shaderEntries = std::move(newShaderEntries);
+    }
+  }
 }
 
 void PipelineCache::MarkPipelineCacheFlushRequired()
@@ -521,4 +613,9 @@ void PipelineCache::MarkProgramCacheFlushRequired()
   mImpl->programEntriesFlushRequired = true;
 }
 
+void PipelineCache::MarkShaderCacheFlushRequired()
+{
+  mImpl->shaderEntriesFlushRequired = true;
+}
+
 } // namespace Dali::Graphics::GLES
index 2a1fd6b..685ad32 100644 (file)
@@ -23,6 +23,8 @@
 #include <dali/graphics-api/graphics-pipeline.h>
 #include <dali/graphics-api/graphics-program-create-info.h>
 #include <dali/graphics-api/graphics-program.h>
+#include <dali/graphics-api/graphics-shader-create-info.h>
+#include <dali/graphics-api/graphics-shader.h>
 
 // INTERNAL INCLUDES
 #include "gles-graphics-resource.h"
@@ -36,6 +38,9 @@ class Pipeline;
 class PipelineImpl;
 class Program;
 class ProgramImpl;
+class Shader;
+class ShaderImpl;
+
 /**
  * @brief PipelineCache manages pipeline and program
  * objects so there are no duplicates created.
@@ -78,6 +83,18 @@ public:
   Graphics::UniquePtr<Graphics::Program> GetProgram(const ProgramCreateInfo& pipelineCreateInfo, Graphics::UniquePtr<Graphics::Program>&& oldProgram);
 
   /**
+   * @brief Retrieves shader matching the spec
+   *
+   * Function returns either existing shader if one is found
+   * in the cache or creates new one.
+   *
+   * @param[in] shaderCreateInfo Valid ShaderCreateInfo structure
+   * @param[in] oldShader previous shader object
+   * @return Shader object
+   */
+  Graphics::UniquePtr<Graphics::Shader> GetShader(const ShaderCreateInfo& shaderCreateInfo, Graphics::UniquePtr<Graphics::Shader>&& oldShader);
+
+  /**
    * @brief Flushes pipeline and program cache
    *
    * Removes cached items when they are no longer needed. This function
@@ -95,6 +112,11 @@ public:
    */
   void MarkProgramCacheFlushRequired();
 
+  /**
+   * @brief Notify that we need to flush shader cache next FlushCache API.
+   */
+  void MarkShaderCacheFlushRequired();
+
 private:
   /**
    * @brief Finds pipeline implementation based on the spec
@@ -104,12 +126,20 @@ 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
- */
  * @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);
 
+  /**
+   * @brief Finds shader implementation based on create info
+   *
+   * @param[in] shadercreateinfo Valid create info structure
+   * @return Returns pointer to shader or nullptr
+   */
+  ShaderImpl* FindShaderImpl(const ShaderCreateInfo& shaderCreateInfo);
+
 private:
   struct Impl;
   std::unique_ptr<Impl> mImpl;
index d7fc0a7..3b28c23 100644 (file)
@@ -148,9 +148,9 @@ bool ProgramImpl::Create()
     const auto* shader = static_cast<const GLES::Shader*>(state.shader);
 
     // Compile shader first (ignored when compiled)
-    if(shader->Compile())
+    if(shader->GetImplementation()->Compile())
     {
-      gl->AttachShader(program, shader->GetGLShader());
+      gl->AttachShader(program, shader->GetImplementation()->GetGLShader());
     }
   }
   gl->LinkProgram(program);
index 050b04b..ee8a888 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_GRAPHICS_GLES_PROGRAM_H
 
 /*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -154,7 +154,7 @@ private:
 ///////////////////////////////////////////////////////////////
 
 /**
- * @brief Wrapper for the pipeline implementation
+ * @brief Wrapper for the program implementation
  *
  * This object is returned back to the client-side
  */
index 5e46e97..676884f 100644 (file)
 
 namespace Dali::Graphics::GLES
 {
-struct Shader::Impl
+struct ShaderImpl::Impl
 {
-  Impl()  = default;
-  ~Impl() = default;
-
-  std::vector<char> source{};
-  uint32_t          glShader{};
-};
-
-Shader::Shader(const Graphics::ShaderCreateInfo& createInfo, Graphics::EglGraphicsController& controller)
-: ShaderResource(createInfo, controller)
-{
-  // 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();
-}
-
-Shader::~Shader() = default;
-
-bool Shader::Compile() const
-{
-  auto gl = GetController().GetGL();
-
-  if(!gl)
+  explicit Impl(Graphics::EglGraphicsController& _controller, const Graphics::ShaderCreateInfo& _createInfo)
+  : controller(_controller)
   {
-    return false;
+    createInfo.pipelineStage  = _createInfo.pipelineStage;
+    createInfo.shaderlanguage = _createInfo.shaderlanguage;
+    createInfo.sourceMode     = _createInfo.sourceMode;
+    createInfo.sourceSize     = _createInfo.sourceSize;
+
+    // Make a copy of source code
+    source.resize(_createInfo.sourceSize);
+    std::copy(reinterpret_cast<const char*>(_createInfo.sourceData),
+              reinterpret_cast<const char*>(_createInfo.sourceData) + _createInfo.sourceSize,
+              source.data());
+
+    // Substitute pointer
+    createInfo.sourceData = source.data();
   }
 
-  if(!mImpl->glShader)
+  ~Impl(){};
+
+  bool Compile()
   {
-    GLenum pipelineStage{0u};
-    switch(GetCreateInfo().pipelineStage)
+    auto gl = controller.GetGL();
+
+    if(!gl)
     {
-      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:
+      return false;
+    }
+
+    if(!glShader)
+    {
+      GLenum pipelineStage{0u};
+      switch(createInfo.pipelineStage)
       {
-        break;
+        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;
+        }
       }
-      case Graphics::PipelineStage::BOTTOM_OF_PIPELINE:
+
+      if(pipelineStage)
       {
-        break;
+        auto       shader = gl->CreateShader(pipelineStage);
+        const auto src    = reinterpret_cast<const char*>(createInfo.sourceData);
+        GLint      size   = createInfo.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);
+          DALI_LOG_ERROR("Code: %s\n", reinterpret_cast<const char*>(createInfo.sourceData));
+          DALI_LOG_ERROR("glCompileShader() failed: \n%s\n", output);
+          gl->DeleteShader(shader);
+          return false;
+        }
+        glShader = shader;
       }
+      return true;
     }
+    return true;
+  }
+
+  void Destroy()
+  {
+    auto gl = controller.GetGL();
 
-    if(pipelineStage)
+    if(gl && glShader)
     {
-      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);
-        DALI_LOG_ERROR("Code: %s\n", reinterpret_cast<const char*>(GetCreateInfo().sourceData));
-        DALI_LOG_ERROR("glCompileShader() failed: \n%s\n", output);
-        gl->DeleteShader(shader);
-        return false;
-      }
-      mImpl->glShader = shader;
+      gl->DeleteShader(glShader);
+      glShader = 0;
     }
-    return true;
   }
-  return true;
+
+  EglGraphicsController& controller;
+  ShaderCreateInfo       createInfo;
+  std::vector<char>      source{};
+
+  uint32_t glShader{};
+  uint32_t refCount{0u};
+  uint32_t flushCount{0u}; ///< Number of frames at refCount=0
+};
+
+ShaderImpl::ShaderImpl(const Graphics::ShaderCreateInfo& createInfo, Graphics::EglGraphicsController& controller)
+{
+  mImpl = std::make_unique<Impl>(controller, createInfo);
+}
+
+ShaderImpl::~ShaderImpl()
+{
+  if(!mImpl->controller.IsShuttingDown())
+  {
+    mImpl->Destroy();
+  }
+}
+
+uint32_t ShaderImpl::Retain()
+{
+  mImpl->flushCount = 0;
+  return ++mImpl->refCount;
+}
+
+uint32_t ShaderImpl::Release()
+{
+  uint32_t remainingCount = --mImpl->refCount;
+  mImpl->flushCount       = 0;
+  return remainingCount;
+}
+
+[[nodiscard]] uint32_t ShaderImpl::GetRefCount() const
+{
+  return mImpl->refCount;
+}
+
+[[nodiscard]] uint32_t ShaderImpl::IncreaseFlushCount()
+{
+  return ++mImpl->flushCount;
+}
+
+[[nodiscard]] uint32_t ShaderImpl::GetFlushCount() const
+{
+  return mImpl->flushCount;
+}
+
+/**
+ * @brief Compiles shader
+ *
+ * @return True on success
+ */
+[[nodiscard]] bool ShaderImpl::Compile() const
+{
+  return mImpl->Compile();
 }
 
-uint32_t Shader::GetGLShader() const
+[[nodiscard]] uint32_t ShaderImpl::GetGLShader() const
 {
   return mImpl->glShader;
 }
 
-void Shader::DestroyResource()
+const ShaderCreateInfo& ShaderImpl::GetCreateInfo() const
+{
+  return mImpl->createInfo;
+}
+
+[[nodiscard]] EglGraphicsController& ShaderImpl::GetController() const
+{
+  return mImpl->controller;
+}
+
+Shader::~Shader()
 {
-  if(mImpl->glShader)
+  if(!mShader->Release())
   {
-    auto gl = GetController().GetGL();
-    if(!gl)
-    {
-      return;
-    }
-    gl->DeleteShader(mImpl->glShader);
+    GetImplementation()->GetController().GetPipelineCache().MarkShaderCacheFlushRequired();
   }
 }
 
+[[nodiscard]] const ShaderCreateInfo& Shader::GetCreateInfo() const
+{
+  return GetImplementation()->GetCreateInfo();
+}
+
 void Shader::DiscardResource()
 {
-  GetController().DiscardResource(this);
+  auto& controller = GetImplementation()->GetController();
+  if(!controller.IsShuttingDown())
+  {
+    controller.DiscardResource(this);
+  }
 }
 
 } // namespace Dali::Graphics::GLES
index 180a14b..28c7370 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_GRAPHICS_GLES_SHADER_H
 
 /*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -27,9 +27,7 @@
 
 namespace Dali::Graphics::GLES
 {
-using ShaderResource = Resource<Graphics::Shader, Graphics::ShaderCreateInfo>;
-
-class Shader : public ShaderResource
+class ShaderImpl
 {
 public:
   /**
@@ -37,29 +35,28 @@ public:
    * @param[in] createInfo Valid createInfo structure
    * @param[in] controller Reference to the controller
    */
-  Shader(const Graphics::ShaderCreateInfo& createInfo, Graphics::EglGraphicsController& controller);
+  ShaderImpl(const Graphics::ShaderCreateInfo& createInfo, Graphics::EglGraphicsController& controller);
+  ~ShaderImpl();
 
-  /**
-   * @brief Destructor
-   */
-  ~Shader() override;
+  uint32_t Retain();
+
+  uint32_t Release();
+
+  [[nodiscard]] uint32_t GetRefCount() const;
 
   /**
-   * @brief Called when GL resources are destroyed
+   * Whilst unreferenced, increase the flush count and return it
+   *
+   * @return The new flush count
    */
-  void DestroyResource() override;
+  [[nodiscard]] uint32_t IncreaseFlushCount();
 
   /**
-   * @brief Called when initializing the resource
+   * Get the flush count whilst unreferenced
    *
-   * @return True on success
+   * @return the flush count
    */
-  bool InitializeResource() override
-  {
-    // The Shader has instant initialization, hence no need to initialize GL resource
-    // here
-    return true;
-  }
+  [[nodiscard]] uint32_t GetFlushCount() const;
 
   /**
    * @brief Compiles shader
@@ -69,17 +66,83 @@ public:
   [[nodiscard]] bool Compile() const;
 
   /**
-   * @brief Called when UniquePtr<> on client-side dies
+   * @brief Destroys GL shader
    */
-  void DiscardResource() override;
+  void Destroy();
 
   uint32_t GetGLShader() const;
 
+  [[nodiscard]] const ShaderCreateInfo& GetCreateInfo() const;
+
+  [[nodiscard]] EglGraphicsController& GetController() const;
+
 private:
+  friend class Shader;
   struct Impl;
   std::unique_ptr<Impl> mImpl{nullptr};
 };
 
+class Shader : public Graphics::Shader
+{
+public:
+  /**
+   * @brief Constructor
+   *
+   * @param[in] impl Pointer to valid implementation
+   */
+  explicit Shader(ShaderImpl* impl)
+  : mShader(impl)
+  {
+    mShader->Retain();
+  }
+
+  /**
+   * @brief Destructor
+   */
+  ~Shader() override;
+
+  [[nodiscard]] ShaderImpl* GetImplementation() const
+  {
+    return mShader;
+  }
+
+  [[nodiscard]] const ShaderCreateInfo& GetCreateInfo() const;
+
+  bool operator==(const GLES::Shader& shader) const
+  {
+    return (shader.mShader == mShader);
+  }
+
+  bool operator==(const GLES::ShaderImpl* shaderImpl) const
+  {
+    return (shaderImpl == mShader);
+  }
+
+  bool operator!=(const GLES::Shader& shader) const
+  {
+    return (shader.mShader != mShader);
+  }
+
+  /**
+   * @brief Called when UniquePtr<> on client-side dies.
+   */
+  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:
+  ShaderImpl* mShader{nullptr};
+};
+
 } // namespace Dali::Graphics::GLES
 
 #endif