Bug 1491442 - Add interfaces mozilla::layers::NativeLayerRoot and NativeLayer, and add CoreAnimation implementations NativeLayerRootCA and NativeLayerCA. r=jrmuizel

NativeLayerRoot and NativeLayer allow building up a flat layer "tree" of sibling
layers. They're created and manipulated with a synchronous-looking API, but any
changes will only be applied to the underlying native layers when ApplyChanges()
is called. This ensures that the modifications can be limited to run within a
CoreAnimation transaction, and on a thread of the caller's choosing.
In the near future I'm planning to have LayerManagerComposite create these
layers. That's the reason for the pseudo-abstracted cross-platform C++ API:
LayerManagerComposite can create and place the layers, and any painting into the
layer surfaces will happen in the compositor implementations with platform-specific
code.
For now, the CoreAnimation implementation is the only implementation. I think
the current API will let us use the same infrastructure for DirectComposite
layers on Windows, but we'll likely need to make some API modifications once we
attempt that.
The classes are threadsafe; their methods can be called on any thread and the
caller is responsible for sufficient synchronization. In reality, there are only
two threads that would have a reason to use these layers: The compositor thread
and the main thread. The main thread creates and destroys the NativeLayerRootCA,
and sometimes it calls ApplyChanges, e.g. during window resizes. It also calls
SetBackingScale. All other methods are usually only called on the compositor
thread.

Differential Revision: https://phabricator.services.mozilla.com/D26407

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Markus Stange 2019-08-16 01:30:02 +00:00
parent 85cbfc2652
commit 3cfb0f5ea9
4 changed files with 687 additions and 0 deletions

73
gfx/layers/NativeLayer.h Normal file
View File

