summaryrefslogtreecommitdiffstats
path: root/gfx/layers/SurfacePoolWayland.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'gfx/layers/SurfacePoolWayland.cpp')
-rw-r--r--gfx/layers/SurfacePoolWayland.cpp246
1 files changed, 246 insertions, 0 deletions
diff --git a/gfx/layers/SurfacePoolWayland.cpp b/gfx/layers/SurfacePoolWayland.cpp
new file mode 100644
index 0000000000..e1229fcaaa
--- /dev/null
+++ b/gfx/layers/SurfacePoolWayland.cpp
@@ -0,0 +1,246 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nullptr; c-basic-offset: 2
+ * -*- 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 "mozilla/layers/SurfacePoolWayland.h"
+
+#include "GLBlitHelper.h"
+#include "mozilla/gfx/DataSurfaceHelpers.h"
+
+namespace mozilla::layers {
+
+using gfx::IntSize;
+using gl::DepthAndStencilBuffer;
+using gl::GLContext;
+using gl::MozFramebuffer;
+using widget::WaylandBuffer;
+
+/* static */ RefPtr<SurfacePool> SurfacePool::Create(size_t aPoolSizeLimit) {
+ return new SurfacePoolWayland(aPoolSizeLimit);
+}
+
+SurfacePoolWayland::SurfacePoolWayland(size_t aPoolSizeLimit)
+ : mMutex("SurfacePoolWayland"), mPoolSizeLimit(aPoolSizeLimit) {}
+
+RefPtr<SurfacePoolHandle> SurfacePoolWayland::GetHandleForGL(GLContext* aGL) {
+ return new SurfacePoolHandleWayland(this, aGL);
+}
+
+template <typename F>
+void SurfacePoolWayland::ForEachEntry(F aFn) {
+ for (auto& iter : mInUseEntries) {
+ aFn(iter.second);
+ }
+ for (auto& entry : mPendingEntries) {
+ aFn(entry);
+ }
+ for (auto& entry : mAvailableEntries) {
+ aFn(entry);
+ }
+}
+
+void SurfacePoolWayland::DestroyGLResourcesForContext(GLContext* aGL) {
+ MutexAutoLock lock(mMutex);
+
+ ForEachEntry([&](SurfacePoolEntry& entry) {
+ if (entry.mGLResources && entry.mGLResources->mGL == aGL) {
+ entry.mGLResources = Nothing();
+ entry.mWaylandBuffer->DestroyGLResources();
+ }
+ });
+ mDepthBuffers.RemoveElementsBy(
+ [&](const DepthBufferEntry& entry) { return entry.mGL == aGL; });
+}
+
+bool SurfacePoolWayland::CanRecycleSurfaceForRequest(
+ const MutexAutoLock& aProofOfLock, const SurfacePoolEntry& aEntry,
+ const IntSize& aSize, GLContext* aGL) {
+ if (aEntry.mSize != aSize) {
+ return false;
+ }
+ if (aEntry.mGLResources) {
+ return aEntry.mGLResources->mGL == aGL;
+ }
+ return aGL == nullptr;
+}
+
+RefPtr<WaylandBuffer> SurfacePoolWayland::ObtainBufferFromPool(
+ const IntSize& aSize, GLContext* aGL) {
+ MutexAutoLock lock(mMutex);
+
+ auto iterToRecycle = std::find_if(
+ mAvailableEntries.begin(), mAvailableEntries.end(),
+ [&](const SurfacePoolEntry& aEntry) {
+ return CanRecycleSurfaceForRequest(lock, aEntry, aSize, aGL);
+ });
+ if (iterToRecycle != mAvailableEntries.end()) {
+ RefPtr<WaylandBuffer> buffer = iterToRecycle->mWaylandBuffer;
+ mInUseEntries.insert({buffer.get(), std::move(*iterToRecycle)});
+ mAvailableEntries.RemoveElementAt(iterToRecycle);
+ return buffer;
+ }
+
+ RefPtr<WaylandBuffer> buffer;
+ if (aGL) {
+ buffer = widget::WaylandBufferDMABUF::Create(
+ LayoutDeviceIntSize::FromUnknownSize(aSize), aGL);
+ } else {
+ buffer = widget::WaylandBufferSHM::Create(
+ LayoutDeviceIntSize::FromUnknownSize(aSize));
+ }
+ if (buffer) {
+ mInUseEntries.insert({buffer.get(), SurfacePoolEntry{aSize, buffer, {}}});
+ }
+
+ return buffer;
+}
+
+void SurfacePoolWayland::ReturnBufferToPool(
+ const RefPtr<WaylandBuffer>& aBuffer) {
+ MutexAutoLock lock(mMutex);
+
+ auto inUseEntryIter = mInUseEntries.find(aBuffer);
+ MOZ_RELEASE_ASSERT(inUseEntryIter != mInUseEntries.end());
+
+ if (aBuffer->IsAttached()) {
+ mPendingEntries.AppendElement(std::move(inUseEntryIter->second));
+ mInUseEntries.erase(inUseEntryIter);
+ } else {
+ mAvailableEntries.AppendElement(std::move(inUseEntryIter->second));
+ mInUseEntries.erase(inUseEntryIter);
+ }
+}
+
+void SurfacePoolWayland::EnforcePoolSizeLimit() {
+ MutexAutoLock lock(mMutex);
+
+ // Enforce the pool size limit, removing least-recently-used entries as
+ // necessary.
+ while (mAvailableEntries.Length() > mPoolSizeLimit) {
+ mAvailableEntries.RemoveElementAt(0);
+ }
+
+ NS_WARNING_ASSERTION(mPendingEntries.Length() < mPoolSizeLimit * 2,
+ "Are we leaking pending entries?");
+ NS_WARNING_ASSERTION(mInUseEntries.size() < mPoolSizeLimit * 2,
+ "Are we leaking in-use entries?");
+}
+
+void SurfacePoolWayland::CollectPendingSurfaces() {
+ MutexAutoLock lock(mMutex);
+ mPendingEntries.RemoveElementsBy([&](auto& entry) {
+ if (!entry.mWaylandBuffer->IsAttached()) {
+ mAvailableEntries.AppendElement(std::move(entry));
+ return true;
+ }
+ return false;
+ });
+}
+
+Maybe<GLuint> SurfacePoolWayland::GetFramebufferForBuffer(
+ const RefPtr<WaylandBuffer>& aBuffer, GLContext* aGL,
+ bool aNeedsDepthBuffer) {
+ MutexAutoLock lock(mMutex);
+ MOZ_RELEASE_ASSERT(aGL);
+
+ auto inUseEntryIter = mInUseEntries.find(aBuffer);
+ MOZ_RELEASE_ASSERT(inUseEntryIter != mInUseEntries.end());
+
+ SurfacePoolEntry& entry = inUseEntryIter->second;
+ if (entry.mGLResources) {
+ // We have an existing framebuffer.
+ MOZ_RELEASE_ASSERT(entry.mGLResources->mGL == aGL,
+ "Recycled surface that still had GL resources from a "
+ "different GL context. "
+ "This shouldn't happen.");
+ if (!aNeedsDepthBuffer || entry.mGLResources->mFramebuffer->HasDepth()) {
+ return Some(entry.mGLResources->mFramebuffer->mFB);
+ }
+ }
+
+ // No usable existing framebuffer, we need to create one.
+
+ if (!aGL->MakeCurrent()) {
+ // Context may have been destroyed.
+ return {};
+ }
+
+ const GLuint tex = aBuffer->GetTexture();
+ auto fb = CreateFramebufferForTexture(lock, aGL, entry.mSize, tex,
+ aNeedsDepthBuffer);
+ if (!fb) {
+ // Framebuffer completeness check may have failed.
+ return {};
+ }
+
+ GLuint fbo = fb->mFB;
+ entry.mGLResources = Some(GLResourcesForBuffer{aGL, std::move(fb)});
+ return Some(fbo);
+}
+
+RefPtr<gl::DepthAndStencilBuffer> SurfacePoolWayland::GetDepthBufferForSharing(
+ const MutexAutoLock& aProofOfLock, GLContext* aGL, const IntSize& aSize) {
+ // Clean out entries for which the weak pointer has become null.
+ mDepthBuffers.RemoveElementsBy(
+ [&](const DepthBufferEntry& entry) { return !entry.mBuffer; });
+
+ for (const auto& entry : mDepthBuffers) {
+ if (entry.mGL == aGL && entry.mSize == aSize) {
+ return entry.mBuffer.get();
+ }
+ }
+ return nullptr;
+}
+
+UniquePtr<MozFramebuffer> SurfacePoolWayland::CreateFramebufferForTexture(
+ const MutexAutoLock& aProofOfLock, GLContext* aGL, const IntSize& aSize,
+ GLuint aTexture, bool aNeedsDepthBuffer) {
+ if (aNeedsDepthBuffer) {
+ // Try to find an existing depth buffer of aSize in aGL and create a
+ // framebuffer that shares it.
+ if (auto buffer = GetDepthBufferForSharing(aProofOfLock, aGL, aSize)) {
+ return MozFramebuffer::CreateForBackingWithSharedDepthAndStencil(
+ aSize, 0, LOCAL_GL_TEXTURE_2D, aTexture, buffer);
+ }
+ }
+
+ // No depth buffer needed or we didn't find one. Create a framebuffer with a
+ // new depth buffer and store a weak pointer to the new depth buffer in
+ // mDepthBuffers.
+ UniquePtr<MozFramebuffer> fb = MozFramebuffer::CreateForBacking(
+ aGL, aSize, 0, aNeedsDepthBuffer, LOCAL_GL_TEXTURE_2D, aTexture);
+ if (fb && fb->GetDepthAndStencilBuffer()) {
+ mDepthBuffers.AppendElement(
+ DepthBufferEntry{aGL, aSize, fb->GetDepthAndStencilBuffer().get()});
+ }
+
+ return fb;
+}
+
+SurfacePoolHandleWayland::SurfacePoolHandleWayland(
+ RefPtr<SurfacePoolWayland> aPool, GLContext* aGL)
+ : mPool(std::move(aPool)), mGL(aGL) {}
+
+void SurfacePoolHandleWayland::OnBeginFrame() {
+ mPool->CollectPendingSurfaces();
+}
+
+void SurfacePoolHandleWayland::OnEndFrame() { mPool->EnforcePoolSizeLimit(); }
+
+RefPtr<WaylandBuffer> SurfacePoolHandleWayland::ObtainBufferFromPool(
+ const IntSize& aSize) {
+ return mPool->ObtainBufferFromPool(aSize, mGL);
+}
+
+void SurfacePoolHandleWayland::ReturnBufferToPool(
+ const RefPtr<WaylandBuffer>& aBuffer) {
+ mPool->ReturnBufferToPool(aBuffer);
+}
+
+Maybe<GLuint> SurfacePoolHandleWayland::GetFramebufferForBuffer(
+ const RefPtr<WaylandBuffer>& aBuffer, bool aNeedsDepthBuffer) {
+ return mPool->GetFramebufferForBuffer(aBuffer, mGL, aNeedsDepthBuffer);
+}
+
+} // namespace mozilla::layers