283 lines
9.5 KiB
Text
283 lines
9.5 KiB
Text
/* -*- Mode: C++; tab-width: 20; 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/. */
|
|
|
|
|
|
#include "mozilla/gfx/2D.h"
|
|
#include "mozilla/gfx/Logging.h"
|
|
#include "mozilla/gfx/MacIOSurface.h"
|
|
#include "mozilla/layers/NativeLayerMacSurfaceHandler.h"
|
|
#include "mozilla/layers/SurfacePoolCA.h"
|
|
#include "GLBlitHelper.h"
|
|
#ifdef XP_MACOSX
|
|
# include "GLContextCGL.h"
|
|
#else
|
|
# include "GLContextEAGL.h"
|
|
#endif
|
|
#include "nsRegion.h"
|
|
|
|
namespace mozilla {
|
|
namespace layers {
|
|
|
|
using gfx::DataSourceSurface;
|
|
using gfx::IntPoint;
|
|
using gfx::IntRect;
|
|
using gfx::IntRegion;
|
|
using gfx::IntSize;
|
|
using gfx::Matrix4x4;
|
|
using gfx::SurfaceFormat;
|
|
using gl::GLContext;
|
|
#ifdef XP_MACOSX
|
|
using gl::GLContextCGL;
|
|
#endif
|
|
|
|
NativeLayerMacSurfaceHandler::NativeLayerMacSurfaceHandler(
|
|
const gfx::IntSize& aSize, SurfacePoolHandleCA* aSurfacePoolHandle)
|
|
: mSize(aSize), mSurfacePoolHandle(aSurfacePoolHandle) {
|
|
MOZ_RELEASE_ASSERT(mSurfacePoolHandle,
|
|
"Need a non-null surface pool handle.");
|
|
}
|
|
|
|
NativeLayerMacSurfaceHandler::~NativeLayerMacSurfaceHandler() {
|
|
if (mInProgressLockedIOSurface) {
|
|
mInProgressLockedIOSurface->Unlock(false);
|
|
mInProgressLockedIOSurface = nullptr;
|
|
}
|
|
if (mInProgressSurface) {
|
|
IOSurfaceDecrementUseCount(mInProgressSurface->mSurface.get());
|
|
mSurfacePoolHandle->ReturnSurfaceToPool(mInProgressSurface->mSurface);
|
|
}
|
|
if (mFrontSurface) {
|
|
mSurfacePoolHandle->ReturnSurfaceToPool(mFrontSurface->mSurface);
|
|
}
|
|
for (const auto& surf : mSurfaces) {
|
|
mSurfacePoolHandle->ReturnSurfaceToPool(surf.mEntry.mSurface);
|
|
}
|
|
}
|
|
|
|
bool NativeLayerMacSurfaceHandler::NextSurface() {
|
|
if (mSize.IsEmpty()) {
|
|
gfxCriticalError()
|
|
<< "NextSurface returning false because of invalid mSize ("
|
|
<< mSize.width << ", " << mSize.height << ").";
|
|
return false;
|
|
}
|
|
|
|
MOZ_RELEASE_ASSERT(!mInProgressSurface,
|
|
"ERROR: Do not call NextSurface twice in sequence. Call "
|
|
"NotifySurfaceReady before the "
|
|
"next call to NextSurface.");
|
|
|
|
Maybe<SurfaceWithInvalidRegion> surf = GetUnusedSurfaceAndCleanUp();
|
|
if (!surf) {
|
|
CFTypeRefPtr<IOSurfaceRef> newSurf =
|
|
mSurfacePoolHandle->ObtainSurfaceFromPool(mSize);
|
|
MOZ_RELEASE_ASSERT(
|
|
newSurf, "NextSurface IOSurfaceCreate failed to create the surface.");
|
|
surf = Some(SurfaceWithInvalidRegion{newSurf, IntRect({}, mSize)});
|
|
}
|
|
|
|
mInProgressSurface = std::move(surf);
|
|
IOSurfaceIncrementUseCount(mInProgressSurface->mSurface.get());
|
|
return true;
|
|
}
|
|
|
|
void NativeLayerMacSurfaceHandler::InvalidateRegionThroughoutSwapchain(
|
|
const IntRegion& aRegion) {
|
|
IntRegion r = aRegion;
|
|
if (mInProgressSurface) {
|
|
mInProgressSurface->mInvalidRegion.OrWith(r);
|
|
}
|
|
if (mFrontSurface) {
|
|
mFrontSurface->mInvalidRegion.OrWith(r);
|
|
}
|
|
for (auto& surf : mSurfaces) {
|
|
surf.mEntry.mInvalidRegion.OrWith(r);
|
|
}
|
|
}
|
|
|
|
template <typename F>
|
|
void NativeLayerMacSurfaceHandler::HandlePartialUpdate(
|
|
const IntRect& aDisplayRect, const IntRegion& aUpdateRegion, F&& aCopyFn) {
|
|
MOZ_RELEASE_ASSERT(IntRect({}, mSize).Contains(aUpdateRegion.GetBounds()),
|
|
"The update region should be within the surface bounds.");
|
|
MOZ_RELEASE_ASSERT(IntRect({}, mSize).Contains(aDisplayRect),
|
|
"The display rect should be within the surface bounds.");
|
|
|
|
MOZ_RELEASE_ASSERT(!mInProgressUpdateRegion);
|
|
MOZ_RELEASE_ASSERT(!mInProgressDisplayRect);
|
|
|
|
mInProgressUpdateRegion = Some(aUpdateRegion);
|
|
mInProgressDisplayRect = Some(aDisplayRect);
|
|
|
|
if (mFrontSurface) {
|
|
// Copy not-overwritten valid content from mFrontSurface so that valid
|
|
// content never gets lost.
|
|
gfx::IntRegion copyRegion;
|
|
copyRegion.Sub(mInProgressSurface->mInvalidRegion, aUpdateRegion);
|
|
copyRegion.SubOut(mFrontSurface->mInvalidRegion);
|
|
|
|
if (!copyRegion.IsEmpty()) {
|
|
// Now copy the valid content, using a caller-provided copy function.
|
|
aCopyFn(mFrontSurface->mSurface, copyRegion);
|
|
mInProgressSurface->mInvalidRegion.SubOut(copyRegion);
|
|
}
|
|
}
|
|
|
|
InvalidateRegionThroughoutSwapchain(aUpdateRegion);
|
|
}
|
|
|
|
Maybe<SurfaceWithInvalidRegion>
|
|
NativeLayerMacSurfaceHandler::GetUnusedSurfaceAndCleanUp() {
|
|
std::vector<SurfaceWithInvalidRegionAndCheckCount> usedSurfaces;
|
|
Maybe<SurfaceWithInvalidRegion> unusedSurface;
|
|
|
|
// Separate mSurfaces into used and unused surfaces.
|
|
for (auto& surf : mSurfaces) {
|
|
if (IOSurfaceIsInUse(surf.mEntry.mSurface.get())) {
|
|
surf.mCheckCount++;
|
|
if (surf.mCheckCount < 10) {
|
|
usedSurfaces.push_back(std::move(surf));
|
|
} else {
|
|
// The window server has been holding on to this surface for an
|
|
// unreasonably long time. This is known to happen sometimes, for
|
|
// example in occluded windows or after a GPU switch. In that case,
|
|
// release our references to the surface so that it doesn't look like
|
|
// we're trying to keep it alive.
|
|
mSurfacePoolHandle->ReturnSurfaceToPool(
|
|
std::move(surf.mEntry.mSurface));
|
|
}
|
|
} else {
|
|
if (unusedSurface) {
|
|
// Multiple surfaces are unused. Keep the most recent one and release
|
|
// any earlier ones. The most recent one requires the least amount of
|
|
// copying during partial repaints.
|
|
mSurfacePoolHandle->ReturnSurfaceToPool(
|
|
std::move(unusedSurface->mSurface));
|
|
}
|
|
unusedSurface = Some(std::move(surf.mEntry));
|
|
}
|
|
}
|
|
|
|
// Put the used surfaces back into mSurfaces.
|
|
mSurfaces = std::move(usedSurfaces);
|
|
|
|
return unusedSurface;
|
|
}
|
|
|
|
RefPtr<gfx::DrawTarget> NativeLayerMacSurfaceHandler::NextSurfaceAsDrawTarget(
|
|
const IntRect& aDisplayRect, const IntRegion& aUpdateRegion,
|
|
gfx::BackendType aBackendType) {
|
|
if (!NextSurface()) {
|
|
return nullptr;
|
|
}
|
|
|
|
auto surf = MakeRefPtr<MacIOSurface>(mInProgressSurface->mSurface);
|
|
if (NS_WARN_IF(!surf->Lock(false))) {
|
|
gfxCriticalError() << "NextSurfaceAsDrawTarget lock surface failed.";
|
|
return nullptr;
|
|
}
|
|
|
|
mInProgressLockedIOSurface = std::move(surf);
|
|
RefPtr<gfx::DrawTarget> dt =
|
|
mInProgressLockedIOSurface->GetAsDrawTargetLocked(aBackendType);
|
|
|
|
HandlePartialUpdate(
|
|
aDisplayRect, aUpdateRegion,
|
|
[&](CFTypeRefPtr<IOSurfaceRef> validSource,
|
|
const gfx::IntRegion& copyRegion) {
|
|
RefPtr<MacIOSurface> source = new MacIOSurface(validSource);
|
|
if (source->Lock(true)) {
|
|
RefPtr<gfx::DrawTarget> sourceDT =
|
|
source->GetAsDrawTargetLocked(aBackendType);
|
|
RefPtr<gfx::SourceSurface> sourceSurface = sourceDT->Snapshot();
|
|
|
|
for (auto iter = copyRegion.RectIter(); !iter.Done(); iter.Next()) {
|
|
const gfx::IntRect& r = iter.Get();
|
|
dt->CopySurface(sourceSurface, r, r.TopLeft());
|
|
}
|
|
source->Unlock(true);
|
|
} else {
|
|
gfxCriticalError() << "HandlePartialUpdate lock surface failed.";
|
|
}
|
|
});
|
|
|
|
return dt;
|
|
}
|
|
|
|
Maybe<GLuint> NativeLayerMacSurfaceHandler::NextSurfaceAsFramebuffer(
|
|
const IntRect& aDisplayRect, const IntRegion& aUpdateRegion,
|
|
bool aNeedsDepth) {
|
|
bool gotNextSurface = NextSurface();
|
|
MOZ_RELEASE_ASSERT(gotNextSurface,
|
|
"NextSurfaceAsFramebuffer needs a surface.");
|
|
|
|
Maybe<GLuint> fbo = mSurfacePoolHandle->GetFramebufferForSurface(
|
|
mInProgressSurface->mSurface, aNeedsDepth);
|
|
MOZ_RELEASE_ASSERT(fbo, "GetFramebufferForSurface failed.");
|
|
|
|
HandlePartialUpdate(
|
|
aDisplayRect, aUpdateRegion,
|
|
[&](CFTypeRefPtr<IOSurfaceRef> validSource,
|
|
const gfx::IntRegion& copyRegion) {
|
|
// Copy copyRegion from validSource to fbo.
|
|
MOZ_RELEASE_ASSERT(mSurfacePoolHandle->gl());
|
|
mSurfacePoolHandle->gl()->MakeCurrent();
|
|
Maybe<GLuint> sourceFBO =
|
|
mSurfacePoolHandle->GetFramebufferForSurface(validSource, false);
|
|
MOZ_RELEASE_ASSERT(
|
|
sourceFBO,
|
|
"GetFramebufferForSurface failed during HandlePartialUpdate.");
|
|
for (auto iter = copyRegion.RectIter(); !iter.Done(); iter.Next()) {
|
|
gfx::IntRect r = iter.Get();
|
|
if (mSurfaceIsFlipped) {
|
|
r.y = mSize.height - r.YMost();
|
|
}
|
|
mSurfacePoolHandle->gl()->BlitHelper()->BlitFramebufferToFramebuffer(
|
|
*sourceFBO, *fbo, r, r, LOCAL_GL_NEAREST);
|
|
}
|
|
});
|
|
|
|
return fbo;
|
|
}
|
|
|
|
bool NativeLayerMacSurfaceHandler::NotifySurfaceReady() {
|
|
MOZ_RELEASE_ASSERT(
|
|
mInProgressSurface,
|
|
"NotifySurfaceReady called without preceding call to NextSurface");
|
|
|
|
if (mInProgressLockedIOSurface) {
|
|
mInProgressLockedIOSurface->Unlock(false);
|
|
mInProgressLockedIOSurface = nullptr;
|
|
}
|
|
|
|
if (mFrontSurface) {
|
|
mSurfaces.push_back({*mFrontSurface, 0});
|
|
mFrontSurface = Nothing();
|
|
}
|
|
|
|
MOZ_RELEASE_ASSERT(mInProgressUpdateRegion);
|
|
IOSurfaceDecrementUseCount(mInProgressSurface->mSurface.get());
|
|
mFrontSurface = std::move(mInProgressSurface);
|
|
mFrontSurface->mInvalidRegion.SubOut(mInProgressUpdateRegion.extract());
|
|
|
|
bool mutatedDisplayRect = false;
|
|
MOZ_RELEASE_ASSERT(mInProgressDisplayRect);
|
|
if (!mDisplayRect.IsEqualInterior(*mInProgressDisplayRect)) {
|
|
mutatedDisplayRect = true;
|
|
mDisplayRect = *mInProgressDisplayRect;
|
|
}
|
|
mInProgressDisplayRect = Nothing();
|
|
return mutatedDisplayRect;
|
|
}
|
|
|
|
void NativeLayerMacSurfaceHandler::DiscardBackbuffers() {
|
|
for (const auto& surf : mSurfaces) {
|
|
mSurfacePoolHandle->ReturnSurfaceToPool(surf.mEntry.mSurface);
|
|
}
|
|
mSurfaces.clear();
|
|
}
|
|
|
|
} // namespace layers
|
|
} // namespace mozilla
|