@ -0,0 +1,73 @@
/* -*- 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_NativeLayer_h
#define mozilla_layers_NativeLayer_h
#include "nsISupportsImpl.h"
#include "nsRegion.h"
namespace mozilla {
namespace layers {
class NativeLayer;
class NativeLayerCA;
// NativeLayerRoot and NativeLayer allow building up a flat layer "tree" of
// sibling layers. These layers provide a cross-platform abstraction for the
// platform's native layers, such as CoreAnimation layers on macOS.
// Every layer has a rectangle that describes its position and size in the
// window. The native layer root is usually be created by the window, and then
// the compositing subsystem uses it to create and place the actual layers.
class NativeLayerRoot {
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(NativeLayerRoot)
virtual already_AddRefed<NativeLayer> CreateLayer() = 0;
virtual void AppendLayer(NativeLayer* aLayer) = 0;
virtual void RemoveLayer(NativeLayer* aLayer) = 0;
protected:
virtual ~NativeLayerRoot() {}
};
// Represents a native layer. Native layers, such as CoreAnimation layers on
// macOS, are used to put pixels on the screen and to refresh and manipulate
// the visual contents of a window efficiently. For example, drawing to a layer
// once and then displaying the layer for multiple frames while moving it to
// different positions will be more efficient than drawing into a window (or a
// non-moving layer) multiple times with different internal offsets.
// There are two sources of "work" for a given composited frame: 1) Our own
// drawing (such as OpenGL compositing into a window or layer) and 2) the
// compositing window manager's work to update the screen. Every pixel we draw
// needs to be copied to the screen by the window manager. This suggests two
// avenues for reducing the work load for a given frame: Drawing fewer pixels
// ourselves, and making the window manager copy fewer pixels to the screen.
// Smart use of native layers allows reducing both work loads: If a visual
// change can be expressed purely as a layer attribute change (such as a change
// in the layer's position), this lets us eliminate our own drawing for that
// change. And secondly, manipulating a small layer rather than a large layer
// will reduce the window manager's work for that frame because it'll only copy
// the pixels of the small layer to the screen.
class NativeLayer {
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(NativeLayer)
virtual NativeLayerCA* AsNativeLayerCA() { return nullptr; }
// The location and size of the layer, in integer device pixels. This also
// determines the size of the surface that should be returned from the next
// call to NextSurface.
virtual void SetRect(const gfx::IntRect& aRect) = 0;
virtual gfx::IntRect GetRect() = 0;
protected:
virtual ~NativeLayer() {}
};
} // namespace layers
} // namespace mozilla
#endif // mozilla_layers_NativeLayer_h

256
gfx/layers/NativeLayerCA.h Normal file
View File

@ -0,0 +1,256 @@
/* -*- 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_NativeLayerCA_h
#define mozilla_layers_NativeLayerCA_h
#include <IOSurface/IOSurface.h>
#include <deque>
#include "mozilla/Maybe.h"
#include "mozilla/Mutex.h"
#include "mozilla/layers/NativeLayer.h"
#include "CFTypeRefPtr.h"
#include "nsRegion.h"
#include "nsISupportsImpl.h"
#ifdef __OBJC__
@class CALayer;
#else
typedef void CALayer;
#endif
namespace mozilla {
namespace layers {
class IOSurfaceRegistry {
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(IOSurfaceRegistry)
virtual void RegisterSurface(CFTypeRefPtr<IOSurfaceRef> aSurface) = 0;
virtual void UnregisterSurface(CFTypeRefPtr<IOSurfaceRef> aSurface) = 0;
protected:
virtual ~IOSurfaceRegistry() {}
};
// NativeLayerRootCA is the CoreAnimation implementation of the NativeLayerRoot
// interface. A NativeLayerRootCA is created by the widget around an existing
// CALayer with a call to CreateForCALayer.
// All methods can be called from any thread, there is internal locking.
// All effects from mutating methods are buffered locally and don't modify the
// underlying CoreAnimation layers until ApplyChanges() is called. This ensures
// that the modifications can be limited to run within a CoreAnimation
// transaction, and on a thread of the caller's choosing.
class NativeLayerRootCA : public NativeLayerRoot {
public:
static already_AddRefed<NativeLayerRootCA> CreateForCALayer(CALayer* aLayer);
// Must be called within a current CATransaction on the transaction's thread.
void ApplyChanges();
void SetBackingScale(float aBackingScale);
// Overridden methods
already_AddRefed<NativeLayer> CreateLayer() override;
void AppendLayer(NativeLayer* aLayer) override;
void RemoveLayer(NativeLayer* aLayer) override;
protected:
explicit NativeLayerRootCA(CALayer* aLayer);
~NativeLayerRootCA() override;
Mutex mMutex; // protects all other fields
nsTArray<RefPtr<NativeLayerCA>> mSublayers; // in z-order
CALayer* mRootCALayer = nullptr; // strong
float mBackingScale = 1.0f;
bool mMutated = false;
};
// NativeLayerCA wraps a CALayer and lets you draw to it. It ensures that only
// fully-drawn frames make their way to the screen, by maintaining a swap chain
// of IOSurfaces.
// All calls to mutating methods are buffered, and don't take effect on the
// underlying CoreAnimation layers until ApplyChanges() is called.
// The two most important methods are NextSurface and NotifySurfaceReady:
// NextSurface takes an available surface from the swap chain or creates a new
// surface if necessary. This surface can then be drawn to. Once drawing is
// finished, NotifySurfaceReady marks the surface as ready. This surface is
// committed to the layer during the next call to ApplyChanges().
// The swap chain keeps track of invalid areas within the surfaces.
//
// Creation and destruction of IOSurface objects is broadcast to an optional
// "surface registry" so that associated objects such as framebuffer objects
// don't need to be recreated on every frame: Instead, the surface registry can
// maintain one object per IOSurface in this layer's swap chain, and those
// objects will be reused in different frames as the layer cycles through the
// surfaces in its swap chain.
class NativeLayerCA : public NativeLayer {
public:
virtual NativeLayerCA* AsNativeLayerCA() override { return this; }
// Overridden methods
void SetRect(const gfx::IntRect& aRect) override;
gfx::IntRect GetRect() override;
// The invalid region of the surface that has been returned from the most
// recent call to NextSurface. Newly-created surfaces are entirely invalid.
// For surfaces that have been used before, the invalid region is the union of
// all invalid regions that have been passed to
// invalidateRegionThroughoutSwapchain since the last time that
// NotifySurfaceReady was called for this surface. Can only be called between
// calls to NextSurface and NotifySurfaceReady. Can be called on any thread.
gfx::IntRegion CurrentSurfaceInvalidRegion();
// Invalidates the specified region in all surfaces that are tracked by this
// layer.
void InvalidateRegionThroughoutSwapchain(const gfx::IntRegion& aRegion);
// Returns an IOSurface that can be drawn to. The size of the IOSurface will
// be the size of the rect that has been passed to SetRect.
// The returned surface is guaranteed to be not in use by the window server.
// After a call to NextSurface, NextSurface must not be called again until
// after NotifySurfaceReady has been called. Can be called on any thread. When
// used from multiple threads, callers need to make sure that they still only
// call NextSurface and NotifySurfaceReady alternatingly and not in any other
// order.
CFTypeRefPtr<IOSurfaceRef> NextSurface();
// Indicates that the surface which has been returned from the most recent
// call to NextSurface is now finished being drawn to and can be displayed on
// the screen. The surface will be used during the next call to the layer's
// ApplyChanges method. Resets the invalid region on the surface to the empty
// region.
void NotifySurfaceReady();
// Consumers may provide an object that implements the IOSurfaceRegistry
// interface.
// The registry's methods, Register/UnregisterSurface, will be called
// synchronously during calls to NextSurface(), SetSurfaceRegistry(), and the
// NativeLayer destructor, on the thread that those things happen to run on.
// If this layer already owns surfaces when SetSurfaceRegistry gets called
// with a non-null surface registry, those surfaces will immediately
// (synchronously) be registered with that registry. If the current surface
// registry is unset (via a call to SetSurfaceRegistry with a different value,
// such as null), and the NativeLayer still owns surfaces, then those surfaces
// will immediately be unregistered.
// Since NativeLayer objects are reference counted and can be used from
// different threads, it is recommended to call SetSurfaceRegistry(nullptr)
// before destroying the NativeLayer so that the UnregisterSurface calls
// happen at a deterministic time and on the right thread.
void SetSurfaceRegistry(RefPtr<IOSurfaceRegistry> aSurfaceRegistry);
RefPtr<IOSurfaceRegistry> GetSurfaceRegistry();
// Whether the surface contents are flipped vertically compared to this
// layer's coordinate system. Can be set on any thread at any time.
void SetSurfaceIsFlipped(bool aIsFlipped);
bool SurfaceIsFlipped();
protected:
friend class NativeLayerRootCA;
NativeLayerCA();
~NativeLayerCA() override;
// To be called by NativeLayerRootCA:
CALayer* UnderlyingCALayer() { return mCALayer; }
void ApplyChanges();
void SetBackingScale(float aBackingScale);
struct SurfaceWithInvalidRegion {
CFTypeRefPtr<IOSurfaceRef> mSurface;
gfx::IntRegion mInvalidRegion;
gfx::IntSize mSize;
};
std::vector<SurfaceWithInvalidRegion> RemoveExcessUnusedSurfaces(
const MutexAutoLock&);
// Controls access to all fields of this class.
Mutex mMutex;
RefPtr<IOSurfaceRegistry> mSurfaceRegistry; // can be null
// Each IOSurface is initially created inside NextSurface.
// The surface stays alive until the recycling mechanism in NextSurface
// determines it is no longer needed, for example because the layer size
// changed or because the swap chain has grown too long, or until the layer
// is destroyed.
// During the surface's lifetime, it will continuously move through the fields
// mInProgressSurface, mReadySurface, and back to front through the
// mSurfaces queue:
//
// mSurfaces.front()
// ------[NextSurface()]-----> mInProgressSurface
// --[NotifySurfaceReady()]--> mReadySurface
// ----[ApplyChanges()]------> mSurfaces.back() --> .... -->
// mSurfaces.front()
//
// We mark an IOSurface as "in use" as long as it is either in
// mInProgressSurface or in mReadySurface. When it is in the mSurfaces queue,
// it is not marked as "in use" by us - but it can be "in use" by the window
// server. Consequently, IOSurfaceIsInUse on a surface from mSurfaces reflects
// whether the window server is still reading from the surface, and we can use
// this indicator to decide when to recycle the surface.
//
// Users of NativeLayerCA normally proceed in this order:
// 1. Begin a frame by calling NextSurface to get the surface.
// 2. Draw to the surface.
// 3. Mark the surface as done by calling NotifySurfaceReady.
// 4. Trigger a CoreAnimation transaction, and call ApplyChanges within the
// transaction.
//
// For two consecutive frames, this results in the following ordering of
// calls:
// I. NextSurface, NotifySurfaceReady, ApplyChanges, NextSurface,
// NotifySurfaceReady, ApplyChanges
//
// In this scenario, either mInProgressSurface or mReadySurface is always
// Nothing().
//
// However, sometimes we see the following ordering instead:
// II. NextSurface, NotifySurfaceReady, NextSurface, ApplyChanges,
// NotifySurfaceReady, ApplyChanges
//
// This has the NextSurface and ApplyChanges calls in the middle reversed.
//
// In that scenario, both mInProgressSurface and mReadySurface will be Some()
// between the calls to NextSurface and ApplyChanges in the middle, and the
// two ApplyChanges invocations will submit two different surfaces. It is
// important that we don't simply discard the first surface during the second
// call to NextSurface in this scenario.
// The surface we returned from the most recent call to NextSurface, before
// the matching call to NotifySurfaceReady.
// Will only be Some() between calls to NextSurface and NotifySurfaceReady.
Maybe<SurfaceWithInvalidRegion> mInProgressSurface;
// The surface that the most recent call to NotifySurfaceReady was for.
// Will only be Some() between calls to NotifySurfaceReady and the next call
// to ApplyChanges.
// Both mInProgressSurface and mReadySurface can be Some() at the same time.
Maybe<SurfaceWithInvalidRegion> mReadySurface;
// The queue of surfaces which make up our "swap chain".
// mSurfaces.front() is the next surface we'll attempt to use.
// mSurfaces.back() is the one we submitted most recently.
std::deque<SurfaceWithInvalidRegion> mSurfaces;
gfx::IntRect mRect;
// Lazily initialized by first call to ApplyChanges.
CALayer* mCALayer = nullptr; // strong
float mBackingScale = 1.0f;
bool mSurfaceIsFlipped = false;
bool mMutated = false;
};
} // namespace layers
} // namespace mozilla
#endif // mozilla_layers_NativeLayerCA_h

352
gfx/layers/NativeLayerCA.mm Normal file
View File

@ -0,0 +1,352 @@
/* -*- 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/. */
#import "mozilla/layers/NativeLayerCA.h"
#import <QuartzCore/QuartzCore.h>
#import <CoreVideo/CVPixelBuffer.h>
#include <utility>
#include <algorithm>
namespace mozilla {
namespace layers {
using gfx::IntPoint;
using gfx::IntSize;
using gfx::IntRect;
using gfx::IntRegion;
/* static */ already_AddRefed<NativeLayerRootCA> NativeLayerRootCA::CreateForCALayer(
CALayer* aLayer) {
RefPtr<NativeLayerRootCA> layerRoot = new NativeLayerRootCA(aLayer);
return layerRoot.forget();
}
NativeLayerRootCA::NativeLayerRootCA(CALayer* aLayer)
: mRootCALayer([aLayer retain]), mMutex("NativeLayerRootCA") {}
NativeLayerRootCA::~NativeLayerRootCA() {
MOZ_RELEASE_ASSERT(mSublayers.IsEmpty(),
"Please clear all layers before destroying the layer root.");
// FIXME: mMutated might be true at this point, which would indicate that, even
// though mSublayers is empty now, this state may not yet have been synced to
// the underlying CALayer. In other words, mRootCALayer might still have sublayers.
// Should we do anything about that?
// We could just clear mRootCALayer's sublayers now, but doing so would be a
// layer tree transformation outside of a transaction, which we want to avoid.
// But we also don't want to trigger a transaction just for clearing the
// window's layers. And we wouldn't expect a NativeLayerRootCA to be destroyed
// while the window is still open and visible. Are layer tree modifications
// outside of CATransactions allowed while the window is closed? Who knows.
[mRootCALayer release];
}
already_AddRefed<NativeLayer> NativeLayerRootCA::CreateLayer() {
RefPtr<NativeLayer> layer = new NativeLayerCA();
return layer.forget();
}
void NativeLayerRootCA::AppendLayer(NativeLayer* aLayer) {
MutexAutoLock lock(mMutex);
RefPtr<NativeLayerCA> layerCA = aLayer->AsNativeLayerCA();
MOZ_RELEASE_ASSERT(layerCA);
mSublayers.AppendElement(layerCA);
layerCA->SetBackingScale(mBackingScale);
mMutated = true;
}
void NativeLayerRootCA::RemoveLayer(NativeLayer* aLayer) {
MutexAutoLock lock(mMutex);
RefPtr<NativeLayerCA> layerCA = aLayer->AsNativeLayerCA();
MOZ_RELEASE_ASSERT(layerCA);
mSublayers.RemoveElement(layerCA);
mMutated = true;
}
// Must be called within a current CATransaction on the transaction's thread.
void NativeLayerRootCA::ApplyChanges() {
MutexAutoLock lock(mMutex);
[CATransaction setDisableActions:YES];
// Call ApplyChanges on our sublayers first, and then update the root layer's
// list of sublayers. The order is important because we need layer->UnderlyingCALayer()
// to be non-null, and the underlying CALayer gets lazily initialized in ApplyChanges().
for (auto layer : mSublayers) {
layer->ApplyChanges();
}
if (mMutated) {
NSMutableArray<CALayer*>* sublayers = [NSMutableArray arrayWithCapacity:mSublayers.Length()];
for (auto layer : mSublayers) {
[sublayers addObject:layer->UnderlyingCALayer()];
}
mRootCALayer.sublayers = sublayers;
mMutated = false;
}
}
void NativeLayerRootCA::SetBackingScale(float aBackingScale) {
MutexAutoLock lock(mMutex);
mBackingScale = aBackingScale;
for (auto layer : mSublayers) {
layer->SetBackingScale(aBackingScale);
}
}
NativeLayerCA::NativeLayerCA() : mMutex("NativeLayerCA") {}
NativeLayerCA::~NativeLayerCA() {
SetSurfaceRegistry(nullptr); // or maybe MOZ_RELEASE_ASSERT(!mSurfaceRegistry) would be better?
if (mInProgressSurface) {
IOSurfaceDecrementUseCount(mInProgressSurface->mSurface.get());
}
if (mReadySurface) {
IOSurfaceDecrementUseCount(mReadySurface->mSurface.get());
}
[mCALayer release];
}
void NativeLayerCA::SetSurfaceRegistry(RefPtr<IOSurfaceRegistry> aSurfaceRegistry) {
MutexAutoLock lock(mMutex);
if (mSurfaceRegistry) {
for (auto surf : mSurfaces) {
mSurfaceRegistry->UnregisterSurface(surf.mSurface);
}
if (mInProgressSurface) {
mSurfaceRegistry->UnregisterSurface(mInProgressSurface->mSurface);
}
if (mReadySurface) {
mSurfaceRegistry->UnregisterSurface(mReadySurface->mSurface);
}
}
mSurfaceRegistry = aSurfaceRegistry;
if (mSurfaceRegistry) {
for (auto surf : mSurfaces) {
mSurfaceRegistry->RegisterSurface(surf.mSurface);
}
if (mInProgressSurface) {
mSurfaceRegistry->RegisterSurface(mInProgressSurface->mSurface);
}
if (mReadySurface) {
mSurfaceRegistry->RegisterSurface(mReadySurface->mSurface);
}
}
}
RefPtr<IOSurfaceRegistry> NativeLayerCA::GetSurfaceRegistry() {
MutexAutoLock lock(mMutex);
return mSurfaceRegistry;
}
void NativeLayerCA::SetSurfaceIsFlipped(bool aIsFlipped) {
MutexAutoLock lock(mMutex);
mSurfaceIsFlipped = aIsFlipped;
mMutated = true;
}
bool NativeLayerCA::SurfaceIsFlipped() {
MutexAutoLock lock(mMutex);
return mSurfaceIsFlipped;
}
void NativeLayerCA::SetRect(const IntRect& aRect) {
MutexAutoLock lock(mMutex);
mRect = aRect;
mMutated = true;
}
IntRect NativeLayerCA::GetRect() {
MutexAutoLock lock(mMutex);
return mRect;
}
void NativeLayerCA::SetBackingScale(float aBackingScale) {
MutexAutoLock lock(mMutex);
mBackingScale = aBackingScale;
mMutated = true;
}
IntRegion NativeLayerCA::CurrentSurfaceInvalidRegion() {
MutexAutoLock lock(mMutex);
MOZ_RELEASE_ASSERT(
mInProgressSurface,
"Only call currentSurfaceInvalidRegion after a call to NextSurface and before the call "
"to notifySurfaceIsReady.");
return mInProgressSurface->mInvalidRegion;
}
void NativeLayerCA::InvalidateRegionThroughoutSwapchain(const IntRegion& aRegion) {
MutexAutoLock lock(mMutex);
IntRegion r = aRegion;
r.AndWith(IntRect(IntPoint(0, 0), mRect.Size()));
if (mInProgressSurface) {
mInProgressSurface->mInvalidRegion.OrWith(r);
}
if (mReadySurface) {
mReadySurface->mInvalidRegion.OrWith(r);
}
for (auto& surf : mSurfaces) {
surf.mInvalidRegion.OrWith(r);
}
}
CFTypeRefPtr<IOSurfaceRef> NativeLayerCA::NextSurface() {
MutexAutoLock lock(mMutex);
IntSize surfaceSize = mRect.Size();
if (surfaceSize.IsEmpty()) {
NSLog(@"NextSurface returning nullptr because of invalid surfaceSize (%d, %d).",
surfaceSize.width, surfaceSize.height);
return nullptr;
}
MOZ_RELEASE_ASSERT(
!mInProgressSurface,
"ERROR: Do not call NextSurface twice in sequence. Call NotifySurfaceReady before the "
"next call to NextSurface.");
// Find the last surface in unusedSurfaces which has the right size. If such
// a surface exists, it is the surface we will recycle.
std::vector<SurfaceWithInvalidRegion> unusedSurfaces = RemoveExcessUnusedSurfaces(lock);
auto surfIter = std::find_if(
unusedSurfaces.rbegin(), unusedSurfaces.rend(),
[surfaceSize](const SurfaceWithInvalidRegion& s) { return s.mSize == surfaceSize; });
Maybe<SurfaceWithInvalidRegion> surf;
if (surfIter != unusedSurfaces.rend()) {
// We found the surface we want to recycle.
surf = Some(*surfIter);
// Remove surf from unusedSurfaces.
// The reverse iterator makes this a bit cumbersome.
unusedSurfaces.erase(std::next(surfIter).base());
} else {
CFTypeRefPtr<IOSurfaceRef> newSurf = CFTypeRefPtr<IOSurfaceRef>::WrapUnderCreateRule(
IOSurfaceCreate((__bridge CFDictionaryRef) @{
(__bridge NSString*)kIOSurfaceWidth : @(surfaceSize.width),
(__bridge NSString*)kIOSurfaceHeight : @(surfaceSize.height),
(__bridge NSString*)kIOSurfacePixelFormat : @(kCVPixelFormatType_32BGRA),
(__bridge NSString*)kIOSurfaceBytesPerElement : @(4),
}));
if (!newSurf) {
NSLog(@"NextSurface returning nullptr because IOSurfaceCreate failed to create the surface.");
return nullptr;
}
if (mSurfaceRegistry) {
mSurfaceRegistry->RegisterSurface(newSurf);
}
surf =
Some(SurfaceWithInvalidRegion{newSurf, IntRect(IntPoint(0, 0), surfaceSize), surfaceSize});
}
// Delete all other unused surfaces.
if (mSurfaceRegistry) {
for (auto unusedSurf : unusedSurfaces) {
mSurfaceRegistry->UnregisterSurface(unusedSurf.mSurface);
}
}
unusedSurfaces.clear();
MOZ_RELEASE_ASSERT(surf);
mInProgressSurface = std::move(surf);
IOSurfaceIncrementUseCount(mInProgressSurface->mSurface.get());
return mInProgressSurface->mSurface;
}
void NativeLayerCA::NotifySurfaceReady() {
MutexAutoLock lock(mMutex);
MOZ_RELEASE_ASSERT(mInProgressSurface,
"NotifySurfaceReady called without preceding call to NextSurface");
if (mReadySurface) {
IOSurfaceDecrementUseCount(mReadySurface->mSurface.get());
mSurfaces.push_back(*mReadySurface);
mReadySurface = Nothing();
}
mReadySurface = std::move(mInProgressSurface);
mReadySurface->mInvalidRegion = IntRect();
mMutated = true;
}
void NativeLayerCA::ApplyChanges() {
MutexAutoLock lock(mMutex);
if (!mCALayer) {
mCALayer = [[CALayer layer] retain];
mCALayer.position = NSZeroPoint;
mCALayer.bounds = NSZeroRect;
mCALayer.anchorPoint = NSZeroPoint;
mCALayer.contentsGravity = kCAGravityTopLeft;
}
if (!mMutated) {
return;
}
mCALayer.contentsScale = mBackingScale;
mCALayer.position = NSMakePoint(mRect.x / mBackingScale, mRect.y / mBackingScale);
mCALayer.bounds = NSMakeRect(0, 0, mRect.width / mBackingScale, mRect.height / mBackingScale);
if (mReadySurface) {
mCALayer.contents = (id)mReadySurface->mSurface.get();
IOSurfaceDecrementUseCount(mReadySurface->mSurface.get());
mSurfaces.push_back(*mReadySurface);
mReadySurface = Nothing();
}
if (mSurfaceIsFlipped) {
CGFloat height = mCALayer.bounds.size.height;
// FIXME: this probably needs adjusting if mCALayer.position is non-zero.
mCALayer.affineTransform = CGAffineTransformMake(1.0, 0.0, 0.0, -1.0, 0.0, height);
} else {
mCALayer.affineTransform = CGAffineTransformIdentity;
}
mMutated = false;
}
// Called when mMutex is already being held by the current thread.
std::vector<NativeLayerCA::SurfaceWithInvalidRegion> NativeLayerCA::RemoveExcessUnusedSurfaces(
const MutexAutoLock&) {
std::vector<SurfaceWithInvalidRegion> usedSurfaces;
std::vector<SurfaceWithInvalidRegion> unusedSurfaces;
// Separate mSurfaces into used and unused surfaces, leaving 2 surfaces behind.
while (mSurfaces.size() > 2) {
auto surf = std::move(mSurfaces.front());
mSurfaces.pop_front();
if (IOSurfaceIsInUse(surf.mSurface.get())) {
usedSurfaces.push_back(std::move(surf));
} else {
unusedSurfaces.push_back(std::move(surf));
}
}
// Put the used surfaces back into mSurfaces, at the beginning.
mSurfaces.insert(mSurfaces.begin(), usedSurfaces.begin(), usedSurfaces.end());
return unusedSurfaces;
}
} // namespace layers
} // namespace mozilla

View File

@ -228,6 +228,7 @@ EXPORTS.mozilla.layers += [
'mlgpu/MLGPUScreenshotGrabber.h',
'mlgpu/ShaderDefinitionsMLGPU.h',
'mlgpu/UtilityMLGPU.h',
'NativeLayer.h',
'opengl/CompositingRenderTargetOGL.h',
'opengl/CompositorOGL.h',
'opengl/MacIOSurfaceTextureClientOGL.h',
@ -293,6 +294,7 @@ if CONFIG['MOZ_X11']:
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
EXPORTS.mozilla.layers += [
'NativeLayerCA.h',
'opengl/GLManager.h',
'TextureSync.h',
]
@ -301,6 +303,7 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
'MacIOSurfaceImage.h',
]
UNIFIED_SOURCES += [
'NativeLayerCA.mm',
'opengl/GLManager.cpp',
'TextureSync.cpp',
]
@ -309,6 +312,9 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
'MacIOSurfaceHelpers.cpp',
'MacIOSurfaceImage.cpp',
]
OS_LIBS += [
'-framework IOSurface',
]
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'android':
UNIFIED_SOURCES += [