Let we add self-depenedency check for native texture 56/320356/10
authorEunki, Hong <eunkiki.hong@samsung.com>
Thu, 14 Nov 2024 01:59:53 +0000 (10:59 +0900)
committerEunki, Hong <eunkiki.hong@samsung.com>
Mon, 25 Nov 2024 10:25:52 +0000 (19:25 +0900)
Since GPU don't assume that NativeImage read done after eglSwapBuffer,
We should add syncFence manually, and check whether previous tbm render done.

But synce eglCreateSync spend a lots of time, we should create only 1 time per
each frame.

TODO : We don't need to client-wait for non-acquried case. But it need to open
new API for NativeImageInterface. So let we implement it later.

Change-Id: I5068bdac203850b241d330e01f23abb312b1da16
Signed-off-by: Eunki, Hong <eunkiki.hong@samsung.com>
dali/internal/graphics/gles-impl/egl-graphics-controller.h
dali/internal/graphics/gles-impl/gles-context.cpp
dali/internal/graphics/gles-impl/gles-graphics-texture.h
dali/internal/graphics/gles-impl/gles-sync-pool.cpp
dali/internal/graphics/gles-impl/gles-sync-pool.h
dali/internal/graphics/gles-impl/gles-texture-dependency-checker.cpp
dali/internal/graphics/gles-impl/gles-texture-dependency-checker.h

