1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nullptr; c-basic-offset: 2
2 * -*- This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "mozilla/layers/SurfacePoolWayland.h"
8 #include "GLBlitHelper.h"
9 #include "mozilla/gfx/DataSurfaceHelpers.h"
11 namespace mozilla::layers
{
14 using gl::DepthAndStencilBuffer
;
16 using gl::MozFramebuffer
;
17 using widget::WaylandBuffer
;
19 /* static */ RefPtr
<SurfacePool
> SurfacePool::Create(size_t aPoolSizeLimit
) {
20 return new SurfacePoolWayland(aPoolSizeLimit
);
23 SurfacePoolWayland::SurfacePoolWayland(size_t aPoolSizeLimit
)
24 : mMutex("SurfacePoolWayland"), mPoolSizeLimit(aPoolSizeLimit
) {}
26 RefPtr
<SurfacePoolHandle
> SurfacePoolWayland::GetHandleForGL(GLContext
* aGL
) {
27 return new SurfacePoolHandleWayland(this, aGL
);
31 void SurfacePoolWayland::ForEachEntry(F aFn
) {
32 for (auto& iter
: mInUseEntries
) {
35 for (auto& entry
: mPendingEntries
) {
38 for (auto& entry
: mAvailableEntries
) {
43 void SurfacePoolWayland::DestroyGLResourcesForContext(GLContext
* aGL
) {
44 MutexAutoLock
lock(mMutex
);
46 ForEachEntry([&](SurfacePoolEntry
& entry
) {
47 if (entry
.mGLResources
&& entry
.mGLResources
->mGL
== aGL
) {
48 entry
.mGLResources
= Nothing();
49 entry
.mWaylandBuffer
->DestroyGLResources();
52 mDepthBuffers
.RemoveElementsBy(
53 [&](const DepthBufferEntry
& entry
) { return entry
.mGL
== aGL
; });
56 bool SurfacePoolWayland::CanRecycleSurfaceForRequest(
57 const MutexAutoLock
& aProofOfLock
, const SurfacePoolEntry
& aEntry
,
58 const IntSize
& aSize
, GLContext
* aGL
) {
59 if (aEntry
.mSize
!= aSize
) {
62 if (aEntry
.mGLResources
) {
63 return aEntry
.mGLResources
->mGL
== aGL
;
65 return aGL
== nullptr;
68 RefPtr
<WaylandBuffer
> SurfacePoolWayland::ObtainBufferFromPool(
69 const IntSize
& aSize
, GLContext
* aGL
) {
70 MutexAutoLock
lock(mMutex
);
72 auto iterToRecycle
= std::find_if(
73 mAvailableEntries
.begin(), mAvailableEntries
.end(),
74 [&](const SurfacePoolEntry
& aEntry
) {
75 return CanRecycleSurfaceForRequest(lock
, aEntry
, aSize
, aGL
);
77 if (iterToRecycle
!= mAvailableEntries
.end()) {
78 RefPtr
<WaylandBuffer
> buffer
= iterToRecycle
->mWaylandBuffer
;
79 mInUseEntries
.insert({buffer
.get(), std::move(*iterToRecycle
)});
80 mAvailableEntries
.RemoveElementAt(iterToRecycle
);
84 RefPtr
<WaylandBuffer
> buffer
;
86 buffer
= widget::WaylandBufferDMABUF::Create(
87 LayoutDeviceIntSize::FromUnknownSize(aSize
), aGL
);
89 buffer
= widget::WaylandBufferSHM::Create(
90 LayoutDeviceIntSize::FromUnknownSize(aSize
));
93 mInUseEntries
.insert({buffer
.get(), SurfacePoolEntry
{aSize
, buffer
, {}}});
99 void SurfacePoolWayland::ReturnBufferToPool(
100 const RefPtr
<WaylandBuffer
>& aBuffer
) {
101 MutexAutoLock
lock(mMutex
);
103 auto inUseEntryIter
= mInUseEntries
.find(aBuffer
);
104 MOZ_RELEASE_ASSERT(inUseEntryIter
!= mInUseEntries
.end());
106 if (aBuffer
->IsAttached()) {
107 mPendingEntries
.AppendElement(std::move(inUseEntryIter
->second
));
108 mInUseEntries
.erase(inUseEntryIter
);
110 mAvailableEntries
.AppendElement(std::move(inUseEntryIter
->second
));
111 mInUseEntries
.erase(inUseEntryIter
);
115 void SurfacePoolWayland::EnforcePoolSizeLimit() {
116 MutexAutoLock
lock(mMutex
);
118 // Enforce the pool size limit, removing least-recently-used entries as
120 while (mAvailableEntries
.Length() > mPoolSizeLimit
) {
121 mAvailableEntries
.RemoveElementAt(0);
124 NS_WARNING_ASSERTION(mPendingEntries
.Length() < mPoolSizeLimit
* 2,
125 "Are we leaking pending entries?");
126 NS_WARNING_ASSERTION(mInUseEntries
.size() < mPoolSizeLimit
* 2,
127 "Are we leaking in-use entries?");
130 void SurfacePoolWayland::CollectPendingSurfaces() {
131 MutexAutoLock
lock(mMutex
);
132 mPendingEntries
.RemoveElementsBy([&](auto& entry
) {
133 if (!entry
.mWaylandBuffer
->IsAttached()) {
134 mAvailableEntries
.AppendElement(std::move(entry
));
141 Maybe
<GLuint
> SurfacePoolWayland::GetFramebufferForBuffer(
142 const RefPtr
<WaylandBuffer
>& aBuffer
, GLContext
* aGL
,
143 bool aNeedsDepthBuffer
) {
144 MutexAutoLock
lock(mMutex
);
145 MOZ_RELEASE_ASSERT(aGL
);
147 auto inUseEntryIter
= mInUseEntries
.find(aBuffer
);
148 MOZ_RELEASE_ASSERT(inUseEntryIter
!= mInUseEntries
.end());
150 SurfacePoolEntry
& entry
= inUseEntryIter
->second
;
151 if (entry
.mGLResources
) {
152 // We have an existing framebuffer.
153 MOZ_RELEASE_ASSERT(entry
.mGLResources
->mGL
== aGL
,
154 "Recycled surface that still had GL resources from a "
155 "different GL context. "
156 "This shouldn't happen.");
157 if (!aNeedsDepthBuffer
|| entry
.mGLResources
->mFramebuffer
->HasDepth()) {
158 return Some(entry
.mGLResources
->mFramebuffer
->mFB
);
162 // No usable existing framebuffer, we need to create one.
164 if (!aGL
->MakeCurrent()) {
165 // Context may have been destroyed.
169 const GLuint tex
= aBuffer
->GetTexture();
170 auto fb
= CreateFramebufferForTexture(lock
, aGL
, entry
.mSize
, tex
,
173 // Framebuffer completeness check may have failed.
177 GLuint fbo
= fb
->mFB
;
178 entry
.mGLResources
= Some(GLResourcesForBuffer
{aGL
, std::move(fb
)});
182 RefPtr
<gl::DepthAndStencilBuffer
> SurfacePoolWayland::GetDepthBufferForSharing(
183 const MutexAutoLock
& aProofOfLock
, GLContext
* aGL
, const IntSize
& aSize
) {
184 // Clean out entries for which the weak pointer has become null.
185 mDepthBuffers
.RemoveElementsBy(
186 [&](const DepthBufferEntry
& entry
) { return !entry
.mBuffer
; });
188 for (const auto& entry
: mDepthBuffers
) {
189 if (entry
.mGL
== aGL
&& entry
.mSize
== aSize
) {
190 return entry
.mBuffer
.get();
196 UniquePtr
<MozFramebuffer
> SurfacePoolWayland::CreateFramebufferForTexture(
197 const MutexAutoLock
& aProofOfLock
, GLContext
* aGL
, const IntSize
& aSize
,
198 GLuint aTexture
, bool aNeedsDepthBuffer
) {
199 if (aNeedsDepthBuffer
) {
200 // Try to find an existing depth buffer of aSize in aGL and create a
201 // framebuffer that shares it.
202 if (auto buffer
= GetDepthBufferForSharing(aProofOfLock
, aGL
, aSize
)) {
203 return MozFramebuffer::CreateForBackingWithSharedDepthAndStencil(
204 aSize
, 0, LOCAL_GL_TEXTURE_2D
, aTexture
, buffer
);
208 // No depth buffer needed or we didn't find one. Create a framebuffer with a
209 // new depth buffer and store a weak pointer to the new depth buffer in
211 UniquePtr
<MozFramebuffer
> fb
= MozFramebuffer::CreateForBacking(
212 aGL
, aSize
, 0, aNeedsDepthBuffer
, LOCAL_GL_TEXTURE_2D
, aTexture
);
213 if (fb
&& fb
->GetDepthAndStencilBuffer()) {
214 mDepthBuffers
.AppendElement(
215 DepthBufferEntry
{aGL
, aSize
, fb
->GetDepthAndStencilBuffer().get()});
221 SurfacePoolHandleWayland::SurfacePoolHandleWayland(
222 RefPtr
<SurfacePoolWayland
> aPool
, GLContext
* aGL
)
223 : mPool(std::move(aPool
)), mGL(aGL
) {}
225 void SurfacePoolHandleWayland::OnBeginFrame() {
226 mPool
->CollectPendingSurfaces();
229 void SurfacePoolHandleWayland::OnEndFrame() { mPool
->EnforcePoolSizeLimit(); }
231 RefPtr
<WaylandBuffer
> SurfacePoolHandleWayland::ObtainBufferFromPool(
232 const IntSize
& aSize
) {
233 return mPool
->ObtainBufferFromPool(aSize
, mGL
);
236 void SurfacePoolHandleWayland::ReturnBufferToPool(
237 const RefPtr
<WaylandBuffer
>& aBuffer
) {
238 mPool
->ReturnBufferToPool(aBuffer
);
241 Maybe
<GLuint
> SurfacePoolHandleWayland::GetFramebufferForBuffer(
242 const RefPtr
<WaylandBuffer
>& aBuffer
, bool aNeedsDepthBuffer
) {
243 return mPool
->GetFramebufferForBuffer(aBuffer
, mGL
, aNeedsDepthBuffer
);
246 } // namespace mozilla::layers