From b21bb9b24145e3f6b66b0e59f0519584aa01a8ab Mon Sep 17 00:00:00 2001 From: stransky Date: Tue, 13 May 2025 08:26:16 +0000 Subject: [PATCH] Bug 1961723 [Linux] Add WaylandBufferDMABUFHolder class to cache wl_buffer and allow WaylandBufferDMABUF to set wl_buffer from external source r=emilio If DMABufSurface are recycled we don't want to create/delete wl_buffer for recycled frames as it eats GPU/power resources. Instead store wl_buffers for DMABuf surfaces and re-use them if possible. The behaviour is inspired by MPV which does the same. Differential Revision: https://phabricator.services.mozilla.com/D248224 --- widget/gtk/WaylandBuffer.cpp | 57 ++++++++++++++++++++++++++++++++++-- widget/gtk/WaylandBuffer.h | 27 +++++++++++++++++ 2 files changed, 81 insertions(+), 3 deletions(-) diff --git a/widget/gtk/WaylandBuffer.cpp b/widget/gtk/WaylandBuffer.cpp index 2fe430280a39..e366a7f09429 100644 --- a/widget/gtk/WaylandBuffer.cpp +++ b/widget/gtk/WaylandBuffer.cpp @@ -123,9 +123,15 @@ void WaylandBuffer::DeleteWlBuffer() { if (!mWLBuffer) { return; } - LOGWAYLAND("WaylandBuffer::DeleteWlBuffer() [%p] wl_buffer [%p]\n", - (void*)this, mWLBuffer); - MozClearPointer(mWLBuffer, wl_buffer_destroy); + LOGWAYLAND("WaylandBuffer::DeleteWlBuffer() [%p] wl_buffer [%p] managed %d", + (void*)this, mWLBuffer, mManagingWLBuffer); + if (mManagingWLBuffer) { + MozClearPointer(mWLBuffer, wl_buffer_destroy); + } else { + // Remove reference to this WaylandBuffer + wl_proxy_set_user_data((wl_proxy*)mWLBuffer, nullptr); + mWLBuffer = nullptr; + } } void WaylandBuffer::ReturnBufferDetached(WaylandSurfaceLock& aSurfaceLock) { @@ -137,6 +143,28 @@ void WaylandBuffer::ReturnBufferDetached(WaylandSurfaceLock& aSurfaceLock) { mAttachedToSurface = nullptr; } +wl_buffer* WaylandBuffer::CreateAndTakeWLBuffer() { + LOGWAYLAND("WaylandBuffer::CreateAndTakeWLBuffer() [%p]", (void*)this); + MOZ_DIAGNOSTIC_ASSERT(!mAttachedToSurface); + + if (!CreateWlBuffer()) { + return nullptr; + } + mManagingWLBuffer = false; + return mWLBuffer; +} + +void WaylandBuffer::SetExternalWLBuffer(wl_buffer* aWLBuffer) { + LOGWAYLAND("WaylandBuffer::SetExternalWLBuffer() [%p] wl_buffer %p", + (void*)this, aWLBuffer); + MOZ_DIAGNOSTIC_ASSERT(!mAttachedToSurface); + MOZ_DIAGNOSTIC_ASSERT(!mWLBuffer); + + mManagingWLBuffer = false; + mWLBuffer = aWLBuffer; + mWLBufferID = reinterpret_cast(mWLBuffer); +} + struct SurfaceAndBuffer { SurfaceAndBuffer(WaylandSurface* aSurface, WaylandBuffer* aBuffer) : mSurface(aSurface), mBuffer(aBuffer) {}; @@ -378,4 +406,27 @@ void WaylandBufferDMABUF::DumpToFile(const char* aHint) { } #endif +WaylandBufferDMABUFHolder::WaylandBufferDMABUFHolder(DMABufSurface* aSurface, + wl_buffer* aWLBuffer) + : mWLBuffer(aWLBuffer) { + mUID = aSurface->GetUID(); + mPID = aSurface->GetPID(); + LOGWAYLAND( + "WaylandBufferDMABUFHolder::WaylandBufferDMABUFHolder wl_buffer [%p] UID " + "%d PID %d", + mWLBuffer, mUID, mPID); +} + +WaylandBufferDMABUFHolder::~WaylandBufferDMABUFHolder() { + LOGWAYLAND( + "WaylandBufferDMABUFHolder::~WaylandBufferDMABUFHolder wl_buffer [%p] " + "UID %d PID %d", + mWLBuffer, mUID, mPID); + MozClearPointer(mWLBuffer, wl_buffer_destroy); +} + +bool WaylandBufferDMABUFHolder::Matches(DMABufSurface* aSurface) const { + return mUID == aSurface->GetUID() && mPID == aSurface->GetPID(); +} + } // namespace mozilla::widget diff --git a/widget/gtk/WaylandBuffer.h b/widget/gtk/WaylandBuffer.h index 3a11e52db1dd..afd3329c0269 100644 --- a/widget/gtk/WaylandBuffer.h +++ b/widget/gtk/WaylandBuffer.h @@ -85,6 +85,13 @@ class WaylandBuffer { virtual void DumpToFile(const char* aHint) = 0; #endif + // Create and move away wl_buffer and mark is as not managed. + // From this point wl_buffer is not owned by WaylandBuffer. + wl_buffer* CreateAndTakeWLBuffer(); + + // Set wl_buffer from external source (WaylandBufferDMABUFHolder). + void SetExternalWLBuffer(wl_buffer* aWLBuffer); + protected: explicit WaylandBuffer(const LayoutDeviceIntSize& aSize); virtual ~WaylandBuffer() = default; @@ -105,6 +112,11 @@ class WaylandBuffer { wl_buffer* mWLBuffer = nullptr; uintptr_t mWLBufferID = 0; + // Owns and manages WL buffer. If set to false, wl_buffer is managed by + // someone else (for instance WaylandBufferDMABUFHolder) + // and WaylandBuffer can't destroy it. + bool mManagingWLBuffer = true; + // Wayland buffer is tied to WaylandSurface. // We keep reference to WaylandSurface until WaylandSurface returns the // buffer. @@ -192,6 +204,21 @@ class WaylandBufferDMABUF final : public WaylandBuffer { RefPtr mDMABufSurface; }; +class WaylandBufferDMABUFHolder final { + public: + bool Matches(DMABufSurface* aSurface) const; + + wl_buffer* GetWLBuffer() { return mWLBuffer; } + + WaylandBufferDMABUFHolder(DMABufSurface* aSurface, wl_buffer* aWLBuffer); + ~WaylandBufferDMABUFHolder(); + + private: + wl_buffer* mWLBuffer = nullptr; + uint32_t mUID = 0; + uint32_t mPID = 0; +}; + } // namespace mozilla::widget #endif // _MOZILLA_WIDGET_GTK_WAYLAND_BUFFER_H