index 273b2e5c0389f12509adf9f2e36ed8970088cedd..911962784c139eab104179e0f21199ccdd331019 100644 (file)
@@ -373,6 +373,10 @@ public:
   void DiscardResource(GLES::Texture* texture)
   {
     mDiscardTextureSet.insert(texture);
+    if(texture->IsNativeTexture())
+    {
+      mTextureDependencyChecker.DiscardNativeTexture(texture);
+    }
   }
 
   /**
index 2d02dcb0b9d7d570b0ba651cd8020570a7073180..1cf5c90a4d1221b363390bc91ed617a6517cea92 100644 (file)
@@ -381,7 +381,12 @@ void Context::Flush(bool reset, const GLES::DrawCallDescriptor& drawCall, GLES::
     // Warning, this may cause glWaitSync to occur on the GPU, or glClientWaitSync to block the CPU.
     dependencyChecker.CheckNeedsSync(this, texture, true);
     texture->Bind(binding);
-    texture->Prepare();
+
+    if(texture->IsNativeTexture())
+    {
+      texture->Prepare();
+      dependencyChecker.MarkNativeTexturePrepared(texture);
+    }
 
     if(programChanged)
     {
@@ -918,6 +923,11 @@ void Context::EndRenderPass(GLES::TextureDependencyChecker& dependencyChecker)
       dependencyChecker.AddTextures(this, framebuffer);
     }
   }
+
+  if(dependencyChecker.GetNativeTextureCount() > 0)
+  {
+    dependencyChecker.CreateNativeTextureSync(this);
+  }
 }
 
 void Context::ClearState()
index 8da8d2cd595280518e6b80982f5858cf1fba397e..0d736bb73e73de0b86fcf73052e9b75ca9bf0dda 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_GRAPHICS_GLES_TEXTURE_H
 
 /*
- * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 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.
@@ -75,6 +75,15 @@ public:
     return mCreateInfo.nativeImagePtr ? BoundTextureType::TEXTURE_EXTERNAL_OES : static_cast<BoundTextureType>(mCreateInfo.textureType);
   }
 
+  /**
+   * @brief Returns wether we are using a native texture or not.
+   * @return True if we are using a native texture. False otherwise.
+   */
+  [[nodiscard]] bool IsNativeTexture() const
+  {
+    return mCreateInfo.nativeImagePtr != nullptr;
+  }
+
   /**
    * @brief Called when initializing the resource
    *
index 6077414f47cc86cf88636c74df336922ede1f578..650a5b791ab54ef3f8bc7c57903d4fbb97ff39e6 100644 (file)
@@ -70,6 +70,33 @@ AgingSyncObject::~AgingSyncObject()
   }
 }
 
+bool AgingSyncObject::IsSynced()
+{
+  bool synced = false;
+  if(egl)
+  {
+    if(eglSyncObject)
+    {
+      DALI_LOG_INFO(gLogSyncFilter, Debug::Verbose, "AgingSyncObject::IsSynced(); EGL::ClientWaitSync\n");
+      synced = eglSyncObject->IsSynced();
+    }
+  }
+  else
+  {
+    auto gl = controller.GetGL();
+    if(gl && glSyncObject)
+    {
+      DALI_LOG_INFO(gLogSyncFilter, Debug::Verbose, "AgingSyncObject::IsSynced(); glClientWaitSync 0ms\n");
+      const GLuint64 TIMEOUT = 0; //0ms!
+      GLenum         result  = gl->ClientWaitSync(glSyncObject, GL_SYNC_FLUSH_COMMANDS_BIT, TIMEOUT);
+
+      synced = (result == GL_ALREADY_SIGNALED || result == GL_CONDITION_SATISFIED);
+    }
+  }
+  DALI_LOG_INFO(gLogSyncFilter, Debug::Verbose, "AgingSyncObject::IsSynced(); Result: %s\n", synced ? "Synced" : "NOT SYNCED");
+  return synced;
+}
+
 bool AgingSyncObject::ClientWait()
 {
   bool synced = false;
@@ -123,35 +150,46 @@ SyncPool::~SyncPool() = default;
 
 AgingSyncObject* SyncPool::AllocateSyncObject(const Context* writeContext, SyncPool::SyncContext syncContext)
 {
-  std::unique_ptr<AgingSyncObject> syncObject = std::make_unique<AgingSyncObject>(mController, writeContext, (syncContext == SyncContext::EGL));
-  mSyncObjects.push_back(std::move(syncObject));
-  return mSyncObjects.back().get();
+  AgingSyncObject* agingSyncObject = new AgingSyncObject(mController, writeContext, (syncContext == SyncContext::EGL));
+
+  // Take ownership of sync object
+  mSyncObjects.PushBack(agingSyncObject);
+  return agingSyncObject;
 }
 
-void SyncPool::Wait(AgingSyncObject* syncPoolObject)
+bool SyncPool::IsSynced(AgingSyncObject* agingSyncObject)
 {
-  if(syncPoolObject != nullptr)
+  if(DALI_LIKELY(agingSyncObject != nullptr))
   {
-    syncPoolObject->syncing = true;
-    syncPoolObject->Wait();
+    return agingSyncObject->IsSynced();
   }
+  return false;
 }
 
-bool SyncPool::ClientWait(AgingSyncObject* syncPoolObject)
+void SyncPool::Wait(AgingSyncObject* agingSyncObject)
 {
-  if(syncPoolObject)
+  if(DALI_LIKELY(agingSyncObject != nullptr))
   {
-    return syncPoolObject->ClientWait();
+    agingSyncObject->syncing = true;
+    agingSyncObject->Wait();
+  }
+}
+
+bool SyncPool::ClientWait(AgingSyncObject* agingSyncObject)
+{
+  if(DALI_LIKELY(agingSyncObject != nullptr))
+  {
+    return agingSyncObject->ClientWait();
   }
   return false;
 }
 
 void SyncPool::FreeSyncObject(AgingSyncObject* agingSyncObject)
 {
-  auto iter = std::find_if(mSyncObjects.begin(), mSyncObjects.end(), [&agingSyncObject](AgingSyncPtrRef agingSyncPtr) { return agingSyncPtr.get() == agingSyncObject; });
-  if(iter != mSyncObjects.end())
+  if(DALI_LIKELY(agingSyncObject != nullptr))
   {
-    iter->reset();
+    // Release memory of sync object
+    mSyncObjects.EraseObject(agingSyncObject);
   }
 }
 
@@ -161,31 +199,28 @@ void SyncPool::FreeSyncObject(AgingSyncObject* agingSyncObject)
  */
 void SyncPool::AgeSyncObjects()
 {
-  DALI_LOG_INFO(gLogSyncFilter, Debug::Verbose, "AgeSyncObjects: count: %d\n", mSyncObjects.size());
+  DALI_LOG_INFO(gLogSyncFilter, Debug::Verbose, "AgeSyncObjects: count: %d\n", mSyncObjects.Count());
 
-  if(!mSyncObjects.empty())
+  if(!mSyncObjects.IsEmpty())
   {
     // Age the remaining sync objects.
-    for(auto& agingSyncObject : mSyncObjects)
+    for(auto iter = mSyncObjects.Begin(); iter != mSyncObjects.End();)
     {
-      if(agingSyncObject != nullptr && (agingSyncObject->glSyncObject != 0 || agingSyncObject->eglSyncObject != nullptr))
+      auto* agingSyncObject = (*iter);
+      if(agingSyncObject != nullptr && (agingSyncObject->glSyncObject != 0 || agingSyncObject->eglSyncObject != nullptr) && agingSyncObject->age > 0)
+      {
+        --agingSyncObject->age;
+        ++iter;
+      }
+      else
       {
-        if(agingSyncObject->age > 0)
-        {
-          agingSyncObject->age--;
-        }
-        else
-        {
-          agingSyncObject.reset();
-        }
+        // Release memory of sync object
+        iter = mSyncObjects.Erase(iter);
       }
     }
   }
-  // Move any old sync objects to the end of the list, and then remove them all.
-  mSyncObjects.erase(std::remove_if(mSyncObjects.begin(), mSyncObjects.end(), [&](std::unique_ptr<AgingSyncObject>& agingSyncObject) { return agingSyncObject == nullptr; }),
-                     mSyncObjects.end());
 
-  DALI_LOG_INFO(gLogSyncFilter, Debug::Verbose, "AgeSyncObjects: count after erase: %d\n", mSyncObjects.size());
+  DALI_LOG_INFO(gLogSyncFilter, Debug::Verbose, "AgeSyncObjects: count after erase: %d\n", mSyncObjects.Count());
 }
 
 } // namespace Dali::Graphics::GLES
