diff --git a/gfx/layers/ipc/LayersSurfaces.ipdlh b/gfx/layers/ipc/LayersSurfaces.ipdlh --- a/gfx/layers/ipc/LayersSurfaces.ipdlh +++ b/gfx/layers/ipc/LayersSurfaces.ipdlh @@ -71,6 +71,8 @@ uint32_t[] offsets; YUVColorSpace yUVColorSpace; FileDescriptor[] fence; + uint32_t uid; + FileDescriptor[] refCount; }; struct SurfaceTextureDescriptor { diff --git a/widget/gtk/WaylandDMABufSurface.h b/widget/gtk/WaylandDMABufSurface.h --- a/widget/gtk/WaylandDMABufSurface.h +++ b/widget/gtk/WaylandDMABufSurface.h @@ -49,9 +49,14 @@ 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 CreateDMABufSurface( const mozilla::layers::SurfaceDescriptor& aDesc); + // Export surface to another process via. SurfaceDescriptor. virtual bool Serialize( mozilla::layers::SurfaceDescriptor& aOutDescriptor) = 0; @@ -82,6 +87,35 @@ 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: @@ -89,7 +123,10 @@ virtual void ReleaseSurface() = 0; bool FenceCreate(int aFd); - virtual ~WaylandDMABufSurface() { FenceDelete(); }; + void GlobalRefCountImport(int aFd); + void GlobalRefCountDelete(); + + virtual ~WaylandDMABufSurface(); SurfaceType mSurfaceType; uint64_t mBufferModifier; @@ -102,6 +139,9 @@ EGLSyncKHR mSync; RefPtr mGL; + + int mGlobalRefCountFd; + uint32_t mUID; }; class WaylandDMABufSurfaceRGBA : public WaylandDMABufSurface { diff --git a/widget/gtk/WaylandDMABufSurface.cpp b/widget/gtk/WaylandDMABufSurface.cpp --- a/widget/gtk/WaylandDMABufSurface.cpp +++ b/widget/gtk/WaylandDMABufSurface.cpp @@ -17,6 +17,9 @@ #include #include #include +#include +#include +#include #include "mozilla/widget/gbm.h" #include "mozilla/widget/va_drmcommon.h" @@ -57,6 +60,61 @@ # 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), @@ -64,12 +122,19 @@ mDrmFormats(), mStrides(), mOffsets(), - mSync(0) { + mSync(0), + mGlobalRefCountFd(0), + mUID(0) { for (auto& slot : mDmabufFds) { slot = -1; } } +WaylandDMABufSurface::~WaylandDMABufSurface() { + FenceDelete(); + GlobalRefCountDelete(); +} + already_AddRefed WaylandDMABufSurface::CreateDMABufSurface( const mozilla::layers::SurfaceDescriptor& aDesc) { @@ -316,6 +381,7 @@ 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(); @@ -329,6 +395,10 @@ close(fd); } } + + if (desc.refCount().Length() > 0) { + GlobalRefCountImport(desc.refCount()[0].ClonePlatformHandle().release()); + } } bool WaylandDMABufSurfaceRGBA::Create(const SurfaceDescriptor& aDesc) { @@ -346,6 +416,7 @@ AutoTArray offsets; AutoTArray images; AutoTArray fenceFDs; + AutoTArray refCountFDs; width.AppendElement(mWidth); height.AppendElement(mHeight); @@ -362,9 +433,14 @@ 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; } @@ -693,6 +769,7 @@ 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++) { @@ -710,6 +787,10 @@ close(fd); } } + + if (aDesc.refCount().Length() > 0) { + GlobalRefCountImport(aDesc.refCount()[0].ClonePlatformHandle().release()); + } } bool WaylandDMABufSurfaceNV12::Serialize( @@ -721,6 +802,7 @@ AutoTArray strides; AutoTArray offsets; AutoTArray fenceFDs; + AutoTArray refCountFDs; for (int i = 0; i < mBufferPlaneCount; i++) { width.AppendElement(mWidth[i]); @@ -737,9 +819,13 @@ 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; } diff --git a/widget/gtk/WindowSurfaceWayland.h b/widget/gtk/WindowSurfaceWayland.h --- a/widget/gtk/WindowSurfaceWayland.h +++ b/widget/gtk/WindowSurfaceWayland.h @@ -36,8 +36,6 @@ int aImageDataSize); private: - int CreateTemporaryFile(int aSize); - wl_shm_pool* mShmPool; int mShmPoolFd; int mAllocatedSize; diff --git a/widget/gtk/WindowSurfaceWayland.cpp b/widget/gtk/WindowSurfaceWayland.cpp --- a/widget/gtk/WindowSurfaceWayland.cpp +++ b/widget/gtk/WindowSurfaceWayland.cpp @@ -206,42 +206,25 @@ 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 { @@ -249,8 +232,7 @@ } 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 @@ -259,7 +241,7 @@ 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,