This mainly provides DrawTargetWebgl, which implements the subset of the DrawTarget API necessary for integration with CanvasRenderingContext2D. It translates them to suitable commands for its internal ClientWebGLContext, which then manages remoting WebGL requests to the parent/GPU process. Currently two shaders are used for drawing Canvas2D primitives, but can be expanded in the future. These are an image shader and a solid color shader. The core of this implementation revolves around TexturePacker and TextureHandle, which cope with the necessity of frequently uploading SourceSurfaces for use with WebGL. TexturePacker implements a bin-packing algorithm for packing these uploads into texture pages, which can either be SharedTextures if they are reasonably small, or StandaloneTextures if they are too big to pack in a SharedTexture. Each upload is assigned a TextureHandle which is used to manage it in a move-to-front cache, so that we can easily eject TextureHandles from the back of the cache if we have too many. These TextureHandles are associated with the SourceSurface that spawned them to more easily manage their lifetimes. There are further dependent caches for dealing with blurred shadows and with text. Shadows are cached in an uploaded texture bound to the SourceSurface that generated them. Text is handled by caching entire runs in the GlyphCache (keyed by both their rendering parameters and their glyphs). The text is first rasterized to a surface and then uploaded to a texture in the GlyphCache which can be reused should the text be encountered again. To deal with commands we can't accelerate, a separate internal DrawTargetSkia is also maintained. The content of the WebGL framebuffer is copied into it so that drawing can then proceed in software from there. It remains in this fallover state until the next frame, when it resets back to using the WebGL framebuffer again. This acceleration is disabled by default. To enable it, you must toggle the pref "gfx.canvas.accelerated" to true. This should be suitably different from the naming of the previous SkiaGL prefs to not alias with them. There are a few dependent prefs that follow from the previous SkiaGL prefs for setting the size limitations for acceleration and also limitations for the internal texture cache. Differential Revision: https://phabricator.services.mozilla.com/D130388
629 lines
19 KiB
C++
629 lines
19 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 "Layers.h"
|
|
#include "mozilla/layers/TextureClient.h"
|
|
#include "mozilla/layers/TextureForwarder.h"
|
|
#include "mozilla/gfx/gfxVars.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() {
|
|
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);
|
|
}
|
|
|
|
PersistentBufferProviderAccelerated::~PersistentBufferProviderAccelerated() {
|
|
MOZ_COUNT_DTOR(PersistentBufferProviderAccelerated);
|
|
}
|
|
|
|
ClientWebGLContext* PersistentBufferProviderAccelerated::AsWebgl() {
|
|
return (ClientWebGLContext*)mDrawTarget->GetNativeSurface(
|
|
NativeSurfaceType::WEBGL_CONTEXT);
|
|
}
|
|
|
|
// static
|
|
already_AddRefed<PersistentBufferProviderShared>
|
|
PersistentBufferProviderShared::Create(gfx::IntSize aSize,
|
|
gfx::SurfaceFormat aFormat,
|
|
KnowsCompositor* aKnowsCompositor) {
|
|
if (!aKnowsCompositor || !aKnowsCompositor->GetTextureForwarder() ||
|
|
!aKnowsCompositor->GetTextureForwarder()->IPCOpen() ||
|
|
// Bug 1556433 - shared buffer provider and direct texture mapping do not
|
|
// synchronize properly
|
|
aKnowsCompositor->SupportsTextureDirectMapping()) {
|
|
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 = TextureClient::CreateForDrawing(
|
|
aKnowsCompositor, aFormat, aSize, BackendSelector::Canvas,
|
|
TextureFlags::DEFAULT | TextureFlags::NON_BLOCKING_READ_LOCK,
|
|
TextureAllocationFlags::ALLOC_DEFAULT);
|
|
|
|
if (!texture) {
|
|
return nullptr;
|
|
}
|
|
|
|
RefPtr<PersistentBufferProviderShared> provider =
|
|
new PersistentBufferProviderShared(aSize, aFormat, aKnowsCompositor,
|
|
texture);
|
|
return provider.forget();
|
|
}
|
|
|
|
PersistentBufferProviderShared::PersistentBufferProviderShared(
|
|
gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
|
|
KnowsCompositor* aKnowsCompositor, RefPtr<TextureClient>& aTexture)
|
|
|
|
: mSize(aSize),
|
|
mFormat(aFormat),
|
|
mKnowsCompositor(aKnowsCompositor),
|
|
mFront(Nothing()) {
|
|
MOZ_ASSERT(aKnowsCompositor);
|
|
|
|
// If our textures have synchronization use a separate permanent back buffer.
|
|
if (aTexture->HasSynchronization()) {
|
|
mPermanentBackBuffer = aTexture;
|
|
if (!mPermanentBackBuffer->Lock(OpenMode::OPEN_READ_WRITE)) {
|
|
mPermanentBackBuffer = nullptr;
|
|
}
|
|
} else {
|
|
if (mTextures.append(aTexture)) {
|
|
mBack = Some<uint32_t>(0);
|
|
}
|
|
}
|
|
|
|
// If we are using webrender and our textures don't have an intermediate
|
|
// buffer, then we have to hold onto the textures for longer to make sure that
|
|
// the GPU has finished using them. So, we need to allow more TextureClients
|
|
// to be created.
|
|
if (!aTexture->HasIntermediateBuffer() && gfxVars::UseWebRender()) {
|
|
++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) {
|
|
MOZ_ASSERT(aKnowsCompositor);
|
|
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) {
|
|
RefPtr<TextureClient> newTexture = TextureClient::CreateForDrawing(
|
|
aKnowsCompositor, mFormat, mSize, BackendSelector::Canvas,
|
|
TextureFlags::DEFAULT | TextureFlags::NON_BLOCKING_READ_LOCK,
|
|
TextureAllocationFlags::ALLOC_DEFAULT);
|
|
|
|
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 our new texture has internal synchronization then create a new
|
|
// permanent back buffer as well.
|
|
if (newTexture->HasSynchronization()) {
|
|
RefPtr<TextureClient> mPermanentBackBuffer =
|
|
TextureClient::CreateForDrawing(
|
|
aKnowsCompositor, mFormat, mSize, BackendSelector::Canvas,
|
|
TextureFlags::DEFAULT | TextureFlags::NON_BLOCKING_READ_LOCK,
|
|
TextureAllocationFlags::ALLOC_DEFAULT);
|
|
if (!mPermanentBackBuffer) {
|
|
return false;
|
|
}
|
|
|
|
if (!mPermanentBackBuffer->Lock(OpenMode::OPEN_READ_WRITE)) {
|
|
mPermanentBackBuffer = nullptr;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
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);
|
|
|
|
if (success && mPermanentBackBuffer) {
|
|
success = prevTexture->CopyToTextureClient(mPermanentBackBuffer,
|
|
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 ((mTextureLockIsUnreliable.isSome() &&
|
|
mTextureLockIsUnreliable == mBack) ||
|
|
(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() &&
|
|
!(mTextureLockIsUnreliable.isSome() &&
|
|
mTextureLockIsUnreliable.ref() == i)) {
|
|
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 = TextureClient::CreateForDrawing(
|
|
mKnowsCompositor, mFormat, mSize, BackendSelector::Canvas,
|
|
TextureFlags::DEFAULT | TextureFlags::NON_BLOCKING_READ_LOCK,
|
|
TextureAllocationFlags::ALLOC_DEFAULT);
|
|
|
|
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()) {
|
|
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);
|
|
}
|
|
}
|
|
|
|
// Clear dirty texture, since new back texture is selected.
|
|
mTextureLockIsUnreliable = Nothing();
|
|
|
|
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() {
|
|
// 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->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();
|
|
|
|
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);
|
|
}
|
|
}
|
|
// Set front texture as dirty texture.
|
|
// The texture's read lock is unreliable after this function call.
|
|
mTextureLockIsUnreliable = mFront;
|
|
}
|
|
|
|
void PersistentBufferProviderShared::Destroy() {
|
|
mSnapshot = nullptr;
|
|
mDrawTarget = nullptr;
|
|
|
|
if (mPermanentBackBuffer) {
|
|
mPermanentBackBuffer->Unlock();
|
|
mPermanentBackBuffer = nullptr;
|
|
}
|
|
|
|
for (auto& mTexture : mTextures) {
|
|
TextureClient* texture = mTexture;
|
|
if (texture && texture->IsLocked()) {
|
|
MOZ_ASSERT(false);
|
|
texture->Unlock();
|
|
}
|
|
}
|
|
|
|
mTextures.clear();
|
|
}
|
|
|
|
} // namespace layers
|
|
} // namespace mozilla
|