mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-12-11 08:13:35 +00:00
df5cfbc8df
Differential Revision: https://phabricator.services.mozilla.com/D54863 --HG-- extra : moz-landing-system : lando
288 lines
13 KiB
C++
288 lines
13 KiB
C++
/* -*- 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 <IOSurface/IOSurface.h>
|
|
|
|
#include <deque>
|
|
#include <unordered_map>
|
|
|
|
#include "mozilla/Atomics.h"
|
|
#include "mozilla/DataMutex.h"
|
|
|
|
#include "mozilla/layers/SurfacePool.h"
|
|
#include "CFTypeRefPtr.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<SurfacePoolHandle> 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> 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<IOSurfaceRef> 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<IOSurfaceRef> 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<GLuint> GetFramebufferForSurface(CFTypeRefPtr<IOSurfaceRef> 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<SurfacePoolCAWrapperForGL> GetWrapperForGL(SurfacePoolCA* aPool,
|
|
gl::GLContext* aGL);
|
|
void DestroyGLResourcesForContext(gl::GLContext* aGL);
|
|
|
|
CFTypeRefPtr<IOSurfaceRef> ObtainSurfaceFromPool(const gfx::IntSize& aSize,
|
|
gl::GLContext* aGL);
|
|
void ReturnSurfaceToPool(CFTypeRefPtr<IOSurfaceRef> aSurface);
|
|
uint64_t CollectPendingSurfaces(uint64_t aCheckGenerationsUpTo);
|
|
void EnforcePoolSizeLimit();
|
|
Maybe<GLuint> GetFramebufferForSurface(CFTypeRefPtr<IOSurfaceRef> aSurface,
|
|
gl::GLContext* aGL,
|
|
bool aNeedsDepthBuffer);
|
|
void OnWrapperDestroyed(gl::GLContext* aGL,
|
|
SurfacePoolCAWrapperForGL* aWrapper);
|
|
uint64_t EstimateTotalMemory();
|
|
|
|
uint64_t mCollectionGeneration = 0;
|
|
|
|
protected:
|
|
struct GLResourcesForSurface {
|
|
RefPtr<gl::GLContext> mGLContext; // non-null
|
|
UniquePtr<gl::MozFramebuffer> mFramebuffer; // non-null
|
|
};
|
|
|
|
struct SurfacePoolEntry {
|
|
gfx::IntSize mSize;
|
|
CFTypeRefPtr<IOSurfaceRef> mIOSurface; // non-null
|
|
Maybe<GLResourcesForSurface> 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 <typename F>
|
|
void MutateEntryStorage(const char* aMutationType,
|
|
const gfx::IntSize& aSize, F aFn);
|
|
|
|
template <typename F>
|
|
void ForEachEntry(F aFn);
|
|
|
|
bool CanRecycleSurfaceForRequest(const SurfacePoolEntry& aEntry,
|
|
const gfx::IntSize& aSize,
|
|
gl::GLContext* aGL);
|
|
|
|
RefPtr<gl::DepthAndStencilBuffer> GetDepthBufferForSharing(
|
|
gl::GLContext* aGL, const gfx::IntSize& aSize);
|
|
UniquePtr<gl::MozFramebuffer> 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<CFTypeRefPtr<IOSurfaceRef>, 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<PendingSurfaceEntry> mPendingEntries;
|
|
|
|
// Stores entries which are available for recycling. These entries are not
|
|
// in use by a NativeLayerCA or by the window server.
|
|
nsTArray<SurfacePoolEntry> 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<gl::GLContext*, SurfacePoolCAWrapperForGL*> mWrappers;
|
|
size_t mPoolSizeLimit = 0;
|
|
|
|
struct DepthBufferEntry {
|
|
RefPtr<gl::GLContext> mGLContext;
|
|
gfx::IntSize mSize;
|
|
WeakPtr<gl::DepthAndStencilBuffer> mBuffer;
|
|
};
|
|
|
|
nsTArray<DepthBufferEntry> mDepthBuffers;
|
|
};
|
|
|
|
DataMutex<LockedPool> 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<SurfacePoolCA> mPool; // non-null
|
|
const RefPtr<gl::GLContext> 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<IOSurfaceRef> ObtainSurfaceFromPool(const gfx::IntSize& aSize);
|
|
void ReturnSurfaceToPool(CFTypeRefPtr<IOSurfaceRef> aSurface);
|
|
Maybe<GLuint> GetFramebufferForSurface(CFTypeRefPtr<IOSurfaceRef> aSurface,
|
|
bool aNeedsDepthBuffer);
|
|
RefPtr<SurfacePool> Pool() override { return mPoolWrapper->mPool; }
|
|
void OnBeginFrame() override;
|
|
void OnEndFrame() override;
|
|
|
|
private:
|
|
friend class SurfacePoolCA;
|
|
SurfacePoolHandleCA(RefPtr<SurfacePoolCAWrapperForGL>&& aPoolWrapper,
|
|
uint64_t aCurrentCollectionGeneration);
|
|
~SurfacePoolHandleCA() override;
|
|
|
|
const RefPtr<SurfacePoolCAWrapperForGL> mPoolWrapper;
|
|
DataMutex<uint64_t> mPreviousFrameCollectionGeneration;
|
|
};
|
|
|
|
} // namespace layers
|
|
} // namespace mozilla
|
|
|
|
#endif // mozilla_layers_SurfacePoolCA_h
|