summaryrefslogtreecommitdiffstats
path: root/gfx/layers/ipc/ImageBridgeChild.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--gfx/layers/ipc/ImageBridgeChild.cpp939
1 files changed, 939 insertions, 0 deletions
diff --git a/gfx/layers/ipc/ImageBridgeChild.cpp b/gfx/layers/ipc/ImageBridgeChild.cpp
new file mode 100644
index 0000000000..6dcf2be415
--- /dev/null
+++ b/gfx/layers/ipc/ImageBridgeChild.cpp
@@ -0,0 +1,939 @@
+/* -*- 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::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;
+ }
+
+ mCanSend = true;
+}
+
+void ImageBridgeChild::BindSameProcess(RefPtr<ImageBridgeParent> aParent) {
+ Open(aParent, aParent->GetThread(), mozilla::ipc::ChildSide);
+
+ 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,
+ const dom::ContentParentId& aContentId, 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) {
+ 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