Bug 1619882 [Wayland] Implement dmabuf global ref count, r=jhorak
authorMartin Stransky <stransky@redhat.com>
Fri, 29 May 2020 15:21:50 +0000
changeset 533005 594ac84515dc7eeddd1d09071e4d6591524d8910
parent 533004 5b828de27b663239abbe1e17a273a313b88d826c
child 533006 45a693471a1cf292bc7fb48a5d2a46e530886747
push id37461
push userccoroiu@mozilla.com
push dateFri, 29 May 2020 21:46:31 +0000
treeherdermozilla-central@a58cc68b0c51 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjhorak
bugs1619882
milestone78.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1619882 [Wayland] Implement dmabuf global ref count, r=jhorak This patch implements: - Global surface UID for better tracking/debugging. The UID is passes acros all processes so we can track surface usage. - WaylandAllocateShmMemory() function which uses shm_open() instead of files at tmp for Shm memory creation. - DMABufRefcount() class based on eventfd() which is used as a global surface reference count. Differential Revision: https://phabricator.services.mozilla.com/D76689
gfx/layers/ipc/LayersSurfaces.ipdlh
widget/gtk/WaylandDMABufSurface.cpp
widget/gtk/WaylandDMABufSurface.h
widget/gtk/WindowSurfaceWayland.cpp
widget/gtk/WindowSurfaceWayland.h
--- a/gfx/layers/ipc/LayersSurfaces.ipdlh
+++ b/gfx/layers/ipc/LayersSurfaces.ipdlh
@@ -66,16 +66,18 @@ struct SurfaceDescriptorDMABuf {
   FileDescriptor[] fds;
   uint32_t[] width;
   uint32_t[] height;
   uint32_t[] format;
   uint32_t[] strides;
   uint32_t[] offsets;
   YUVColorSpace yUVColorSpace;
   FileDescriptor[] fence;
+  uint32_t uid;
+  FileDescriptor[] refCount;
 };
 
 struct SurfaceTextureDescriptor {
   uint64_t handle;
   IntSize size;
   SurfaceFormat format;
   bool continuous;
   bool ignoreTransform;
--- a/widget/gtk/WaylandDMABufSurface.cpp
+++ b/widget/gtk/WaylandDMABufSurface.cpp
@@ -12,16 +12,19 @@
 #include <stdbool.h>
 #include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
 #include <sys/time.h>
 #include <dlfcn.h>
+#include <sys/mman.h>
+#include <sys/eventfd.h>
+#include <poll.h>
 
 #include "mozilla/widget/gbm.h"
 #include "mozilla/widget/va_drmcommon.h"
 #include "GLContextTypes.h"  // for GLContext, etc
 #include "GLContextEGL.h"
 #include "GLContextProvider.h"
 
 #include "mozilla/layers/LayersSurfaces.h"
@@ -52,29 +55,91 @@ using namespace mozilla::layers;
 #ifndef GBM_BO_USE_TEXTURING
 #  define GBM_BO_USE_TEXTURING (1 << 5)
 #endif
 
 #ifndef VA_FOURCC_NV12
 #  define VA_FOURCC_NV12 0x3231564E
 #endif
 
+bool WaylandDMABufSurface::IsGlobalRefSet() const {
+  if (!mGlobalRefCountFd) {
+    return false;
+  }
+  struct pollfd pfd;
+  pfd.fd = mGlobalRefCountFd;
+  pfd.events = POLLIN;
+  return poll(&pfd, 1, 0) == 1;
+}
+
+void WaylandDMABufSurface::GlobalRefRelease() {
+  MOZ_ASSERT(mGlobalRefCountFd);
+  uint64_t counter;
+  if (read(mGlobalRefCountFd, &counter, sizeof(counter)) != sizeof(counter)) {
+    // EAGAIN means the refcount is already zero. It happens when we release
+    // last reference to the surface.
+    if (errno != EAGAIN) {
+      NS_WARNING("Failed to unref dmabuf global ref count!");
+    }
+  }
+}
+
+void WaylandDMABufSurface::GlobalRefAdd() {
+  MOZ_ASSERT(mGlobalRefCountFd);
+  uint64_t counter = 1;
+  if (write(mGlobalRefCountFd, &counter, sizeof(counter)) != sizeof(counter)) {
+    NS_WARNING("Failed to ref dmabuf global ref count!");
+  }
+}
+
+void WaylandDMABufSurface::GlobalRefCountCreate() {
+  MOZ_ASSERT(!mGlobalRefCountFd);
+  mGlobalRefCountFd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK | EFD_SEMAPHORE);
+  if (mGlobalRefCountFd < 0) {
+    NS_WARNING("Failed to create dmabuf global ref count!");
+    mGlobalRefCountFd = 0;
+    return;
+  }
+}
+
+void WaylandDMABufSurface::GlobalRefCountImport(int aFd) {
+  MOZ_ASSERT(!mGlobalRefCountFd);
+  mGlobalRefCountFd = aFd;
+  GlobalRefAdd();
+}
+
+void WaylandDMABufSurface::GlobalRefCountDelete() {
+  MOZ_ASSERT(mGlobalRefCountFd);
+  if (mGlobalRefCountFd) {
+    GlobalRefRelease();
+    close(mGlobalRefCountFd);
+    mGlobalRefCountFd = 0;
+  }
+}
+
 WaylandDMABufSurface::WaylandDMABufSurface(SurfaceType aSurfaceType)
     : mSurfaceType(aSurfaceType),
       mBufferModifier(DRM_FORMAT_MOD_INVALID),
       mBufferPlaneCount(0),
       mDrmFormats(),
       mStrides(),
       mOffsets(),
