Changed FBOs to use EglSync 55/305855/9
authorDavid Steele <david.steele@samsung.com>
Wed, 7 Feb 2024 11:46:13 +0000 (11:46 +0000)
committerDavid Steele <david.steele@samsung.com>
Wed, 14 Feb 2024 17:14:04 +0000 (17:14 +0000)
For texture dependencies between shared context and render surface
context (i.e. Window/Scene surface), e.g. when an offscreen is
rendered on-screen, have changed to use EglClientWaitSync with
timeout = FOREVER.

This blocks the CPU when sending draw calls to the GPU where
that draw call uses an offscreen texture. However, in testing
gaussian-blur-view.example, this lasts approx 10ms on some frames
and 16/17 ms on others, yielding an FPS of 41.

Changed egl-sync-implementation.cpp to have multiple versions
of the file for each platform, rather than old-style #ifdefs.

(Todo: could check if the offscreen was re-rendered this frame, as
there is no need to sync if it hasn't changed. But, client can use
RENDER_ONCE to force this behaviour).

Change-Id: I64eaf82c5203e6acb6e0fc76b0f0e36136defcdf

20 files changed:
automated-tests/src/dali-adaptor/dali-test-suite-utils/test-graphics-sync-impl.cpp
automated-tests/src/dali-adaptor/dali-test-suite-utils/test-graphics-sync-impl.h
automated-tests/src/dali-adaptor/dali-test-suite-utils/test-graphics-sync-object.h
dali/internal/graphics/android/egl-sync-implementation-android.cpp [new file with mode: 0644]
dali/internal/graphics/common/egl-include.h
dali/internal/graphics/file.list
dali/internal/graphics/generic/egl-sync-implementation.cpp [new file with mode: 0644]
dali/internal/graphics/gles-impl/egl-sync-object.cpp
dali/internal/graphics/gles-impl/egl-sync-object.h
dali/internal/graphics/gles-impl/gles-context.cpp
dali/internal/graphics/gles-impl/gles-sync-object.cpp
dali/internal/graphics/gles-impl/gles-sync-object.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
dali/internal/graphics/gles/egl-sync-implementation.h
dali/internal/graphics/macos/egl-sync-implementation-macos.cpp [new file with mode: 0644]
dali/internal/graphics/tizen/egl-sync-implementation-tizen.cpp [moved from dali/internal/graphics/gles/egl-sync-implementation.cpp with 70% similarity]
dali/internal/graphics/windows-gl/egl-sync-implementation-windows.cpp [new file with mode: 0644]

index 17f53a7..bb4a500 100644 (file)
@@ -38,6 +38,16 @@ bool TestSyncObject::IsSynced()
   return synced;
 }
 
