fune/gfx/layers/PersistentBufferProvider.cpp
Andrew Osmond 4d74abb189 Bug 1877429 - Prevent offscreen canvas2d updates from racing with compositing. r=gfx-reviewers,lsalzman
When OffscreenCanvas::CommitFrameToCompositor uses the non-remote
texture canvas path with Skia, it uses ImageBridgeChild for compositing.
When ImageContainer::SetCurrentImages is called, there was an
intermediate state where the relevant textures were not yet marked as
read only for the compositor's consumption, because the event to do so
was dispatched asynchronously to the ImageBridgeChild thread. If the
owning thread of the canvas (main or DOM worker) ran immediately after
CommitFrameToCompositor, then we could run into texture reuse since
nothing marked the texture yet as being used for compositing. This had
the end result of sometimes displaying back buffer textures currently
being used for drawing on the display pipeline.

This patch makes it so that we mark OffscreenCanvas textures as read
only for the compositor before dispatching, and releasing the lock
either when we swap the images in the ImageContainer (winning the race
with ImageBridgeChild), or after the compositor has finished with it
(losing the race, if any, with ImageBridgeChild).

Additionally, to handle better the case where we run out of buffers, we
need to implement ImageBridgeChild::SyncWithCompositor, to be analogous
to how WebRenderBridgeChild::SyncWithCompositor works. We achieve this
by calling from ImageBridgeChild back into the appropriate
WebRenderBridgeChild based on the window ID associated with the canvas,

It also adds a new pref, gfx.offscreencanvas.shared-provider, which
allows one to switch between PersistentBufferProviderShared and Basic.
The latter of which is used if we fallback from using shared buffers if
it takes too long to get the shared buffers back from the compositor.

Differential Revision: https://phabricator.services.mozilla.com/D200991
2024-02-07 20:25:52 +00:00