-      mSync(0) {
+      mSync(0),
+      mGlobalRefCountFd(0),
+      mUID(0) {
   for (auto& slot : mDmabufFds) {
     slot = -1;
   }
 }
 
+WaylandDMABufSurface::~WaylandDMABufSurface() {
+  FenceDelete();
+  GlobalRefCountDelete();
+}
+
 already_AddRefed<WaylandDMABufSurface>
 WaylandDMABufSurface::CreateDMABufSurface(
     const mozilla::layers::SurfaceDescriptor& aDesc) {
   const SurfaceDescriptorDMABuf& desc = aDesc.get_SurfaceDescriptorDMABuf();
   RefPtr<WaylandDMABufSurface> surf;
 
   switch (desc.bufferType()) {
     case SURFACE_RGBA:
@@ -311,29 +376,34 @@ void WaylandDMABufSurfaceRGBA::ImportSur
   if (mBufferModifier != DRM_FORMAT_MOD_INVALID) {
     mGmbFormat = WaylandDisplayGet()->GetExactGbmFormat(desc.format()[0]);
   } else {
     mDrmFormats[0] = desc.format()[0];
   }
   mBufferPlaneCount = desc.fds().Length();
   mGbmBufferFlags = desc.flags();
   MOZ_RELEASE_ASSERT(mBufferPlaneCount <= DMABUF_BUFFER_PLANES);
+  mUID = desc.uid();
 
   for (int i = 0; i < mBufferPlaneCount; i++) {
     mDmabufFds[i] = desc.fds()[i].ClonePlatformHandle().release();
     mStrides[i] = desc.strides()[i];
     mOffsets[i] = desc.offsets()[i];
   }
 
   if (desc.fence().Length() > 0) {
     int fd = desc.fence()[0].ClonePlatformHandle().release();
     if (!FenceCreate(fd)) {
       close(fd);
     }
   }
+
+  if (desc.refCount().Length() > 0) {
+    GlobalRefCountImport(desc.refCount()[0].ClonePlatformHandle().release());
+  }
 }
 
 bool WaylandDMABufSurfaceRGBA::Create(const SurfaceDescriptor& aDesc) {
   ImportSurfaceDescriptor(aDesc);
   return true;
 }
 
 bool WaylandDMABufSurfaceRGBA::Serialize(
@@ -341,35 +411,41 @@ bool WaylandDMABufSurfaceRGBA::Serialize
   AutoTArray<uint32_t, DMABUF_BUFFER_PLANES> width;
   AutoTArray<uint32_t, DMABUF_BUFFER_PLANES> height;
   AutoTArray<uint32_t, DMABUF_BUFFER_PLANES> format;
   AutoTArray<ipc::FileDescriptor, DMABUF_BUFFER_PLANES> fds;
   AutoTArray<uint32_t, DMABUF_BUFFER_PLANES> strides;
   AutoTArray<uint32_t, DMABUF_BUFFER_PLANES> offsets;
   AutoTArray<uintptr_t, DMABUF_BUFFER_PLANES> images;
   AutoTArray<ipc::FileDescriptor, 1> fenceFDs;
+  AutoTArray<ipc::FileDescriptor, 1> refCountFDs;
 
   width.AppendElement(mWidth);
   height.AppendElement(mHeight);
   format.AppendElement(mGmbFormat->mFormat);
   for (int i = 0; i < mBufferPlaneCount; i++) {
     fds.AppendElement(ipc::FileDescriptor(mDmabufFds[i]));
     strides.AppendElement(mStrides[i]);
     offsets.AppendElement(mOffsets[i]);
   }
 
   if (mSync) {
     auto* egl = gl::GLLibraryEGL::Get();
     fenceFDs.AppendElement(ipc::FileDescriptor(
         egl->fDupNativeFenceFDANDROID(egl->Display(), mSync)));
   }
 
-  aOutDescriptor = SurfaceDescriptorDMABuf(
-      mSurfaceType, mBufferModifier, mGbmBufferFlags, fds, width, height,
-      format, strides, offsets, GetYUVColorSpace(), fenceFDs);
+  if (mGlobalRefCountFd) {
+    refCountFDs.AppendElement(ipc::FileDescriptor(mGlobalRefCountFd));
+  }
+
+  aOutDescriptor =
+      SurfaceDescriptorDMABuf(mSurfaceType, mBufferModifier, mGbmBufferFlags,
+                              fds, width, height, format, strides, offsets,
+                              GetYUVColorSpace(), fenceFDs, mUID, refCountFDs);
 
   return true;
 }
 
 bool WaylandDMABufSurfaceRGBA::CreateWLBuffer() {
   nsWaylandDisplay* display = WaylandDisplayGet();
   if (!display->GetDmabuf()) {
     return false;
@@ -688,16 +764,17 @@ bool WaylandDMABufSurfaceNV12::Create(co
 }
 
 void WaylandDMABufSurfaceNV12::ImportSurfaceDescriptor(
     const SurfaceDescriptorDMABuf& aDesc) {
   mSurfaceFormat = gfx::SurfaceFormat::NV12;
   mBufferPlaneCount = aDesc.fds().Length();
   mBufferModifier = aDesc.modifier();
   mColorSpace = aDesc.yUVColorSpace();
+  mUID = aDesc.uid();
 
   MOZ_RELEASE_ASSERT(mBufferPlaneCount <= DMABUF_BUFFER_PLANES);
   for (int i = 0; i < mBufferPlaneCount; i++) {
     mDmabufFds[i] = aDesc.fds()[i].ClonePlatformHandle().release();
     mWidth[i] = aDesc.width()[i];
     mHeight[i] = aDesc.height()[i];
     mDrmFormats[i] = aDesc.format()[i];
     mStrides[i] = aDesc.strides()[i];
@@ -705,46 +782,55 @@ void WaylandDMABufSurfaceNV12::ImportSur
   }
 
   if (aDesc.fence().Length() > 0) {
     int fd = aDesc.fence()[0].ClonePlatformHandle().release();
     if (!FenceCreate(fd)) {
       close(fd);
     }
   }
+
+  if (aDesc.refCount().Length() > 0) {
+    GlobalRefCountImport(aDesc.refCount()[0].ClonePlatformHandle().release());
+  }
 }
 
 bool WaylandDMABufSurfaceNV12::Serialize(
     mozilla::layers::SurfaceDescriptor& aOutDescriptor) {
   AutoTArray<uint32_t, DMABUF_BUFFER_PLANES> width;
   AutoTArray<uint32_t, DMABUF_BUFFER_PLANES> height;
   AutoTArray<uint32_t, DMABUF_BUFFER_PLANES> format;
   AutoTArray<ipc::FileDescriptor, DMABUF_BUFFER_PLANES> fds;
   AutoTArray<uint32_t, DMABUF_BUFFER_PLANES> strides;
   AutoTArray<uint32_t, DMABUF_BUFFER_PLANES> offsets;
   AutoTArray<ipc::FileDescriptor, 1> fenceFDs;
+  AutoTArray<ipc::FileDescriptor, 1> refCountFDs;
 
   for (int i = 0; i < mBufferPlaneCount; i++) {
     width.AppendElement(mWidth[i]);
     height.AppendElement(mHeight[i]);
     format.AppendElement(mDrmFormats[i]);
     fds.AppendElement(ipc::FileDescriptor(mDmabufFds[i]));
     strides.AppendElement(mStrides[i]);
     offsets.AppendElement(mOffsets[i]);
   }
 
   if (mSync) {
     auto* egl = gl::GLLibraryEGL::Get();
     fenceFDs.AppendElement(ipc::FileDescriptor(
         egl->fDupNativeFenceFDANDROID(egl->Display(), mSync)));
   }
 
+  if (mGlobalRefCountFd) {
+    refCountFDs.AppendElement(ipc::FileDescriptor(mGlobalRefCountFd));
+  }
+
   aOutDescriptor = SurfaceDescriptorDMABuf(
       mSurfaceType, mBufferModifier, 0, fds, width, height, format, strides,
-      offsets, GetYUVColorSpace(), fenceFDs);
+      offsets, GetYUVColorSpace(), fenceFDs, mUID, refCountFDs);
   return true;
 }
 
 #if 0
 already_AddRefed<gl::GLContext> MyCreateGLContextEGL() {
   nsCString discardFailureId;
   if (!gl::GLLibraryEGL::EnsureInitialized(true, &discardFailureId)) {
     gfxCriticalNote << "Failed to load EGL library: " << discardFailureId.get();
--- a/widget/gtk/WaylandDMABufSurface.h
+++ b/widget/gtk/WaylandDMABufSurface.h
@@ -44,19 +44,24 @@ class WaylandDMABufSurface {
  public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(WaylandDMABufSurface)
 
   enum SurfaceType {
     SURFACE_RGBA,
     SURFACE_NV12,
   };
 
+  // Import surface from SurfaceDescriptor. This is usually
+  // used to copy surface from another process over IPC.
+  // When a global reference counter was created for the surface
+  // (see bellow) it's automatically referenced.
   static already_AddRefed<WaylandDMABufSurface> CreateDMABufSurface(
       const mozilla::layers::SurfaceDescriptor& aDesc);
 
+  // Export surface to another process via. SurfaceDescriptor.
   virtual bool Serialize(
       mozilla::layers::SurfaceDescriptor& aOutDescriptor) = 0;
 
   virtual int GetWidth(int aPlane = 0) = 0;
   virtual int GetHeight(int aPlane = 0) = 0;
   virtual mozilla::gfx::SurfaceFormat GetFormat() = 0;
   virtual mozilla::gfx::SurfaceFormat GetFormatGL() = 0;
 
@@ -77,36 +82,71 @@ class WaylandDMABufSurface {
     return mozilla::gfx::YUVColorSpace::UNKNOWN;
   };
   virtual bool IsFullRange() { return false; };
 
   void FenceSet();
   void FenceWait();
   void FenceDelete();
 
+  // Set and get a global surface UID. The UID is shared across process
+  // and it's used to track surface lifetime in various parts of rendering
+  // engine.
+  void SetUID(uint32_t aUID) { mUID = aUID; };
+  uint32_t GetUID() const { return mUID; };
+
+  // Creates a global reference counter objects attached to the surface.
+  // It's created as unreferenced, i.e. IsGlobalRefSet() returns false
+  // right after GlobalRefCountCreate() call.
+  //
+  // The counter is shared by all surface instances across processes
+  // so it tracks global surface usage.
+  //
+  // The counter is automatically referenced when a new surface instance is
+  // created with SurfaceDescriptor (usually copied to another process over IPC)
+  // and it's unreferenced when surface is deleted.
+  //
+  // So without any additional GlobalRefAdd()/GlobalRefRelease() calls
+  // the IsGlobalRefSet() returns true if any other process use the surface.
+  void GlobalRefCountCreate();
+
+  // If global reference counter was created by GlobalRefCountCreate()
+  // returns true when there's an active surface reference.
+  bool IsGlobalRefSet() const;
+
+  // Add/Remove additional reference to the surface global reference counter.
+  void GlobalRefAdd();
+  void GlobalRefRelease();
+
   WaylandDMABufSurface(SurfaceType aSurfaceType);
 
  protected:
   virtual bool Create(const mozilla::layers::SurfaceDescriptor& aDesc) = 0;
   virtual void ReleaseSurface() = 0;
   bool FenceCreate(int aFd);
 
-  virtual ~WaylandDMABufSurface() { FenceDelete(); };
+  void GlobalRefCountImport(int aFd);
+  void GlobalRefCountDelete();
+
+  virtual ~WaylandDMABufSurface();
 
   SurfaceType mSurfaceType;
   uint64_t mBufferModifier;
 
   int mBufferPlaneCount;
   int mDmabufFds[DMABUF_BUFFER_PLANES];
   uint32_t mDrmFormats[DMABUF_BUFFER_PLANES];
   uint32_t mStrides[DMABUF_BUFFER_PLANES];
   uint32_t mOffsets[DMABUF_BUFFER_PLANES];
 
   EGLSyncKHR mSync;
   RefPtr<mozilla::gl::GLContext> mGL;
+
+  int mGlobalRefCountFd;
+  uint32_t mUID;
 };
 
 class WaylandDMABufSurfaceRGBA : public WaylandDMABufSurface {
  public:
   static already_AddRefed<WaylandDMABufSurfaceRGBA> CreateDMABufSurface(
       int aWidth, int aHeight, int aWaylandDMABufSurfaceFlags);
 
   bool Serialize(mozilla::layers::SurfaceDescriptor& aOutDescriptor);
--- a/widget/gtk/WindowSurfaceWayland.cpp
+++ b/widget/gtk/WindowSurfaceWayland.cpp
@@ -201,70 +201,52 @@ available and widget.wayland_dmabuf_back
 
 #define BUFFER_BPP 4
 gfx::SurfaceFormat WindowBackBuffer::mFormat = gfx::SurfaceFormat::B8G8R8A8;
 
 nsWaylandDisplay* WindowBackBuffer::GetWaylandDisplay() {
   return mWindowSurfaceWayland->GetWaylandDisplay();
 }
 
-int WaylandShmPool::CreateTemporaryFile(int aSize) {
-  const char* tmppath = getenv("XDG_RUNTIME_DIR");
-  MOZ_RELEASE_ASSERT(tmppath, "Missing XDG_RUNTIME_DIR env variable.");
-
-  nsPrintfCString tmpname("%s/mozilla-shared-XXXXXX", tmppath);
-
-  char* filename;
-  int fd = -1;
-  int ret = 0;
-
-  if (tmpname.GetMutableData(&filename)) {
-    fd = mkstemp(filename);
-    if (fd >= 0) {
-      int flags = fcntl(fd, F_GETFD);
-      if (flags >= 0) {
-        fcntl(fd, F_SETFD, flags | FD_CLOEXEC);
-      }
-    }
-  }
-
+static int WaylandAllocateShmMemory(int aSize) {
+  static int counter = 0;
+  nsPrintfCString shmName("/wayland.mozilla.ipc.%d", counter++);
+  int fd = shm_open(shmName.get(), O_CREAT | O_RDWR | O_EXCL, 0600);
   if (fd >= 0) {
-    unlink(tmpname.get());
+    shm_unlink(shmName.get());
   } else {
-    printf_stderr("Unable to create mapping file %s\n", filename);
+    printf_stderr("Unable to SHM memory segment\n");
     MOZ_CRASH();
   }
 
+  int ret = 0;
 #ifdef HAVE_POSIX_FALLOCATE
   do {
     ret = posix_fallocate(fd, 0, aSize);
   } while (ret == EINTR);
   if (ret != 0) {
     close(fd);
-    MOZ_CRASH_UNSAFE_PRINTF(
-        "posix_fallocate() fails on %s size %d error code %d\n", filename,
-        aSize, ret);
+    MOZ_CRASH("posix_fallocate() fails to allocate shm memory");
   }
 #else
   do {
     ret = ftruncate(fd, aSize);
   } while (ret < 0 && errno == EINTR);
   if (ret < 0) {
     close(fd);
-    MOZ_CRASH_UNSAFE_PRINTF("ftruncate() fails on %s size %d error code %d\n",
-                            filename, aSize, ret);
+    MOZ_CRASH("ftruncate() fails to allocate shm memory");
   }
 #endif
 
   return fd;
 }
 
 WaylandShmPool::WaylandShmPool(nsWaylandDisplay* aWaylandDisplay, int aSize)
     : mAllocatedSize(aSize) {
-  mShmPoolFd = CreateTemporaryFile(mAllocatedSize);
+  mShmPoolFd = WaylandAllocateShmMemory(mAllocatedSize);
   mImageData = mmap(nullptr, mAllocatedSize, PROT_READ | PROT_WRITE, MAP_SHARED,
                     mShmPoolFd, 0);
   MOZ_RELEASE_ASSERT(mImageData != MAP_FAILED,
                      "Unable to map drawing surface!");
 
   mShmPool =
       wl_shm_create_pool(aWaylandDisplay->GetShm(), mShmPoolFd, mAllocatedSize);
 
--- a/widget/gtk/WindowSurfaceWayland.h
+++ b/widget/gtk/WindowSurfaceWayland.h
@@ -31,18 +31,16 @@ class WaylandShmPool {
 
   bool Resize(int aSize);
   wl_shm_pool* GetShmPool() { return mShmPool; };
   void* GetImageData() { return mImageData; };
   void SetImageDataFromPool(class WaylandShmPool* aSourcePool,
                             int aImageDataSize);
 
  private:
-  int CreateTemporaryFile(int aSize);
-
   wl_shm_pool* mShmPool;
   int mShmPoolFd;
   int mAllocatedSize;
   void* mImageData;
 };
 
 // Holds actual graphics data for wl_surface
 class WindowBackBuffer {