From 9005624091e3f5a9e9ff4d17d2bc5d32928ce35a Mon Sep 17 00:00:00 2001
From: Jocelyn Turcotte <jocelyn.turcotte@digia.com>
Date: Thu, 28 Aug 2014 19:31:04 +0200
Subject: [PATCH] <chromium> Convert sync points to GL fence syncs.

Chromium is always producing and consuming the textures on the GPU
thread, switching the GL context accordingly, and are using sync
points to externally know when it is correct to send the consumming
GL commands down the pipe of their respective GL context.

Since Qt is consuming those textures in a different thread,
synchronizing when commands are handed down to GL isn't always enough.
The GL driver could decide to do additional scheduling and end up
executing Qt's consuming GL commands before Chromium's producing ones
even if they were sent to their respective context in the right order.

To prevent this, convert each sync point into a real GL fence sync
and allow Qt to communicate the dependency between consuming and
producing commands down to GL even across threads.

gfx::GLFence can now be converted to a POD TransferableFence to allow
waiting for or destroying the sync using a QOpenGLContext, which
gl_fence.cc wouldn't be able to use through Chromium's GL function
table.

Change-Id: I8a9e2de6ed84b2e16f5504c5d66dc3580b87140a
Reviewed-by: Andras Becsi <andras.becsi@digia.com>
Reviewed-by: Michael Bruning <michael.bruning@digia.com>
---
 .../content/common/gpu/gpu_channel_manager.cc |  1 +
 .../content/common/gpu/gpu_channel_manager.h  |  3 ++
 .../common/gpu/gpu_command_buffer_stub.cc     | 32 +++++++++++++++++++
 .../common/gpu/gpu_command_buffer_stub.h      |  1 +
 chromium/ui/gl/gl_fence.cc                    | 26 ++++++++++++++-
 chromium/ui/gl/gl_fence.h                     | 28 ++++++++++++++++
 6 files changed, 90 insertions(+), 1 deletion(-)

diff --git a/chromium/content/common/gpu/gpu_channel_manager.cc b/chromium/content/common/gpu/gpu_channel_manager.cc
index 2c93ef44910..97bbbbb0b81 100644
--- a/chromium/content/common/gpu/gpu_channel_manager.cc
+++ b/chromium/content/common/gpu/gpu_channel_manager.cc
@@ -56,6 +56,7 @@ GpuChannelManager::~GpuChannelManager() {
     default_offscreen_surface_ = NULL;
   }
   DCHECK(image_operations_.empty());
+  DCHECK(sync_point_gl_fences_.empty());
 }
 
 gpu::gles2::ProgramCache* GpuChannelManager::program_cache() {
diff --git a/chromium/content/common/gpu/gpu_channel_manager.h b/chromium/content/common/gpu/gpu_channel_manager.h
index 4c23f8009d0..dcaf3f11c05 100644
--- a/chromium/content/common/gpu/gpu_channel_manager.h
+++ b/chromium/content/common/gpu/gpu_channel_manager.h
@@ -27,6 +27,7 @@ class WaitableEvent;
 }
 
 namespace gfx {
+class GLFence;
 class GLShareGroup;
 struct GpuMemoryBufferHandle;
 }
@@ -97,6 +98,8 @@ class GpuChannelManager : public IPC::Listener,
   GpuChannel* LookupChannel(int32 client_id);
 
   SyncPointManager* sync_point_manager() { return sync_point_manager_.get(); }
+  typedef base::hash_map<uint32, gfx::GLFence*> SyncPointGLFences;
+  SyncPointGLFences sync_point_gl_fences_;
 
   gfx::GLSurface* GetDefaultOffscreenSurface();
 
diff --git a/chromium/content/common/gpu/gpu_command_buffer_stub.cc b/chromium/content/common/gpu/gpu_command_buffer_stub.cc
index 37f50a8cdf4..84d611a8ce2 100644
--- a/chromium/content/common/gpu/gpu_command_buffer_stub.cc
+++ b/chromium/content/common/gpu/gpu_command_buffer_stub.cc
@@ -35,6 +35,7 @@
 #include "gpu/command_buffer/service/memory_tracking.h"
 #include "gpu/command_buffer/service/query_manager.h"
 #include "ui/gl/gl_bindings.h"
