Files
tubestation/gfx/layers/ipc/CanvasChild.cpp
Butkovits Atila 2406ce261c Backed out 8 changesets (bug 1942129) for causing bustages at SharedMemoryMapping.h. CLOSED TREE
Backed out changeset 8d9053f1c203 (bug 1942129)
Backed out changeset 393e3c507c27 (bug 1942129)
Backed out changeset 8240d353d224 (bug 1942129)
Backed out changeset 8c4cd026b720 (bug 1942129)
Backed out changeset 742634b0d6e9 (bug 1942129)
Backed out changeset d16857f9812f (bug 1942129)
Backed out changeset 7ff7af041ee7 (bug 1942129)
Backed out changeset ef41d9e4c7de (bug 1942129)
2025-03-04 00:43:23 +02:00

716 lines
22 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 "CanvasChild.h"
#include "MainThreadUtils.h"
#include "mozilla/dom/WorkerPrivate.h"
#include "mozilla/dom/WorkerRef.h"
#include "mozilla/dom/WorkerRunnable.h"
#include "mozilla/gfx/CanvasManagerChild.h"
#include "mozilla/gfx/CanvasShutdownManager.h"
#include "mozilla/gfx/DrawTargetRecording.h"
#include "mozilla/gfx/Tools.h"
#include "mozilla/gfx/Rect.h"
#include "mozilla/gfx/Point.h"
#include "mozilla/ipc/Endpoint.h"
#include "mozilla/ipc/ProcessChild.h"
#include "mozilla/layers/CanvasDrawEventRecorder.h"
#include "mozilla/layers/ImageDataSerializer.h"
#include "mozilla/layers/SourceSurfaceSharedData.h"
#include "mozilla/AppShutdown.h"
#include "mozilla/Maybe.h"
#include "mozilla/Mutex.h"
#include "nsIObserverService.h"
#include "RecordedCanvasEventImpl.h"
namespace mozilla {
namespace layers {
class RecorderHelpers final : public CanvasDrawEventRecorder::Helpers {
public:
NS_DECL_OWNINGTHREAD
explicit RecorderHelpers(const RefPtr<CanvasChild>& aCanvasChild)
: mCanvasChild(aCanvasChild) {}
~RecorderHelpers() override = default;
bool InitTranslator(TextureType aTextureType, TextureType aWebglTextureType,
gfx::BackendType aBackendType, Handle&& aReadHandle,
nsTArray<Handle>&& aBufferHandles, uint64_t aBufferSize,
CrossProcessSemaphoreHandle&& aReaderSem,
CrossProcessSemaphoreHandle&& aWriterSem) override {
NS_ASSERT_OWNINGTHREAD(RecorderHelpers);
if (NS_WARN_IF(!mCanvasChild)) {
return false;
}
return mCanvasChild->SendInitTranslator(
aTextureType, aWebglTextureType, aBackendType, std::move(aReadHandle),
std::move(aBufferHandles), aBufferSize, std::move(aReaderSem),
std::move(aWriterSem));
}
bool AddBuffer(Handle&& aBufferHandle, uint64_t aBufferSize) override {
NS_ASSERT_OWNINGTHREAD(RecorderHelpers);
if (!mCanvasChild) {
return false;
}
return mCanvasChild->SendAddBuffer(std::move(aBufferHandle), aBufferSize);
}
bool ReaderClosed() override {
NS_ASSERT_OWNINGTHREAD(RecorderHelpers);
if (!mCanvasChild) {
return false;
}
return !mCanvasChild->CanSend() || AppShutdown::IsShutdownImpending();
}
bool RestartReader() override {
NS_ASSERT_OWNINGTHREAD(RecorderHelpers);
if (!mCanvasChild) {
return false;
}
return mCanvasChild->SendRestartTranslation();
}
private:
const WeakPtr<CanvasChild> mCanvasChild;
};
class SourceSurfaceCanvasRecording final : public gfx::SourceSurface {
public:
MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(SourceSurfaceCanvasRecording, final)
SourceSurfaceCanvasRecording(
const RemoteTextureOwnerId aTextureOwnerId,
const RefPtr<gfx::SourceSurface>& aRecordedSuface,
CanvasChild* aCanvasChild,
const RefPtr<CanvasDrawEventRecorder>& aRecorder)
: mTextureOwnerId(aTextureOwnerId),
mRecordedSurface(aRecordedSuface),
mCanvasChild(aCanvasChild),
mRecorder(aRecorder) {
// It's important that AddStoredObject is called first because that will
// run any pending processing required by recorded objects that have been
// deleted off the main thread.
mRecorder->AddStoredObject(this);
mRecorder->RecordEvent(RecordedAddSurfaceAlias(this, aRecordedSuface));
}
~SourceSurfaceCanvasRecording() {
ReferencePtr surfaceAlias = this;
if (NS_IsMainThread()) {
ReleaseOnMainThread(std::move(mRecorder), surfaceAlias,
std::move(mRecordedSurface), std::move(mCanvasChild));
return;
}
mRecorder->AddPendingDeletion(
[recorder = std::move(mRecorder), surfaceAlias,
aliasedSurface = std::move(mRecordedSurface),
canvasChild = std::move(mCanvasChild)]() mutable -> void {
ReleaseOnMainThread(std::move(recorder), surfaceAlias,
std::move(aliasedSurface),
std::move(canvasChild));
});
}
gfx::SurfaceType GetType() const final { return mRecordedSurface->GetType(); }
gfx::IntSize GetSize() const final { return mRecordedSurface->GetSize(); }
gfx::SurfaceFormat GetFormat() const final {
return mRecordedSurface->GetFormat();
}
already_AddRefed<gfx::DataSourceSurface> GetDataSurface() final {
EnsureDataSurfaceOnMainThread();
return do_AddRef(mDataSourceSurface);
}
void AttachSurface() { mDetached = false; }
void DetachSurface() { mDetached = true; }
void InvalidateDataSurface() {
if (mDataSourceSurface && mMayInvalidate) {
// This must be the only reference to the data left.
MOZ_ASSERT(mDataSourceSurface->hasOneRef());
mDataSourceSurface =
gfx::Factory::CopyDataSourceSurface(mDataSourceSurface);
mMayInvalidate = false;
}
}
already_AddRefed<gfx::SourceSurface> ExtractSubrect(
const gfx::IntRect& aRect) final {
return mRecordedSurface->ExtractSubrect(aRect);
}
bool GetSurfaceDescriptor(SurfaceDescriptor& aDesc) const final {
aDesc = SurfaceDescriptorCanvasSurface(
static_cast<gfx::CanvasManagerChild*>(mCanvasChild->Manager())->Id(),
uintptr_t(gfx::ReferencePtr(this)));
return true;
}
private:
void EnsureDataSurfaceOnMainThread() {
// The data can only be retrieved on the main thread.
if (!mDataSourceSurface && NS_IsMainThread()) {
mDataSourceSurface = mCanvasChild->GetDataSurface(
mTextureOwnerId, mRecordedSurface, mDetached, mMayInvalidate);
}
}
// Used to ensure that clean-up that requires it is done on the main thread.
static void ReleaseOnMainThread(RefPtr<CanvasDrawEventRecorder> aRecorder,
ReferencePtr aSurfaceAlias,
RefPtr<gfx::SourceSurface> aAliasedSurface,
RefPtr<CanvasChild> aCanvasChild) {
MOZ_ASSERT(NS_IsMainThread());
aRecorder->RemoveStoredObject(aSurfaceAlias);
aRecorder->RecordEvent(RecordedRemoveSurfaceAlias(aSurfaceAlias));
aAliasedSurface = nullptr;
aCanvasChild = nullptr;
aRecorder = nullptr;
}
const RemoteTextureOwnerId mTextureOwnerId;
RefPtr<gfx::SourceSurface> mRecordedSurface;
RefPtr<CanvasChild> mCanvasChild;
RefPtr<CanvasDrawEventRecorder> mRecorder;
RefPtr<gfx::DataSourceSurface> mDataSourceSurface;
bool mDetached = false;
bool mMayInvalidate = false;
};
class CanvasDataShmemHolder {
public:
CanvasDataShmemHolder(ipc::SharedMemory* aShmem, CanvasChild* aCanvasChild)
: mMutex("CanvasChild::DataShmemHolder::mMutex"),
mShmem(aShmem),
mCanvasChild(aCanvasChild) {}
bool Init(dom::ThreadSafeWorkerRef* aWorkerRef) {
if (!aWorkerRef) {
return true;
}
RefPtr<dom::StrongWorkerRef> workerRef = dom::StrongWorkerRef::Create(
aWorkerRef->Private(), "CanvasChild::DataShmemHolder",
[this]() { DestroyWorker(); });
if (NS_WARN_IF(!workerRef)) {
return false;
}
MutexAutoLock lock(mMutex);
mWorkerRef = new dom::ThreadSafeWorkerRef(workerRef);
return true;
}
void Destroy() {
class DestroyRunnable final : public dom::WorkerThreadRunnable {
public:
explicit DestroyRunnable(CanvasDataShmemHolder* aShmemHolder)
: dom::WorkerThreadRunnable("CanvasDataShmemHolder::Destroy"),
mShmemHolder(aShmemHolder) {}
bool WorkerRun(JSContext* aCx,
dom::WorkerPrivate* aWorkerPrivate) override {
mShmemHolder->Destroy();
return true;
}
void PostRun(JSContext* aCx, dom::WorkerPrivate* aWorkerPrivate,
bool aRunResult) override {}
bool PreDispatch(dom::WorkerPrivate* aWorkerPrivate) override {
return true;
}
void PostDispatch(dom::WorkerPrivate* aWorkerPrivate,
bool aDispatchResult) override {}
private:
CanvasDataShmemHolder* mShmemHolder;
};
mMutex.Lock();
if (mCanvasChild) {
if (mWorkerRef) {
if (!mWorkerRef->Private()->IsOnCurrentThread()) {
auto task = MakeRefPtr<DestroyRunnable>(this);
dom::WorkerPrivate* worker = mWorkerRef->Private();
mMutex.Unlock();
task->Dispatch(worker);
return;
}
} else if (!NS_IsMainThread()) {
mMutex.Unlock();
NS_DispatchToMainThread(NS_NewRunnableFunction(
"CanvasDataShmemHolder::Destroy", [this]() { Destroy(); }));
return;
}
mCanvasChild->ReturnDataSurfaceShmem(mShmem.forget());
mCanvasChild = nullptr;
mWorkerRef = nullptr;
}
mMutex.Unlock();
delete this;
}
void DestroyWorker() {
MutexAutoLock lock(mMutex);
mCanvasChild = nullptr;
mWorkerRef = nullptr;
}
private:
Mutex mMutex;
RefPtr<ipc::SharedMemory> mShmem;
RefPtr<CanvasChild> mCanvasChild MOZ_GUARDED_BY(mMutex);
RefPtr<dom::ThreadSafeWorkerRef> mWorkerRef MOZ_GUARDED_BY(mMutex);
};
CanvasChild::CanvasChild(dom::ThreadSafeWorkerRef* aWorkerRef)
: mWorkerRef(aWorkerRef) {}
CanvasChild::~CanvasChild() { MOZ_ASSERT(!mWorkerRef); }
static void NotifyCanvasDeviceChanged() {
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
if (obs) {
obs->NotifyObservers(nullptr, "canvas-device-reset", nullptr);
}
}
ipc::IPCResult CanvasChild::RecvNotifyDeviceChanged() {
NS_ASSERT_OWNINGTHREAD(CanvasChild);
NotifyCanvasDeviceChanged();
mRecorder->RecordEvent(RecordedDeviceChangeAcknowledged());
return IPC_OK();
}
ipc::IPCResult CanvasChild::RecvNotifyDeviceReset(
const nsTArray<RemoteTextureOwnerId>& aOwnerIds) {
NS_ASSERT_OWNINGTHREAD(CanvasChild);
if (auto* manager = gfx::CanvasShutdownManager::MaybeGet()) {
manager->OnRemoteCanvasReset(aOwnerIds);
}
mRecorder->RecordEvent(RecordedDeviceResetAcknowledged());
return IPC_OK();
}
/* static */ bool CanvasChild::mDeactivated = false;
ipc::IPCResult CanvasChild::RecvDeactivate() {
NS_ASSERT_OWNINGTHREAD(CanvasChild);
RefPtr<CanvasChild> self(this);
mDeactivated = true;
if (auto* cm = gfx::CanvasManagerChild::Get()) {
cm->DeactivateCanvas();
}
NotifyCanvasDeviceChanged();
return IPC_OK();
}
ipc::IPCResult CanvasChild::RecvBlockCanvas() {
mBlocked = true;
if (auto* cm = gfx::CanvasManagerChild::Get()) {
cm->BlockCanvas();
}
return IPC_OK();
}
bool CanvasChild::EnsureRecorder(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
TextureType aTextureType,
TextureType aWebglTextureType) {
NS_ASSERT_OWNINGTHREAD(CanvasChild);
if (!mRecorder) {
gfx::BackendType backendType =
gfxPlatform::GetPlatform()->GetPreferredCanvasBackend();
auto recorder = MakeRefPtr<CanvasDrawEventRecorder>(mWorkerRef);
if (!recorder->Init(aTextureType, aWebglTextureType, backendType,
MakeUnique<RecorderHelpers>(this))) {
return false;
}
mRecorder = recorder.forget();
}
if (NS_WARN_IF(mRecorder->GetTextureType() != aTextureType)) {
// The recorder has already been initialized with a different type. This can
// happen if there is a device reset or fallback that causes a switch to a
// different unaccelerated texture type (i.e. unknown). In that case, just
// fall back to non-remote rendering.
return false;
}
EnsureDataSurfaceShmem(aSize, aFormat);
return true;
}
void CanvasChild::ActorDestroy(ActorDestroyReason aWhy) {
NS_ASSERT_OWNINGTHREAD(CanvasChild);
if (mRecorder) {
mRecorder->DetachResources();
}
mTextureInfo.clear();
}
void CanvasChild::Destroy() {
NS_ASSERT_OWNINGTHREAD(CanvasChild);
if (CanSend()) {
Send__delete__(this);
}
mWorkerRef = nullptr;
}
bool CanvasChild::EnsureBeginTransaction() {
NS_ASSERT_OWNINGTHREAD(CanvasChild);
if (!mIsInTransaction) {
RecordEvent(RecordedCanvasBeginTransaction());
mIsInTransaction = true;
}
return true;
}
void CanvasChild::EndTransaction() {
NS_ASSERT_OWNINGTHREAD(CanvasChild);
if (mIsInTransaction) {
RecordEvent(RecordedCanvasEndTransaction());
mIsInTransaction = false;
mDormant = false;
} else if (mRecorder) {
// Schedule to drop free buffers if we have no non-empty transactions.
if (!mDormant) {
mDormant = true;
NS_DelayedDispatchToCurrentThread(
NewRunnableMethod("CanvasChild::DropFreeBuffersWhenDormant", this,
&CanvasChild::DropFreeBuffersWhenDormant),
StaticPrefs::gfx_canvas_remote_drop_buffer_milliseconds());
}
}
// If we are continuously drawing/recording, then we need to periodically
// flush our external surface/image references, to ensure they actually get
// freed on a timely basis.
if (mRecorder) {
mRecorder->ClearProcessedExternalSurfaces();
mRecorder->ClearProcessedExternalImages();
}
++mTransactionsSinceGetDataSurface;
}
void CanvasChild::DropFreeBuffersWhenDormant() {
NS_ASSERT_OWNINGTHREAD(CanvasChild);
// Drop any free buffers if we have not had any non-empty transactions.
if (mDormant && mRecorder) {
mRecorder->DropFreeBuffers();
// Notify CanvasTranslator it is dormant.
SendDropFreeBuffersWhenDormant();
}
}
void CanvasChild::ClearCachedResources() {
NS_ASSERT_OWNINGTHREAD(CanvasChild);
if (mRecorder) {
mRecorder->DropFreeBuffers();
// Notify CanvasTranslator it is about to be minimized.
SendClearCachedResources();
}
}
bool CanvasChild::ShouldBeCleanedUp() const {
NS_ASSERT_OWNINGTHREAD(CanvasChild);
// Always return true if we've been deactivated.
if (Deactivated()) {
return true;
}
// We can only be cleaned up if nothing else references our recorder.
return !mRecorder || (mRecorder->hasOneRef() && mTextureInfo.empty());
}
already_AddRefed<gfx::DrawTargetRecording> CanvasChild::CreateDrawTarget(
const RemoteTextureOwnerId& aTextureOwnerId, gfx::IntSize aSize,
gfx::SurfaceFormat aFormat) {
NS_ASSERT_OWNINGTHREAD(CanvasChild);
MOZ_ASSERT(mTextureInfo.find(aTextureOwnerId) == mTextureInfo.end());
if (!mRecorder) {
return nullptr;
}
RefPtr<gfx::DrawTarget> dummyDt = gfx::Factory::CreateDrawTarget(
gfx::BackendType::SKIA, gfx::IntSize(1, 1), aFormat);
RefPtr<gfx::DrawTargetRecording> dt = MakeAndAddRef<gfx::DrawTargetRecording>(
mRecorder, aTextureOwnerId, dummyDt, aSize);
dt->SetOptimizeTransform(true);
mTextureInfo.insert({aTextureOwnerId, {}});
return dt.forget();
}
bool CanvasChild::EnsureDataSurfaceShmem(gfx::IntSize aSize,
gfx::SurfaceFormat aFormat) {
NS_ASSERT_OWNINGTHREAD(CanvasChild);
if (!mRecorder) {
return false;
}
size_t sizeRequired =
ImageDataSerializer::ComputeRGBBufferSize(aSize, aFormat);
if (!sizeRequired) {
return false;
}
sizeRequired = ipc::SharedMemory::PageAlignedSize(sizeRequired);
if (!mDataSurfaceShmemAvailable || mDataSurfaceShmem->Size() < sizeRequired) {
RecordEvent(RecordedPauseTranslation());
auto dataSurfaceShmem = MakeRefPtr<ipc::SharedMemory>();
if (!dataSurfaceShmem->Create(sizeRequired) ||
!dataSurfaceShmem->Map(sizeRequired)) {
return false;
}
auto shmemHandle = dataSurfaceShmem->TakeHandle();
if (!shmemHandle) {
return false;
}
if (!SendSetDataSurfaceBuffer(std::move(shmemHandle), sizeRequired)) {
return false;
}
mDataSurfaceShmem = dataSurfaceShmem.forget();
mDataSurfaceShmemAvailable = true;
}
MOZ_ASSERT(mDataSurfaceShmemAvailable);
return true;
}
void CanvasChild::RecordEvent(const gfx::RecordedEvent& aEvent) {
NS_ASSERT_OWNINGTHREAD(CanvasChild);
// We drop mRecorder in ActorDestroy to break the reference cycle.
if (!mRecorder) {
return;
}
mRecorder->RecordEvent(aEvent);
}
int64_t CanvasChild::CreateCheckpoint() {
NS_ASSERT_OWNINGTHREAD(CanvasChild);
return mRecorder->CreateCheckpoint();
}
already_AddRefed<gfx::DataSourceSurface> CanvasChild::GetDataSurface(
const RemoteTextureOwnerId aTextureOwnerId,
const gfx::SourceSurface* aSurface, bool aDetached, bool& aMayInvalidate) {
NS_ASSERT_OWNINGTHREAD(CanvasChild);
MOZ_ASSERT(aSurface);
// mTransactionsSinceGetDataSurface is used to determine if we want to prepare
// a DataSourceSurface in the GPU process up front at the end of the
// transaction, but that only makes sense if the canvas JS is requesting data
// in between transactions.
if (!mIsInTransaction) {
mTransactionsSinceGetDataSurface = 0;
}
if (!EnsureBeginTransaction()) {
return nullptr;
}
gfx::IntSize ssSize = aSurface->GetSize();
gfx::SurfaceFormat ssFormat = aSurface->GetFormat();
auto stride = ImageDataSerializer::ComputeRGBStride(ssFormat, ssSize.width);
// Shmem is only valid if the surface is the latest snapshot (not detached).
if (!aDetached) {
// If there is a shmem associated with this snapshot id, then we want to try
// use that directly without having to allocate a new shmem for retrieval.
auto it = mTextureInfo.find(aTextureOwnerId);
if (it != mTextureInfo.end() && it->second.mSnapshotShmem) {
const auto shmemPtr =
reinterpret_cast<uint8_t*>(it->second.mSnapshotShmem->Memory());
MOZ_ASSERT(shmemPtr);
mRecorder->RecordEvent(RecordedPrepareShmem(aTextureOwnerId));
auto checkpoint = CreateCheckpoint();
if (NS_WARN_IF(!mRecorder->WaitForCheckpoint(checkpoint))) {
return nullptr;
}
auto* closure =
new CanvasDataShmemHolder(it->second.mSnapshotShmem, this);
if (NS_WARN_IF(!closure->Init(mWorkerRef))) {
delete closure;
return nullptr;
}
RefPtr<gfx::DataSourceSurface> dataSurface =
gfx::Factory::CreateWrappingDataSourceSurface(
shmemPtr, stride, ssSize, ssFormat, ReleaseDataShmemHolder,
closure);
aMayInvalidate = true;
return dataSurface.forget();
}
}
RecordEvent(RecordedPrepareDataForSurface(aSurface));
if (!EnsureDataSurfaceShmem(ssSize, ssFormat)) {
return nullptr;
}
RecordEvent(RecordedGetDataForSurface(aSurface));
auto checkpoint = CreateCheckpoint();
if (NS_WARN_IF(!mRecorder->WaitForCheckpoint(checkpoint))) {
return nullptr;
}
auto* closure = new CanvasDataShmemHolder(mDataSurfaceShmem, this);
if (NS_WARN_IF(!closure->Init(mWorkerRef))) {
delete closure;
return nullptr;
}
mDataSurfaceShmemAvailable = false;
auto* data = static_cast<uint8_t*>(mDataSurfaceShmem->Memory());
RefPtr<gfx::DataSourceSurface> dataSurface =
gfx::Factory::CreateWrappingDataSourceSurface(
data, stride, ssSize, ssFormat, ReleaseDataShmemHolder, closure);
aMayInvalidate = false;
return dataSurface.forget();
}
/* static */ void CanvasChild::ReleaseDataShmemHolder(void* aClosure) {
auto* shmemHolder = static_cast<CanvasDataShmemHolder*>(aClosure);
shmemHolder->Destroy();
}
already_AddRefed<gfx::SourceSurface> CanvasChild::WrapSurface(
const RefPtr<gfx::SourceSurface>& aSurface,
const RemoteTextureOwnerId aTextureOwnerId) {
NS_ASSERT_OWNINGTHREAD(CanvasChild);
if (!aSurface) {
return nullptr;
}
return MakeAndAddRef<SourceSurfaceCanvasRecording>(aTextureOwnerId, aSurface,
this, mRecorder);
}
void CanvasChild::ReturnDataSurfaceShmem(
already_AddRefed<ipc::SharedMemory> aDataSurfaceShmem) {
RefPtr<ipc::SharedMemory> data = aDataSurfaceShmem;
// We can only reuse the latest data surface shmem.
if (data == mDataSurfaceShmem) {
MOZ_ASSERT(!mDataSurfaceShmemAvailable);
mDataSurfaceShmemAvailable = true;
}
}
void CanvasChild::AttachSurface(const RefPtr<gfx::SourceSurface>& aSurface) {
if (auto* surface =
static_cast<SourceSurfaceCanvasRecording*>(aSurface.get())) {
surface->AttachSurface();
}
}
void CanvasChild::DetachSurface(const RefPtr<gfx::SourceSurface>& aSurface,
bool aInvalidate) {
if (auto* surface =
static_cast<SourceSurfaceCanvasRecording*>(aSurface.get())) {
surface->DetachSurface();
if (aInvalidate) {
surface->InvalidateDataSurface();
}
}
}
ipc::IPCResult CanvasChild::RecvNotifyRequiresRefresh(
const RemoteTextureOwnerId aTextureOwnerId) {
auto it = mTextureInfo.find(aTextureOwnerId);
if (it != mTextureInfo.end()) {
it->second.mRequiresRefresh = true;
}
return IPC_OK();
}
bool CanvasChild::RequiresRefresh(
const RemoteTextureOwnerId aTextureOwnerId) const {
if (mBlocked) {
return true;
}
auto it = mTextureInfo.find(aTextureOwnerId);
if (it != mTextureInfo.end()) {
return it->second.mRequiresRefresh;
}
return false;
}
ipc::IPCResult CanvasChild::RecvSnapshotShmem(
const RemoteTextureOwnerId aTextureOwnerId, Handle&& aShmemHandle,
uint32_t aShmemSize, SnapshotShmemResolver&& aResolve) {
auto it = mTextureInfo.find(aTextureOwnerId);
if (it != mTextureInfo.end()) {
auto shmem = MakeRefPtr<ipc::SharedMemory>();
if (NS_WARN_IF(!shmem->SetHandle(std::move(aShmemHandle),
ipc::SharedMemory::RightsReadOnly)) ||
NS_WARN_IF(!shmem->Map(aShmemSize))) {
shmem = nullptr;
} else {
it->second.mSnapshotShmem = std::move(shmem);
}
aResolve(true);
} else {
aResolve(false);
}
return IPC_OK();
}
ipc::IPCResult CanvasChild::RecvNotifyTextureDestruction(
const RemoteTextureOwnerId aTextureOwnerId) {
auto it = mTextureInfo.find(aTextureOwnerId);
if (it == mTextureInfo.end()) {
MOZ_ASSERT(!CanSend());
return IPC_OK();
}
mTextureInfo.erase(aTextureOwnerId);
return IPC_OK();
}
} // namespace layers
} // namespace mozilla