diff --git a/widget/gtk/WaylandBuffer.h b/widget/gtk/WaylandBuffer.h --- a/widget/gtk/WaylandBuffer.h +++ b/widget/gtk/WaylandBuffer.h @@ -18,10 +18,12 @@ #include "nsTArray.h" #include "nsWaylandDisplay.h" namespace mozilla::widget { +class WaylandSurface; + // Allocates and owns shared memory for Wayland drawing surface class WaylandShmPool { public: NS_INLINE_DECL_THREADSAFE_REFCOUNTING(WaylandShmPool); @@ -43,52 +45,47 @@ class WaylandBuffer { public: NS_INLINE_DECL_THREADSAFE_REFCOUNTING(WaylandBuffer); - void AttachAndCommit(wl_surface* aSurface); - virtual wl_buffer* GetWlBuffer() = 0; virtual already_AddRefed Lock() { return nullptr; }; virtual void* GetImageData() { return nullptr; } virtual GLuint GetTexture() { return 0; } virtual void DestroyGLResources() {}; LayoutDeviceIntSize GetSize() { return mSize; }; bool IsMatchingSize(const LayoutDeviceIntSize& aSize) { return aSize == mSize; } - bool IsAttached() { return mAttached; } + + // wl_buffer is borrowed by WaylandSurface to attach. + // We store reference to WaylandSurface unless we don't have + // wl_buffer available. + wl_buffer* BorrowBuffer(RefPtr aWaylandSurface); + bool IsAttached() { return !!mSurface; } + static gfx::SurfaceFormat GetSurfaceFormat() { return mFormat; } - static void BufferReleaseCallbackHandler(void* aData, wl_buffer* aBuffer); - void SetBufferReleaseFunc(void (*aBufferReleaseFunc)(void* aData, - wl_buffer* aBuffer)) { - mBufferReleaseFunc = aBufferReleaseFunc; - } - void SetBufferReleaseData(void* aBufferReleaseData) { - mBufferReleaseData = aBufferReleaseData; - } + void BufferReleaseCallbackHandler(wl_buffer* aBuffer); protected: explicit WaylandBuffer(const LayoutDeviceIntSize& aSize); virtual ~WaylandBuffer() = default; - void BufferReleaseCallbackHandler(wl_buffer* aBuffer); + virtual wl_buffer* GetWlBuffer() = 0; LayoutDeviceIntSize mSize; - bool mAttached = false; - void (*mBufferReleaseFunc)(void* aData, wl_buffer* aBuffer) = nullptr; - void* mBufferReleaseData = nullptr; + RefPtr mSurface; static gfx::SurfaceFormat mFormat; }; // Holds actual graphics data for wl_surface class WaylandBufferSHM final : public WaylandBuffer { public: static RefPtr Create(const LayoutDeviceIntSize& aSize); - wl_buffer* GetWlBuffer() override { return mWLBuffer; }; + void ReleaseWlBuffer(); already_AddRefed Lock() override; void* GetImageData() override { return mShmPool->GetImageData(); } void Clear(); size_t GetBufferAge() { return mBufferAge; }; @@ -98,10 +95,12 @@ void ResetBufferAge() { mBufferAge = 0; }; #ifdef MOZ_LOGGING void DumpToFile(const char* aHint); #endif + protected: + wl_buffer* GetWlBuffer() override { return mWLBuffer; }; private: explicit WaylandBufferSHM(const LayoutDeviceIntSize& aSize); ~WaylandBufferSHM() override; @@ -120,22 +119,26 @@ #endif }; class WaylandBufferDMABUF final : public WaylandBuffer { public: - static RefPtr Create(const LayoutDeviceIntSize& aSize, - gl::GLContext* aGL); + static already_AddRefed CreateRGBA( + const LayoutDeviceIntSize& aSize, gl::GLContext* aGL); + static already_AddRefed CreateExternal( + RefPtr aSurface); - wl_buffer* GetWlBuffer() override { return mDMABufSurface->GetWlBuffer(); }; GLuint GetTexture() override { return mDMABufSurface->GetTexture(); }; void DestroyGLResources() override { mDMABufSurface->ReleaseTextures(); }; + protected: + wl_buffer* GetWlBuffer() override { return mDMABufSurface->GetWlBuffer(); }; + private: explicit WaylandBufferDMABUF(const LayoutDeviceIntSize& aSize); - ~WaylandBufferDMABUF() = default; + ~WaylandBufferDMABUF(); - RefPtr mDMABufSurface; + RefPtr mDMABufSurface; }; } // namespace mozilla::widget #endif // _MOZILLA_WIDGET_GTK_WAYLAND_BUFFER_H diff --git a/widget/gtk/WaylandBuffer.cpp b/widget/gtk/WaylandBuffer.cpp --- a/widget/gtk/WaylandBuffer.cpp +++ b/widget/gtk/WaylandBuffer.cpp @@ -3,10 +3,12 @@ * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "WaylandBuffer.h" +#include "WaylandSurface.h" +#include "WaylandSurfaceLock.h" #include #include #include @@ -84,89 +86,120 @@ WaylandShmPool::~WaylandShmPool() { MozClearPointer(mShmPool, wl_shm_pool_destroy); } -static const struct wl_buffer_listener sBufferListenerWaylandBuffer = { - WaylandBuffer::BufferReleaseCallbackHandler}; - WaylandBuffer::WaylandBuffer(const LayoutDeviceIntSize& aSize) : mSize(aSize) {} -void WaylandBuffer::AttachAndCommit(wl_surface* aSurface) { - LOGWAYLAND( - "WaylandBuffer::AttachAndCommit [%p] wl_surface %p ID %d wl_buffer " - "%p ID %d\n", - (void*)this, (void*)aSurface, - aSurface ? wl_proxy_get_id((struct wl_proxy*)aSurface) : -1, - (void*)GetWlBuffer(), - GetWlBuffer() ? wl_proxy_get_id((struct wl_proxy*)GetWlBuffer()) : -1); - +wl_buffer* WaylandBuffer::BorrowBuffer(RefPtr aWaylandSurface) { + MOZ_RELEASE_ASSERT(!mSurface, "We're already attached!"); wl_buffer* buffer = GetWlBuffer(); if (buffer) { - mAttached = true; - wl_surface_attach(aSurface, buffer, 0, 0); - wl_surface_commit(aSurface); + mSurface = std::move(aWaylandSurface); } + LOGWAYLAND( + "WaylandBuffer::BorrowBuffer() [%p] WaylandSurface [%p] wl_buffer [%p]", + (void*)this, aWaylandSurface.get(), buffer); + return buffer; } void WaylandBuffer::BufferReleaseCallbackHandler(wl_buffer* aBuffer) { - mAttached = false; + LOGWAYLAND( + "WaylandBuffer::BufferReleaseCallbackHandler() [%p] WaylandSurface [%p] " + "received wl_buffer [%p]", + (void*)this, mSurface.get(), aBuffer); - if (mBufferReleaseFunc) { - mBufferReleaseFunc(mBufferReleaseData, aBuffer); - } + WaylandSurfaceLock lock(mSurface); + + // We want WaylandBuffer::IsAttached() to return false at DetachedLocked() + // so set WaylandBuffer::mSurface to nullptr now. + RefPtr surface = std::move(mSurface); + surface->DetachedLocked(lock, this); } -void WaylandBuffer::BufferReleaseCallbackHandler(void* aData, - wl_buffer* aBuffer) { - auto* buffer = reinterpret_cast(aData); +static void BufferReleaseCallbackHandler(void* aData, wl_buffer* aBuffer) { + LOGWAYLAND("BufferReleaseCallbackHandler() [%p] received wl_buffer [%p]", + aData, aBuffer); + RefPtr buffer = static_cast(aData); + if (!buffer) { + NS_WARNING( + "WaylandBuffer::BufferReleaseCallbackHandler() called after wl_buffer " + "release. Possible compositor bug?"); + return; + } buffer->BufferReleaseCallbackHandler(aBuffer); } +static const struct wl_buffer_listener sBufferListenerWaylandBuffer = { + BufferReleaseCallbackHandler}; + /* static */ RefPtr WaylandBufferSHM::Create( const LayoutDeviceIntSize& aSize) { RefPtr buffer = new WaylandBufferSHM(aSize); nsWaylandDisplay* waylandDisplay = WaylandDisplayGet(); + LOGWAYLAND("WaylandBufferSHM::Create() [%p] [%d x %d]", (void*)buffer, + aSize.width, aSize.height); + int size = aSize.width * aSize.height * BUFFER_BPP; buffer->mShmPool = WaylandShmPool::Create(waylandDisplay, size); if (!buffer->mShmPool) { + LOGWAYLAND(" failed to create shmPool"); return nullptr; } buffer->mWLBuffer = wl_shm_pool_create_buffer( buffer->mShmPool->GetShmPool(), 0, aSize.width, aSize.height, aSize.width * BUFFER_BPP, WL_SHM_FORMAT_ARGB8888); if (!buffer->mWLBuffer) { + LOGWAYLAND(" failed to create wl_buffer"); return nullptr; } - wl_buffer_add_listener(buffer->GetWlBuffer(), &sBufferListenerWaylandBuffer, - buffer.get()); + if (wl_buffer_add_listener(buffer->GetWlBuffer(), + &sBufferListenerWaylandBuffer, buffer.get()) < 0) { + LOGWAYLAND(" failed to attach listener"); + return nullptr; + } - LOGWAYLAND("WaylandBufferSHM Created [%p] WaylandDisplay [%p]\n", - buffer.get(), waylandDisplay); + LOGWAYLAND(" created [%p] WaylandDisplay [%p]\n", buffer.get(), + waylandDisplay); return buffer; } WaylandBufferSHM::WaylandBufferSHM(const LayoutDeviceIntSize& aSize) - : WaylandBuffer(aSize) {} + : WaylandBuffer(aSize) { + LOGWAYLAND("WaylandBufferSHM::WaylandBufferSHM() [%p]\n", (void*)this); +} WaylandBufferSHM::~WaylandBufferSHM() { + LOGWAYLAND("WaylandBufferSHM::~WaylandBufferSHM() [%p]\n", (void*)this); + ReleaseWlBuffer(); +} + +void WaylandBufferSHM::ReleaseWlBuffer() { + LOGWAYLAND("WaylandBufferSHM::ReleaseWlBuffer() [%p] wl_buffer [%p]\n", + (void*)this, mWLBuffer); + // Set mWLBuffer user data explicitly, ensure BufferReleaseCallbackHandler() + // will get nullptr aData reference. + wl_proxy_set_user_data((struct wl_proxy*)mWLBuffer, nullptr); MozClearPointer(mWLBuffer, wl_buffer_destroy); } already_AddRefed WaylandBufferSHM::Lock() { + LOGWAYLAND("WaylandBufferSHM::lock() [%p]\n", (void*)this); return gfxPlatform::CreateDrawTargetForData( static_cast(mShmPool->GetImageData()), mSize.ToUnknownSize(), BUFFER_BPP * mSize.width, GetSurfaceFormat()); } void WaylandBufferSHM::Clear() { - memset(mShmPool->GetImageData(), 0, mSize.height * mSize.width * BUFFER_BPP); + LOGWAYLAND("WaylandBufferSHM::Clear() [%p]\n", (void*)this); + memset(mShmPool->GetImageData(), 0xff, + mSize.height * mSize.width * BUFFER_BPP); } #ifdef MOZ_LOGGING void WaylandBufferSHM::DumpToFile(const char* aHint) { if (!mDumpSerial) { @@ -195,31 +228,73 @@ } } #endif /* static */ -RefPtr WaylandBufferDMABUF::Create( +already_AddRefed WaylandBufferDMABUF::CreateRGBA( const LayoutDeviceIntSize& aSize, GLContext* aGL) { RefPtr buffer = new WaylandBufferDMABUF(aSize); const auto flags = static_cast(DMABUF_TEXTURE | DMABUF_ALPHA); buffer->mDMABufSurface = DMABufSurfaceRGBA::CreateDMABufSurface(aSize.width, aSize.height, flags); if (!buffer->mDMABufSurface || !buffer->mDMABufSurface->CreateTexture(aGL)) { + LOGWAYLAND(" failed to create texture"); return nullptr; } + LOGWAYLAND("WaylandBufferDMABUF::CreateRGBA() [%p] UID %d [%d x %d]", + (void*)buffer, buffer->mDMABufSurface->GetUID(), aSize.width, + aSize.height); + if (!buffer->mDMABufSurface->CreateWlBuffer()) { + LOGWAYLAND(" failed to create wl_buffer"); return nullptr; } - wl_buffer_add_listener(buffer->GetWlBuffer(), &sBufferListenerWaylandBuffer, - buffer.get()); + if (wl_buffer_add_listener(buffer->GetWlBuffer(), + &sBufferListenerWaylandBuffer, buffer.get()) < 0) { + LOGWAYLAND(" failed to attach listener"); + return nullptr; + } - return buffer; + return buffer.forget(); +} + +/* static */ +already_AddRefed WaylandBufferDMABUF::CreateExternal( + RefPtr aSurface) { + const auto size = + LayoutDeviceIntSize(aSurface->GetWidth(), aSurface->GetWidth()); + RefPtr buffer = new WaylandBufferDMABUF(size); + + LOGWAYLAND("WaylandBufferDMABUF::CreateExternal() [%p] UID %d [%d x %d]", + (void*)buffer, aSurface->GetUID(), size.width, size.height); + + buffer->mDMABufSurface = aSurface; + + if (!buffer->mDMABufSurface->CreateWlBuffer()) { + LOGWAYLAND(" failed to create wl_buffer"); + return nullptr; + } + + if (wl_buffer_add_listener(buffer->GetWlBuffer(), + &sBufferListenerWaylandBuffer, buffer.get()) < 0) { + LOGWAYLAND(" failed to attach listener!"); + return nullptr; + } + + LOGWAYLAND(" wl_buffer [%p]", buffer->GetWlBuffer()); + return buffer.forget(); } WaylandBufferDMABUF::WaylandBufferDMABUF(const LayoutDeviceIntSize& aSize) - : WaylandBuffer(aSize) {} + : WaylandBuffer(aSize) { + LOGWAYLAND("WaylandBufferDMABUF::WaylandBufferDMABUF [%p]\n", (void*)this); +} + +WaylandBufferDMABUF::~WaylandBufferDMABUF() { + LOGWAYLAND("WaylandBufferDMABUF::~WaylandBufferDMABUF [%p]\n", (void*)this); +} } // namespace mozilla::widget