diff options
Diffstat (limited to 'gfx/layers/ipc/ImageBridgeChild.cpp')
-rw-r--r-- | gfx/layers/ipc/ImageBridgeChild.cpp | 975 |
1 files changed, 975 insertions, 0 deletions
diff --git a/gfx/layers/ipc/ImageBridgeChild.cpp b/gfx/layers/ipc/ImageBridgeChild.cpp new file mode 100644 index 0000000000..32489afabd --- /dev/null +++ b/gfx/layers/ipc/ImageBridgeChild.cpp @@ -0,0 +1,975 @@ +/* -*- 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 "nsGlobalWindowInner.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 +#include "WindowRenderer.h" + +#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, const RefPtr<FwdTransactionTracker>& aTracker) { + MOZ_ASSERT(aCompositable); + MOZ_ASSERT(aCompositable->GetIPCHandle()); + MOZ_ASSERT(aCompositable->IsConnected()); + + mTxn->AddNoSwapEdit(CompositableOperation( + aCompositable->GetIPCHandle(), + OpUseRemoteTexture(aTextureId, aOwnerId, aSize, aFlags))); + TrackFwdTransaction(aTracker); +} + +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."); + + mSectionAllocator = nullptr; + + 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), + mFwdTransactionCounter(this), + 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, const RefPtr<FwdTransactionTracker> aTracker) { + if (!aContainer) { + return; + } + + if (!InImageBridgeChildThread()) { + RefPtr<Runnable> runnable = WrapRunnable( + RefPtr<ImageBridgeChild>(this), &ImageBridgeChild::UpdateCompositable, + aContainer, aTextureId, aOwnerId, aSize, aFlags, aTracker); + 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, aTracker); + 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::SyncWithCompositor(const Maybe<uint64_t>& aWindowID) { + if (NS_WARN_IF(InImageBridgeChildThread())) { + MOZ_ASSERT_UNREACHABLE("Cannot call on ImageBridge thread!"); + return; + } + + if (!aWindowID) { + return; + } + + const auto fnSyncWithWindow = [&]() { + if (auto* window = nsGlobalWindowInner::GetInnerWindowWithId(*aWindowID)) { + if (auto* widget = window->GetNearestWidget()) { + if (auto* renderer = widget->GetWindowRenderer()) { + if (auto* kc = renderer->AsKnowsCompositor()) { + kc->SyncWithCompositor(); + } + } + } + } + }; + + if (NS_IsMainThread()) { + fnSyncWithWindow(); + return; + } + + SynchronousTask task("SyncWithCompositor Lock"); + RefPtr<Runnable> runnable = + NS_NewRunnableFunction("ImageBridgeChild::SyncWithCompositor", [&]() { + AutoCompleteTask complete(&task); + fnSyncWithWindow(); + }); + NS_DispatchToMainThread(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; + } + + mSectionAllocator = MakeUnique<FixedSizeSmallShmemSectionAllocator>(this); + mCanSend = true; +} + +void ImageBridgeChild::BindSameProcess(RefPtr<ImageBridgeParent> aParent) { + Open(aParent, aParent->GetThread(), mozilla::ipc::ChildSide); + + mSectionAllocator = MakeUnique<FixedSizeSmallShmemSectionAllocator>(this); + 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(); +} + +FixedSizeSmallShmemSectionAllocator* ImageBridgeChild::GetTileLockAllocator() { + return mSectionAllocator.get(); +} + +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 |