/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; 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/. */ #ifndef mozilla_layers_SurfacePoolCA_h #define mozilla_layers_SurfacePoolCA_h #include #include #include #include "mozilla/Atomics.h" #include "mozilla/DataMutex.h" #include "mozilla/layers/SurfacePool.h" #include "CFTypeRefPtr.h" #include "MozFramebuffer.h" #include "nsISupportsImpl.h" namespace mozilla { namespace gl { class MozFramebuffer; } // namespace gl namespace layers { class SurfacePoolHandleCA; struct SurfacePoolCAWrapperForGL; // An implementation of SurfacePool for IOSurfaces and GL framebuffers. // The goal of having this pool is to avoid creating and destroying IOSurfaces // and framebuffers frequently, because doing so is expensive. // SurfacePoolCA is threadsafe. All its data is wrapped inside LockedPool, and // each access to LockedPool is guarded with a lock through DataMutex. // // The pool satisfies the following requirements: // - It can be shared across windows, even across windows with different // GLContexts. // - The number of unused surfaces that are available for recycling is capped // to a fixed value per pool, regardless of how many windows use that pool. // - When all windows are closed (all handles are gone), no surfaces are kept // alive (the pool is destroyed). // - There is an explicit way of deleting GL resources for a GLContext so that // it can happen at a deterministic time on the right thread. // - Additionally, once a GLContext is no longer being used in any window // (really: any pool handle), all surface-associated GL resources of that // context are destroyed. // - For every IOSurface, only one set of GL resources is in existence at any // given time. We don't want there to be framebuffers in two different // GLContexts for one surface. // - We do not want to recycle an IOSurface that currently has GL resources of // context A for a pool handle that uses context B. // - We need to delay IOSurface recycling until the window server is done with // the surface (`!IOSurfaceIsInUse(surf)`) class SurfacePoolCA final : public SurfacePool { public: // Get a handle for a new window. aGL can be nullptr. RefPtr GetHandleForGL(gl::GLContext* aGL) override; // Destroy all GL resources associated with aGL managed by this pool. void DestroyGLResourcesForContext(gl::GLContext* aGL) override; private: friend struct SurfacePoolCAWrapperForGL; friend class SurfacePoolHandleCA; friend RefPtr SurfacePool::Create(size_t aPoolSizeLimit); explicit SurfacePoolCA(size_t aPoolSizeLimit); ~SurfacePoolCA() override; // Get an existing surface of aSize from the pool or create a new surface. // The returned surface is guaranteed not to be in use by the window server. CFTypeRefPtr ObtainSurfaceFromPool(const gfx::IntSize& aSize, gl::GLContext* aGL); // Place a surface that was previously obtained from this pool back into the // pool. aSurface may or may not be in use by the window server. void ReturnSurfaceToPool(CFTypeRefPtr aSurface); // Re-run checks whether the window server still uses IOSurfaces which are // eligible for recycling. The purpose of the "generation" counter is to // reduce the number of calls to IOSurfaceIsInUse in a scenario where many // windows / handles are calling CollectPendingSurfaces in the same frame // (i.e. multiple simultaneously-animating windows). uint64_t CollectPendingSurfaces(uint64_t aCheckGenerationsUpTo); // Enforce the pool size limit by evicting surfaces as necessary. This should // happen at the end of the frame so that we can temporarily exceed the limit // within a frame. void EnforcePoolSizeLimit(); // Get or create the framebuffer for the given surface and GL context. // The returned framebuffer handle will become invalid once // DestroyGLResourcesForContext or DecrementGLContextHandleCount are called. // The framebuffer's depth buffer (if present) may be shared between multiple // framebuffers! Do not assume anything about the depth buffer's existing // contents (i.e. clear it at the beginning of the draw), and do not // interleave drawing commands to different framebuffers in such a way that // the shared depth buffer could cause trouble. Maybe GetFramebufferForSurface(CFTypeRefPtr aSurface, gl::GLContext* aGL, bool aNeedsDepthBuffer); // Called by the destructor of SurfacePoolCAWrapperForGL so that we can clear // our weak reference to it and delete GL resources. void OnWrapperDestroyed(gl::GLContext* aGL, SurfacePoolCAWrapperForGL* aWrapper); // The actual pool implementation lives in LockedPool, which is accessed in // a thread-safe manner. struct LockedPool { explicit LockedPool(size_t aPoolSizeLimit); LockedPool(LockedPool&&) = default; ~LockedPool(); RefPtr GetWrapperForGL(SurfacePoolCA* aPool, gl::GLContext* aGL); void DestroyGLResourcesForContext(gl::GLContext* aGL); CFTypeRefPtr ObtainSurfaceFromPool(const gfx::IntSize& aSize, gl::GLContext* aGL); void ReturnSurfaceToPool(CFTypeRefPtr aSurface); uint64_t CollectPendingSurfaces(uint64_t aCheckGenerationsUpTo); void EnforcePoolSizeLimit(); Maybe GetFramebufferForSurface(CFTypeRefPtr aSurface, gl::GLContext* aGL, bool aNeedsDepthBuffer); void OnWrapperDestroyed(gl::GLContext* aGL, SurfacePoolCAWrapperForGL* aWrapper); uint64_t EstimateTotalMemory(); uint64_t mCollectionGeneration = 0; protected: struct GLResourcesForSurface { RefPtr mGLContext; // non-null UniquePtr mFramebuffer; // non-null }; struct SurfacePoolEntry { gfx::IntSize mSize; CFTypeRefPtr mIOSurface; // non-null Maybe mGLResources; }; struct PendingSurfaceEntry { SurfacePoolEntry mEntry; // The value of LockedPool::mCollectionGeneration at the time // IOSurfaceIsInUse was last called for mEntry.mIOSurface. uint64_t mPreviousCheckGeneration; // The number of times an IOSurfaceIsInUse check has been performed. uint64_t mCheckCount; }; template void MutateEntryStorage(const char* aMutationType, const gfx::IntSize& aSize, F aFn); template void ForEachEntry(F aFn); bool CanRecycleSurfaceForRequest(const SurfacePoolEntry& aEntry, const gfx::IntSize& aSize, gl::GLContext* aGL); RefPtr GetDepthBufferForSharing( gl::GLContext* aGL, const gfx::IntSize& aSize); UniquePtr CreateFramebufferForTexture( gl::GLContext* aGL, const gfx::IntSize& aSize, GLuint aTexture, bool aNeedsDepthBuffer); // Every IOSurface that is managed by the pool is wrapped in a // SurfacePoolEntry object. Every entry is stored in one of three buckets at // any given time: mInUseEntries, mPendingEntries, or mAvailableEntries. All // mutations to these buckets are performed via calls to // MutateEntryStorage(). Entries can move between the buckets in the // following ways: // // [new] // | Create // v // +----------------------------------------------------------------+ // | mInUseEntries | // +------+------------------------------+--------------------------+ // | ^ | Start waiting for // | | Recycle v // | | +-----------------------------+ // | | | mPendingEntries | // | | +--+--------------------+-----+ // | Retain | | Stop waiting for | // v | v | // +-------------------+-------------------------+ | // | mAvailableEntries | | // +-----------------------------+---------------+ | // | Evict | Eject // v v // [destroyed] [destroyed] // // Each arrow corresponds to one invocation of MutateEntryStorage() with the // arrow's label passed as the aMutationType string parameter. // Stores the entries for surfaces that are in use by NativeLayerCA, i.e. an // entry is inside mInUseEntries between calls to ObtainSurfaceFromPool() // and ReturnSurfaceToPool(). std::unordered_map, SurfacePoolEntry> mInUseEntries; // Stores entries which are no longer in use by NativeLayerCA but are still // in use by the window server, i.e. for which // IOSurfaceIsInUse(pendingSurfaceEntry.mEntry.mIOSurface.get()) still // returns true. These entries are checked once per frame inside // CollectPendingSurfaces(), and returned to mAvailableEntries once the // window server is done. nsTArray mPendingEntries; // Stores entries which are available for recycling. These entries are not // in use by a NativeLayerCA or by the window server. nsTArray mAvailableEntries; // Keeps weak references to SurfacePoolCAWrapperForGL instances. // For each GLContext* value (including nullptr), only one wrapper can // exist at any time. The wrapper keeps a strong reference to us and // notifies us when it gets destroyed. At that point we can call // DestroyGLResourcesForContext because we know no other SurfaceHandles for // that context exist. std::unordered_map mWrappers; size_t mPoolSizeLimit = 0; struct DepthBufferEntry { RefPtr mGLContext; gfx::IntSize mSize; WeakPtr mBuffer; }; nsTArray mDepthBuffers; }; DataMutex mPool; }; // One process-wide instance per (SurfacePoolCA*, GLContext*) pair. // Keeps the SurfacePool alive, and the SurfacePool has a weak reference to the // wrapper so that it can ensure that there's only one wrapper for it per // GLContext* at any time. struct SurfacePoolCAWrapperForGL { NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SurfacePoolCAWrapperForGL); const RefPtr mPool; // non-null const RefPtr mGL; // can be null SurfacePoolCAWrapperForGL(SurfacePoolCA* aPool, gl::GLContext* aGL) : mPool(aPool), mGL(aGL) {} protected: ~SurfacePoolCAWrapperForGL() { mPool->OnWrapperDestroyed(mGL, this); } }; // A surface pool handle that is stored on NativeLayerCA and keeps the // SurfacePool alive. class SurfacePoolHandleCA final : public SurfacePoolHandle { public: SurfacePoolHandleCA* AsSurfacePoolHandleCA() override { return this; } const auto& gl() { return mPoolWrapper->mGL; } CFTypeRefPtr ObtainSurfaceFromPool(const gfx::IntSize& aSize); void ReturnSurfaceToPool(CFTypeRefPtr aSurface); Maybe GetFramebufferForSurface(CFTypeRefPtr aSurface, bool aNeedsDepthBuffer); RefPtr Pool() override { return mPoolWrapper->mPool; } void OnBeginFrame() override; void OnEndFrame() override; private: friend class SurfacePoolCA; SurfacePoolHandleCA(RefPtr&& aPoolWrapper, uint64_t aCurrentCollectionGeneration); ~SurfacePoolHandleCA() override; const RefPtr mPoolWrapper; DataMutex mPreviousFrameCollectionGeneration; }; } // namespace layers } // namespace mozilla #endif // mozilla_layers_SurfacePoolCA_h