index e92cdf0541fb95ebd4c6e7234db8330d47badcbc..7c25d933fecbbfa1278ae328d74a5a272155585b 100644 (file)
@@ -19,6 +19,7 @@
 
 #include <dali/graphics-api/graphics-types.h>
 #include <dali/integration-api/gl-abstraction.h>
+#include <dali/integration-api/ordered-set.h>
 #include <dali/public-api/common/vector-wrapper.h>
 
 namespace Dali
@@ -52,6 +53,7 @@ struct AgingSyncObject
   bool    syncing{false};
   bool    egl{false};
 
+  bool IsSynced();
   void Wait();
   bool ClientWait();
 };
@@ -86,6 +88,13 @@ public:
    */
   AgingSyncObject* AllocateSyncObject(const Context* writeContext, SyncContext syncContext);
 
+  /**
+   * Check whether given object is synced in the CPU
+   * @param syncPoolObject The object to check synced.
+   * @return true if the sync object was signaled, false if it timed out
+   */
+  bool IsSynced(AgingSyncObject* syncPoolObject);
+
   /**
    * Wait on a sync object in any context in the GPU
    * @param syncPoolObject The object to wait on.
@@ -112,8 +121,8 @@ public:
   void AgeSyncObjects();
 
 private:
-  std::vector<std::unique_ptr<AgingSyncObject>> mSyncObjects;
-  EglGraphicsController&                        mController;
+  Dali::Integration::OrderedSet<AgingSyncObject> mSyncObjects; ///< The list of sync objects in this pool (owned)
+  EglGraphicsController&                         mController;
 };
 
 } // namespace GLES
index 0ce3e445f584d91bdd3e1388765348e66608b707..3260ec7678ebacfa95927c97e208b779c58415b9 100644 (file)
@@ -27,9 +27,33 @@ extern Debug::Filter* gLogSyncFilter;
 
 namespace Dali::Graphics::GLES
 {
+TextureDependencyChecker::~TextureDependencyChecker()
+{
+  if(DALI_LIKELY(!Dali::Graphics::EglGraphicsController::IsShuttingDown()))
+  {
+    for(auto& textureDependency : mFramebufferTextureDependencies)
+    {
+      for(auto texture : textureDependency.textures)
+      {
+        texture->SetDependencyIndex(0xffffffff);
+      }
+      mController.GetSyncPool().FreeSyncObject(textureDependency.agingSyncObject);
+    }
+    mFramebufferTextureDependencies.clear();
+
+    for(uint32_t nativeIndex = 0u; nativeIndex < 2u; ++nativeIndex)
+    {
+      for(auto& nativeTextureDependency : mNativeTextureDependencies[nativeIndex])
+      {
+        mController.GetSyncPool().FreeSyncObject(nativeTextureDependency.agingSyncObject);
+      }
+      mNativeTextureDependencies[nativeIndex].clear();
+    }
+  }
+}
 void TextureDependencyChecker::Reset()
 {
-  for(auto& textureDependency : mTextureDependencies)
+  for(auto& textureDependency : mFramebufferTextureDependencies)
   {
     for(auto texture : textureDependency.textures)
     {
@@ -40,14 +64,28 @@ void TextureDependencyChecker::Reset()
       mController.GetSyncPool().FreeSyncObject(textureDependency.agingSyncObject);
     }
   }
-  mTextureDependencies.clear();
+  mFramebufferTextureDependencies.clear();
+
+  if(mNativeTextureDependencies[0].size() > 0 || mNativeTextureDependencies[1].size())
+  {
+    DALI_ASSERT_ALWAYS(mIsFirstPreparedNativeTextureDependency && "CreateNativeTextureSync should be called before PostRender!");
+
+    // Remove all infomations about previous native textures
+    for(auto& nativeTextureDependency : mNativeTextureDependencies[mPreviousNativeTextureDependencyIndex])
+    {
+      mController.GetSyncPool().FreeSyncObject(nativeTextureDependency.agingSyncObject);
+    }
+    mNativeTextureDependencies[mPreviousNativeTextureDependencyIndex].clear();
+
+    mCurrentNativeTextureDependencyIndex = __sync_fetch_and_xor(&mPreviousNativeTextureDependencyIndex, 1);
+  }
 }
 
 void TextureDependencyChecker::AddTextures(const GLES::Context* writeContext, const GLES::Framebuffer* framebuffer)
 {
-  uint32_t index = mTextureDependencies.size();
-  mTextureDependencies.emplace_back();
-  TextureDependency& textureDependency = mTextureDependencies.back();
+  uint32_t index = mFramebufferTextureDependencies.size();
+  mFramebufferTextureDependencies.emplace_back();
+  auto& textureDependency = mFramebufferTextureDependencies.back();
 
   for(int i = 0; i < 3; ++i)
   {
@@ -87,9 +125,9 @@ void TextureDependencyChecker::AddTextures(const GLES::Context* writeContext, co
 void TextureDependencyChecker::CheckNeedsSync(const GLES::Context* readContext, const GLES::Texture* texture, bool cpu)
 {
   uint32_t dependencyIndex = texture->GetDependencyIndex();
-  if(dependencyIndex < mTextureDependencies.size())
+  if(dependencyIndex < mFramebufferTextureDependencies.size())
   {
-    auto& textureDependency = mTextureDependencies[dependencyIndex];
+    auto& textureDependency = mFramebufferTextureDependencies[dependencyIndex];
     if(!textureDependency.syncing && textureDependency.writeContext != readContext)
     {
       // Needs syncing!
@@ -110,6 +148,114 @@ void TextureDependencyChecker::CheckNeedsSync(const GLES::Context* readContext,
       }
     }
   }
+
+  // Native dependency check
+  if(texture->IsNativeTexture())
+  {
+    // TODO : Optimize here. For now, we don't have too much EndPass call. So just keep this logic.
+    for(auto& nativeTextureDependency : mNativeTextureDependencies[mPreviousNativeTextureDependencyIndex])
+    {
+      if(nativeTextureDependency.synced)
+      {
+        // Fast-out if we know it is already synced
+        continue;
+      }
+
+      auto iter = nativeTextureDependency.textures.find(texture);
+      if(iter != nativeTextureDependency.textures.end())
+      {
+        if(nativeTextureDependency.agingSyncObject != nullptr)
+        {
+          if(cpu)
+          {
+            DALI_LOG_INFO(gLogSyncFilter, Debug::Verbose, "TextureDependencyChecker::CheckNeedsSync (for native) Insert CPU WAIT");
+            nativeTextureDependency.synced = mController.GetSyncPool().ClientWait(nativeTextureDependency.agingSyncObject);
+          }
+          else
+          {
+            // Wait on the sync object in GPU. This will ensure that the writeContext completes its tasks prior
+            // to the sync point.
+            // However, this may instead timeout, and we can't tell the difference (at least, for glFenceSync)
+            DALI_LOG_INFO(gLogSyncFilter, Debug::Verbose, "TextureDependencyChecker::CheckNeedsSync (for native) Insert GPU WAIT");
+            mController.GetSyncPool().Wait(nativeTextureDependency.agingSyncObject);
+          }
+        }
+
+        nativeTextureDependency.textures.erase(iter);
+      }
+    }
+  }
+}
+
+void TextureDependencyChecker::MarkNativeTexturePrepared(const GLES::Texture* texture)
+{
+  if(DALI_LIKELY(texture->IsNativeTexture()))
+  {
+    if(mIsFirstPreparedNativeTextureDependency)
+    {
+      mNativeTextureDependencies[mCurrentNativeTextureDependencyIndex].emplace_back();
+      mIsFirstPreparedNativeTextureDependency = false;
+    }
+
+    if(DALI_LIKELY(!mNativeTextureDependencies[mCurrentNativeTextureDependencyIndex].empty()))
+    {
+      auto& nativeTextureDependency = mNativeTextureDependencies[mCurrentNativeTextureDependencyIndex].back();
+      nativeTextureDependency.textures.insert(texture);
+    }
+  }
+}
+
+void TextureDependencyChecker::DiscardNativeTexture(const GLES::Texture* texture)
+{
+  if(DALI_LIKELY(texture->IsNativeTexture()))
+  {
+    for(uint32_t nativeIndex = 0u; nativeIndex < 2u; ++nativeIndex)
+    {
+      for(auto iter = mNativeTextureDependencies[nativeIndex].begin(); iter != mNativeTextureDependencies[nativeIndex].end();)
+      {
+        auto& nativeTextureDependency = *iter;
+
+        bool isErased = false;
+
+        auto jter = nativeTextureDependency.textures.find(texture);
+        if(jter != nativeTextureDependency.textures.end())
+        {
+          nativeTextureDependency.textures.erase(jter);
+          if(nativeTextureDependency.textures.empty())
+          {
+            mController.GetSyncPool().FreeSyncObject(nativeTextureDependency.agingSyncObject);
+            iter = mNativeTextureDependencies[nativeIndex].erase(iter);
+
+            isErased = true;
+          }
+        }
+
+        if(!isErased)
+        {
+          ++iter;
+        }
+      }
+    }
+  }
+}
+
+void TextureDependencyChecker::CreateNativeTextureSync(const GLES::Context* writeContext)
+{
+  if(mIsFirstPreparedNativeTextureDependency)
+  {
+    return;
+  }
+
+  // Reset flag
+  mIsFirstPreparedNativeTextureDependency = true;
+
+  if(DALI_LIKELY(!mNativeTextureDependencies[mCurrentNativeTextureDependencyIndex].empty()))
+  {
+    auto& nativeTextureDependency = mNativeTextureDependencies[mCurrentNativeTextureDependencyIndex].back();
+
+    DALI_LOG_INFO(gLogSyncFilter, Debug::Verbose, "TextureDependencyChecker::CreateNativeTextureSync() Allocating sync object\n");
+    nativeTextureDependency.agingSyncObject = mController.GetSyncPool().AllocateSyncObject(writeContext, SyncPool::SyncContext::EGL);
+  }
 }
 
 } // namespace Dali::Graphics::GLES
index fd19c1f4b2b684a74795ff0378bbdb21214ac7f8..4a2b013f73c2724b35f5ea46786aea1a262d17f7 100644 (file)
@@ -19,6 +19,7 @@
 
 // EXTERNAL INCLUDES
 #include <cstddef>
+#include <unordered_set>
 
 // INTERNAL INCLUDES
 #include <dali/public-api/common/vector-wrapper.h>
@@ -48,10 +49,15 @@ class TextureDependencyChecker
 {
 public:
   explicit TextureDependencyChecker(EglGraphicsController& controller)
-  : mController(controller)
+  : mController(controller),
+    mCurrentNativeTextureDependencyIndex(0u),
+    mPreviousNativeTextureDependencyIndex(1u),
+    mIsFirstPreparedNativeTextureDependency(true)
   {
   }
 
+  ~TextureDependencyChecker();
+
   /**
    * Clear all the textures. Call at the start of a frame
    */
