SurfacePoolWayland.cpp (10360B)
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/. */ 5 6 #include "mozilla/layers/SurfacePoolWayland.h" 7 8 #include "GLBlitHelper.h" 9 #include "mozilla/gfx/DataSurfaceHelpers.h" 10 11 #ifdef MOZ_LOGGING 12 # undef LOG 13 # undef LOGVERBOSE 14 # include "mozilla/Logging.h" 15 # include "nsTArray.h" 16 # include "Units.h" 17 extern mozilla::LazyLogModule gWidgetCompositorLog; 18 # define LOG(str, ...) \ 19 MOZ_LOG(gWidgetCompositorLog, mozilla::LogLevel::Debug, \ 20 (str, ##__VA_ARGS__)) 21 # define LOGVERBOSE(str, ...) \ 22 MOZ_LOG(gWidgetCompositorLog, mozilla::LogLevel::Verbose, \ 23 (str, ##__VA_ARGS__)) 24 #else 25 # define LOG(args) 26 # define LOGVERBOSE(args) 27 #endif /* MOZ_LOGGING */ 28 29 namespace mozilla::layers { 30 31 using gfx::IntSize; 32 using gl::DepthAndStencilBuffer; 33 using gl::GLContext; 34 using gl::MozFramebuffer; 35 using widget::WaylandBuffer; 36 37 /* static */ RefPtr<SurfacePool> SurfacePool::Create(size_t aPoolSizeLimit) { 38 return new SurfacePoolWayland(aPoolSizeLimit); 39 } 40 41 SurfacePoolWayland::SurfacePoolWayland(size_t aPoolSizeLimit) 42 : mMutex("SurfacePoolWayland"), mPoolSizeLimit(aPoolSizeLimit) {} 43 44 RefPtr<SurfacePoolHandle> SurfacePoolWayland::GetHandleForGL(GLContext* aGL) { 45 return new SurfacePoolHandleWayland(this, aGL); 46 } 47 48 template <typename F> 49 void SurfacePoolWayland::ForEachEntry(F aFn) { 50 for (auto& iter : mInUseEntries) { 51 aFn(iter.second); 52 } 53 for (auto& entry : mPendingEntries) { 54 aFn(entry); 55 } 56 for (auto& entry : mAvailableEntries) { 57 aFn(entry); 58 } 59 } 60 61 void SurfacePoolWayland::DestroyGLResourcesForContext(GLContext* aGL) { 62 MutexAutoLock lock(mMutex); 63 64 ForEachEntry([&](SurfacePoolEntry& entry) { 65 if (entry.mGLResources && entry.mGLResources->mGL == aGL) { 66 entry.mGLResources = Nothing(); 67 entry.mWaylandBuffer->DestroyGLResources(); 68 } 69 }); 70 mDepthBuffers.RemoveElementsBy( 71 [&](const DepthBufferEntry& entry) { return entry.mGL == aGL; }); 72 } 73 74 bool SurfacePoolWayland::CanRecycleSurfaceForRequest( 75 const MutexAutoLock& aProofOfLock, const SurfacePoolEntry& aEntry, 76 const IntSize& aSize, GLContext* aGL) { 77 MOZ_DIAGNOSTIC_ASSERT(!aEntry.mWaylandBuffer->IsAttached()); 78 if (aEntry.mSize != aSize) { 79 LOGVERBOSE( 80 "SurfacePoolWayland::CanRecycleSurfaceForRequest(): can't recycle due " 81 "to different sizes."); 82 return false; 83 } 84 if (aEntry.mGLResources) { 85 LOGVERBOSE( 86 "SurfacePoolWayland::CanRecycleSurfaceForRequest(): mGLResources " 87 "recycle %d", 88 aEntry.mGLResources->mGL == aGL); 89 return aEntry.mGLResources->mGL == aGL; 90 } 91 LOGVERBOSE( 92 "SurfacePoolWayland::CanRecycleSurfaceForRequest(): aGL recycle %d", 93 aGL == nullptr); 94 return aGL == nullptr; 95 } 96 97 RefPtr<WaylandBuffer> SurfacePoolWayland::ObtainBufferFromPool( 98 const IntSize& aSize, GLContext* aGL, RefPtr<widget::DRMFormat> aFormat) { 99 MutexAutoLock lock(mMutex); 100 101 auto iterToRecycle = std::find_if( 102 mAvailableEntries.begin(), mAvailableEntries.end(), 103 [&](const SurfacePoolEntry& aEntry) { 104 return CanRecycleSurfaceForRequest(lock, aEntry, aSize, aGL); 105 }); 106 if (iterToRecycle != mAvailableEntries.end()) { 107 RefPtr<WaylandBuffer> buffer = iterToRecycle->mWaylandBuffer; 108 mInUseEntries.insert({buffer.get(), std::move(*iterToRecycle)}); 109 mAvailableEntries.RemoveElementAt(iterToRecycle); 110 LOGVERBOSE( 111 "SurfacePoolWayland::ObtainBufferFromPool() recycled [%p] U[%zu] " 112 "P[%zu] " 113 "A[%zu]", 114 buffer.get(), mInUseEntries.size(), mPendingEntries.Length(), 115 mAvailableEntries.Length()); 116 return buffer; 117 } 118 119 RefPtr<WaylandBuffer> buffer; 120 if (aGL) { 121 buffer = widget::WaylandBufferDMABUF::CreateRGBA( 122 LayoutDeviceIntSize::FromUnknownSize(aSize), aGL, aFormat); 123 } else { 124 buffer = widget::WaylandBufferSHM::Create( 125 LayoutDeviceIntSize::FromUnknownSize(aSize)); 126 } 127 if (buffer) { 128 mInUseEntries.insert({buffer.get(), SurfacePoolEntry{aSize, buffer, {}}}); 129 } 130 LOGVERBOSE( 131 "SurfacePoolWayland::ObtainBufferFromPool() created [%p] U[%d] P[%d] " 132 "A[%d]", 133 buffer.get(), (int)mInUseEntries.size(), (int)mPendingEntries.Length(), 134 (int)mAvailableEntries.Length()); 135 return buffer; 136 } 137 138 void SurfacePoolWayland::ReturnBufferToPool( 139 const RefPtr<WaylandBuffer>& aBuffer) { 140 MutexAutoLock lock(mMutex); 141 142 auto inUseEntryIter = mInUseEntries.find(aBuffer); 143 MOZ_RELEASE_ASSERT(inUseEntryIter != mInUseEntries.end()); 144 145 if (aBuffer->IsAttached()) { 146 mPendingEntries.AppendElement(std::move(inUseEntryIter->second)); 147 } else { 148 mAvailableEntries.AppendElement(std::move(inUseEntryIter->second)); 149 } 150 mInUseEntries.erase(inUseEntryIter); 151 152 LOGVERBOSE( 153 "SurfacePoolWayland::ReturnBufferToPool() buffer [%p] U[%d] P[%d] A[%d]", 154 aBuffer.get(), (int)mInUseEntries.size(), (int)mPendingEntries.Length(), 155 (int)mAvailableEntries.Length()); 156 } 157 158 void SurfacePoolWayland::EnforcePoolSizeLimit() { 159 MutexAutoLock lock(mMutex); 160 161 // Enforce the pool size limit, removing least-recently-used entries as 162 // necessary. 163 while (mAvailableEntries.Length() > mPoolSizeLimit) { 164 mAvailableEntries.RemoveElementAt(0); 165 } 166 167 if (mPendingEntries.Length() > mPoolSizeLimit * 2) { 168 LOG("SurfacePoolWayland() mPendingEntries num %d mPoolSizeLimit %d Are we " 169 "leaking pending entries?", 170 (int)mPendingEntries.Length(), (int)mPoolSizeLimit); 171 } 172 if (mInUseEntries.size() > mPoolSizeLimit * 2) { 173 LOG("SurfacePoolWayland() mInUseEntries num %d mPoolSizeLimit %d Are we " 174 "leaking in-use entries?", 175 (int)mInUseEntries.size(), (int)mPoolSizeLimit); 176 } 177 } 178 179 void SurfacePoolWayland::CollectPendingSurfaces() { 180 MutexAutoLock lock(mMutex); 181 mPendingEntries.RemoveElementsBy([&](auto& entry) { 182 LOGVERBOSE( 183 "SurfacePoolWayland::CollectPendingSurfaces() [%p] attached [%d]", 184 entry.mWaylandBuffer.get(), entry.mWaylandBuffer->IsAttached()); 185 if (!entry.mWaylandBuffer->IsAttached()) { 186 mAvailableEntries.AppendElement(std::move(entry)); 187 return true; 188 } 189 return false; 190 }); 191 LOGVERBOSE("SurfacePoolWayland::CollectPendingSurfaces() U[%d] P[%d] A[%d]", 192 (int)mInUseEntries.size(), (int)mPendingEntries.Length(), 193 (int)mAvailableEntries.Length()); 194 } 195 196 Maybe<GLuint> SurfacePoolWayland::GetFramebufferForBuffer( 197 const RefPtr<WaylandBuffer>& aBuffer, GLContext* aGL, 198 bool aNeedsDepthBuffer) { 199 MutexAutoLock lock(mMutex); 200 MOZ_RELEASE_ASSERT(aGL); 201 202 auto inUseEntryIter = mInUseEntries.find(aBuffer); 203 MOZ_RELEASE_ASSERT(inUseEntryIter != mInUseEntries.end()); 204 205 SurfacePoolEntry& entry = inUseEntryIter->second; 206 if (entry.mGLResources) { 207 // We have an existing framebuffer. 208 MOZ_RELEASE_ASSERT(entry.mGLResources->mGL == aGL, 209 "Recycled surface that still had GL resources from a " 210 "different GL context. " 211 "This shouldn't happen."); 212 if (!aNeedsDepthBuffer || entry.mGLResources->mFramebuffer->HasDepth()) { 213 return Some(entry.mGLResources->mFramebuffer->mFB); 214 } 215 } 216 217 // No usable existing framebuffer, we need to create one. 218 219 if (!aGL->MakeCurrent()) { 220 // Context may have been destroyed. 221 return {}; 222 } 223 224 const GLuint tex = aBuffer->GetTexture(); 225 auto fb = CreateFramebufferForTexture(lock, aGL, entry.mSize, tex, 226 aNeedsDepthBuffer); 227 if (!fb) { 228 // Framebuffer completeness check may have failed. 229 return {}; 230 } 231 232 GLuint fbo = fb->mFB; 233 entry.mGLResources = Some(GLResourcesForBuffer{aGL, std::move(fb)}); 234 return Some(fbo); 235 } 236 237 RefPtr<gl::DepthAndStencilBuffer> SurfacePoolWayland::GetDepthBufferForSharing( 238 const MutexAutoLock& aProofOfLock, GLContext* aGL, const IntSize& aSize) { 239 // Clean out entries for which the weak pointer has become null. 240 mDepthBuffers.RemoveElementsBy( 241 [&](const DepthBufferEntry& entry) { return !entry.mBuffer; }); 242 243 for (const auto& entry : mDepthBuffers) { 244 if (entry.mGL == aGL && entry.mSize == aSize) { 245 return entry.mBuffer.get(); 246 } 247 } 248 return nullptr; 249 } 250 251 UniquePtr<MozFramebuffer> SurfacePoolWayland::CreateFramebufferForTexture( 252 const MutexAutoLock& aProofOfLock, GLContext* aGL, const IntSize& aSize, 253 GLuint aTexture, bool aNeedsDepthBuffer) { 254 if (aNeedsDepthBuffer) { 255 // Try to find an existing depth buffer of aSize in aGL and create a 256 // framebuffer that shares it. 257 if (auto buffer = GetDepthBufferForSharing(aProofOfLock, aGL, aSize)) { 258 return MozFramebuffer::CreateForBackingWithSharedDepthAndStencil( 259 aSize, 0, LOCAL_GL_TEXTURE_2D, aTexture, buffer); 260 } 261 } 262 263 // No depth buffer needed or we didn't find one. Create a framebuffer with a 264 // new depth buffer and store a weak pointer to the new depth buffer in 265 // mDepthBuffers. 266 UniquePtr<MozFramebuffer> fb = MozFramebuffer::CreateForBacking( 267 aGL, aSize, 0, aNeedsDepthBuffer, LOCAL_GL_TEXTURE_2D, aTexture); 268 if (fb && fb->GetDepthAndStencilBuffer()) { 269 mDepthBuffers.AppendElement( 270 DepthBufferEntry{aGL, aSize, fb->GetDepthAndStencilBuffer().get()}); 271 } 272 273 return fb; 274 } 275 276 SurfacePoolHandleWayland::SurfacePoolHandleWayland( 277 RefPtr<SurfacePoolWayland> aPool, GLContext* aGL) 278 : mPool(std::move(aPool)), mGL(aGL) {} 279 280 void SurfacePoolHandleWayland::OnBeginFrame() { 281 mPool->CollectPendingSurfaces(); 282 } 283 284 void SurfacePoolHandleWayland::OnEndFrame() { mPool->EnforcePoolSizeLimit(); } 285 286 RefPtr<WaylandBuffer> SurfacePoolHandleWayland::ObtainBufferFromPool( 287 const IntSize& aSize, RefPtr<widget::DRMFormat> aFormat) { 288 return mPool->ObtainBufferFromPool(aSize, mGL, aFormat); 289 } 290 291 void SurfacePoolHandleWayland::ReturnBufferToPool( 292 const RefPtr<WaylandBuffer>& aBuffer) { 293 mPool->ReturnBufferToPool(aBuffer); 294 } 295 296 Maybe<GLuint> SurfacePoolHandleWayland::GetFramebufferForBuffer( 297 const RefPtr<WaylandBuffer>& aBuffer, bool aNeedsDepthBuffer) { 298 return mPool->GetFramebufferForBuffer(aBuffer, mGL, aNeedsDepthBuffer); 299 } 300 301 } // namespace mozilla::layers