зеркало из https://github.com/mozilla/gecko-dev.git
675 строки
20 KiB
C++
675 строки
20 KiB
C++
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
|
/* 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 "PersistentBufferProvider.h"
|
|
|
|
#include "mozilla/layers/TextureClient.h"
|
|
#include "mozilla/layers/TextureForwarder.h"
|
|
#include "mozilla/gfx/gfxVars.h"
|
|
#include "mozilla/gfx/DrawTargetWebgl.h"
|
|
#include "mozilla/gfx/Logging.h"
|
|
#include "mozilla/Maybe.h"
|
|
#include "mozilla/StaticPrefs_layers.h"
|
|
#include "pratom.h"
|
|
#include "gfxPlatform.h"
|
|
|
|
namespace mozilla {
|
|
|
|
using namespace gfx;
|
|
|
|
namespace layers {
|
|
|
|
PersistentBufferProviderBasic::PersistentBufferProviderBasic(DrawTarget* aDt)
|
|
: mDrawTarget(aDt) {
|
|
MOZ_COUNT_CTOR(PersistentBufferProviderBasic);
|
|
}
|
|
|
|
PersistentBufferProviderBasic::~PersistentBufferProviderBasic() {
|
|
MOZ_COUNT_DTOR(PersistentBufferProviderBasic);
|
|
Destroy();
|
|
}
|
|
|
|
already_AddRefed<gfx::DrawTarget>
|
|
PersistentBufferProviderBasic::BorrowDrawTarget(
|
|
const gfx::IntRect& aPersistedRect) {
|
|
MOZ_ASSERT(!mSnapshot);
|
|
RefPtr<gfx::DrawTarget> dt(mDrawTarget);
|
|
return dt.forget();
|
|
}
|
|
|
|
bool PersistentBufferProviderBasic::ReturnDrawTarget(
|
|
already_AddRefed<gfx::DrawTarget> aDT) {
|
|
RefPtr<gfx::DrawTarget> dt(aDT);
|
|
MOZ_ASSERT(mDrawTarget == dt);
|
|
if (dt) {
|
|
// Since SkiaGL default to storing drawing command until flush
|
|
// we have to flush it before present.
|
|
dt->Flush();
|
|
}
|
|
return true;
|
|
}
|
|
|
|
already_AddRefed<gfx::SourceSurface>
|
|
PersistentBufferProviderBasic::BorrowSnapshot(gfx::DrawTarget* aTarget) {
|
|
mSnapshot = mDrawTarget->Snapshot();
|
|
RefPtr<SourceSurface> snapshot = mSnapshot;
|
|
return snapshot.forget();
|
|
}
|
|
|
|
void PersistentBufferProviderBasic::ReturnSnapshot(
|
|
already_AddRefed<gfx::SourceSurface> aSnapshot) {
|
|
RefPtr<SourceSurface> snapshot = aSnapshot;
|
|
MOZ_ASSERT(!snapshot || snapshot == mSnapshot);
|
|
mSnapshot = nullptr;
|
|
}
|
|
|
|
void PersistentBufferProviderBasic::Destroy() {
|
|
mSnapshot = nullptr;
|
|
mDrawTarget = nullptr;
|
|
}
|
|
|
|
// static
|
|
already_AddRefed<PersistentBufferProviderBasic>
|
|
PersistentBufferProviderBasic::Create(gfx::IntSize aSize,
|
|
gfx::SurfaceFormat aFormat,
|
|
gfx::BackendType aBackend) {
|
|
RefPtr<DrawTarget> dt =
|
|
gfxPlatform::GetPlatform()->CreateDrawTargetForBackend(aBackend, aSize,
|
|
aFormat);
|
|
|
|
if (dt) {
|
|
// This is simply to ensure the DrawTarget gets initialized, and will detect
|
|
// a device reset, even if we're on the main thread.
|
|
dt->ClearRect(Rect(0, 0, 0, 0));
|
|
}
|
|
|
|
if (!dt || !dt->IsValid()) {
|
|
return nullptr;
|
|
}
|
|
|
|
RefPtr<PersistentBufferProviderBasic> provider =
|
|
new PersistentBufferProviderBasic(dt);
|
|
|
|
return provider.forget();
|
|
}
|
|
|
|
PersistentBufferProviderAccelerated::PersistentBufferProviderAccelerated(
|
|
DrawTarget* aDt)
|
|
: PersistentBufferProviderBasic(aDt) {
|
|
MOZ_COUNT_CTOR(PersistentBufferProviderAccelerated);
|
|
MOZ_ASSERT(aDt->GetBackendType() == BackendType::WEBGL);
|
|
}
|
|
|
|
PersistentBufferProviderAccelerated::~PersistentBufferProviderAccelerated() {
|
|
MOZ_COUNT_DTOR(PersistentBufferProviderAccelerated);
|
|
}
|
|
|
|
inline gfx::DrawTargetWebgl*
|
|
PersistentBufferProviderAccelerated::GetDrawTargetWebgl() const {
|
|
return static_cast<gfx::DrawTargetWebgl*>(mDrawTarget.get());
|
|
}
|
|
|
|
Maybe<layers::SurfaceDescriptor>
|
|
PersistentBufferProviderAccelerated::GetFrontBuffer() {
|
|
return GetDrawTargetWebgl()->GetFrontBuffer();
|
|
}
|
|
|
|
already_AddRefed<gfx::DrawTarget>
|
|
PersistentBufferProviderAccelerated::BorrowDrawTarget(
|
|
const gfx::IntRect& aPersistedRect) {
|
|
GetDrawTargetWebgl()->BeginFrame(aPersistedRect);
|
|
return PersistentBufferProviderBasic::BorrowDrawTarget(aPersistedRect);
|
|
}
|
|
|
|
bool PersistentBufferProviderAccelerated::ReturnDrawTarget(
|
|
already_AddRefed<gfx::DrawTarget> aDT) {
|
|
bool result = PersistentBufferProviderBasic::ReturnDrawTarget(std::move(aDT));
|
|
GetDrawTargetWebgl()->EndFrame();
|
|
return result;
|
|
}
|
|
|
|
already_AddRefed<gfx::SourceSurface>
|
|
PersistentBufferProviderAccelerated::BorrowSnapshot(gfx::DrawTarget* aTarget) {
|
|
mSnapshot = GetDrawTargetWebgl()->GetOptimizedSnapshot(aTarget);
|
|
return do_AddRef(mSnapshot);
|
|
}
|
|
|
|
bool PersistentBufferProviderAccelerated::RequiresRefresh() const {
|
|
return GetDrawTargetWebgl()->RequiresRefresh();
|
|
}
|
|
|
|
void PersistentBufferProviderAccelerated::OnMemoryPressure() {
|
|
GetDrawTargetWebgl()->OnMemoryPressure();
|
|
}
|
|
|
|
static already_AddRefed<TextureClient> CreateTexture(
|
|
KnowsCompositor* aKnowsCompositor, gfx::SurfaceFormat aFormat,
|
|
gfx::IntSize aSize, bool aWillReadFrequently) {
|
|
TextureAllocationFlags flags = ALLOC_DEFAULT;
|
|
if (aWillReadFrequently) {
|
|
flags = TextureAllocationFlags(flags | ALLOC_DO_NOT_ACCELERATE);
|
|
}
|
|
return TextureClient::CreateForDrawing(
|
|
aKnowsCompositor, aFormat, aSize, BackendSelector::Canvas,
|
|
TextureFlags::DEFAULT | TextureFlags::NON_BLOCKING_READ_LOCK, flags);
|
|
}
|
|
|
|
// static
|
|
already_AddRefed<PersistentBufferProviderShared>
|
|
PersistentBufferProviderShared::Create(gfx::IntSize aSize,
|
|
gfx::SurfaceFormat aFormat,
|
|
KnowsCompositor* aKnowsCompositor,
|
|
bool aWillReadFrequently) {
|
|
if (!aKnowsCompositor || !aKnowsCompositor->GetTextureForwarder() ||
|
|
!aKnowsCompositor->GetTextureForwarder()->IPCOpen()) {
|
|
return nullptr;
|
|
}
|
|
|
|
if (!StaticPrefs::layers_shared_buffer_provider_enabled()) {
|
|
return nullptr;
|
|
}
|
|
|
|
#ifdef XP_WIN
|
|
// Bug 1285271 - Disable shared buffer provider on Windows with D2D due to
|
|
// instability, unless we are remoting the canvas drawing to the GPU process.
|
|
if (gfxPlatform::GetPlatform()->GetPreferredCanvasBackend() ==
|
|
BackendType::DIRECT2D1_1 &&
|
|
!TextureData::IsRemote(aKnowsCompositor, BackendSelector::Canvas)) {
|
|
return nullptr;
|
|
}
|
|
#endif
|
|
|
|
RefPtr<TextureClient> texture =
|
|
CreateTexture(aKnowsCompositor, aFormat, aSize, aWillReadFrequently);
|
|
if (!texture) {
|
|
return nullptr;
|
|
}
|
|
|
|
RefPtr<PersistentBufferProviderShared> provider =
|
|
new PersistentBufferProviderShared(aSize, aFormat, aKnowsCompositor,
|
|
texture, aWillReadFrequently);
|
|
return provider.forget();
|
|
}
|
|
|
|
PersistentBufferProviderShared::PersistentBufferProviderShared(
|
|
gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
|
|
KnowsCompositor* aKnowsCompositor, RefPtr<TextureClient>& aTexture,
|
|
bool aWillReadFrequently)
|
|
|
|
: mSize(aSize),
|
|
mFormat(aFormat),
|
|
mKnowsCompositor(aKnowsCompositor),
|
|
mFront(Nothing()),
|
|
mWillReadFrequently(aWillReadFrequently) {
|
|
MOZ_ASSERT(aKnowsCompositor);
|
|
if (mTextures.append(aTexture)) {
|
|
mBack = Some<uint32_t>(0);
|
|
}
|
|
|
|
// XXX KnowsCompositor could be used for mMaxAllowedTextures
|
|
if (gfxVars::UseWebRenderTripleBufferingWin()) {
|
|
++mMaxAllowedTextures;
|
|
}
|
|
|
|
MOZ_COUNT_CTOR(PersistentBufferProviderShared);
|
|
}
|
|
|
|
PersistentBufferProviderShared::~PersistentBufferProviderShared() {
|
|
MOZ_COUNT_DTOR(PersistentBufferProviderShared);
|
|
|
|
if (IsActivityTracked()) {
|
|
mKnowsCompositor->GetActiveResourceTracker()->RemoveObject(this);
|
|
}
|
|
|
|
Destroy();
|
|
}
|
|
|
|
bool PersistentBufferProviderShared::SetKnowsCompositor(
|
|
KnowsCompositor* aKnowsCompositor, bool& aOutLostFrontTexture) {
|
|
MOZ_ASSERT(aKnowsCompositor);
|
|
MOZ_ASSERT(!aOutLostFrontTexture);
|
|
if (!aKnowsCompositor) {
|
|
return false;
|
|
}
|
|
|
|
if (mKnowsCompositor == aKnowsCompositor) {
|
|
// The forwarder should not change most of the time.
|
|
return true;
|
|
}
|
|
|
|
if (IsActivityTracked()) {
|
|
mKnowsCompositor->GetActiveResourceTracker()->RemoveObject(this);
|
|
}
|
|
|
|
if (mKnowsCompositor->GetTextureForwarder() !=
|
|
aKnowsCompositor->GetTextureForwarder() ||
|
|
mKnowsCompositor->GetCompositorBackendType() !=
|
|
aKnowsCompositor->GetCompositorBackendType()) {
|
|
// We are going to be used with an different and/or incompatible forwarder.
|
|
// This should be extremely rare. We have to copy the front buffer into a
|
|
// texture that is compatible with the new forwarder.
|
|
|
|
// Grab the current front buffer.
|
|
RefPtr<TextureClient> prevTexture = GetTexture(mFront);
|
|
|
|
// Get rid of everything else
|
|
Destroy();
|
|
|
|
if (prevTexture && !prevTexture->IsValid()) {
|
|
aOutLostFrontTexture = true;
|
|
} else if (prevTexture && prevTexture->IsValid()) {
|
|
RefPtr<TextureClient> newTexture =
|
|
CreateTexture(aKnowsCompositor, mFormat, mSize, mWillReadFrequently);
|
|
|
|
MOZ_ASSERT(newTexture);
|
|
if (!newTexture) {
|
|
return false;
|
|
}
|
|
|
|
// If we early-return in one of the following branches, we will
|
|
// leave the buffer provider in an empty state, since we called
|
|
// Destroy. Not ideal but at least we won't try to use it with a
|
|
// an incompatible ipc channel.
|
|
|
|
if (!newTexture->Lock(OpenMode::OPEN_WRITE)) {
|
|
return false;
|
|
}
|
|
|
|
if (!prevTexture->Lock(OpenMode::OPEN_READ)) {
|
|
newTexture->Unlock();
|
|
return false;
|
|
}
|
|
|
|
bool success =
|
|
prevTexture->CopyToTextureClient(newTexture, nullptr, nullptr);
|
|
|
|
prevTexture->Unlock();
|
|
newTexture->Unlock();
|
|
|
|
if (!success) {
|
|
return false;
|
|
}
|
|
|
|
if (!mTextures.append(newTexture)) {
|
|
return false;
|
|
}
|
|
mFront = Some<uint32_t>(mTextures.length() - 1);
|
|
mBack = mFront;
|
|
}
|
|
}
|
|
|
|
mKnowsCompositor = aKnowsCompositor;
|
|
|
|
return true;
|
|
}
|
|
|
|
TextureClient* PersistentBufferProviderShared::GetTexture(
|
|
const Maybe<uint32_t>& aIndex) {
|
|
if (aIndex.isNothing() || !CheckIndex(aIndex.value())) {
|
|
return nullptr;
|
|
}
|
|
return mTextures[aIndex.value()];
|
|
}
|
|
|
|
already_AddRefed<gfx::DrawTarget>
|
|
PersistentBufferProviderShared::BorrowDrawTarget(
|
|
const gfx::IntRect& aPersistedRect) {
|
|
if (!mKnowsCompositor->GetTextureForwarder() ||
|
|
!mKnowsCompositor->GetTextureForwarder()->IPCOpen()) {
|
|
return nullptr;
|
|
}
|
|
|
|
MOZ_ASSERT(!mSnapshot);
|
|
|
|
if (IsActivityTracked()) {
|
|
mKnowsCompositor->GetActiveResourceTracker()->MarkUsed(this);
|
|
} else {
|
|
mKnowsCompositor->GetActiveResourceTracker()->AddObject(this);
|
|
}
|
|
|
|
if (mDrawTarget) {
|
|
RefPtr<gfx::DrawTarget> dt(mDrawTarget);
|
|
return dt.forget();
|
|
}
|
|
|
|
auto previousBackBuffer = mBack;
|
|
|
|
TextureClient* tex = GetTexture(mBack);
|
|
|
|
// First try to reuse the current back buffer. If we can do that it means
|
|
// we can skip copying its content to the new back buffer.
|
|
if (tex && tex->IsReadLocked()) {
|
|
// The back buffer is currently used by the compositor, we can't draw
|
|
// into it.
|
|
tex = nullptr;
|
|
}
|
|
|
|
if (!tex) {
|
|
// Try to grab an already allocated texture if any is available.
|
|
for (uint32_t i = 0; i < mTextures.length(); ++i) {
|
|
if (!mTextures[i]->IsReadLocked()) {
|
|
mBack = Some(i);
|
|
tex = mTextures[i];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!tex) {
|
|
// We have to allocate a new texture.
|
|
if (mTextures.length() >= mMaxAllowedTextures) {
|
|
// We should never need to buffer that many textures, something's wrong.
|
|
// In theory we throttle the main thread when the compositor can't keep
|
|
// up, so we shoud never get in a situation where we sent 4 textures to
|
|
// the compositor and the latter has not released any of them. In
|
|
// practice, though, the throttling mechanism appears to have some issues,
|
|
// especially when switching between layer managers (during tab-switch).
|
|
// To make sure we don't get too far ahead of the compositor, we send a
|
|
// sync ping to the compositor thread...
|
|
mKnowsCompositor->SyncWithCompositor();
|
|
// ...and try again.
|
|
for (uint32_t i = 0; i < mTextures.length(); ++i) {
|
|
if (!mTextures[i]->IsReadLocked()) {
|
|
gfxCriticalNote << "Managed to allocate after flush.";
|
|
mBack = Some(i);
|
|
tex = mTextures[i];
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!tex) {
|
|
gfxCriticalNote << "Unexpected BufferProvider over-production.";
|
|
// It would be pretty bad to keep piling textures up at this point so we
|
|
// call NotifyInactive to remove some of our textures.
|
|
NotifyInactive();
|
|
// Give up now. The caller can fall-back to a non-shared buffer
|
|
// provider.
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
RefPtr<TextureClient> newTexture =
|
|
CreateTexture(mKnowsCompositor, mFormat, mSize, mWillReadFrequently);
|
|
|
|
MOZ_ASSERT(newTexture);
|
|
if (newTexture) {
|
|
if (mTextures.append(newTexture)) {
|
|
tex = newTexture;
|
|
mBack = Some<uint32_t>(mTextures.length() - 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!tex) {
|
|
return nullptr;
|
|
}
|
|
|
|
if (mPermanentBackBuffer) {
|
|
// If we have a permanent back buffer lock the selected one and switch to
|
|
// the permanent one before borrowing the DrawTarget. We will copy back into
|
|
// the selected one when ReturnDrawTarget is called, before we make it the
|
|
// new front buffer.
|
|
if (!tex->Lock(OpenMode::OPEN_WRITE)) {
|
|
return nullptr;
|
|
}
|
|
tex = mPermanentBackBuffer;
|
|
} else {
|
|
// Copy from the previous back buffer if required.
|
|
Maybe<TextureClientAutoLock> autoReadLock;
|
|
TextureClient* previous = nullptr;
|
|
if (mBack != previousBackBuffer && !aPersistedRect.IsEmpty()) {
|
|
if (tex->HasSynchronization()) {
|
|
// We are about to read lock a texture that is in use by the compositor
|
|
// and has synchronization. To prevent possible future contention we
|
|
// switch to using a permanent back buffer.
|
|
mPermanentBackBuffer = CreateTexture(mKnowsCompositor, mFormat, mSize,
|
|
mWillReadFrequently);
|
|
if (!mPermanentBackBuffer) {
|
|
return nullptr;
|
|
}
|
|
if (!tex->Lock(OpenMode::OPEN_WRITE)) {
|
|
return nullptr;
|
|
}
|
|
tex = mPermanentBackBuffer;
|
|
}
|
|
|
|
previous = GetTexture(previousBackBuffer);
|
|
if (previous) {
|
|
autoReadLock.emplace(previous, OpenMode::OPEN_READ);
|
|
}
|
|
}
|
|
|
|
if (!tex->Lock(OpenMode::OPEN_READ_WRITE)) {
|
|
return nullptr;
|
|
}
|
|
|
|
if (autoReadLock.isSome() && autoReadLock->Succeeded() && previous) {
|
|
DebugOnly<bool> success =
|
|
previous->CopyToTextureClient(tex, &aPersistedRect, nullptr);
|
|
MOZ_ASSERT(success);
|
|
}
|
|
}
|
|
|
|
mDrawTarget = tex->BorrowDrawTarget();
|
|
if (mDrawTarget) {
|
|
// This is simply to ensure the DrawTarget gets initialized, and will detect
|
|
// a device reset, even if we're on the main thread.
|
|
mDrawTarget->ClearRect(Rect(0, 0, 0, 0));
|
|
|
|
if (!mDrawTarget->IsValid()) {
|
|
mDrawTarget = nullptr;
|
|
}
|
|
}
|
|
|
|
RefPtr<gfx::DrawTarget> dt(mDrawTarget);
|
|
return dt.forget();
|
|
}
|
|
|
|
bool PersistentBufferProviderShared::ReturnDrawTarget(
|
|
already_AddRefed<gfx::DrawTarget> aDT) {
|
|
RefPtr<gfx::DrawTarget> dt(aDT);
|
|
MOZ_ASSERT(mDrawTarget == dt);
|
|
// Can't change the current front buffer while its snapshot is borrowed!
|
|
MOZ_ASSERT(!mSnapshot);
|
|
|
|
TextureClient* back = GetTexture(mBack);
|
|
MOZ_ASSERT(back);
|
|
|
|
mDrawTarget = nullptr;
|
|
dt = nullptr;
|
|
|
|
// If we have a permanent back buffer we have actually been drawing to that,
|
|
// so now we must copy to the shared one.
|
|
if (mPermanentBackBuffer && back) {
|
|
DebugOnly<bool> success =
|
|
mPermanentBackBuffer->CopyToTextureClient(back, nullptr, nullptr);
|
|
MOZ_ASSERT(success);
|
|
|
|
// Let our permanent back buffer know that we have finished drawing.
|
|
mPermanentBackBuffer->EndDraw();
|
|
}
|
|
|
|
if (back) {
|
|
back->Unlock();
|
|
mFront = mBack;
|
|
}
|
|
|
|
return !!back;
|
|
}
|
|
|
|
TextureClient* PersistentBufferProviderShared::GetTextureClient() {
|
|
// Can't access the front buffer while drawing.
|
|
MOZ_ASSERT(!mDrawTarget);
|
|
TextureClient* texture = GetTexture(mFront);
|
|
if (!texture) {
|
|
gfxCriticalNote
|
|
<< "PersistentBufferProviderShared: front buffer unavailable";
|
|
return nullptr;
|
|
}
|
|
|
|
// Sometimes, for example on tab switch, we re-forward our texture. So if we
|
|
// are and it is still read locked, then borrow and return our DrawTarget to
|
|
// force it to be copied to a texture that we will safely read lock.
|
|
if (texture->IsReadLocked()) {
|
|
RefPtr<DrawTarget> dt =
|
|
BorrowDrawTarget(IntRect(0, 0, mSize.width, mSize.height));
|
|
|
|
// If we failed to borrow a DrawTarget then all our textures must be read
|
|
// locked or we failed to create one, so we'll just return the current front
|
|
// buffer even though that might lead to contention.
|
|
if (dt) {
|
|
ReturnDrawTarget(dt.forget());
|
|
texture = GetTexture(mFront);
|
|
if (!texture) {
|
|
gfxCriticalNote
|
|
<< "PersistentBufferProviderShared: front buffer unavailable";
|
|
return nullptr;
|
|
}
|
|
}
|
|
} else {
|
|
// If it isn't read locked then make sure it is set as updated, so that we
|
|
// will always read lock even if we've forwarded the texture before.
|
|
texture->SetUpdated();
|
|
}
|
|
|
|
return texture;
|
|
}
|
|
|
|
already_AddRefed<gfx::SourceSurface>
|
|
PersistentBufferProviderShared::BorrowSnapshot(gfx::DrawTarget* aTarget) {
|
|
// If we have a permanent back buffer we can always use that to snapshot.
|
|
if (mPermanentBackBuffer) {
|
|
mSnapshot = mPermanentBackBuffer->BorrowSnapshot();
|
|
return do_AddRef(mSnapshot);
|
|
}
|
|
|
|
if (mDrawTarget) {
|
|
auto back = GetTexture(mBack);
|
|
MOZ_ASSERT(back && back->IsLocked());
|
|
mSnapshot = back->BorrowSnapshot();
|
|
return do_AddRef(mSnapshot);
|
|
}
|
|
|
|
auto front = GetTexture(mFront);
|
|
if (!front || front->IsLocked()) {
|
|
MOZ_ASSERT(false);
|
|
return nullptr;
|
|
}
|
|
|
|
if (front->IsReadLocked() && front->HasSynchronization()) {
|
|
// We are about to read lock a texture that is in use by the compositor and
|
|
// has synchronization. To prevent possible future contention we switch to
|
|
// using a permanent back buffer.
|
|
mPermanentBackBuffer =
|
|
CreateTexture(mKnowsCompositor, mFormat, mSize, mWillReadFrequently);
|
|
if (!mPermanentBackBuffer ||
|
|
!mPermanentBackBuffer->Lock(OpenMode::OPEN_READ_WRITE)) {
|
|
return nullptr;
|
|
}
|
|
|
|
if (!front->Lock(OpenMode::OPEN_READ)) {
|
|
return nullptr;
|
|
}
|
|
|
|
DebugOnly<bool> success =
|
|
front->CopyToTextureClient(mPermanentBackBuffer, nullptr, nullptr);
|
|
MOZ_ASSERT(success);
|
|
front->Unlock();
|
|
mSnapshot = mPermanentBackBuffer->BorrowSnapshot();
|
|
return do_AddRef(mSnapshot);
|
|
}
|
|
|
|
if (!front->Lock(OpenMode::OPEN_READ)) {
|
|
return nullptr;
|
|
}
|
|
|
|
mSnapshot = front->BorrowSnapshot();
|
|
|
|
return do_AddRef(mSnapshot);
|
|
}
|
|
|
|
void PersistentBufferProviderShared::ReturnSnapshot(
|
|
already_AddRefed<gfx::SourceSurface> aSnapshot) {
|
|
RefPtr<SourceSurface> snapshot = aSnapshot;
|
|
MOZ_ASSERT(!snapshot || snapshot == mSnapshot);
|
|
|
|
mSnapshot = nullptr;
|
|
snapshot = nullptr;
|
|
|
|
if (mDrawTarget || mPermanentBackBuffer) {
|
|
return;
|
|
}
|
|
|
|
auto front = GetTexture(mFront);
|
|
if (front) {
|
|
front->Unlock();
|
|
}
|
|
}
|
|
|
|
void PersistentBufferProviderShared::NotifyInactive() {
|
|
ClearCachedResources();
|
|
}
|
|
|
|
void PersistentBufferProviderShared::ClearCachedResources() {
|
|
RefPtr<TextureClient> front = GetTexture(mFront);
|
|
RefPtr<TextureClient> back = GetTexture(mBack);
|
|
|
|
// Clear all textures (except the front and back ones that we just kept).
|
|
mTextures.clear();
|
|
mPermanentBackBuffer = nullptr;
|
|
|
|
if (back) {
|
|
if (mTextures.append(back)) {
|
|
mBack = Some<uint32_t>(0);
|
|
}
|
|
if (front == back) {
|
|
mFront = mBack;
|
|
}
|
|
}
|
|
|
|
if (front && front != back) {
|
|
if (mTextures.append(front)) {
|
|
mFront = Some<uint32_t>(mTextures.length() - 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
void PersistentBufferProviderShared::Destroy() {
|
|
mSnapshot = nullptr;
|
|
mDrawTarget = nullptr;
|
|
|
|
if (mPermanentBackBuffer) {
|
|
mPermanentBackBuffer->Unlock();
|
|
mPermanentBackBuffer = nullptr;
|
|
}
|
|
|
|
for (auto& texture : mTextures) {
|
|
if (texture && texture->IsLocked()) {
|
|
MOZ_ASSERT(false);
|
|
texture->Unlock();
|
|
}
|
|
}
|
|
|
|
mTextures.clear();
|
|
}
|
|
|
|
bool PersistentBufferProviderShared::IsAccelerated() const {
|
|
#ifdef XP_WIN
|
|
// Detect if we're using D2D canvas.
|
|
if (mWillReadFrequently || mTextures.empty() || !mTextures[0]) {
|
|
return false;
|
|
}
|
|
auto* data = mTextures[0]->GetInternalData();
|
|
if (data && data->AsD3D11TextureData()) {
|
|
return true;
|
|
}
|
|
#endif
|
|
return false;
|
|
}
|
|
|
|
} // namespace layers
|
|
} // namespace mozilla
|