+#include "ui/gl/gl_fence.h"
 #include "ui/gl/gl_switches.h"
 
 #if defined(OS_WIN)
@@ -111,6 +112,15 @@ const int64 kHandleMoreWorkPeriodBusyMs = 1;
 // Prevents idle work from being starved.
 const int64 kMaxTimeSinceIdleMs = 10;
 
+void DestroyGLFence(GpuChannelManager::SyncPointGLFences &fences, uint32 sync_point)
+{
+  GpuChannelManager::SyncPointGLFences::iterator it = fences.find(sync_point);
+  if (it != fences.end()) {
+    delete it->second;
+    fences.erase(it);
+  }
+}
+
 }  // namespace
 
 GpuCommandBufferStub::GpuCommandBufferStub(
@@ -143,6 +153,7 @@ GpuCommandBufferStub::GpuCommandBufferStub(
       last_memory_allocation_valid_(false),
       watchdog_(watchdog),
       sync_point_wait_count_(0),
+      last_fence_sync_point_(0),
       delayed_work_scheduled_(false),
       previous_messages_processed_(0),
       active_url_(active_url),
@@ -194,7 +205,10 @@ bool GpuCommandBufferStub::OnMessageReceived(const IPC::Message& message) {
   if (decoder_.get() && message.type() != GpuCommandBufferMsg_Echo::ID &&
       message.type() != GpuCommandBufferMsg_WaitForTokenInRange::ID &&
       message.type() != GpuCommandBufferMsg_WaitForGetOffsetInRange::ID &&
+#if !defined(TOOLKIT_QT)
+      // Qt needs the context to be current here to insert/destroy fences.
       message.type() != GpuCommandBufferMsg_RetireSyncPoint::ID &&
+#endif
       message.type() != GpuCommandBufferMsg_SetLatencyInfo::ID) {
     if (!MakeCurrent())
       return false;
@@ -402,6 +416,7 @@ void GpuCommandBufferStub::Destroy() {
                     OnWillDestroyStub());
 
   if (decoder_) {
+    DestroyGLFence(channel_->gpu_channel_manager()->sync_point_gl_fences_, last_fence_sync_point_);
     decoder_->Destroy(have_context);
     decoder_.reset();
   }
@@ -835,6 +850,23 @@ void GpuCommandBufferStub::OnRetireSyncPoint(uint32 sync_point) {
   if (context_group_->mailbox_manager()->UsesSync() && MakeCurrent())
     context_group_->mailbox_manager()->PushTextureUpdates();
   GpuChannelManager* manager = channel_->gpu_channel_manager();
+
+#if defined(TOOLKIT_QT)
+  // Only keep the last fence alive to keep its temporary ownership in GpuCommandBufferStub
+  // simple in case where Qt would not pick this fence to eventually destroy it.
+  DestroyGLFence(manager->sync_point_gl_fences_, last_fence_sync_point_);
+  // We submitted all resource-producing GL commands, convert the logical sync point into a GL fence
+  // to allow Qt's GL context to wait for the results of commands submitted in this context using the
+  // sync point as reference.
+  scoped_ptr<gfx::GLFence> fence = scoped_ptr<gfx::GLFence>(gfx::GLFence::CreateWithoutFlush());
+  if (fence)
+    manager->sync_point_gl_fences_.insert(std::make_pair(sync_point, fence.release()));
+  // Flush regardless of the success of the fence creation to at least make sure that commands
+  // producing our textures are in the pipe before the scene graph inserts its own on the other thread.
+  glFlush();
+  last_fence_sync_point_ = sync_point;
+#endif
+
   manager->sync_point_manager()->RetireSyncPoint(sync_point);
 }
 
diff --git a/chromium/content/common/gpu/gpu_command_buffer_stub.h b/chromium/content/common/gpu/gpu_command_buffer_stub.h
index f660f1df3e8..d6dfb6fbba8 100644
--- a/chromium/content/common/gpu/gpu_command_buffer_stub.h
+++ b/chromium/content/common/gpu/gpu_command_buffer_stub.h
@@ -266,6 +266,7 @@ class GpuCommandBufferStub
   // A queue of sync points associated with this stub.
   std::deque<uint32> sync_points_;
   int sync_point_wait_count_;
+  uint32 last_fence_sync_point_;
 
   bool delayed_work_scheduled_;
   uint64 previous_messages_processed_;
diff --git a/chromium/ui/gl/gl_fence.cc b/chromium/ui/gl/gl_fence.cc
index 073e6eea314..69714e7d36e 100644
--- a/chromium/ui/gl/gl_fence.cc
+++ b/chromium/ui/gl/gl_fence.cc
@@ -33,6 +33,10 @@ class GLFenceNVFence: public gfx::GLFence {
     }
   }
 
+  virtual gfx::TransferableFence Transfer() OVERRIDE {
+    return gfx::TransferableFence();
+  }
+
   virtual bool HasCompleted() OVERRIDE {
     return !!glTestFenceNV(fence_);
   }
@@ -69,6 +73,16 @@ class GLFenceARBSync: public gfx::GLFence {
     }
   }
 
+  virtual gfx::TransferableFence Transfer() OVERRIDE {
+    gfx::TransferableFence ret;
+    if (sync_) {
+      ret.type = gfx::TransferableFence::ArbSync;
+      ret.arb.sync = sync_;
+      sync_ = 0;
+    }
+    return ret;
+  }
+
   virtual bool HasCompleted() OVERRIDE {
     // Handle the case where FenceSync failed.
     if (!sync_)
@@ -98,7 +112,8 @@ class GLFenceARBSync: public gfx::GLFence {
 
  private:
   virtual ~GLFenceARBSync() {
-    glDeleteSync(sync_);
+    if (sync_)
+      glDeleteSync(sync_);
   }
 
   GLsync sync_;
@@ -118,6 +133,15 @@ class EGLFenceSync : public gfx::GLFence {
     }
   }
 
+  virtual gfx::TransferableFence Transfer() OVERRIDE {
+    gfx::TransferableFence ret;
+    ret.type = gfx::TransferableFence::EglSync;
+    ret.egl.display = display_;
+    ret.egl.sync = sync_;
+    sync_ = 0;
+    return ret;
+  }
+
   virtual bool HasCompleted() OVERRIDE {
     EGLint value = 0;
     eglGetSyncAttribKHR(display_, sync_, EGL_SYNC_STATUS_KHR, &value);
diff --git a/chromium/ui/gl/gl_fence.h b/chromium/ui/gl/gl_fence.h
index 021f3456f13..1d4e4cb72a0 100644
--- a/chromium/ui/gl/gl_fence.h
+++ b/chromium/ui/gl/gl_fence.h
@@ -8,8 +8,34 @@
 #include "base/basictypes.h"
 #include "ui/gl/gl_export.h"
 
+typedef void *EGLDisplay;
+typedef void *EGLSyncKHR;
+typedef struct __GLsync *GLsync;
+
 namespace gfx {
 
+union TransferableFence {
+    enum SyncType {
+        NoSync,
+        EglSync,
+        ArbSync
+    };
+    SyncType type;
+    struct {
+        SyncType type;
+        EGLDisplay display;
+        EGLSyncKHR sync;
+    } egl;
+    struct {
+        SyncType type;
+        GLsync sync;
+    } arb;
+
+    TransferableFence() : type(NoSync) { }
+    operator bool() { return type != NoSync; }
+    void reset() { type = NoSync; }
+};
+
 class GL_EXPORT GLFence {
  public:
   GLFence();
@@ -23,6 +49,8 @@ class GL_EXPORT GLFence {
   // context.
   static GLFence* CreateWithoutFlush();
 
+  virtual TransferableFence Transfer() = 0;
+
   virtual bool HasCompleted() = 0;
   virtual void ClientWait() = 0;
 
-- 
GitLab