зеркало из https://github.com/mozilla/gecko-dev.git
547 строки
18 KiB
Plaintext
547 строки
18 KiB
Plaintext
/* -*- 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>
|
|
|
|
#include "GLContextCGL.h"
|
|
#include "MozFramebuffer.h"
|
|
#include "ScopedGLHelpers.h"
|
|
|
|
@interface CALayer (PrivateSetContentsOpaque)
|
|
- (void)setContentsOpaque:(BOOL)opaque;
|
|
@end
|
|
|
|
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)
|
|
: mMutex("NativeLayerRootCA"), mRootCALayer([aLayer retain]) {}
|
|
|
|
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;
|
|
}
|
|
|
|
void NativeLayerRootCA::SetLayers(const nsTArray<RefPtr<NativeLayer>>& aLayers) {
|
|
MutexAutoLock lock(mMutex);
|
|
|
|
// Ideally, we'd just be able to do mSublayers = std::move(aLayers).
|
|
// However, aLayers has a different type: it carries NativeLayer objects, whereas mSublayers
|
|
// carries NativeLayerCA objects, so we have to downcast all the elements first. There's one other
|
|
// reason to look at all the elements in aLayers first: We need to make sure any new layers know
|
|
// about our current backing scale.
|
|
|
|
nsTArray<RefPtr<NativeLayerCA>> layersCA(aLayers.Length());
|
|
for (auto& layer : aLayers) {
|
|
RefPtr<NativeLayerCA> layerCA = layer->AsNativeLayerCA();
|
|
MOZ_RELEASE_ASSERT(layerCA);
|
|
layerCA->SetBackingScale(mBackingScale);
|
|
layersCA.AppendElement(std::move(layerCA));
|
|
}
|
|
|
|
if (layersCA != mSublayers) {
|
|
mSublayers = std::move(layersCA);
|
|
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 (mInProgressLockedIOSurface) {
|
|
mInProgressLockedIOSurface->Unlock(false);
|
|
mInProgressLockedIOSurface = nullptr;
|
|
}
|
|
if (mInProgressSurface) {
|
|
IOSurfaceDecrementUseCount(mInProgressSurface->mSurface.get());
|
|
}
|
|
if (mReadySurface) {
|
|
IOSurfaceDecrementUseCount(mReadySurface->mSurface.get());
|
|
}
|
|
|
|
[mContentCALayer release];
|
|
[mWrappingCALayer 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);
|
|
|
|
if (aIsFlipped != mSurfaceIsFlipped) {
|
|
mSurfaceIsFlipped = aIsFlipped;
|
|
mMutatedSize = true;
|
|
}
|
|
}
|
|
|
|
bool NativeLayerCA::SurfaceIsFlipped() {
|
|
MutexAutoLock lock(mMutex);
|
|
|
|
return mSurfaceIsFlipped;
|
|
}
|
|
|
|
void NativeLayerCA::SetRect(const IntRect& aRect) {
|
|
MutexAutoLock lock(mMutex);
|
|
|
|
if (aRect.TopLeft() != mPosition) {
|
|
mPosition = aRect.TopLeft();
|
|
mMutatedPosition = true;
|
|
}
|
|
if (aRect.Size() != mSize) {
|
|
mSize = aRect.Size();
|
|
mMutatedSize = true;
|
|
}
|
|
}
|
|
|
|
IntRect NativeLayerCA::GetRect() {
|
|
MutexAutoLock lock(mMutex);
|
|
return IntRect(mPosition, mSize);
|
|
}
|
|
|
|
void NativeLayerCA::SetBackingScale(float aBackingScale) {
|
|
MutexAutoLock lock(mMutex);
|
|
|
|
if (aBackingScale != mBackingScale) {
|
|
mBackingScale = aBackingScale;
|
|
mMutatedClipRect = true;
|
|
mMutatedPosition = true;
|
|
mMutatedSize = true;
|
|
}
|
|
}
|
|
|
|
void NativeLayerCA::SetIsOpaque(bool aIsOpaque) {
|
|
MutexAutoLock lock(mMutex);
|
|
|
|
if (aIsOpaque != mIsOpaque) {
|
|
mIsOpaque = aIsOpaque;
|
|
mMutatedIsOpaque = true;
|
|
}
|
|
}
|
|
|
|
bool NativeLayerCA::IsOpaque() {
|
|
MutexAutoLock lock(mMutex);
|
|
return mIsOpaque;
|
|
}
|
|
|
|
void NativeLayerCA::SetClipRect(const Maybe<gfx::IntRect>& aClipRect) {
|
|
MutexAutoLock lock(mMutex);
|
|
|
|
if (aClipRect != mClipRect) {
|
|
mClipRect = aClipRect;
|
|
mMutatedClipRect = true;
|
|
}
|
|
}
|
|
|
|
Maybe<gfx::IntRect> NativeLayerCA::ClipRect() {
|
|
MutexAutoLock lock(mMutex);
|
|
return mClipRect;
|
|
}
|
|
|
|
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), mSize));
|
|
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);
|
|
return NextSurfaceLocked(lock);
|
|
}
|
|
|
|
CFTypeRefPtr<IOSurfaceRef> NativeLayerCA::NextSurfaceLocked(const MutexAutoLock& aLock) {
|
|
IntSize surfaceSize = mSize;
|
|
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(aLock);
|
|
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.
|
|
for (auto unusedSurf : unusedSurfaces) {
|
|
if (mSurfaceRegistry) {
|
|
mSurfaceRegistry->UnregisterSurface(unusedSurf.mSurface);
|
|
}
|
|
mFramebuffers.erase(unusedSurf.mSurface);
|
|
}
|
|
unusedSurfaces.clear();
|
|
|
|
MOZ_RELEASE_ASSERT(surf);
|
|
mInProgressSurface = std::move(surf);
|
|
IOSurfaceIncrementUseCount(mInProgressSurface->mSurface.get());
|
|
return mInProgressSurface->mSurface;
|
|
}
|
|
|
|
RefPtr<gfx::DrawTarget> NativeLayerCA::NextSurfaceAsDrawTarget(gfx::BackendType aBackendType) {
|
|
MutexAutoLock lock(mMutex);
|
|
CFTypeRefPtr<IOSurfaceRef> surface = NextSurfaceLocked(lock);
|
|
if (!surface) {
|
|
return nullptr;
|
|
}
|
|
|
|
mInProgressLockedIOSurface = new MacIOSurface(std::move(surface));
|
|
mInProgressLockedIOSurface->Lock(false);
|
|
return mInProgressLockedIOSurface->GetAsDrawTargetLocked(aBackendType);
|
|
}
|
|
|
|
void NativeLayerCA::SetGLContext(gl::GLContext* aContext) {
|
|
MutexAutoLock lock(mMutex);
|
|
|
|
RefPtr<gl::GLContextCGL> glContextCGL = gl::GLContextCGL::Cast(aContext);
|
|
MOZ_RELEASE_ASSERT(glContextCGL, "Unexpected GLContext type");
|
|
|
|
if (glContextCGL != mGLContext) {
|
|
mFramebuffers.clear();
|
|
mGLContext = glContextCGL;
|
|
}
|
|
}
|
|
|
|
gl::GLContext* NativeLayerCA::GetGLContext() {
|
|
MutexAutoLock lock(mMutex);
|
|
return mGLContext;
|
|
}
|
|
|
|
Maybe<GLuint> NativeLayerCA::NextSurfaceAsFramebuffer(bool aNeedsDepth) {
|
|
MutexAutoLock lock(mMutex);
|
|
CFTypeRefPtr<IOSurfaceRef> surface = NextSurfaceLocked(lock);
|
|
if (!surface) {
|
|
return Nothing();
|
|
}
|
|
|
|
return Some(GetOrCreateFramebufferForSurface(lock, std::move(surface), aNeedsDepth));
|
|
}
|
|
|
|
GLuint NativeLayerCA::GetOrCreateFramebufferForSurface(const MutexAutoLock&,
|
|
CFTypeRefPtr<IOSurfaceRef> aSurface,
|
|
bool aNeedsDepth) {
|
|
auto fbCursor = mFramebuffers.find(aSurface);
|
|
if (fbCursor != mFramebuffers.end()) {
|
|
return fbCursor->second->mFB;
|
|
}
|
|
|
|
MOZ_RELEASE_ASSERT(
|
|
mGLContext, "Only call NextSurfaceAsFramebuffer when a GLContext is set on this NativeLayer");
|
|
mGLContext->MakeCurrent();
|
|
GLuint tex = mGLContext->CreateTexture();
|
|
{
|
|
const gl::ScopedBindTexture bindTex(mGLContext, tex, LOCAL_GL_TEXTURE_RECTANGLE_ARB);
|
|
CGLTexImageIOSurface2D(mGLContext->GetCGLContext(), LOCAL_GL_TEXTURE_RECTANGLE_ARB,
|
|
LOCAL_GL_RGBA, mSize.width, mSize.height, LOCAL_GL_BGRA,
|
|
LOCAL_GL_UNSIGNED_INT_8_8_8_8_REV, aSurface.get(), 0);
|
|
}
|
|
|
|
auto fb = gl::MozFramebuffer::CreateWith(mGLContext, mSize, 0, aNeedsDepth,
|
|
LOCAL_GL_TEXTURE_RECTANGLE_ARB, tex);
|
|
GLuint fbo = fb->mFB;
|
|
mFramebuffers.insert({aSurface, std::move(fb)});
|
|
|
|
return fbo;
|
|
}
|
|
|
|
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();
|
|
}
|
|
|
|
if (mInProgressLockedIOSurface) {
|
|
mInProgressLockedIOSurface->Unlock(false);
|
|
mInProgressLockedIOSurface = nullptr;
|
|
}
|
|
|
|
mReadySurface = std::move(mInProgressSurface);
|
|
mReadySurface->mInvalidRegion = IntRect();
|
|
}
|
|
|
|
void NativeLayerCA::ApplyChanges() {
|
|
MutexAutoLock lock(mMutex);
|
|
|
|
if (!mWrappingCALayer) {
|
|
mWrappingCALayer = [[CALayer layer] retain];
|
|
mWrappingCALayer.position = NSZeroPoint;
|
|
mWrappingCALayer.bounds = NSZeroRect;
|
|
mWrappingCALayer.anchorPoint = NSZeroPoint;
|
|
mWrappingCALayer.contentsGravity = kCAGravityTopLeft;
|
|
mContentCALayer = [[CALayer layer] retain];
|
|
mContentCALayer.anchorPoint = NSZeroPoint;
|
|
mContentCALayer.contentsGravity = kCAGravityTopLeft;
|
|
[mWrappingCALayer addSublayer:mContentCALayer];
|
|
}
|
|
|
|
// CALayers have a position and a size, specified through the position and the bounds properties.
|
|
// layer.bounds.origin must always be (0, 0).
|
|
// A layer's position affects the layer's entire layer subtree. In other words, each layer's
|
|
// position is relative to its superlayer's position. We implement the clip rect using
|
|
// masksToBounds on mWrappingCALayer. So mContentCALayer's position is relative to the clip rect
|
|
// position.
|
|
// Note: The Core Animation docs on "Positioning and Sizing Sublayers" say:
|
|
// Important: Always use integral numbers for the width and height of your layer.
|
|
// We hope that this refers to integral physical pixels, and not to integral logical coordinates.
|
|
|
|
auto globalClipOrigin = mClipRect ? mClipRect->TopLeft() : gfx::IntPoint{};
|
|
auto globalLayerOrigin = mPosition;
|
|
auto clipToLayerOffset = globalLayerOrigin - globalClipOrigin;
|
|
|
|
if (mMutatedClipRect) {
|
|
mWrappingCALayer.position =
|
|
CGPointMake(globalClipOrigin.x / mBackingScale, globalClipOrigin.y / mBackingScale);
|
|
if (mClipRect) {
|
|
mWrappingCALayer.masksToBounds = YES;
|
|
mWrappingCALayer.bounds =
|
|
CGRectMake(0, 0, mClipRect->Width() / mBackingScale, mClipRect->Height() / mBackingScale);
|
|
} else {
|
|
mWrappingCALayer.masksToBounds = NO;
|
|
}
|
|
}
|
|
|
|
if (mMutatedPosition || mMutatedClipRect) {
|
|
mContentCALayer.position =
|
|
CGPointMake(clipToLayerOffset.x / mBackingScale, clipToLayerOffset.y / mBackingScale);
|
|
}
|
|
|
|
if (mMutatedSize) {
|
|
mContentCALayer.bounds =
|
|
CGRectMake(0, 0, mSize.width / mBackingScale, mSize.height / mBackingScale);
|
|
mContentCALayer.contentsScale = mBackingScale;
|
|
if (mSurfaceIsFlipped) {
|
|
CGFloat height = mSize.height / mBackingScale;
|
|
mContentCALayer.affineTransform = CGAffineTransformMake(1.0, 0.0, 0.0, -1.0, 0.0, height);
|
|
} else {
|
|
mContentCALayer.affineTransform = CGAffineTransformIdentity;
|
|
}
|
|
}
|
|
|
|
if (mMutatedIsOpaque) {
|
|
mContentCALayer.opaque = mIsOpaque;
|
|
if ([mContentCALayer respondsToSelector:@selector(setContentsOpaque:)]) {
|
|
// The opaque property seems to not be enough when using IOSurface contents.
|
|
// Additionally, call the private method setContentsOpaque.
|
|
[mContentCALayer setContentsOpaque:mIsOpaque];
|
|
}
|
|
}
|
|
|
|
mMutatedPosition = false;
|
|
mMutatedSize = false;
|
|
mMutatedIsOpaque = false;
|
|
mMutatedClipRect = false;
|
|
|
|
if (mReadySurface) {
|
|
mContentCALayer.contents = (id)mReadySurface->mSurface.get();
|
|
IOSurfaceDecrementUseCount(mReadySurface->mSurface.get());
|
|
mSurfaces.push_back(*mReadySurface);
|
|
mReadySurface = Nothing();
|
|
}
|
|
}
|
|
|
|
// 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
|