+void TestSyncObject::Wait()
+{
+  mTrace.PushCall("SyncObject::Wait", ""); // Trace the method
+}
+
+void TestSyncObject::ClientWait()
+{
+  mTrace.PushCall("SyncObject::ClientWait", ""); // Trace the method
+}
+
 TestGraphicsSyncImplementation::TestGraphicsSyncImplementation()
 {
   Initialize();
index cbc5331..21fd358 100644 (file)
@@ -2,7 +2,7 @@
 #define TEST_SYNC_IMPLEMENTATION_H
 
 /*
- * Copyright (c) 2021 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.
@@ -40,7 +40,10 @@ class TestSyncObject : public Integration::GraphicsSyncAbstraction::SyncObject
 public:
   TestSyncObject(TraceCallStack& trace);
   ~TestSyncObject() override;
-  bool            IsSynced() override;
+  bool IsSynced() override;
+  void Wait() override;
+  void ClientWait() override;
+
   bool            synced;
   TraceCallStack& mTrace;
 };
index c33de6c..6886687 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_TEST_GRAPHICS_SYNC_OBJECT_H_
 
 /*
- * Copyright (c) 2021 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.
@@ -19,8 +19,7 @@
 
 #include <dali/graphics-api/graphics-sync-object-create-info.h>
 #include <dali/graphics-api/graphics-sync-object.h>
-
-#include <test-graphics-sync-impl.h>
+#include "test-graphics-sync-impl.h"
 
 namespace Dali
 {
diff --git a/dali/internal/graphics/android/egl-sync-implementation-android.cpp b/dali/internal/graphics/android/egl-sync-implementation-android.cpp
new file mode 100644 (file)
index 0000000..df12970
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ * 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.
+ * 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 <dali/internal/graphics/gles/egl-sync-implementation.h>
+
+// EXTERNAL INCLUDES
+
+#include <dali/integration-api/debug.h>
+
+// INTERNAL INCLUDES
+#include <dali/internal/graphics/gles/egl-debug.h>
+#include <dali/internal/graphics/gles/egl-implementation.h>
+
+#if defined(DEBUG_ENABLED)
+Debug::Filter* gLogSyncFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_FENCE_SYNC");
+#endif
+
+namespace Dali
+{
+namespace Internal
+{
+namespace Adaptor
+{
+EglSyncObject::EglSyncObject(EglImplementation& eglImpl)
+: mPollCounter(3),
+  mEglImplementation(eglImpl)
+{
+}
+
+EglSyncObject::~EglSyncObject()
+{
+}
+
+bool EglSyncObject::IsSynced()
+{
+  return true;
+}
+
+void EglSyncObject::Wait()
+{
+}
+
+void EglSyncObject::ClientWait()
+{
+}
+
+EglSyncImplementation::EglSyncImplementation()
+: mEglImplementation(NULL),
+  mSyncInitialized(false),
+  mSyncInitializeFailed(false)
+{
+}
+
+EglSyncImplementation::~EglSyncImplementation()
+{
+}
+
+void EglSyncImplementation::Initialize(EglImplementation* eglImpl)
+{
+  mEglImplementation = eglImpl;
+}
+
+Integration::GraphicsSyncAbstraction::SyncObject* EglSyncImplementation::CreateSyncObject()
+{
+  DALI_ASSERT_ALWAYS(mEglImplementation && "Sync Implementation not initialized");
+  return new EglSyncObject(*mEglImplementation);
+}
+
+void EglSyncImplementation::DestroySyncObject(Integration::GraphicsSyncAbstraction::SyncObject* syncObject)
+{
+  DALI_ASSERT_ALWAYS(mEglImplementation && "Sync Implementation not initialized");
+  delete static_cast<EglSyncObject*>(syncObject);
+}
+
+void EglSyncImplementation::InitializeEglSync()
+{
+}
+
+} // namespace Adaptor
+} // namespace Internal
+} // namespace Dali
index 96ce884..905291c 100644 (file)
@@ -5,6 +5,8 @@
 #include <EGL/egl.h>
 
 // Undef unneded symbols that fail to compile on MS Windows
+#if defined(_WIN32) || defined(__VC32__) && !defined(__CYGWIN__) /* Win32 and WinCE */
+
 #undef ERROR
 
 #undef OPAQUE
@@ -20,6 +22,7 @@
 #undef max
 
 #undef DIFFERENCE
+#endif
 
 #include <EGL/eglext.h>
 
index 8041345..3f8b5dd 100644 (file)
@@ -3,7 +3,6 @@
 SET( adaptor_graphics_gles_src_files
     ${adaptor_graphics_dir}/gles/egl-debug.cpp
     ${adaptor_graphics_dir}/gles/egl-implementation.cpp
-    ${adaptor_graphics_dir}/gles/egl-sync-implementation.cpp
     ${adaptor_graphics_dir}/gles/gl-extensions.cpp
     ${adaptor_graphics_dir}/gles/gl-extensions-support.cpp
     ${adaptor_graphics_dir}/gles/gl-implementation.cpp
@@ -17,31 +16,37 @@ INCLUDE( ${adaptor_graphics_dir}/gles-impl/file.list )
 # module: graphics, backend: tizen
 SET( adaptor_graphics_tizen_src_files
     ${adaptor_graphics_dir}/tizen/egl-image-extensions-tizen.cpp
+    ${adaptor_graphics_dir}/tizen/egl-sync-implementation-tizen.cpp
 )
 
 # module: graphics, backend: ubuntu
 SET( adaptor_graphics_ubuntu_src_files
     ${adaptor_graphics_dir}/generic/egl-image-extensions-generic.cpp
+    ${adaptor_graphics_dir}/generic/egl-sync-implementation.cpp
 )
 
 # module: graphics, backend: libuv-x11
 SET( adaptor_graphics_x11_src_files
     ${adaptor_graphics_dir}/generic/egl-image-extensions-generic.cpp
+    ${adaptor_graphics_dir}/generic/egl-sync-implementation.cpp
 )
 
 # module: graphics, backend: android
 SET( adaptor_graphics_android_src_files
     ${adaptor_graphics_dir}/android/egl-image-extensions-android.cpp
+    ${adaptor_graphics_dir}/android/egl-sync-implementation-android.cpp
 )
 
 # module: graphics, backend: windows
 SET( adaptor_graphics_windows_src_files
     ${adaptor_graphics_dir}/windows-gl/egl-image-extensions.cpp
+    ${adaptor_graphics_dir}/windows/egl-sync-implementation-windows.cpp
 )
 
 # module: graphics, backend: macos
 SET( adaptor_graphics_macos_src_files
     ${adaptor_graphics_dir}/macos/egl-image-extensions.cpp
+    ${adaptor_graphics_dir}/macos/egl-sync-implementation-macos.cpp
 )
 
 # include GLES implementation
diff --git a/dali/internal/graphics/generic/egl-sync-implementation.cpp b/dali/internal/graphics/generic/egl-sync-implementation.cpp
new file mode 100644 (file)
index 0000000..842407f
--- /dev/null
@@ -0,0 +1,143 @@
+/*
+ * 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.
+ * 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 <dali/internal/graphics/gles/egl-sync-implementation.h>
+
+// EXTERNAL INCLUDES
+
+#include <dali/integration-api/debug.h>
+
+// INTERNAL INCLUDES
+#include <dali/internal/graphics/gles/egl-debug.h>
+#include <dali/internal/graphics/gles/egl-implementation.h>
+
+#if defined(DEBUG_ENABLED)
+Debug::Filter* gLogSyncFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_FENCE_SYNC");
+#endif
+
+namespace Dali
+{
+namespace Internal
+{
+namespace Adaptor
+{
+EglSyncObject::EglSyncObject(EglImplementation& eglImpl)
+: mPollCounter(3),
+  mEglImplementation(eglImpl)
+{
+  EGLDisplay display = mEglImplementation.GetDisplay();
+  mEglSync           = eglCreateSync(display, EGL_SYNC_FENCE, NULL);
+}
+
+EglSyncObject::~EglSyncObject()
+{
+  if(mEglSync && mEglImplementation.IsGlesInitialized())
+  {
+    EGLDisplay display = mEglImplementation.GetDisplay();
+    eglDestroySync(display, mEglSync);
+  }
+}
+
+bool EglSyncObject::IsSynced()
+{
+  DALI_LOG_INFO(gLogSyncFilter, Debug::General, "eglClientWaitSync\n");
+  auto result = eglClientWaitSync(mEglImplementation.GetDisplay(), mEglSync, 0 | EGL_SYNC_FLUSH_COMMANDS_BIT, 0);
+
+  if(result == EGL_FALSE)
+  {
+    EGLint error = eglGetError();
+    if(EGL_SUCCESS != error)
+    {
+      DALI_LOG_ERROR("eglClientSyncWait failed: %#0.4x\n", error);
+    }
+  }
+  else if(result == EGL_CONDITION_SATISFIED)
+  {
+    DALI_LOG_INFO(gLogSyncFilter, Debug::General, "eglClientWaitSync Synced!\n");
+    return true;
+  }
+  DALI_LOG_INFO(gLogSyncFilter, Debug::General, "eglClientWaitSync not synced :(\n");
+  return false;
+}
+
+void EglSyncObject::Wait()
+{
+  DALI_LOG_INFO(gLogSyncFilter, Debug::General, "eglWaitSync\n");
+  if(!eglWaitSync(mEglImplementation.GetDisplay(), mEglSync, 0))
+  {
+    EGLint error = eglGetError();
+    if(EGL_SUCCESS != error)
+    {
+      DALI_LOG_ERROR("eglSyncWait failed: %#0.4x\n", error);
+    }
+  }
+}
+
+void EglSyncObject::ClientWait()
+{
+  DALI_LOG_INFO(gLogSyncFilter, Debug::General, "eglWaitSync (blocking)\n");
+  auto result = eglClientWaitSync(mEglImplementation.GetDisplay(), mEglSync, EGL_SYNC_FLUSH_COMMANDS_BIT_KHR, EGL_FOREVER);
+  if(result == EGL_FALSE)
+  {
+    EGLint error = eglGetError();
+    if(EGL_SUCCESS != error)
+    {
+      DALI_LOG_ERROR("eglSyncWait failed: %#0.4x\n", error);
+    }
+  }
+  else if(result == EGL_CONDITION_SATISFIED)
+  {
+    DALI_LOG_INFO(gLogSyncFilter, Debug::General, "eglClientWaitSync Synced!\n");
+  }
+}
+
+EglSyncImplementation::EglSyncImplementation()
+: mEglImplementation(NULL),
+  mSyncInitialized(false),
+  mSyncInitializeFailed(false)
+{
+}
+
+EglSyncImplementation::~EglSyncImplementation()
+{
+}
+
+void EglSyncImplementation::Initialize(EglImplementation* eglImpl)
+{
+  mEglImplementation = eglImpl;
+}
+
+Integration::GraphicsSyncAbstraction::SyncObject* EglSyncImplementation::CreateSyncObject()
+{
+  DALI_ASSERT_ALWAYS(mEglImplementation && "Sync Implementation not initialized");
+  return new EglSyncObject(*mEglImplementation);
+}
+
+void EglSyncImplementation::DestroySyncObject(Integration::GraphicsSyncAbstraction::SyncObject* syncObject)
+{
+  DALI_ASSERT_ALWAYS(mEglImplementation && "Sync Implementation not initialized");
+  delete static_cast<EglSyncObject*>(syncObject);
+}
+
+void EglSyncImplementation::InitializeEglSync()
+{
+}
+
+} // namespace Adaptor
+} // namespace Internal
+} // namespace Dali
index 14410b4..44dd8f4 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2021 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.
index d014a7f..05ad261 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_GRAPHICS_EGL_SYNC_OBJECT_H
 
 /*
- * Copyright (c) 2021 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.
@@ -24,6 +24,7 @@
 // INTERNAL INCLUDES
 #include <dali/internal/graphics/gles-impl/gles-graphics-resource.h>
 
+//#include <dali/internal/graphics/gles/egl-sync-implementation.h>
 namespace Dali::Internal::Adaptor
 {
 class EglSyncImplementation;
@@ -34,6 +35,9 @@ namespace Dali::Graphics::EGL
 {
 using SyncObjectResource = GLES::Resource<Graphics::SyncObject, Graphics::SyncObjectCreateInfo>;
 
+/**
+ * Proxy to EglSyncObject that also implements a graphics resource
+ */
 class SyncObject : public SyncObjectResource
 {
 public:
index b7e2b5e..c90b2a8 100644 (file)
@@ -378,8 +378,8 @@ void Context::Flush(bool reset, const GLES::DrawCallDescriptor& drawCall, GLES::
       texture->InitializeResource();
     }
 
-    // Warning, this may cause glWaitSync to occur on the GPU.
-    dependencyChecker.CheckNeedsSync(this, texture);
+    // 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();
 
index f44fdc3..1d1bc11 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2021 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.
index 32a89f6..37d0a81 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_GRAPHICS_GLES_SYNC_OBJECT_H
 
 /*
- * Copyright (c) 2021 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.
@@ -35,6 +35,9 @@ namespace Dali::Graphics::GLES
 {
 using SyncObjectResource = Resource<Graphics::SyncObject, Graphics::SyncObjectCreateInfo>;
 
+/**
+ * Class that maintains a glFenceSync object.
+ */
 class SyncObject : public SyncObjectResource
 {
 public:
index 770deb0..f02e48c 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * 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.
 
 // Internal Headers
 #include <dali/internal/graphics/gles-impl/egl-graphics-controller.h>
+#include <dali/internal/graphics/gles/egl-sync-implementation.h>
+
+#if defined(DEBUG_ENABLED)
+extern Debug::Filter* gLogSyncFilter;
+#endif
 
 namespace Dali::Graphics::GLES
 {
-AgingSyncObject::AgingSyncObject(Graphics::EglGraphicsController& controller, const Context* writeContext)
+AgingSyncObject::AgingSyncObject(Graphics::EglGraphicsController& controller, const Context* writeContext, bool _egl)
 : controller(controller),
-  writeContext(writeContext)
+  writeContext(writeContext),
+  egl(_egl)
 {
-  auto gl = controller.GetGL();
-  if(gl)
+  if(egl)
+  {
+    eglSyncObject = static_cast<Internal::Adaptor::EglSyncObject*>(controller.GetEglSyncImplementation().CreateSyncObject());
+    DALI_LOG_INFO(gLogSyncFilter, Debug::Verbose, "AgingSyncObject::cons; EGL::CreateSyncObject: %p\n", eglSyncObject);
+  }
+  else
   {
-    glSyncObject = gl->FenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
+    auto gl = controller.GetGL();
+    if(gl)
+    {
+      glSyncObject = gl->FenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
+    }
   }
 }
 
 AgingSyncObject::~AgingSyncObject()
 {
-  auto gl = controller.GetGL();
-  if(gl && glSyncObject != nullptr)
+  if(!controller.IsShuttingDown())
   {
-    gl->DeleteSync(glSyncObject);
+    if(egl)
+    {
+      DALI_LOG_INFO(gLogSyncFilter, Debug::Verbose, "AgingSyncObject::dstr; EGL::DestroySyncObject: %p\n", eglSyncObject);
+      controller.GetEglSyncImplementation().DestroySyncObject(eglSyncObject);
+    }
+    else
+    {
+      auto gl = controller.GetGL();
+      if(gl && glSyncObject != nullptr)
+      {
+        gl->DeleteSync(glSyncObject);
+      }
+    }
+  }
+}
+
+bool AgingSyncObject::ClientWait()
+{
+  bool synced = false;
+  if(egl)
+  {
+    if(eglSyncObject)
+    {
+      DALI_LOG_INFO(gLogSyncFilter, Debug::Verbose, "AgingSyncObject::ClientWait(); EGL::ClientWaitSync\n");
+      eglSyncObject->ClientWait();
+      synced = true;
+    }
+  }
+  else
+  {
+    if(glSyncObject)
+    {
+      DALI_LOG_INFO(gLogSyncFilter, Debug::Verbose, "AgingSyncObject::ClientWait(); glClientWaitSync 1ms\n");
+      const GLuint64 TIMEOUT = 1000000; //1ms!
+      auto           gl      = controller.GetGL();
+      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::ClientWait(); Result: %s\n", synced ? "Synced" : "NOT SYNCED");
+  return synced;
+}
+
+void AgingSyncObject::Wait()
+{
+  if(egl)
+  {
+    if(eglSyncObject)
+    {
+      DALI_LOG_INFO(gLogSyncFilter, Debug::Verbose, "AgingSyncObject::Wait(); EGL::WaitSync\n");
+      eglSyncObject->Wait();
+    }
+  }
+  else
+  {
+    if(glSyncObject)
+    {
+      DALI_LOG_INFO(gLogSyncFilter, Debug::Verbose, "AgingSyncObject::Wait(); glWaitSync\n");
+      auto gl = controller.GetGL();
+      gl->WaitSync(glSyncObject, 0, 0ull);
+    }
   }
 }
 
 SyncPool::~SyncPool() = default;
 
-AgingSyncObject* SyncPool::AllocateSyncObject(const Context* writeContext)
+AgingSyncObject* SyncPool::AllocateSyncObject(const Context* writeContext, SyncPool::SyncContext syncContext)
 {
-  std::unique_ptr<AgingSyncObject> syncObject = std::make_unique<AgingSyncObject>(mController, writeContext);
+  std::unique_ptr<AgingSyncObject> syncObject = std::make_unique<AgingSyncObject>(mController, writeContext, (syncContext == SyncContext::EGL));
   mSyncObjects.push_back(std::move(syncObject));
   return mSyncObjects.back().get();
 }
 
 void SyncPool::Wait(AgingSyncObject* syncPoolObject)
 {
-  auto gl = mController.GetGL();
-  if(gl && syncPoolObject->glSyncObject != nullptr)
+  if(syncPoolObject != nullptr)
   {
     syncPoolObject->syncing = true;
-    gl->WaitSync(syncPoolObject->glSyncObject, 0, GL_TIMEOUT_IGNORED);
+    syncPoolObject->Wait();
+  }
+}
+
+bool SyncPool::ClientWait(AgingSyncObject* syncPoolObject)
+{
+  if(syncPoolObject)
+  {
+    return syncPoolObject->ClientWait();
   }
+  return false;
 }
 
 void SyncPool::FreeSyncObject(AgingSyncObject* agingSyncObject)
@@ -79,12 +161,14 @@ void SyncPool::FreeSyncObject(AgingSyncObject* agingSyncObject)
  */
 void SyncPool::AgeSyncObjects()
 {
+  DALI_LOG_INFO(gLogSyncFilter, Debug::Verbose, "AgeSyncObjects: count: %d\n", mSyncObjects.size());
+
   if(!mSyncObjects.empty())
   {
     // Age the remaining sync objects.
     for(auto& agingSyncObject : mSyncObjects)
     {
-      if(agingSyncObject != nullptr && agingSyncObject->glSyncObject != 0)
+      if(agingSyncObject != nullptr && (agingSyncObject->glSyncObject != 0 || agingSyncObject->eglSyncObject != nullptr))
       {
         if(agingSyncObject->age > 0)
         {
@@ -100,6 +184,8 @@ void SyncPool::AgeSyncObjects()
   // 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());
 }
 
 } // namespace Dali::Graphics::GLES
index d394b03..e92cdf0 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_GRAPHICS_GLES_SYNC_POOL_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.
 #include <dali/integration-api/gl-abstraction.h>
 #include <dali/public-api/common/vector-wrapper.h>
 
-namespace Dali::Graphics
+namespace Dali
+{
+namespace Internal::Adaptor
+{
+class EglSyncObject;
+}
+
+namespace Graphics
 {
 class EglGraphicsController;
 
@@ -31,14 +38,22 @@ class Context;
 
 struct AgingSyncObject
 {
-  AgingSyncObject(Graphics::EglGraphicsController& controller, const Context* writeContext);
+  AgingSyncObject(Graphics::EglGraphicsController& controller, const Context* writeContext, bool egl = false);
   ~AgingSyncObject();
 
   EglGraphicsController& controller;
   const Context*         writeContext;
-  GLsync                 glSyncObject{0};
-  uint8_t                age{2};
-  bool                   syncing{false};
+  union
+  {
+    GLsync                            glSyncObject;
+    Internal::Adaptor::EglSyncObject* eglSyncObject;
+  };
+  uint8_t age{2};
+  bool    syncing{false};
+  bool    egl{false};
+
+  void Wait();
+  bool ClientWait();
 };
 using AgingSyncPtrRef = std::unique_ptr<AgingSyncObject>&;
 
@@ -51,6 +66,12 @@ using AgingSyncPtrRef = std::unique_ptr<AgingSyncObject>&;
 class SyncPool
 {
 public:
+  enum class SyncContext
+  {
+    EGL, ///< Use EGL sync when syncing between multiple contexts
+    GL   ///< Use GL sync when syncing in the same context
+  };
+
   explicit SyncPool(Graphics::EglGraphicsController& graphicsController)
   : mController(graphicsController)
   {
@@ -63,19 +84,26 @@ public:
    * @param writeContext
    * @return An owned ptr to a sync object
    */
-  AgingSyncObject* AllocateSyncObject(const Context* writeContext);
+  AgingSyncObject* AllocateSyncObject(const Context* writeContext, SyncContext syncContext);
 
   /**
-   * Wait on a sync object in any context
+   * Wait on a sync object in any context in the GPU
    * @param syncPoolObject The object to wait on.
    */
   void Wait(AgingSyncObject* syncPoolObject);
 
   /**
+   * Wait on a sync object in any context in the CPU
+   * @param syncPoolObject The object to wait on.
+   * @return true if the sync object was signaled, false if it timed out
+   */
+  bool ClientWait(AgingSyncObject* syncPoolObject);
+
+  /**
    * Delete the sync object if it's not needed.
-   *
+   * @param syncPoolObject The object to delete.
    */
-  void FreeSyncObject(AgingSyncObject* agingSyncObject);
+  void FreeSyncObject(AgingSyncObject* syncPoolObject);
 
   /**
    * Age outstanding sync objects. Call at the end of each frame.
@@ -89,6 +117,7 @@ private:
 };
 
 } // namespace GLES
-} // namespace Dali::Graphics
+} // namespace Graphics
+} // namespace Dali
 
 #endif //DALI_GRAPHICS_GLES_SYNC_POOL_H
index cb0ed25..0ce3e44 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * 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.
 #include "gles-texture-dependency-checker.h"
 
 // EXTERNAL INCLUDES
+#include <dali/integration-api/debug.h>
 #include <dali/internal/graphics/gles-impl/egl-graphics-controller.h>
 
+#if defined(DEBUG_ENABLED)
+extern Debug::Filter* gLogSyncFilter;
+#endif
+
 namespace Dali::Graphics::GLES
 {
 void TextureDependencyChecker::Reset()
@@ -70,12 +75,16 @@ void TextureDependencyChecker::AddTextures(const GLES::Context* writeContext, co
       texture->SetDependencyIndex(index);
     }
   }
-  textureDependency.writeContext    = const_cast<GLES::Context*>(writeContext);
-  textureDependency.framebuffer     = const_cast<GLES::Framebuffer*>(framebuffer);
-  textureDependency.agingSyncObject = mController.GetSyncPool().AllocateSyncObject(writeContext);
+  textureDependency.writeContext = const_cast<GLES::Context*>(writeContext);
+  textureDependency.framebuffer  = const_cast<GLES::Framebuffer*>(framebuffer);
+
+  // We have to check on different EGL contexts: The shared resource context is used to write to fbos,
+  // but they are usually drawn onto separate scene context.
+  DALI_LOG_INFO(gLogSyncFilter, Debug::Verbose, "TextureDependencyChecker::AddTextures() Allocating sync object\n");
+  textureDependency.agingSyncObject = mController.GetSyncPool().AllocateSyncObject(writeContext, SyncPool::SyncContext::EGL);
 }
 
-void TextureDependencyChecker::CheckNeedsSync(const GLES::Context* readContext, const GLES::Texture* texture)
+void TextureDependencyChecker::CheckNeedsSync(const GLES::Context* readContext, const GLES::Texture* texture, bool cpu)
 {
   uint32_t dependencyIndex = texture->GetDependencyIndex();
   if(dependencyIndex < mTextureDependencies.size())
@@ -86,9 +95,19 @@ void TextureDependencyChecker::CheckNeedsSync(const GLES::Context* readContext,
       // Needs syncing!
       textureDependency.syncing = true;
 
-      // Wait on the sync object in GPU. This will ensure that the writeContext completes its tasks prior
-      // to the sync point.
-      mController.GetSyncPool().Wait(textureDependency.agingSyncObject);
+      if(cpu)
+      {
+        DALI_LOG_INFO(gLogSyncFilter, Debug::Verbose, "TextureDependencyChecker::CheckNeedsSync Insert CPU WAIT");
+        mController.GetSyncPool().ClientWait(textureDependency.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 Insert GPU WAIT");
+        mController.GetSyncPool().Wait(textureDependency.agingSyncObject);
+      }
     }
   }
 }
index 8861229..63b1127 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_GLES_TEXTURE_DEPENDENCY_CHECKER_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.
@@ -63,12 +63,21 @@ public:
 
   /**
    * Check if the given texture needs syncing before being read.  This
-   * will perform a glWaitSync() (GPU side semaphore) if the texture
-   * needs syncing.
+   * will perform either a glWaitSync() (GPU side semaphore), or a
+   * glClientWaitSync(CPU fence) if the texture needs syncing.
    * @param[in] readContext The context that the texture is being read (drawn with)
    * @param[in] texture The texture being read
+   * @param[in] cpuSync True if glClientWaitSync should be used instead of glWaitSync
    */
-  void CheckNeedsSync(const Context* readContext, const Texture* texture);
+  void CheckNeedsSync(const Context* readContext, const Texture* texture, bool cpuSync = false);
+
+  /**
+   * Get the number of (offscreen) textures for dependency checking
+   */
+  size_t GetTextureCount() const
+  {
+    return mTextureDependencies.size();
+  }
 
 private:
   struct TextureDependency
index 6a29ca9..b1bd6ad 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_INTERNAL_ADAPTOR_EGL_SYNC_IMPLEMENTATION_H
 
 /*
- * Copyright (c) 2021 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.
@@ -50,11 +50,24 @@ public:
 
   bool IsSynced() override;
 
+  /**
+   * Set up a GPU wait (returns immediately on CPU) for this sync. Can work across
+   * EGL contexts.
+   */
+  void Wait() override;
+
+  /**
+   * Wait on the CPU until the GPU executes this sync. Warning: could be a long time!
+   * Can work across EGL contexts.
+   */
+  void ClientWait() override;
+
 private:
 #ifdef _ARCH_ARM_
   EGLSyncKHR mEglSync;
 #else
-  int mPollCounter; // Implementations without fence sync use a 3 frame counter
+  EGLSync mEglSync;
+  int     mPollCounter; // Implementations without fence sync use a 3 frame counter
 #endif
   EglImplementation& mEglImplementation;
 };
diff --git a/dali/internal/graphics/macos/egl-sync-implementation-macos.cpp b/dali/internal/graphics/macos/egl-sync-implementation-macos.cpp
new file mode 100644 (file)
index 0000000..df12970
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ * 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.
+ * 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 <dali/internal/graphics/gles/egl-sync-implementation.h>
+
+// EXTERNAL INCLUDES
+
+#include <dali/integration-api/debug.h>
+
+// INTERNAL INCLUDES
+#include <dali/internal/graphics/gles/egl-debug.h>
+#include <dali/internal/graphics/gles/egl-implementation.h>
+
+#if defined(DEBUG_ENABLED)
+Debug::Filter* gLogSyncFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_FENCE_SYNC");
+#endif
+
+namespace Dali
+{
+namespace Internal
+{
+namespace Adaptor
+{
+EglSyncObject::EglSyncObject(EglImplementation& eglImpl)
+: mPollCounter(3),
+  mEglImplementation(eglImpl)
+{
+}
+
+EglSyncObject::~EglSyncObject()
+{
+}
+
+bool EglSyncObject::IsSynced()
+{
+  return true;
+}
+
+void EglSyncObject::Wait()
+{
+}
+
+void EglSyncObject::ClientWait()
+{
+}
+
+EglSyncImplementation::EglSyncImplementation()
+: mEglImplementation(NULL),
+  mSyncInitialized(false),
+  mSyncInitializeFailed(false)
+{
+}
+
+EglSyncImplementation::~EglSyncImplementation()
+{
+}
+
+void EglSyncImplementation::Initialize(EglImplementation* eglImpl)
+{
+  mEglImplementation = eglImpl;
+}
+
+Integration::GraphicsSyncAbstraction::SyncObject* EglSyncImplementation::CreateSyncObject()
+{
+  DALI_ASSERT_ALWAYS(mEglImplementation && "Sync Implementation not initialized");
+  return new EglSyncObject(*mEglImplementation);
+}
+
+void EglSyncImplementation::DestroySyncObject(Integration::GraphicsSyncAbstraction::SyncObject* syncObject)
+{
+  DALI_ASSERT_ALWAYS(mEglImplementation && "Sync Implementation not initialized");
+  delete static_cast<EglSyncObject*>(syncObject);
+}
+
+void EglSyncImplementation::InitializeEglSync()
+{
+}
+
+} // namespace Adaptor
+} // namespace Internal
+} // namespace Dali
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2021 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.
@@ -31,6 +31,7 @@
 #include <dali/integration-api/debug.h>
 
 // INTERNAL INCLUDES
+#include <dali/internal/graphics/gles/egl-debug.h>
 #include <dali/internal/graphics/gles/egl-implementation.h>
 
 #ifdef _ARCH_ARM_
 static PFNEGLCREATESYNCKHRPROC     eglCreateSyncKHR     = NULL;
 static PFNEGLCLIENTWAITSYNCKHRPROC eglClientWaitSyncKHR = NULL;
 static PFNEGLDESTROYSYNCKHRPROC    eglDestroySyncKHR    = NULL;
+static PFNEGLWAITSYNCKHRPROC       eglWaitSyncKHR       = NULL;
 
 #endif
 
+#if defined(DEBUG_ENABLED)
+Debug::Filter* gLogSyncFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_FENCE_SYNC");
+#endif
+
 namespace Dali
 {
 namespace Internal
@@ -50,9 +56,9 @@ namespace Adaptor
 {
 #ifdef _ARCH_ARM_
 
-EglSyncObject::EglSyncObject(EglImplementation& eglSyncImpl)
+EglSyncObject::EglSyncObject(EglImplementation& eglImpl)
 : mEglSync(NULL),
-  mEglImplementation(eglSyncImpl)
+  mEglImplementation(eglImpl)
 {
   EGLDisplay display = mEglImplementation.GetDisplay();
   mEglSync           = eglCreateSyncKHR(display, EGL_SYNC_FENCE_KHR, NULL);
@@ -61,6 +67,10 @@ EglSyncObject::EglSyncObject(EglImplementation& eglSyncImpl)
     DALI_LOG_ERROR("eglCreateSyncKHR failed %#0.4x\n", eglGetError());
     mEglSync = NULL;
   }
+  else
+  {
+    DALI_LOG_INFO(gLogSyncFilter, Debug::General, "eglCreateSyncKHR Success: %p\n", mEglSync);
+  }
 }
 
 EglSyncObject::~EglSyncObject()
@@ -73,6 +83,10 @@ EglSyncObject::~EglSyncObject()
     {
       DALI_LOG_ERROR("eglDestroySyncKHR failed %#0.4x\n", error);
     }
+    else
+    {
+      DALI_LOG_INFO(gLogSyncFilter, Debug::General, "eglDestroySyncKHR Success: %p\n", mEglSync);
+    }
   }
 }
 
@@ -82,6 +96,7 @@ bool EglSyncObject::IsSynced()
 
   if(mEglSync != NULL)
   {
+    DALI_LOG_INFO(gLogSyncFilter, Debug::General, "eglClientWaitSync no timeout\n");
     EGLint result = eglClientWaitSyncKHR(mEglImplementation.GetDisplay(), mEglSync, 0, 0ull);
     EGLint error  = eglGetError();
     if(EGL_SUCCESS != error)
@@ -94,9 +109,46 @@ bool EglSyncObject::IsSynced()
     }
   }
 
+  DALI_LOG_INFO(gLogSyncFilter, Debug::General, "eglClientWaitSync(%p, 0, 0) %s\n", mEglSync, synced ? "Synced" : "NOT SYNCED");
   return synced;
 }
 
+void EglSyncObject::ClientWait()
+{
+  bool synced = false;
+  if(mEglSync != nullptr)
+  {
+    DALI_LOG_INFO(gLogSyncFilter, Debug::General, "eglClientWaitSync FOREVER\n");
+    auto result = eglClientWaitSyncKHR(mEglImplementation.GetDisplay(), mEglSync, EGL_SYNC_FLUSH_COMMANDS_BIT_KHR, EGL_FOREVER_KHR);
+    if(result == EGL_FALSE)
+    {
+      Egl::PrintError(eglGetError());
+    }
+    else if(result == EGL_CONDITION_SATISFIED_KHR)
+    {
+      synced = true;
+    }
+  }
+  DALI_LOG_INFO(gLogSyncFilter, Debug::General, "eglClientWaitSync(%p, 0, FOREVER) %s\n", mEglSync, synced ? "Synced" : "NOT SYNCED");
+}
+
+void EglSyncObject::Wait()
+{
+  if(mEglSync != nullptr)
+  {
+    DALI_LOG_INFO(gLogSyncFilter, Debug::General, "eglWaitSync\n");
+    auto result = eglWaitSyncKHR(mEglImplementation.GetDisplay(), mEglSync, 0);
+    if(EGL_FALSE == result)
+    {
+      Egl::PrintError(eglGetError());
+    }
+    else
+    {
+      DALI_LOG_INFO(gLogSyncFilter, Debug::General, "eglWaitSync() %p synced!\n", mEglSync);
+    }
+  }
+}
+
 EglSyncImplementation::EglSyncImplementation()
 : mEglImplementation(NULL),
   mSyncInitialized(false),
@@ -152,10 +204,11 @@ void EglSyncImplementation::InitializeEglSync()
   {
     eglCreateSyncKHR     = reinterpret_cast<PFNEGLCREATESYNCKHRPROC>(eglGetProcAddress("eglCreateSyncKHR"));
     eglClientWaitSyncKHR = reinterpret_cast<PFNEGLCLIENTWAITSYNCKHRPROC>(eglGetProcAddress("eglClientWaitSyncKHR"));
+    eglWaitSyncKHR       = reinterpret_cast<PFNEGLWAITSYNCKHRPROC>(eglGetProcAddress("eglWaitSyncKHR"));
     eglDestroySyncKHR    = reinterpret_cast<PFNEGLDESTROYSYNCKHRPROC>(eglGetProcAddress("eglDestroySyncKHR"));
   }
 
-  if(eglCreateSyncKHR && eglClientWaitSyncKHR && eglDestroySyncKHR)
+  if(eglCreateSyncKHR && eglClientWaitSyncKHR && eglWaitSyncKHR && eglDestroySyncKHR)
   {
     mSyncInitialized = true;
   }
@@ -179,12 +232,15 @@ EglSyncObject::~EglSyncObject()
 
 bool EglSyncObject::IsSynced()
 {
-  if(mPollCounter <= 0)
-  {
-    return true;
-  }
-  --mPollCounter;
-  return false;
+  return true;
+}
+
+void EglSyncObject::Wait()
+{
+}
+
+void EglSyncObject::ClientWait()
+{
 }
 
 EglSyncImplementation::EglSyncImplementation()
diff --git a/dali/internal/graphics/windows-gl/egl-sync-implementation-windows.cpp b/dali/internal/graphics/windows-gl/egl-sync-implementation-windows.cpp
new file mode 100644 (file)
index 0000000..df12970
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ * 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.
+ * 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 <dali/internal/graphics/gles/egl-sync-implementation.h>
+
+// EXTERNAL INCLUDES
+
+#include <dali/integration-api/debug.h>
+
+// INTERNAL INCLUDES
+#include <dali/internal/graphics/gles/egl-debug.h>
+#include <dali/internal/graphics/gles/egl-implementation.h>
+
+#if defined(DEBUG_ENABLED)
+Debug::Filter* gLogSyncFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_FENCE_SYNC");
+#endif
+
+namespace Dali
+{
+namespace Internal
+{
+namespace Adaptor
+{
+EglSyncObject::EglSyncObject(EglImplementation& eglImpl)
+: mPollCounter(3),
+  mEglImplementation(eglImpl)
+{
+}
+
+EglSyncObject::~EglSyncObject()
+{
+}
+
+bool EglSyncObject::IsSynced()
+{
+  return true;
+}
+
+void EglSyncObject::Wait()
+{
+}
+
+void EglSyncObject::ClientWait()
+{
+}
+
+EglSyncImplementation::EglSyncImplementation()
+: mEglImplementation(NULL),
+  mSyncInitialized(false),
+  mSyncInitializeFailed(false)
+{
+}
+
+EglSyncImplementation::~EglSyncImplementation()
+{
+}
+
+void EglSyncImplementation::Initialize(EglImplementation* eglImpl)
+{
+  mEglImplementation = eglImpl;
+}
+
+Integration::GraphicsSyncAbstraction::SyncObject* EglSyncImplementation::CreateSyncObject()
+{
+  DALI_ASSERT_ALWAYS(mEglImplementation && "Sync Implementation not initialized");
+  return new EglSyncObject(*mEglImplementation);
+}
+
+void EglSyncImplementation::DestroySyncObject(Integration::GraphicsSyncAbstraction::SyncObject* syncObject)
+{
+  DALI_ASSERT_ALWAYS(mEglImplementation && "Sync Implementation not initialized");
+  delete static_cast<EglSyncObject*>(syncObject);
+}
+
+void EglSyncImplementation::InitializeEglSync()
+{
+}
+
+} // namespace Adaptor
+} // namespace Internal
+} // namespace Dali