Bug 1709603: Use a separate permanent canvas back buffer when texture has synchronization. r=lsalzman

Differential Revision: https://phabricator.services.mozilla.com/D125201
This commit is contained in:
Bob Owen 2021-09-10 16:37:05 +00:00
Родитель 82f617d91d
Коммит 74b9880c08
6 изменённых файлов: 104 добавлений и 14 удалений

Просмотреть файл

@ -147,8 +147,17 @@ PersistentBufferProviderShared::PersistentBufferProviderShared(
mKnowsCompositor(aKnowsCompositor),
mFront(Nothing()) {
MOZ_ASSERT(aKnowsCompositor);
if (mTextures.append(aTexture)) {
mBack = Some<uint32_t>(0);
// 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
@ -230,6 +239,24 @@ bool PersistentBufferProviderShared::SetKnowsCompositor(
// 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;
}
@ -242,6 +269,11 @@ bool PersistentBufferProviderShared::SetKnowsCompositor(
bool success =
prevTexture->CopyToTextureClient(newTexture, nullptr, nullptr);
if (success && mPermanentBackBuffer) {
success = prevTexture->CopyToTextureClient(mPermanentBackBuffer,
nullptr, nullptr);
}
prevTexture->Unlock();
newTexture->Unlock();
@ -369,7 +401,17 @@ PersistentBufferProviderShared::BorrowDrawTarget(
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()) {
@ -421,6 +463,17 @@ bool PersistentBufferProviderShared::ReturnDrawTarget(
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;
@ -469,6 +522,12 @@ TextureClient* PersistentBufferProviderShared::GetTextureClient() {
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());
@ -499,7 +558,7 @@ void PersistentBufferProviderShared::ReturnSnapshot(
mSnapshot = nullptr;
snapshot = nullptr;
if (mDrawTarget) {
if (mDrawTarget || mPermanentBackBuffer) {
return;
}
@ -543,6 +602,11 @@ void PersistentBufferProviderShared::Destroy() {
mSnapshot = nullptr;
mDrawTarget = nullptr;
if (mPermanentBackBuffer) {
mPermanentBackBuffer->Unlock();
mPermanentBackBuffer = nullptr;
}
for (auto& mTexture : mTextures) {
TextureClient* texture = mTexture;
if (texture && texture->IsLocked()) {

Просмотреть файл

@ -174,6 +174,11 @@ class PersistentBufferProviderShared : public PersistentBufferProvider,
gfx::IntSize mSize;
gfx::SurfaceFormat mFormat;
RefPtr<KnowsCompositor> mKnowsCompositor;
// If the texture has its own synchronization then copying back from the
// previous texture can cause contention issues and even deadlocks. So we use
// a separate permanent back buffer and copy into the shared back buffer when
// the DrawTarget is returned, before making it the new front buffer.
RefPtr<TextureClient> mPermanentBackBuffer;
// We may need two extra textures if webrender is enabled.
static const size_t kMaxTexturesAllowed = 4;
Vector<RefPtr<TextureClient>, kMaxTexturesAllowed + 2> mTextures;

Просмотреть файл

@ -819,6 +819,13 @@ gfx::DrawTarget* TextureClient::BorrowDrawTarget() {
return mBorrowedDrawTarget;
}
void TextureClient::EndDraw() {
MOZ_ASSERT(!mBorrowedDrawTarget ||
mBorrowedDrawTarget->refCount() <= mExpectedDtRefs);
mBorrowedDrawTarget = nullptr;
mData->EndDraw();
}
already_AddRefed<gfx::SourceSurface> TextureClient::BorrowSnapshot() {
MOZ_ASSERT(mIsLocked);

Просмотреть файл

@ -269,6 +269,12 @@ class TextureData {
return nullptr;
}
/**
* When the TextureData is not being Unlocked, this can be used to inform a
* TextureData that drawing has finished until the next BorrowDrawTarget.
*/
virtual void EndDraw() {}
virtual already_AddRefed<gfx::SourceSurface> BorrowSnapshot() {
return nullptr;
}
@ -466,6 +472,12 @@ class TextureClient : public AtomicRefCountedWithFinalize<TextureClient> {
*/
gfx::DrawTarget* BorrowDrawTarget();
/**
* When the TextureClient is not being Unlocked, this can be used to inform it
* that drawing has finished until the next BorrowDrawTarget.
*/
void EndDraw();
already_AddRefed<gfx::SourceSurface> BorrowSnapshot();
/**

Просмотреть файл

@ -61,27 +61,30 @@ bool RecordedTextureData::Lock(OpenMode aMode) {
mCanvasChild->RecordEvent(RecordedTextureLock(mTextureId, aMode));
if (aMode & OpenMode::OPEN_WRITE) {
mCanvasChild->OnTextureWriteLock();
mSnapshot = nullptr;
}
mLockedMode = aMode;
return true;
}
void RecordedTextureData::Unlock() {
if ((mLockedMode & OpenMode::OPEN_WRITE) &&
mCanvasChild->ShouldCacheDataSurface()) {
mSnapshot = mDT->Snapshot();
mDT->DetachAllSnapshots();
}
mCanvasChild->RecordEvent(RecordedTextureUnlock(mTextureId));
mLockedMode = OpenMode::OPEN_NONE;
}
already_AddRefed<gfx::DrawTarget> RecordedTextureData::BorrowDrawTarget() {
mSnapshot = nullptr;
return do_AddRef(mDT);
}
void RecordedTextureData::EndDraw() {
MOZ_ASSERT(mDT->hasOneRef());
if (mCanvasChild->ShouldCacheDataSurface()) {
mSnapshot = mDT->Snapshot();
mCanvasChild->RecordEvent(RecordedCacheDataSurface(mSnapshot.get()));
}
}
already_AddRefed<gfx::SourceSurface> RecordedTextureData::BorrowSnapshot() {
MOZ_ASSERT(mDT);
@ -101,9 +104,6 @@ bool RecordedTextureData::Serialize(SurfaceDescriptor& aDescriptor) {
void RecordedTextureData::OnForwardedToHost() {
mCanvasChild->OnTextureForwarded();
if (mSnapshot && mCanvasChild->ShouldCacheDataSurface()) {
mCanvasChild->RecordEvent(RecordedCacheDataSurface(mSnapshot.get()));
}
}
TextureFlags RecordedTextureData::GetTextureFlags() const {

Просмотреть файл

@ -28,6 +28,8 @@ class RecordedTextureData final : public TextureData {
already_AddRefed<gfx::DrawTarget> BorrowDrawTarget() final;
void EndDraw() final;
already_AddRefed<gfx::SourceSurface> BorrowSnapshot() final;
void Deallocate(LayersIPCChannel* aAllocator) final;