775 lines
23 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/KnowsCompositor.h"
#include "mozilla/layers/RemoteTextureMap.h"
#include "mozilla/layers/TextureClient.h"
#include "mozilla/layers/TextureForwarder.h"
#include "mozilla/gfx/gfxVars.h"
#include "mozilla/gfx/CanvasManagerChild.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();
}
static already_AddRefed<TextureClient> CreateTexture(
KnowsCompositor* aKnowsCompositor, gfx::SurfaceFormat aFormat,
gfx::IntSize aSize, bool aWillReadFrequently = false,
Maybe<RemoteTextureOwnerId> aRemoteTextureOwnerId = {}) {
TextureAllocationFlags flags = ALLOC_DEFAULT;
if (aWillReadFrequently) {
flags = TextureAllocationFlags(flags | ALLOC_DO_NOT_ACCELERATE);
}
if (aRemoteTextureOwnerId) {
flags = TextureAllocationFlags(flags | ALLOC_FORCE_REMOTE);
}
RefPtr<TextureClient> tc = TextureClient::CreateForDrawing(
aKnowsCompositor, aFormat, aSize, BackendSelector::Canvas,
TextureFlags::DEFAULT | TextureFlags::NON_BLOCKING_READ_LOCK, flags);
if (tc && aRemoteTextureOwnerId) {
if (TextureData* td = tc->GetInternalData()) {
td->SetRemoteTextureOwnerId(*aRemoteTextureOwnerId);
}
}
return tc.forget();
}
// static
already_AddRefed<PersistentBufferProviderAccelerated>
PersistentBufferProviderAccelerated::Create(gfx::IntSize aSize,
gfx::SurfaceFormat aFormat,
KnowsCompositor* aKnowsCompositor) {
if (!aKnowsCompositor || !aKnowsCompositor->GetTextureForwarder() ||
!aKnowsCompositor->GetTextureForwarder()->IPCOpen()) {
return nullptr;
}
if (!DrawTargetWebgl::CanCreate(aSize, aFormat)) {
#ifdef XP_WIN
// Direct2D acceleration does not require DrawTargetWebgl, but still
// requires PersistentBufferProviderAccelerated.
if (!TextureData::IsRemote(aKnowsCompositor, BackendSelector::Canvas,
aFormat, aSize)) {
return nullptr;
}
#else
return nullptr;
#endif
}
auto remoteTextureOwnerId = RemoteTextureOwnerId::GetNext();
RefPtr<TextureClient> texture = CreateTexture(
aKnowsCompositor, aFormat, aSize, false, Some(remoteTextureOwnerId));
if (!texture) {
return nullptr;
}
RefPtr<PersistentBufferProviderAccelerated> provider =
new PersistentBufferProviderAccelerated(texture);
return provider.forget();
}
PersistentBufferProviderAccelerated::PersistentBufferProviderAccelerated(
const RefPtr<TextureClient>& aTexture)
: mTexture(aTexture) {
MOZ_COUNT_CTOR(PersistentBufferProviderAccelerated);
}
PersistentBufferProviderAccelerated::~PersistentBufferProviderAccelerated() {
MOZ_COUNT_DTOR(PersistentBufferProviderAccelerated);
Destroy();
}
void PersistentBufferProviderAccelerated::Destroy() {
mSnapshot = nullptr;
mDrawTarget = nullptr;
if (mTexture) {
if (mTexture->IsLocked()) {
MOZ_ASSERT(false);
mTexture->Unlock();
}
mTexture = nullptr;
}
}
already_AddRefed<gfx::DrawTarget>
PersistentBufferProviderAccelerated::BorrowDrawTarget(
const gfx::IntRect& aPersistedRect) {
if (!mDrawTarget) {
if (aPersistedRect.IsEmpty()) {
mTexture->GetInternalData()->InvalidateContents();
}
if (!mTexture->Lock(OpenMode::OPEN_READ_WRITE)) {
return nullptr;
}
mDrawTarget = mTexture->BorrowDrawTarget();
if (!mDrawTarget || !mDrawTarget->IsValid()) {
mDrawTarget = nullptr;
mTexture->Unlock();
return nullptr;
}
}
return do_AddRef(mDrawTarget);
}
bool PersistentBufferProviderAccelerated::ReturnDrawTarget(
already_AddRefed<gfx::DrawTarget> aDT) {
{
RefPtr<gfx::DrawTarget> dt(aDT);
MOZ_ASSERT(mDrawTarget == dt);
if (!mDrawTarget) {
return false;
}
mDrawTarget = nullptr;
}
mTexture->Unlock();
return true;
}
already_AddRefed<gfx::SourceSurface>
PersistentBufferProviderAccelerated::BorrowSnapshot(gfx::DrawTarget* aTarget) {
if (mDrawTarget) {
MOZ_ASSERT(mTexture->IsLocked());
} else {
if (mTexture->IsLocked()) {
MOZ_ASSERT(false);
return nullptr;
}
if (!mTexture->Lock(OpenMode::OPEN_READ)) {
return nullptr;
}
}
mSnapshot = mTexture->BorrowSnapshot();
return do_AddRef(mSnapshot);
}
void PersistentBufferProviderAccelerated::ReturnSnapshot(
already_AddRefed<gfx::SourceSurface> aSnapshot) {
RefPtr<SourceSurface> snapshot = aSnapshot;
MOZ_ASSERT(!snapshot || snapshot == mSnapshot);
snapshot = nullptr;
mTexture->ReturnSnapshot(mSnapshot.forget());
if (!mDrawTarget) {
mTexture->Unlock();
}
}
Maybe<SurfaceDescriptor> PersistentBufferProviderAccelerated::GetFrontBuffer() {
SurfaceDescriptor desc;
if (mTexture->GetInternalData()->Serialize(desc)) {
return Some(desc);
}
return Nothing();
}
bool PersistentBufferProviderAccelerated::RequiresRefresh() const {
return mTexture->GetInternalData()->RequiresRefresh();
}
already_AddRefed<FwdTransactionTracker>
PersistentBufferProviderAccelerated::UseCompositableForwarder(
CompositableForwarder* aForwarder) {
return mTexture->GetInternalData()->UseCompositableForwarder(aForwarder);
}
// static
already_AddRefed<PersistentBufferProviderShared>
PersistentBufferProviderShared::Create(gfx::IntSize aSize,
gfx::SurfaceFormat aFormat,
KnowsCompositor* aKnowsCompositor,
bool aWillReadFrequently,
const Maybe<uint64_t>& aWindowID) {
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.
aWillReadFrequently = true;
#endif
RefPtr<TextureClient> texture =
CreateTexture(aKnowsCompositor, aFormat, aSize, aWillReadFrequently);
if (!texture) {
return nullptr;
}
RefPtr<PersistentBufferProviderShared> provider =
new PersistentBufferProviderShared(aSize, aFormat, aKnowsCompositor,
texture, aWillReadFrequently,
aWindowID);
return provider.forget();
}
PersistentBufferProviderShared::PersistentBufferProviderShared(
gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
KnowsCompositor* aKnowsCompositor, RefPtr<TextureClient>& aTexture,
bool aWillReadFrequently, const Maybe<uint64_t>& aWindowID)
: mSize(aSize),
mFormat(aFormat),
mKnowsCompositor(aKnowsCompositor),
mFront(Nothing()),
mWillReadFrequently(aWillReadFrequently),
mWindowID(aWindowID) {
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()) {
if (auto* cm = CanvasManagerChild::Get()) {
cm->GetActiveResourceTracker()->RemoveObject(this);
} else {
MOZ_ASSERT_UNREACHABLE("Tracked but no CanvasManagerChild!");
}
}
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()) {
if (auto* cm = CanvasManagerChild::Get()) {
cm->GetActiveResourceTracker()->RemoveObject(this);
} else {
MOZ_ASSERT_UNREACHABLE("Tracked but no CanvasManagerChild!");
}
}
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;
}
auto* cm = CanvasManagerChild::Get();
if (NS_WARN_IF(!cm)) {
return nullptr;
}
MOZ_ASSERT(!mSnapshot);
if (IsActivityTracked()) {
cm->GetActiveResourceTracker()->MarkUsed(this);
} else {
cm->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(mWindowID);
// ...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();
}
} // namespace layers
} // namespace mozilla