@@ -78,22 +84,62 @@ public:
   /**
    * Get the number of (offscreen) textures for dependency checking
    */
-  size_t GetTextureCount() const
+  size_t GetFramebufferTextureCount() const
   {
-    return mTextureDependencies.size();
+    return mFramebufferTextureDependencies.size();
   }
 
+  /**
+   * Get the number of (offscreen) textures for dependency checking
+   */
+  size_t GetNativeTextureCount() const
+  {
+    return mNativeTextureDependencies[mCurrentNativeTextureDependencyIndex].size();
+  }
+
+public: ///< For NativeTexture dependency checker
+  /**
+   * @brief Add prepared native image texture to dependency list
+   */
+  void MarkNativeTexturePrepared(const Texture* texture);
+
+  /**
+   * @brief Remove native image texture from dependency list.
+   * It will be called at discarding texture.
+   */
+  void DiscardNativeTexture(const Texture* texture);
+
+  /**
+   * @brief Create Sync object for native images.
+   * It will be called at EndRenderPass.
+   */
+  void CreateNativeTextureSync(const Context* writeContext);
+
 private:
-  struct TextureDependency
+  struct FramebufferTextureDependency
   {
     std::vector<Texture*> textures;
     Context*              writeContext{nullptr};
     Framebuffer*          framebuffer{nullptr};
-    AgingSyncObject*      agingSyncObject;
+    AgingSyncObject*      agingSyncObject{nullptr};
     bool                  syncing{false};
   };
-  std::vector<TextureDependency> mTextureDependencies;
-  EglGraphicsController&         mController;
+  std::vector<FramebufferTextureDependency> mFramebufferTextureDependencies;
+
+  struct NativeTextureDependency
+  {
+    std::unordered_set<const Texture*> textures;
+    AgingSyncObject*                   agingSyncObject{nullptr};
+    bool                               synced{false};
+  };
+  std::vector<NativeTextureDependency> mNativeTextureDependencies[2];
+
+  EglGraphicsController& mController;
+
+  uint32_t mCurrentNativeTextureDependencyIndex;  // 0 or 1, toggled every frame
+  uint32_t mPreviousNativeTextureDependencyIndex; // 0 or 1, toggled every frame
+
+  bool mIsFirstPreparedNativeTextureDependency : 1;
 };
 
 } // namespace GLES