Fix pipeline hash
[platform/core/uifw/dali-core.git] / dali / internal / render / renderers / pipeline-cache.cpp
index 3f655f4..c2545c0 100644 (file)
@@ -29,6 +29,8 @@ namespace Dali::Internal::Render
 {
 namespace
 {
+constexpr uint32_t CACHE_CLEAN_FRAME_COUNT = 600; // 60fps * 10sec
+
 // Helper to get the vertex input format
 Dali::Graphics::VertexInputFormat GetPropertyVertexFormat(Property::Type propertyType)
 {
@@ -190,10 +192,10 @@ constexpr Graphics::BlendOp ConvertBlendEquation(DevelBlendEquation::Type blendE
 }
 } // namespace
 
-PipelineCacheL0* PipelineCache::GetPipelineCacheL0(std::size_t hash, Program* program, Render::Geometry* geometry)
+PipelineCacheL0Ptr PipelineCache::GetPipelineCacheL0(Program* program, Render::Geometry* geometry)
 {
-  auto it = std::find_if(level0nodes.begin(), level0nodes.end(), [hash, program, geometry](PipelineCacheL0& item) {
-    return ((item.hash == hash && item.program == program && item.geometry == geometry));
+  auto it = std::find_if(level0nodes.begin(), level0nodes.end(), [program, geometry](PipelineCacheL0& item) {
+    return ((item.program == program && item.geometry == geometry));
   });
 
   // Add new node to cache
@@ -209,8 +211,14 @@ PipelineCacheL0* PipelineCache::GetPipelineCacheL0(std::size_t hash, Program* pr
     {
       const VertexBuffer::Format& vertexFormat = *vertexBuffer->GetFormat();
 
+      uint32_t                  divisor         = vertexBuffer->GetDivisor();
+      Graphics::VertexInputRate vertexInputRate = (divisor == 0
+                                                     ? Graphics::VertexInputRate::PER_VERTEX
+                                                     : Graphics::VertexInputRate::PER_INSTANCE);
+
       vertexInputState.bufferBindings.emplace_back(vertexFormat.size, // stride
-                                                   Graphics::VertexInputRate::PER_VERTEX);
+                                                   vertexInputRate);
+      //@todo Add the actual rate to the graphics struct
 
       const uint32_t attributeCount          = vertexBuffer->GetAttributeCount();
       uint32_t       lastBoundAttributeIndex = 0;
@@ -237,18 +245,17 @@ PipelineCacheL0* PipelineCache::GetPipelineCacheL0(std::size_t hash, Program* pr
       ++bindingIndex;
     }
     PipelineCacheL0 level0;
-    level0.hash       = hash;
     level0.program    = program;
     level0.geometry   = geometry;
     level0.inputState = vertexInputState;
-    level0nodes.emplace_back(std::move(level0));
-    it = level0nodes.end() - 1;
+
+    it = level0nodes.insert(level0nodes.end(), std::move(level0));
   }
 
-  return &*it;
+  return it;
 }
 
-PipelineCacheL1* PipelineCacheL0::GetPipelineCacheL1(Render::Renderer* renderer, bool usingReflection)
+PipelineCacheL1Ptr PipelineCacheL0::GetPipelineCacheL1(Render::Renderer* renderer, bool usingReflection)
 {
   // hash must be collision free
   uint32_t hash = 0;
@@ -293,11 +300,8 @@ PipelineCacheL1* PipelineCacheL0::GetPipelineCacheL1(Render::Renderer* renderer,
   hash = (topo & 0xffu) | ((cull << 8u) & 0xff00u) | ((uint32_t(poly) << 16u) & 0xff0000u);
 
   // If L1 not found by hash, create rasterization state describing pipeline and store it
-  auto it = std::find_if(level1nodes.begin(), level1nodes.end(), [hash](PipelineCacheL1& item) {
-    return item.hashCode == hash;
-  });
+  auto it = std::find_if(level1nodes.begin(), level1nodes.end(), [hash](PipelineCacheL1& item) { return item.hashCode == hash; });
 
-  PipelineCacheL1* retval = nullptr;
   if(it == level1nodes.end())
   {
     PipelineCacheL1 item;
@@ -306,39 +310,56 @@ PipelineCacheL1* PipelineCacheL0::GetPipelineCacheL1(Render::Renderer* renderer,
     item.rs.frontFace   = Graphics::FrontFace::COUNTER_CLOCKWISE;
     item.rs.polygonMode = poly; // not in use
     item.ia.topology    = geometry->GetTopology();
-    level1nodes.emplace_back(std::move(item));
-    retval = &level1nodes.back();
+
+    it = level1nodes.insert(level1nodes.end(), std::move(item));
   }
-  else
+
+  return it;
+}
+
+void PipelineCacheL0::ClearUnusedCache()
+{
+  for(auto iter = level1nodes.begin(); iter != level1nodes.end();)
   {
-    retval = &*it;
+    if(iter->ClearUnusedCache())
+    {
+      iter = level1nodes.erase(iter);
+    }
+    else
+    {
+      iter++;
+    }
   }
-  return retval;
 }
 
-PipelineCacheL2* PipelineCacheL1::GetPipelineCacheL2(bool blend, bool premul, BlendingOptions& blendingOptions)
+PipelineCacheL2Ptr PipelineCacheL1::GetPipelineCacheL2(bool blend, bool premul, BlendingOptions& blendingOptions)
 {
   // early out
   if(!blend)
   {
+    if(DALI_UNLIKELY(noBlends.empty()))
+    {
+      noBlends.emplace_back(PipelineCacheL2{});
+    }
+
+    auto& noBlend = *noBlends.begin();
+
     if(noBlend.pipeline == nullptr)
     {
       // reset all before returning if pipeline has never been created for that case
       noBlend.hash = 0;
       memset(&noBlend.colorBlendState, 0, sizeof(Graphics::ColorBlendState));
     }
-    return &noBlend;
+    return noBlends.begin();
   }
 
   auto bitmask = uint32_t(blendingOptions.GetBitmask());
 
   // Find by bitmask (L2 entries must be sorted by bitmask)
-  auto it = std::find_if(level2nodes.begin(), level2nodes.end(), [bitmask](PipelineCacheL2& item) {
-    return item.hash == bitmask;
-  });
+  auto it = std::find_if(level2nodes.begin(), level2nodes.end(), [bitmask](PipelineCacheL2& item) { return item.hash == bitmask; });
 
   // TODO: find better way of blend constants lookup
-  PipelineCacheL2* retval = nullptr;
+  PipelineCacheL2Ptr retval = level2nodes.end();
   if(it != level2nodes.end())
   {
     bool hasBlendColor = blendingOptions.GetBlendColor();
@@ -347,20 +368,20 @@ PipelineCacheL2* PipelineCacheL1::GetPipelineCacheL2(bool blend, bool premul, Bl
       Vector4 v(it->colorBlendState.blendConstants);
       if(v == *blendingOptions.GetBlendColor())
       {
-        retval = &*it;
+        retval = it;
       }
       ++it;
     }
     if(!hasBlendColor)
     {
-      retval = &*it;
+      retval = it;
     }
   }
 
-  if(!retval)
+  if(retval == level2nodes.end())
   {
     // create new entry and return it with null pipeline
-    PipelineCacheL2 l2;
+    PipelineCacheL2 l2{};
     l2.pipeline           = nullptr;
     auto& colorBlendState = l2.colorBlendState;
     colorBlendState.SetBlendEnable(true);
@@ -391,11 +412,10 @@ PipelineCacheL2* PipelineCacheL1::GetPipelineCacheL2(bool blend, bool premul, Bl
     }
 
     l2.hash = blendingOptions.GetBitmask();
-    level2nodes.emplace_back(std::move(l2));
 
-    std::sort(level2nodes.begin(), level2nodes.end(), [](PipelineCacheL2& lhs, PipelineCacheL2& rhs) {
-      return lhs.hash < rhs.hash;
-    });
+    auto upperBound = std::upper_bound(level2nodes.begin(), level2nodes.end(), l2, [](const PipelineCacheL2& lhs, const PipelineCacheL2& rhs) { return lhs.hash < rhs.hash; });
+
+    level2nodes.insert(upperBound, std::move(l2));
 
     // run same function to retrieve retval
     retval = GetPipelineCacheL2(blend, premul, blendingOptions);
@@ -404,6 +424,28 @@ PipelineCacheL2* PipelineCacheL1::GetPipelineCacheL2(bool blend, bool premul, Bl
   return retval;
 }
 
+bool PipelineCacheL1::ClearUnusedCache()
+{
+  for(auto iter = level2nodes.begin(); iter != level2nodes.end();)
+  {
+    if(iter->referenceCount == 0)
+    {
+      iter = level2nodes.erase(iter);
+    }
+    else
+    {
+      iter++;
+    }
+  }
+
+  if(!noBlends.empty() && noBlends.begin()->referenceCount > 0)
+  {
+    return false;
+  }
+
+  return level2nodes.empty();
+}
+
 void PipelineCacheQueryInfo::GenerateHash()
 {
   // Lightweight hash value generation.
@@ -453,12 +495,14 @@ PipelineResult PipelineCache::GetPipeline(const PipelineCacheQueryInfo& queryInf
   // If we can reuse latest bound pipeline, Fast return.
   if(ReuseLatestBoundPipeline(latestUsedCacheIndex, queryInfo))
   {
+    mLatestResult[latestUsedCacheIndex].level2->referenceCount++;
     return mLatestResult[latestUsedCacheIndex];
   }
 
-  auto* level0 = GetPipelineCacheL0(queryInfo.hash, queryInfo.program, queryInfo.geometry);
-  auto* level1 = level0->GetPipelineCacheL1(queryInfo.renderer, queryInfo.cameraUsingReflection);
-  auto* level2 = level1->GetPipelineCacheL2(queryInfo.blendingEnabled, queryInfo.alphaPremultiplied, *queryInfo.blendingOptions);
+  auto level0 = GetPipelineCacheL0(queryInfo.program, queryInfo.geometry);
+  auto level1 = level0->GetPipelineCacheL1(queryInfo.renderer, queryInfo.cameraUsingReflection);
+
+  PipelineCachePtr level2 = level1->GetPipelineCacheL2(queryInfo.blendingEnabled, queryInfo.alphaPremultiplied, *queryInfo.blendingOptions);
 
   // Create new pipeline at level2 if requested
   if(level2->pipeline == nullptr && createNewIfNotFound)
@@ -482,10 +526,10 @@ PipelineResult PipelineCache::GetPipeline(const PipelineCacheQueryInfo& queryInf
   PipelineResult result{};
 
   result.pipeline = level2->pipeline.get();
-  result.level0   = level0;
-  result.level1   = level1;
   result.level2   = level2;
 
+  level2->referenceCount++;
+
   // Copy query and result
   mLatestQuery[latestUsedCacheIndex]  = queryInfo;
   mLatestResult[latestUsedCacheIndex] = result;
@@ -498,4 +542,39 @@ bool PipelineCache::ReuseLatestBoundPipeline(const int latestUsedCacheIndex, con
   return mLatestResult[latestUsedCacheIndex].pipeline != nullptr && PipelineCacheQueryInfo::Equal(queryInfo, mLatestQuery[latestUsedCacheIndex]);
 }
 
+void PipelineCache::PreRender()
+{
+  CleanLatestUsedCache();
+
+  // We don't need to check this every frame
+  if(++mFrameCount >= CACHE_CLEAN_FRAME_COUNT)
+  {
+    mFrameCount = 0u;
+    ClearUnusedCache();
+  }
+}
+
+void PipelineCache::ClearUnusedCache()
+{
+  for(auto iter = level0nodes.begin(); iter != level0nodes.end();)
+  {
+    iter->ClearUnusedCache();
+
+    if(iter->level1nodes.empty())
+    {
+      iter = level0nodes.erase(iter);
+    }
+    else
+    {
+      iter++;
+    }
+  }
+}
+
+void PipelineCache::ResetPipeline(PipelineCachePtr pipelineCache)
+{
+  // TODO : Can we always assume that pipelineCache input is valid iterator?
+  pipelineCache->referenceCount--;
+}
+
 } // namespace Dali::Internal::Render