fune/gfx/layers/ipc/ImageBridgeChild.cpp
Noemi Erli d637b3f05e Backed out 24 changesets (bug 1824465) for causing failures in nsISupportsImpl.cpp CLOSED TREE
Backed out changeset a5c8f22b7170 (bug 1824465)
Backed out changeset b551b655ac72 (bug 1824465)
Backed out changeset 0e6768a6419c (bug 1824465)
Backed out changeset fb115ebb7fe0 (bug 1824465)
Backed out changeset aaa7a2c8aa3b (bug 1824465)
Backed out changeset 7ef94bfa90b3 (bug 1824465)
Backed out changeset a4238fd6b86f (bug 1824465)
Backed out changeset 3a88e4cfbe45 (bug 1824465)
Backed out changeset 40c2467d3162 (bug 1824465)
Backed out changeset 8f900395c72c (bug 1824465)
Backed out changeset 92e4c6e4d73c (bug 1824465)
Backed out changeset 445c5d5d9661 (bug 1824465)
Backed out changeset de51ed5389d9 (bug 1824465)
Backed out changeset 72049d72bcb6 (bug 1824465)
Backed out changeset 126773c2427a (bug 1824465)
Backed out changeset 886e76bc80be (bug 1824465)
Backed out changeset a69a851411f0 (bug 1824465)
Backed out changeset 703599cf6189 (bug 1824465)
Backed out changeset 11ecb78ebc15 (bug 1824465)
Backed out changeset 563255aaa1e1 (bug 1824465)
Backed out changeset d1bf32c2a6c6 (bug 1824465)
Backed out changeset ef28b2777487 (bug 1824465)
Backed out changeset a2015d354bb1 (bug 1824465)
Backed out changeset 31d6b53fdc6a (bug 1824465)
2023-04-06 01:50:55 +03:00

