mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-03-01 13:57:32 +00:00

Even though drawing a canvas to itself tries to use ExtractSubrect to get a limited copy where applicable, the canvas still inadvertently forces the copy-on-write snapshot to also force a copy due to SurfaceFromElementResult holding a subversive extra reference to the snapshot, keeping it alive when it need not be. RecordedTextureData also holds a reference to the snapshot that needs to be cleaned up as well when the DT is modified. Differential Revision: https://phabricator.services.mozilla.com/D216520
231 lines
7.6 KiB
C++
231 lines
7.6 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 "TextureRecorded.h"
|
|
#include "mozilla/gfx/DrawTargetRecording.h"
|
|
#include "mozilla/layers/CompositableForwarder.h"
|
|
|
|
#include "RecordedCanvasEventImpl.h"
|
|
|
|
namespace mozilla {
|
|
namespace layers {
|
|
|
|
// The texture ID is used in the GPU process both to lookup the real texture in
|
|
// the canvas threads and to lookup the SurfaceDescriptor for that texture in
|
|
// the compositor thread. It is therefore important that the ID is unique (per
|
|
// recording process), otherwise an old descriptor can be picked up. This means
|
|
// we can't use the pointer in the recording process as an ID like we do for
|
|
// other objects.
|
|
static int64_t sNextRecordedTextureId = 0;
|
|
|
|
RecordedTextureData::RecordedTextureData(
|
|
already_AddRefed<CanvasChild> aCanvasChild, gfx::IntSize aSize,
|
|
gfx::SurfaceFormat aFormat, TextureType aTextureType,
|
|
TextureType aWebglTextureType)
|
|
: mCanvasChild(aCanvasChild), mSize(aSize), mFormat(aFormat) {}
|
|
|
|
RecordedTextureData::~RecordedTextureData() {
|
|
// We need the translator to drop its reference for the DrawTarget first,
|
|
// because the TextureData might need to destroy its DrawTarget within a lock.
|
|
mSnapshot = nullptr;
|
|
DetachSnapshotWrapper();
|
|
if (mDT) {
|
|
mDT->DetachTextureData(this);
|
|
mDT = nullptr;
|
|
}
|
|
mCanvasChild->CleanupTexture(mTextureId);
|
|
mCanvasChild->RecordEvent(RecordedTextureDestruction(
|
|
mTextureId, ToRemoteTextureTxnType(mFwdTransactionTracker),
|
|
ToRemoteTextureTxnId(mFwdTransactionTracker)));
|
|
}
|
|
|
|
void RecordedTextureData::FillInfo(TextureData::Info& aInfo) const {
|
|
aInfo.size = mSize;
|
|
aInfo.format = mFormat;
|
|
aInfo.supportsMoz2D = true;
|
|
aInfo.hasSynchronization = true;
|
|
}
|
|
|
|
void RecordedTextureData::SetRemoteTextureOwnerId(
|
|
RemoteTextureOwnerId aRemoteTextureOwnerId) {
|
|
mRemoteTextureOwnerId = aRemoteTextureOwnerId;
|
|
}
|
|
|
|
void RecordedTextureData::InvalidateContents() { mInvalidContents = true; }
|
|
|
|
bool RecordedTextureData::Lock(OpenMode aMode) {
|
|
if (!mCanvasChild->EnsureBeginTransaction()) {
|
|
return false;
|
|
}
|
|
|
|
if (!mRemoteTextureOwnerId.IsValid()) {
|
|
MOZ_ASSERT(false);
|
|
return false;
|
|
}
|
|
|
|
// If modifying the texture, then allocate a new remote texture id.
|
|
if (aMode & OpenMode::OPEN_WRITE) {
|
|
mUsedRemoteTexture = false;
|
|
}
|
|
|
|
bool wasInvalidContents = mInvalidContents;
|
|
mInvalidContents = false;
|
|
|
|
if (!mDT) {
|
|
mTextureId = sNextRecordedTextureId++;
|
|
mDT = mCanvasChild->CreateDrawTarget(mTextureId, mRemoteTextureOwnerId,
|
|
mSize, mFormat);
|
|
if (!mDT) {
|
|
return false;
|
|
}
|
|
|
|
mDT->AttachTextureData(this);
|
|
|
|
// We lock the TextureData when we create it to get the remote DrawTarget.
|
|
mLockedMode = aMode;
|
|
return true;
|
|
}
|
|
|
|
mCanvasChild->RecordEvent(
|
|
RecordedTextureLock(mTextureId, aMode, wasInvalidContents));
|
|
mLockedMode = aMode;
|
|
return true;
|
|
}
|
|
|
|
void RecordedTextureData::DetachSnapshotWrapper(bool aInvalidate,
|
|
bool aRelease) {
|
|
if (mSnapshotWrapper) {
|
|
// If the snapshot only has one ref, then we don't need to worry about
|
|
// copying before invalidation since it is about to be deleted. Otherwise,
|
|
// we need to ensure any internal data is appropriately copied before
|
|
// shmems are potentially overwritten if there are still existing users.
|
|
mCanvasChild->DetachSurface(mSnapshotWrapper,
|
|
aInvalidate && !mSnapshotWrapper->hasOneRef());
|
|
if (aRelease) {
|
|
mSnapshotWrapper = nullptr;
|
|
}
|
|
}
|
|
}
|
|
|
|
void RecordedTextureData::Unlock() {
|
|
if ((mLockedMode == OpenMode::OPEN_READ_WRITE) &&
|
|
mCanvasChild->ShouldCacheDataSurface()) {
|
|
DetachSnapshotWrapper();
|
|
mSnapshot = mDT->Snapshot();
|
|
mDT->DetachAllSnapshots();
|
|
mCanvasChild->RecordEvent(RecordedCacheDataSurface(mSnapshot.get()));
|
|
}
|
|
|
|
mCanvasChild->RecordEvent(RecordedTextureUnlock(mTextureId));
|
|
|
|
mLockedMode = OpenMode::OPEN_NONE;
|
|
}
|
|
|
|
already_AddRefed<gfx::DrawTarget> RecordedTextureData::BorrowDrawTarget() {
|
|
if (mLockedMode & OpenMode::OPEN_WRITE) {
|
|
// The snapshot will be invalidated.
|
|
mSnapshot = nullptr;
|
|
DetachSnapshotWrapper(true);
|
|
}
|
|
return do_AddRef(mDT);
|
|
}
|
|
|
|
void RecordedTextureData::EndDraw() {
|
|
MOZ_ASSERT(mDT->hasOneRef());
|
|
MOZ_ASSERT(mLockedMode == OpenMode::OPEN_READ_WRITE);
|
|
|
|
if (mCanvasChild->ShouldCacheDataSurface()) {
|
|
DetachSnapshotWrapper();
|
|
mSnapshot = mDT->Snapshot();
|
|
mCanvasChild->RecordEvent(RecordedCacheDataSurface(mSnapshot.get()));
|
|
}
|
|
}
|
|
|
|
void RecordedTextureData::DrawTargetWillChange() {
|
|
// The DrawTargetRecording will be modified, so ensure that possibly the last
|
|
// reference to a snapshot is discarded so that it does not inadvertently
|
|
// force a copy.
|
|
mSnapshot = nullptr;
|
|
DetachSnapshotWrapper(true);
|
|
}
|
|
|
|
already_AddRefed<gfx::SourceSurface> RecordedTextureData::BorrowSnapshot() {
|
|
if (mSnapshotWrapper) {
|
|
// The DT is unmodified since the last time snapshot was borrowed, so it
|
|
// is safe to reattach the snapshot for shmem readbacks.
|
|
mCanvasChild->AttachSurface(mSnapshotWrapper);
|
|
return do_AddRef(mSnapshotWrapper);
|
|
}
|
|
|
|
// There are some failure scenarios where we have no DrawTarget and
|
|
// BorrowSnapshot is called in an attempt to copy to a new texture.
|
|
if (!mDT) {
|
|
return nullptr;
|
|
}
|
|
|
|
RefPtr<gfx::SourceSurface> wrapper = mCanvasChild->WrapSurface(
|
|
mSnapshot ? mSnapshot : mDT->Snapshot(), mTextureId);
|
|
mSnapshotWrapper = wrapper;
|
|
return wrapper.forget();
|
|
}
|
|
|
|
void RecordedTextureData::ReturnSnapshot(
|
|
already_AddRefed<gfx::SourceSurface> aSnapshot) {
|
|
RefPtr<gfx::SourceSurface> snapshot = aSnapshot;
|
|
// The snapshot needs to be marked detached but we keep the wrapper around
|
|
// so that it can be reused without repeatedly creating it and accidentally
|
|
// reading back data for each new instantiation.
|
|
DetachSnapshotWrapper(false, false);
|
|
}
|
|
|
|
void RecordedTextureData::Deallocate(LayersIPCChannel* aAllocator) {}
|
|
|
|
bool RecordedTextureData::Serialize(SurfaceDescriptor& aDescriptor) {
|
|
if (!mRemoteTextureOwnerId.IsValid()) {
|
|
MOZ_ASSERT_UNREACHABLE("Missing remote texture owner id!");
|
|
return false;
|
|
}
|
|
|
|
// If something is querying the id, assume it is going to be composited.
|
|
if (!mUsedRemoteTexture) {
|
|
mLastRemoteTextureId = RemoteTextureId::GetNext();
|
|
mCanvasChild->RecordEvent(
|
|
RecordedPresentTexture(mTextureId, mLastRemoteTextureId));
|
|
mUsedRemoteTexture = true;
|
|
} else if (!mLastRemoteTextureId.IsValid()) {
|
|
MOZ_ASSERT_UNREACHABLE("Missing remote texture id!");
|
|
return false;
|
|
}
|
|
|
|
aDescriptor = SurfaceDescriptorRemoteTexture(mLastRemoteTextureId,
|
|
mRemoteTextureOwnerId);
|
|
return true;
|
|
}
|
|
|
|
already_AddRefed<FwdTransactionTracker>
|
|
RecordedTextureData::UseCompositableForwarder(
|
|
CompositableForwarder* aForwarder) {
|
|
return FwdTransactionTracker::GetOrCreate(mFwdTransactionTracker);
|
|
}
|
|
|
|
void RecordedTextureData::OnForwardedToHost() {
|
|
// Compositing with RecordedTextureData requires RemoteTextureMap.
|
|
MOZ_CRASH("OnForwardedToHost not supported!");
|
|
}
|
|
|
|
TextureFlags RecordedTextureData::GetTextureFlags() const {
|
|
// With WebRender, resource open happens asynchronously on RenderThread.
|
|
// Use WAIT_HOST_USAGE_END to keep TextureClient alive during host side usage.
|
|
return TextureFlags::WAIT_HOST_USAGE_END;
|
|
}
|
|
|
|
bool RecordedTextureData::RequiresRefresh() const {
|
|
return mCanvasChild->RequiresRefresh(mTextureId);
|
|
}
|
|
|
|
} // namespace layers
|
|
} // namespace mozilla
|