Bug 1281425 - Implement wayland loop to process wl_display events for threads and bind wl_registry for wl_shm, r?jhorak draft
authorMartin Stransky <stransky@redhat.com>
Thu, 02 Nov 2017 17:08:33 +0100
changeset 705020 2486e1fa21fdcbbb8b3c71f5d1fa478f5e908a12
parent 705018 8fc01c909d66c22e806a454b922e34a2de2487b7
child 742230 1d0be45c377a854f236a364e302935cd7caa464f
push id91327
push userstransky@redhat.com
push dateWed, 29 Nov 2017 09:54:31 +0000
reviewersjhorak
bugs1281425
milestone58.0a1
Bug 1281425 - Implement wayland loop to process wl_display events for threads and bind wl_registry for wl_shm, r?jhorak MozReview-Commit-ID: 1pS1wfOiOo4
widget/gtk/WindowSurfaceWayland.cpp
widget/gtk/WindowSurfaceWayland.h
--- a/widget/gtk/WindowSurfaceWayland.cpp
+++ b/widget/gtk/WindowSurfaceWayland.cpp
@@ -20,35 +20,197 @@
 #include <sys/mman.h>
 #include <assert.h>
 #include <fcntl.h>
 #include <errno.h>
 
 namespace mozilla {
 namespace widget {
 
+// TODO: How many rendering threads do we actualy handle?
+static nsCOMArray<nsWaylandDisplay> gWaylandDisplays;
+static StaticMutex gWaylandDisplaysMutex;
+
+// Each thread which is using wayland connection (wl_display) has to operate
+// its own wl_event_queue. Main Firefox thread wl_event_queue is handled
+// by Gtk main loop, other threads/wl_event_queue has to be handled by us.
+//
+// nsWaylandDisplay is our interface to wayland compositor. It provides wayland
+// global objects as we need (wl_display, wl_shm) and operates wl_event_queue on
+// compositor (not the main) thread.
+static nsWaylandDisplay* WaylandDisplayGet(wl_display *aDisplay);
+static void WaylandDisplayRelease(wl_display *aDisplay);
+static void WaylandDisplayLoop(wl_display *aDisplay);
+
+// TODO: is the 60pfs loop correct?
+#define EVENT_LOOP_DELAY (1000/60)
+
+// Get WaylandDisplay for given wl_display and actual calling thread.
+static nsWaylandDisplay*
+WaylandDisplayGetLocked(wl_display *aDisplay, const StaticMutexAutoLock&)
+{
+  nsWaylandDisplay* waylandDisplay = nullptr;
+
+  int len = gWaylandDisplays.Count();
+  for (int i = 0; i < len; i++) {
+    if (gWaylandDisplays[i]->Matches(aDisplay)) {
+      waylandDisplay = gWaylandDisplays[i];
+      break;
+    }
+  }
+
+  if (!waylandDisplay) {
+    waylandDisplay = new nsWaylandDisplay(aDisplay);
+    gWaylandDisplays.AppendObject(waylandDisplay);
+  }
+
+  NS_ADDREF(waylandDisplay);
+  return waylandDisplay;
+}
+
+static nsWaylandDisplay*
+WaylandDisplayGet(wl_display *aDisplay)
+{
+  StaticMutexAutoLock lock(gWaylandDisplaysMutex);
+  return WaylandDisplayGetLocked(aDisplay, lock);
+}
+
+static bool
+WaylandDisplayReleaseLocked(wl_display *aDisplay,
+                            const StaticMutexAutoLock&)
+{
+  int len = gWaylandDisplays.Count();
+  for (int i = 0; i < len; i++) {
+    if (gWaylandDisplays[i]->Matches(aDisplay)) {
+      int rc = gWaylandDisplays[i]->Release();
+      // nsCOMArray::AppendObject()/RemoveObjectAt() also call AddRef()/Release()
+      // so remove WaylandDisplay when ref count is 1.
+      if (rc == 1) {
+        gWaylandDisplays.RemoveObjectAt(i);
+      }
+      return true;
+    }
+  }
+  MOZ_ASSERT(false, "Missing nsWaylandDisplay for this thread!");
+  return false;
+}
+
+static void
+WaylandDisplayRelease(wl_display *aDisplay)
+{
+  StaticMutexAutoLock lock(gWaylandDisplaysMutex);
+  WaylandDisplayReleaseLocked(aDisplay, lock);
+}
+
+static void
+WaylandDisplayLoopLocked(wl_display* aDisplay,
+                         const StaticMutexAutoLock&)
+{
+  int len = gWaylandDisplays.Count();
+  for (int i = 0; i < len; i++) {
+    if (gWaylandDisplays[i]->Matches(aDisplay)) {
+      if (gWaylandDisplays[i]->DisplayLoop()) {
+        MessageLoop::current()->PostDelayedTask(
+            NewRunnableFunction(&WaylandDisplayLoop, aDisplay), EVENT_LOOP_DELAY);
+      }
+      break;
+    }
+  }
+}
+
+static void
+WaylandDisplayLoop(wl_display* aDisplay)
+{
+  MOZ_ASSERT(!NS_IsMainThread());
+  StaticMutexAutoLock lock(gWaylandDisplaysMutex);
+  WaylandDisplayLoopLocked(aDisplay, lock);
+}
+
+static void
+global_registry_handler(void *data, wl_registry *registry, uint32_t id,
+                        const char *interface, uint32_t version)
+{
+  if (strcmp(interface, "wl_shm") == 0) {
+    auto interface = reinterpret_cast<nsWaylandDisplay *>(data);
+    auto shm = static_cast<wl_shm*>(
+        wl_registry_bind(registry, id, &wl_shm_interface, 1));
+    wl_proxy_set_queue((struct wl_proxy *)shm, interface->GetEventQueue());
+    interface->SetShm(shm);
+  }
+}
+
+static void
+global_registry_remover(void *data, wl_registry *registry, uint32_t id)
+{
+}
+
+static const struct wl_registry_listener registry_listener = {
+  global_registry_handler,
+  global_registry_remover
+};
+
+wl_shm*
+nsWaylandDisplay::GetShm()
+{
+  MOZ_ASSERT(mThreadId == PR_GetCurrentThread());
+
+  if (!mShm) {
+    // wl_shm is not provided by Gtk so we need to query wayland directly
+    // See weston/simple-shm.c and create_display() for reference.
+    wl_registry* registry = wl_display_get_registry(mDisplay);
+    wl_registry_add_listener(registry, &registry_listener, this);
+
+    wl_proxy_set_queue((struct wl_proxy *)registry, mEventQueue);
+    wl_display_roundtrip_queue(mDisplay, mEventQueue);
+    MOZ_RELEASE_ASSERT(mShm, "Wayland registry query failed!");
+  }
+
+  return(mShm);
+}
+
+bool
+nsWaylandDisplay::DisplayLoop()
+{
+  wl_display_dispatch_queue_pending(mDisplay, mEventQueue);
+  return true;
+}
+
 bool
 nsWaylandDisplay::Matches(wl_display *aDisplay)
 {
   return mThreadId == PR_GetCurrentThread() && aDisplay == mDisplay;
 }
 
 NS_IMPL_ISUPPORTS(nsWaylandDisplay, nsISupports);
 
 nsWaylandDisplay::nsWaylandDisplay(wl_display *aDisplay)
-{
-  mThreadId = PR_GetCurrentThread();
-  mDisplay = aDisplay;
-
+  : mThreadId(PR_GetCurrentThread())
+  , mDisplay(aDisplay)
+  , mDisplay(aDisplay)
   // gfx::SurfaceFormat::B8G8R8A8 is a basic Wayland format
   // and is always present.
-  mFormat = gfx::SurfaceFormat::B8G8R8A8;
+  , mFormat(gfx::SurfaceFormat::B8G8R8A8)
+  , mShm(nullptr)
+{
+  if (NS_IsMainThread()) {
+    // Use default event queue in main thread operated by Gtk+.
+    mEventQueue = nullptr;
+  } else {
+    mEventQueue = wl_display_create_queue(mDisplay);
+    MessageLoop::current()->PostTask(NewRunnableFunction(&WaylandDisplayLoop,
+                                                         mDisplay));
+  }
 }
 
 nsWaylandDisplay::~nsWaylandDisplay()
 {
   MOZ_ASSERT(mThreadId == PR_GetCurrentThread());
   // Owned by Gtk+, we don't need to release
   mDisplay = nullptr;
+
+  if (mEventQueue) {
+    wl_event_queue_destroy(mEventQueue);
+    mEventQueue = nullptr;
+  }
 }
 
 }  // namespace widget
 }  // namespace mozilla
--- a/widget/gtk/WindowSurfaceWayland.h
+++ b/widget/gtk/WindowSurfaceWayland.h
@@ -15,24 +15,31 @@ namespace widget {
 // Our general connection to Wayland display server,
 // holds our display connection and runs event loop.
 class nsWaylandDisplay : public nsISupports {
   NS_DECL_THREADSAFE_ISUPPORTS
 
 public:
   nsWaylandDisplay(wl_display *aDisplay);
 
+  wl_shm*             GetShm();
+  void                SetShm(wl_shm* aShm)   { mShm = aShm; };
+
   wl_display*         GetDisplay()           { return mDisplay; };
+  wl_event_queue*     GetEventQueue()        { return mEventQueue; };
   gfx::SurfaceFormat  GetSurfaceFormat()     { return mFormat; };
+  bool                DisplayLoop();
   bool                Matches(wl_display *aDisplay);
 
 private:
   virtual ~nsWaylandDisplay();
 
   PRThread*           mThreadId;
   gfx::SurfaceFormat  mFormat;
+  wl_shm*             mShm;
+  wl_event_queue*     mEventQueue;
   wl_display*         mDisplay;
 };
 
 }  // namespace widget
 }  // namespace mozilla
 
 #endif // _MOZILLA_WIDGET_GTK_WINDOW_SURFACE_WAYLAND_H