946 lines
28 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 "ImageBridgeChild.h"
#include <vector> // for vector
#include "ImageBridgeParent.h" // for ImageBridgeParent
#include "ImageContainer.h" // for ImageContainer
#include "SynchronousTask.h"
#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
#include "mozilla/Monitor.h" // for Monitor, MonitorAutoLock
#include "mozilla/ReentrantMonitor.h" // for ReentrantMonitor, etc
#include "mozilla/StaticMutex.h"
#include "mozilla/StaticPtr.h" // for StaticRefPtr
#include "mozilla/dom/ContentChild.h"
#include "mozilla/gfx/Point.h" // for IntSize
#include "mozilla/gfx/gfxVars.h"
#include "mozilla/ipc/Endpoint.h"
#include "mozilla/ipc/MessageChannel.h" // for MessageChannel, etc
#include "mozilla/layers/CompositableClient.h" // for CompositableChild, etc
#include "mozilla/layers/CompositorThread.h"
#include "mozilla/layers/ISurfaceAllocator.h" // for ISurfaceAllocator
#include "mozilla/layers/ImageClient.h" // for ImageClient
#include "mozilla/layers/LayersMessages.h" // for CompositableOperation
#include "mozilla/layers/TextureClient.h" // for TextureClient
#include "mozilla/layers/TextureClient.h"
#include "mozilla/media/MediaSystemResourceManager.h" // for MediaSystemResourceManager
#include "mozilla/media/MediaSystemResourceManagerChild.h" // for MediaSystemResourceManagerChild
#include "mozilla/mozalloc.h" // for operator new, etc
#include "transport/runnable_utils.h"
#include "nsContentUtils.h"
#include "nsISupportsImpl.h" // for ImageContainer::AddRef, etc
#include "nsTArray.h" // for AutoTArray, nsTArray, etc
#include "nsTArrayForwardDeclare.h" // for AutoTArray
#include "nsThreadUtils.h" // for NS_IsMainThread
#if defined(XP_WIN)
# include "mozilla/gfx/DeviceManagerDx.h"
#endif
namespace mozilla {
namespace ipc {
class Shmem;
} // namespace ipc
namespace layers {
using namespace mozilla::ipc;
using namespace mozilla::gfx;
using namespace mozilla::media;
typedef std::vector<CompositableOperation> OpVector;
typedef nsTArray<OpDestroy> OpDestroyVector;
struct CompositableTransaction {
CompositableTransaction() : mFinished(true) {}
~CompositableTransaction() { End(); }
bool Finished() const { return mFinished; }
void Begin() {
MOZ_ASSERT(mFinished);
mFinished = false;
}
void End() {
mFinished = true;
mOperations.clear();
mDestroyedActors.Clear();
}
bool IsEmpty() const {
return mOperations.empty() && mDestroyedActors.IsEmpty();
}
void AddNoSwapEdit(const CompositableOperation& op) {
MOZ_ASSERT(!Finished(), "forgot BeginTransaction?");
mOperations.push_back(op);
}
OpVector mOperations;
OpDestroyVector mDestroyedActors;
bool mFinished;
};
struct AutoEndTransaction final {
explicit AutoEndTransaction(CompositableTransaction* aTxn) : mTxn(aTxn) {}
~AutoEndTransaction() { mTxn->End(); }
CompositableTransaction* mTxn;
};
void ImageBridgeChild::UseTextures(
CompositableClient* aCompositable,
const nsTArray<TimedTextureClient>& aTextures) {
MOZ_ASSERT(aCompositable);
MOZ_ASSERT(aCompositable->GetIPCHandle());
MOZ_ASSERT(aCompositable->IsConnected());
AutoTArray<TimedTexture, 4> textures;
for (auto& t : aTextures) {
MOZ_ASSERT(t.mTextureClient);
MOZ_ASSERT(t.mTextureClient->GetIPDLActor());
if (!t.mTextureClient->IsSharedWithCompositor()) {
return;
}
bool readLocked = t.mTextureClient->OnForwardedToHost();
textures.AppendElement(TimedTexture(
WrapNotNull(t.mTextureClient->GetIPDLActor()), t.mTimeStamp,
t.mPictureRect, t.mFrameID, t.mProducerID, readLocked));
// Wait end of usage on host side if TextureFlags::RECYCLE is set
HoldUntilCompositableRefReleasedIfNecessary(t.mTextureClient);
}
mTxn->AddNoSwapEdit(CompositableOperation(aCompositable->GetIPCHandle(),
OpUseTexture(textures)));
}
void ImageBridgeChild::UseRemoteTexture(CompositableClient* aCompositable,
const RemoteTextureId aTextureId,
const RemoteTextureOwnerId aOwnerId,
const gfx::IntSize aSize,
const TextureFlags aFlags) {
MOZ_ASSERT(aCompositable);
MOZ_ASSERT(aCompositable->GetIPCHandle());
MOZ_ASSERT(aCompositable->IsConnected());
mTxn->AddNoSwapEdit(CompositableOperation(
aCompositable->GetIPCHandle(),
OpUseRemoteTexture(aTextureId, aOwnerId, aSize, aFlags)));
}
void ImageBridgeChild::EnableRemoteTexturePushCallback(
CompositableClient* aCompositable, const RemoteTextureOwnerId aOwnerId,
const gfx::IntSize aSize, const TextureFlags aFlags) {
MOZ_ASSERT(aCompositable);
MOZ_ASSERT(aCompositable->GetIPCHandle());
MOZ_ASSERT(aCompositable->IsConnected());
mTxn->AddNoSwapEdit(CompositableOperation(
aCompositable->GetIPCHandle(),
OpEnableRemoteTexturePushCallback(aOwnerId, aSize, aFlags)));
}
void ImageBridgeChild::HoldUntilCompositableRefReleasedIfNecessary(
TextureClient* aClient) {
if (!aClient) {
return;
}
// Wait ReleaseCompositableRef only when TextureFlags::RECYCLE or
// TextureFlags::WAIT_HOST_USAGE_END is set on ImageBridge.
bool waitNotifyNotUsed =
aClient->GetFlags() & TextureFlags::RECYCLE ||
aClient->GetFlags() & TextureFlags::WAIT_HOST_USAGE_END;
if (!waitNotifyNotUsed) {
return;
}
aClient->SetLastFwdTransactionId(GetFwdTransactionId());
mTexturesWaitingNotifyNotUsed.emplace(aClient->GetSerial(), aClient);
}
void ImageBridgeChild::NotifyNotUsed(uint64_t aTextureId,
uint64_t aFwdTransactionId) {
auto it = mTexturesWaitingNotifyNotUsed.find(aTextureId);
if (it != mTexturesWaitingNotifyNotUsed.end()) {
if (aFwdTransactionId < it->second->GetLastFwdTransactionId()) {
// Released on host side, but client already requested newer use texture.
return;
}
mTexturesWaitingNotifyNotUsed.erase(it);
}
}
void ImageBridgeChild::CancelWaitForNotifyNotUsed(uint64_t aTextureId) {
MOZ_ASSERT(InImageBridgeChildThread());
mTexturesWaitingNotifyNotUsed.erase(aTextureId);
}
// Singleton
static StaticMutex sImageBridgeSingletonLock MOZ_UNANNOTATED;
static StaticRefPtr<ImageBridgeChild> sImageBridgeChildSingleton;
static StaticRefPtr<nsIThread> sImageBridgeChildThread;
// dispatched function
void ImageBridgeChild::ShutdownStep1(SynchronousTask* aTask) {
AutoCompleteTask complete(aTask);
MOZ_ASSERT(InImageBridgeChildThread(),
"Should be in ImageBridgeChild thread.");
MediaSystemResourceManager::Shutdown();
// Force all managed protocols to shut themselves down cleanly
nsTArray<PTextureChild*> textures;
ManagedPTextureChild(textures);
for (int i = textures.Length() - 1; i >= 0; --i) {
RefPtr<TextureClient> client = TextureClient::AsTextureClient(textures[i]);
if (client) {
client->Destroy();
}
}
if (mCanSend) {
SendWillClose();
}
MarkShutDown();
// From now on, no message can be sent through the image bridge from the
// client side except the final Stop message.
}
// dispatched function
void ImageBridgeChild::ShutdownStep2(SynchronousTask* aTask) {
AutoCompleteTask complete(aTask);
MOZ_ASSERT(InImageBridgeChildThread(),
"Should be in ImageBridgeChild thread.");
if (!mDestroyed) {
Close();
}
}
void ImageBridgeChild::ActorDestroy(ActorDestroyReason aWhy) {
mCanSend = false;
mDestroyed = true;
{
MutexAutoLock lock(mContainerMapLock);
mImageContainerListeners.clear();
}
}
void ImageBridgeChild::ActorDealloc() { this->Release(); }
void ImageBridgeChild::CreateImageClientSync(SynchronousTask* aTask,
RefPtr<ImageClient>* result,
CompositableType aType,
ImageContainer* aImageContainer) {
AutoCompleteTask complete(aTask);
*result = CreateImageClientNow(aType, aImageContainer);
}
ImageBridgeChild::ImageBridgeChild(uint32_t aNamespace)
: mNamespace(aNamespace),
mCanSend(false),
mDestroyed(false),
mFwdTransactionId(0),
mContainerMapLock("ImageBridgeChild.mContainerMapLock") {
MOZ_ASSERT(mNamespace);
MOZ_ASSERT(NS_IsMainThread());
mTxn = new CompositableTransaction();
}
ImageBridgeChild::~ImageBridgeChild() { delete mTxn; }
void ImageBridgeChild::MarkShutDown() {
mTexturesWaitingNotifyNotUsed.clear();
mCanSend = false;
}
void ImageBridgeChild::Connect(CompositableClient* aCompositable,
ImageContainer* aImageContainer) {
MOZ_ASSERT(aCompositable);
MOZ_ASSERT(InImageBridgeChildThread());
MOZ_ASSERT(CanSend());
CompositableHandle handle = CompositableHandle::GetNext();
// ImageClient of ImageContainer provides aImageContainer.
// But offscreen canvas does not provide it.
if (aImageContainer) {
MutexAutoLock lock(mContainerMapLock);
MOZ_ASSERT(mImageContainerListeners.find(uint64_t(handle)) ==
mImageContainerListeners.end());
mImageContainerListeners.emplace(
uint64_t(handle), aImageContainer->GetImageContainerListener());
}
aCompositable->InitIPDL(handle);
SendNewCompositable(handle, aCompositable->GetTextureInfo());
}
void ImageBridgeChild::ForgetImageContainer(const CompositableHandle& aHandle) {
MutexAutoLock lock(mContainerMapLock);
mImageContainerListeners.erase(aHandle.Value());
}
/* static */
RefPtr<ImageBridgeChild> ImageBridgeChild::GetSingleton() {
StaticMutexAutoLock lock(sImageBridgeSingletonLock);
return sImageBridgeChildSingleton;
}
void ImageBridgeChild::UpdateImageClient(RefPtr<ImageContainer> aContainer) {
if (!aContainer) {
return;
}
if (!InImageBridgeChildThread()) {
RefPtr<Runnable> runnable =
WrapRunnable(RefPtr<ImageBridgeChild>(this),
&ImageBridgeChild::UpdateImageClient, aContainer);
GetThread()->Dispatch(runnable.forget());
return;
}
if (!CanSend()) {
return;
}
RefPtr<ImageClient> client = aContainer->GetImageClient();
if (NS_WARN_IF(!client)) {
return;
}
// If the client has become disconnected before this event was dispatched,
// early return now.
if (!client->IsConnected()) {
return;
}
BeginTransaction();
client->UpdateImage(aContainer);
EndTransaction();
}
void ImageBridgeChild::UpdateCompositable(
const RefPtr<ImageContainer> aContainer, const RemoteTextureId aTextureId,
const RemoteTextureOwnerId aOwnerId, const gfx::IntSize aSize,
const TextureFlags aFlags) {
if (!aContainer) {
return;
}
if (!InImageBridgeChildThread()) {
RefPtr<Runnable> runnable = WrapRunnable(
RefPtr<ImageBridgeChild>(this), &ImageBridgeChild::UpdateCompositable,
aContainer, aTextureId, aOwnerId, aSize, aFlags);
GetThread()->Dispatch(runnable.forget());
return;
}
if (!CanSend()) {
return;
}
RefPtr<ImageClient> client = aContainer->GetImageClient();
if (NS_WARN_IF(!client)) {
return;
}
// If the client has become disconnected before this event was dispatched,
// early return now.
if (!client->IsConnected()) {
return;
}
BeginTransaction();
UseRemoteTexture(client, aTextureId, aOwnerId, aSize, aFlags);
EndTransaction();
}
void ImageBridgeChild::FlushAllImagesSync(SynchronousTask* aTask,
ImageClient* aClient,
ImageContainer* aContainer) {
AutoCompleteTask complete(aTask);
if (!CanSend()) {
return;
}
MOZ_ASSERT(aClient);
BeginTransaction();
if (aContainer) {
aContainer->ClearImagesFromImageBridge();
}
aClient->FlushAllImages();
EndTransaction();
}
void ImageBridgeChild::FlushAllImages(ImageClient* aClient,
ImageContainer* aContainer) {
MOZ_ASSERT(aClient);
MOZ_ASSERT(!InImageBridgeChildThread());
if (InImageBridgeChildThread()) {
NS_ERROR(
"ImageBridgeChild::FlushAllImages() is called on ImageBridge thread.");
return;
}
SynchronousTask task("FlushAllImages Lock");
// RefPtrs on arguments are not needed since this dispatches synchronously.
RefPtr<Runnable> runnable = WrapRunnable(
RefPtr<ImageBridgeChild>(this), &ImageBridgeChild::FlushAllImagesSync,
&task, aClient, aContainer);
GetThread()->Dispatch(runnable.forget());
task.Wait();
}
void ImageBridgeChild::BeginTransaction() {
MOZ_ASSERT(CanSend());
MOZ_ASSERT(mTxn->Finished(), "uncommitted txn?");
UpdateFwdTransactionId();
mTxn->Begin();
}
void ImageBridgeChild::EndTransaction() {
MOZ_ASSERT(CanSend());
MOZ_ASSERT(!mTxn->Finished(), "forgot BeginTransaction?");
AutoEndTransaction _(mTxn);
if (mTxn->IsEmpty()) {
return;
}
AutoTArray<CompositableOperation, 10> cset;
cset.SetCapacity(mTxn->mOperations.size());
if (!mTxn->mOperations.empty()) {
cset.AppendElements(&mTxn->mOperations.front(), mTxn->mOperations.size());
}
if (!SendUpdate(cset, mTxn->mDestroyedActors, GetFwdTransactionId())) {
NS_WARNING("could not send async texture transaction");
return;
}
}
bool ImageBridgeChild::InitForContent(Endpoint<PImageBridgeChild>&& aEndpoint,
uint32_t aNamespace) {
MOZ_ASSERT(NS_IsMainThread());
gfxPlatform::GetPlatform();
if (!sImageBridgeChildThread) {
nsCOMPtr<nsIThread> thread;
nsresult rv = NS_NewNamedThread("ImageBridgeChld", getter_AddRefs(thread));
MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv),
"Failed to start ImageBridgeChild thread!");
sImageBridgeChildThread = thread.forget();
}
RefPtr<ImageBridgeChild> child = new ImageBridgeChild(aNamespace);
child->GetThread()->Dispatch(NS_NewRunnableFunction(
"layers::ImageBridgeChild::Bind",
[child, endpoint = std::move(aEndpoint)]() mutable {
child->Bind(std::move(endpoint));
}));
// Assign this after so other threads can't post messages before we connect to
// IPDL.
{
StaticMutexAutoLock lock(sImageBridgeSingletonLock);
sImageBridgeChildSingleton = child;
}
return true;
}
bool ImageBridgeChild::ReinitForContent(Endpoint<PImageBridgeChild>&& aEndpoint,
uint32_t aNamespace) {
MOZ_ASSERT(NS_IsMainThread());
// Note that at this point, ActorDestroy may not have been called yet,
// meaning mCanSend is still true. In this case we will try to send a
// synchronous WillClose message to the parent, and will certainly get a
// false result and a MsgDropped processing error. This is okay.
ShutdownSingleton();
return InitForContent(std::move(aEndpoint), aNamespace);
}
void ImageBridgeChild::Bind(Endpoint<PImageBridgeChild>&& aEndpoint) {
if (!aEndpoint.Bind(this)) {
return;
}
// This reference is dropped in DeallocPImageBridgeChild.
this->AddRef();
mCanSend = true;
}
void ImageBridgeChild::BindSameProcess(RefPtr<ImageBridgeParent> aParent) {
Open(aParent, aParent->GetThread(), mozilla::ipc::ChildSide);
// This reference is dropped in DeallocPImageBridgeChild.
this->AddRef();
mCanSend = true;
}
/* static */
void ImageBridgeChild::ShutDown() {
MOZ_ASSERT(NS_IsMainThread());
ShutdownSingleton();
if (sImageBridgeChildThread) {
sImageBridgeChildThread->Shutdown();
sImageBridgeChildThread = nullptr;
}
}
/* static */
void ImageBridgeChild::ShutdownSingleton() {
MOZ_ASSERT(NS_IsMainThread());
if (RefPtr<ImageBridgeChild> child = GetSingleton()) {
child->WillShutdown();
StaticMutexAutoLock lock(sImageBridgeSingletonLock);
sImageBridgeChildSingleton = nullptr;
}
}
void ImageBridgeChild::WillShutdown() {
{
SynchronousTask task("ImageBridge ShutdownStep1 lock");
RefPtr<Runnable> runnable =
WrapRunnable(RefPtr<ImageBridgeChild>(this),
&ImageBridgeChild::ShutdownStep1, &task);
GetThread()->Dispatch(runnable.forget());
task.Wait();
}
{
SynchronousTask task("ImageBridge ShutdownStep2 lock");
RefPtr<Runnable> runnable =
WrapRunnable(RefPtr<ImageBridgeChild>(this),
&ImageBridgeChild::ShutdownStep2, &task);
GetThread()->Dispatch(runnable.forget());
task.Wait();
}
}
void ImageBridgeChild::InitSameProcess(uint32_t aNamespace) {
NS_ASSERTION(NS_IsMainThread(), "Should be on the main Thread!");
MOZ_ASSERT(!sImageBridgeChildSingleton);
MOZ_ASSERT(!sImageBridgeChildThread);
nsCOMPtr<nsIThread> thread;
nsresult rv = NS_NewNamedThread("ImageBridgeChld", getter_AddRefs(thread));
MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv),
"Failed to start ImageBridgeChild thread!");
sImageBridgeChildThread = thread.forget();
RefPtr<ImageBridgeChild> child = new ImageBridgeChild(aNamespace);
RefPtr<ImageBridgeParent> parent = ImageBridgeParent::CreateSameProcess();
RefPtr<Runnable> runnable =
WrapRunnable(child, &ImageBridgeChild::BindSameProcess, parent);
child->GetThread()->Dispatch(runnable.forget());
// Assign this after so other threads can't post messages before we connect to
// IPDL.
{
StaticMutexAutoLock lock(sImageBridgeSingletonLock);
sImageBridgeChildSingleton = child;
}
}
/* static */
void ImageBridgeChild::InitWithGPUProcess(
Endpoint<PImageBridgeChild>&& aEndpoint, uint32_t aNamespace) {
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(!sImageBridgeChildSingleton);
MOZ_ASSERT(!sImageBridgeChildThread);
nsCOMPtr<nsIThread> thread;
nsresult rv = NS_NewNamedThread("ImageBridgeChld", getter_AddRefs(thread));
MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv),
"Failed to start ImageBridgeChild thread!");
sImageBridgeChildThread = thread.forget();
RefPtr<ImageBridgeChild> child = new ImageBridgeChild(aNamespace);
child->GetThread()->Dispatch(NS_NewRunnableFunction(
"layers::ImageBridgeChild::Bind",
[child, endpoint = std::move(aEndpoint)]() mutable {
child->Bind(std::move(endpoint));
}));
// Assign this after so other threads can't post messages before we connect to
// IPDL.
{
StaticMutexAutoLock lock(sImageBridgeSingletonLock);
sImageBridgeChildSingleton = child;
}
}
bool InImageBridgeChildThread() {
return sImageBridgeChildThread &&
sImageBridgeChildThread->IsOnCurrentThread();
}
nsISerialEventTarget* ImageBridgeChild::GetThread() const {
return sImageBridgeChildThread;
}
/* static */
void ImageBridgeChild::IdentifyCompositorTextureHost(
const TextureFactoryIdentifier& aIdentifier) {
if (RefPtr<ImageBridgeChild> child = GetSingleton()) {
child->UpdateTextureFactoryIdentifier(aIdentifier);
}
}
void ImageBridgeChild::UpdateTextureFactoryIdentifier(
const TextureFactoryIdentifier& aIdentifier) {
IdentifyTextureHost(aIdentifier);
}
RefPtr<ImageClient> ImageBridgeChild::CreateImageClient(
CompositableType aType, ImageContainer* aImageContainer) {
if (InImageBridgeChildThread()) {
return CreateImageClientNow(aType, aImageContainer);
}
SynchronousTask task("CreateImageClient Lock");
RefPtr<ImageClient> result = nullptr;
RefPtr<Runnable> runnable = WrapRunnable(
RefPtr<ImageBridgeChild>(this), &ImageBridgeChild::CreateImageClientSync,
&task, &result, aType, aImageContainer);
GetThread()->Dispatch(runnable.forget());
task.Wait();
return result;
}
RefPtr<ImageClient> ImageBridgeChild::CreateImageClientNow(
CompositableType aType, ImageContainer* aImageContainer) {
MOZ_ASSERT(InImageBridgeChildThread());
if (!CanSend()) {
return nullptr;
}
RefPtr<ImageClient> client =
ImageClient::CreateImageClient(aType, this, TextureFlags::NO_FLAGS);
MOZ_ASSERT(client, "failed to create ImageClient");
if (client) {
client->Connect(aImageContainer);
}
return client;
}
bool ImageBridgeChild::AllocUnsafeShmem(size_t aSize, ipc::Shmem* aShmem) {
if (!InImageBridgeChildThread()) {
return DispatchAllocShmemInternal(aSize, aShmem,
true); // true: unsafe
}
if (!CanSend()) {
return false;
}
return PImageBridgeChild::AllocUnsafeShmem(aSize, aShmem);
}
bool ImageBridgeChild::AllocShmem(size_t aSize, ipc::Shmem* aShmem) {
if (!InImageBridgeChildThread()) {
return DispatchAllocShmemInternal(aSize, aShmem,
false); // false: unsafe
}
if (!CanSend()) {
return false;
}
return PImageBridgeChild::AllocShmem(aSize, aShmem);
}
void ImageBridgeChild::ProxyAllocShmemNow(SynchronousTask* aTask, size_t aSize,
ipc::Shmem* aShmem, bool aUnsafe,
bool* aSuccess) {
AutoCompleteTask complete(aTask);
if (!CanSend()) {
return;
}
bool ok = false;
if (aUnsafe) {
ok = AllocUnsafeShmem(aSize, aShmem);
} else {
ok = AllocShmem(aSize, aShmem);
}
*aSuccess = ok;
}
bool ImageBridgeChild::DispatchAllocShmemInternal(size_t aSize,
ipc::Shmem* aShmem,
bool aUnsafe) {
SynchronousTask task("AllocatorProxy alloc");
bool success = false;
RefPtr<Runnable> runnable = WrapRunnable(
RefPtr<ImageBridgeChild>(this), &ImageBridgeChild::ProxyAllocShmemNow,
&task, aSize, aShmem, aUnsafe, &success);
GetThread()->Dispatch(runnable.forget());
task.Wait();
return success;
}
void ImageBridgeChild::ProxyDeallocShmemNow(SynchronousTask* aTask,
ipc::Shmem* aShmem, bool* aResult) {
AutoCompleteTask complete(aTask);
if (!CanSend()) {
return;
}
*aResult = DeallocShmem(*aShmem);
}
bool ImageBridgeChild::DeallocShmem(ipc::Shmem& aShmem) {
if (InImageBridgeChildThread()) {
if (!CanSend()) {
return false;
}
return PImageBridgeChild::DeallocShmem(aShmem);
}
// If we can't post a task, then we definitely cannot send, so there's
// no reason to queue up this send.
if (!CanPostTask()) {
return false;
}
SynchronousTask task("AllocatorProxy Dealloc");
bool result = false;
RefPtr<Runnable> runnable = WrapRunnable(
RefPtr<ImageBridgeChild>(this), &ImageBridgeChild::ProxyDeallocShmemNow,
&task, &aShmem, &result);
GetThread()->Dispatch(runnable.forget());
task.Wait();
return result;
}
PTextureChild* ImageBridgeChild::AllocPTextureChild(
const SurfaceDescriptor&, ReadLockDescriptor&, const LayersBackend&,
const TextureFlags&, const uint64_t& aSerial,
const wr::MaybeExternalImageId& aExternalImageId) {
MOZ_ASSERT(CanSend());
return TextureClient::CreateIPDLActor();
}
bool ImageBridgeChild::DeallocPTextureChild(PTextureChild* actor) {
return TextureClient::DestroyIPDLActor(actor);
}
PMediaSystemResourceManagerChild*
ImageBridgeChild::AllocPMediaSystemResourceManagerChild() {
MOZ_ASSERT(CanSend());
return new mozilla::media::MediaSystemResourceManagerChild();
}
bool ImageBridgeChild::DeallocPMediaSystemResourceManagerChild(
PMediaSystemResourceManagerChild* aActor) {
MOZ_ASSERT(aActor);
delete static_cast<mozilla::media::MediaSystemResourceManagerChild*>(aActor);
return true;
}
mozilla::ipc::IPCResult ImageBridgeChild::RecvParentAsyncMessages(
nsTArray<AsyncParentMessageData>&& aMessages) {
for (AsyncParentMessageArray::index_type i = 0; i < aMessages.Length(); ++i) {
const AsyncParentMessageData& message = aMessages[i];
switch (message.type()) {
case AsyncParentMessageData::TOpNotifyNotUsed: {
const OpNotifyNotUsed& op = message.get_OpNotifyNotUsed();
NotifyNotUsed(op.TextureId(), op.fwdTransactionId());
break;
}
default:
NS_ERROR("unknown AsyncParentMessageData type");
return IPC_FAIL_NO_REASON(this);
}
}
return IPC_OK();
}
RefPtr<ImageContainerListener> ImageBridgeChild::FindListener(
const CompositableHandle& aHandle) {
RefPtr<ImageContainerListener> listener;
MutexAutoLock lock(mContainerMapLock);
auto it = mImageContainerListeners.find(aHandle.Value());
if (it != mImageContainerListeners.end()) {
listener = it->second;
}
return listener;
}
mozilla::ipc::IPCResult ImageBridgeChild::RecvDidComposite(
nsTArray<ImageCompositeNotification>&& aNotifications) {
for (auto& n : aNotifications) {
RefPtr<ImageContainerListener> listener = FindListener(n.compositable());
if (listener) {
listener->NotifyComposite(n);
}
}
return IPC_OK();
}
mozilla::ipc::IPCResult ImageBridgeChild::RecvReportFramesDropped(
const CompositableHandle& aHandle, const uint32_t& aFrames) {
RefPtr<ImageContainerListener> listener = FindListener(aHandle);
if (listener) {
listener->NotifyDropped(aFrames);
}
return IPC_OK();
}
PTextureChild* ImageBridgeChild::CreateTexture(
const SurfaceDescriptor& aSharedData, ReadLockDescriptor&& aReadLock,
LayersBackend aLayersBackend, TextureFlags aFlags, uint64_t aSerial,
wr::MaybeExternalImageId& aExternalImageId) {
MOZ_ASSERT(CanSend());
return SendPTextureConstructor(aSharedData, std::move(aReadLock),
aLayersBackend, aFlags, aSerial,
aExternalImageId);
}
static bool IBCAddOpDestroy(CompositableTransaction* aTxn,
const OpDestroy& op) {
if (aTxn->Finished()) {
return false;
}
aTxn->mDestroyedActors.AppendElement(op);
return true;
}
bool ImageBridgeChild::DestroyInTransaction(PTextureChild* aTexture) {
return IBCAddOpDestroy(mTxn, OpDestroy(WrapNotNull(aTexture)));
}
bool ImageBridgeChild::DestroyInTransaction(const CompositableHandle& aHandle) {
return IBCAddOpDestroy(mTxn, OpDestroy(aHandle));
}
void ImageBridgeChild::RemoveTextureFromCompositable(
CompositableClient* aCompositable, TextureClient* aTexture) {
MOZ_ASSERT(CanSend());
MOZ_ASSERT(aTexture);
MOZ_ASSERT(aTexture->IsSharedWithCompositor());
MOZ_ASSERT(aCompositable->IsConnected());
if (!aTexture || !aTexture->IsSharedWithCompositor() ||
!aCompositable->IsConnected()) {
return;
}
mTxn->AddNoSwapEdit(CompositableOperation(
aCompositable->GetIPCHandle(),
OpRemoveTexture(WrapNotNull(aTexture->GetIPDLActor()))));
}
bool ImageBridgeChild::IsSameProcess() const {
return OtherPid() == base::GetCurrentProcId();
}
bool ImageBridgeChild::CanPostTask() const {
// During shutdown, the cycle collector may free objects that are holding a
// reference to ImageBridgeChild. Since this happens on the main thread,
// ImageBridgeChild will attempt to post a task to the ImageBridge thread.
// However the thread manager has already been shut down, so the task cannot
// post.
//
// It's okay if this races. We only care about the shutdown case where
// everything's happening on the main thread. Even if it races outside of
// shutdown, it's still harmless to post the task, since the task must
// check CanSend().
return !mDestroyed;
}
void ImageBridgeChild::ReleaseCompositable(const CompositableHandle& aHandle) {
if (!InImageBridgeChildThread()) {
// If we can't post a task, then we definitely cannot send, so there's
// no reason to queue up this send.
if (!CanPostTask()) {
return;
}
RefPtr<Runnable> runnable =
WrapRunnable(RefPtr<ImageBridgeChild>(this),
&ImageBridgeChild::ReleaseCompositable, aHandle);
GetThread()->Dispatch(runnable.forget());
return;
}
if (!CanSend()) {
return;
}
if (!DestroyInTransaction(aHandle)) {
SendReleaseCompositable(aHandle);
}
{
MutexAutoLock lock(mContainerMapLock);
mImageContainerListeners.erase(aHandle.Value());
}
}
bool ImageBridgeChild::CanSend() const {
MOZ_ASSERT(InImageBridgeChildThread());
return mCanSend;
}
void ImageBridgeChild::HandleFatalError(const char* aMsg) const {
dom::ContentChild::FatalErrorIfNotUsingGPUProcess(aMsg, OtherPid());
}
wr::MaybeExternalImageId ImageBridgeChild::GetNextExternalImageId() {
static uint32_t sNextID = 1;
++sNextID;
MOZ_RELEASE_ASSERT(sNextID != UINT32_MAX);
uint64_t imageId = mNamespace;
imageId = imageId << 32 | sNextID;
return Some(wr::ToExternalImageId(imageId));
}
} // namespace layers
} // namespace mozilla