/* -*- 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 // for vector #include "ImageBridgeParent.h" // for ImageBridgeParent #include "ImageContainer.h" // for ImageContainer #include "Layers.h" // for Layer, etc #include "ShadowLayers.h" // for ShadowLayerForwarder #include "base/message_loop.h" // for MessageLoop #include "base/platform_thread.h" // for PlatformThread #include "base/process.h" // for ProcessId #include "base/task.h" // for NewRunnableFunction, etc #include "base/thread.h" // for Thread #include "mozilla/Assertions.h" // for MOZ_ASSERT, etc #include "mozilla/Monitor.h" // for Monitor, MonitorAutoLock #include "mozilla/ReentrantMonitor.h" // for ReentrantMonitor, etc #include "mozilla/ipc/MessageChannel.h" // for MessageChannel, etc #include "mozilla/ipc/Transport.h" // for Transport #include "mozilla/gfx/gfxVars.h" #include "mozilla/gfx/Point.h" // for IntSize #include "mozilla/layers/AsyncCanvasRenderer.h" #include "mozilla/media/MediaSystemResourceManager.h" // for MediaSystemResourceManager #include "mozilla/media/MediaSystemResourceManagerChild.h" // for MediaSystemResourceManagerChild #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/dom/ContentChild.h" #include "mozilla/mozalloc.h" // for operator new, etc #include "mtransport/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 #include "nsXULAppAPI.h" // for XRE_GetIOMessageLoop #include "mozilla/StaticMutex.h" #include "mozilla/StaticPtr.h" // for StaticRefPtr #include "mozilla/layers/TextureClient.h" #include "SynchronousTask.h" #if defined(XP_WIN) # include "mozilla/gfx/DeviceManagerDx.h" #endif namespace mozilla { namespace ipc { class Shmem; } // namespace ipc namespace layers { using base::ProcessId; using base::Thread; using namespace mozilla::ipc; using namespace mozilla::gfx; using namespace mozilla::media; typedef std::vector OpVector; typedef nsTArray 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& aTextures, const Maybe& aRenderRoot) { MOZ_ASSERT(aCompositable); MOZ_ASSERT(aCompositable->GetIPCHandle()); MOZ_ASSERT(aCompositable->IsConnected()); AutoTArray 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(nullptr, 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::UseComponentAlphaTextures( CompositableClient* aCompositable, TextureClient* aTextureOnBlack, TextureClient* aTextureOnWhite) { MOZ_CRASH("should not be called"); } 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; static StaticRefPtr sImageBridgeChildSingleton; static Thread* sImageBridgeChildThread = nullptr; // 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 textures; ManagedPTextureChild(textures); for (int i = textures.Length() - 1; i >= 0; --i) { RefPtr 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* result, CompositableType aType, ImageContainer* aImageContainer) { AutoCompleteTask complete(aTask); *result = CreateImageClientNow(aType, aImageContainer); } // dispatched function void ImageBridgeChild::CreateCanvasClientSync( SynchronousTask* aTask, CanvasClient::CanvasClientType aType, TextureFlags aFlags, RefPtr* const outResult) { AutoCompleteTask complete(aTask); *outResult = CreateCanvasClientNow(aType, aFlags); } 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()); // Note: this is static, rather than per-IBC, so IDs are not re-used across // ImageBridgeChild instances. This is relevant for the GPU process, where // we don't want old IDs to potentially leak into a recreated ImageBridge. static uint64_t sNextID = 1; uint64_t id = sNextID++; // ImageClient of ImageContainer provides aImageContainer. // But offscreen canvas does not provide it. if (aImageContainer) { MutexAutoLock lock(mContainerMapLock); MOZ_ASSERT(mImageContainerListeners.find(id) == mImageContainerListeners.end()); mImageContainerListeners.emplace( id, aImageContainer->GetImageContainerListener()); } CompositableHandle handle(id); aCompositable->InitIPDL(handle); SendNewCompositable(handle, aCompositable->GetTextureInfo(), GetCompositorBackendType()); } void ImageBridgeChild::ForgetImageContainer(const CompositableHandle& aHandle) { MutexAutoLock lock(mContainerMapLock); mImageContainerListeners.erase(aHandle.Value()); } Thread* ImageBridgeChild::GetThread() const { return sImageBridgeChildThread; } /* static */ RefPtr ImageBridgeChild::GetSingleton() { StaticMutexAutoLock lock(sImageBridgeSingletonLock); return sImageBridgeChildSingleton; } void ImageBridgeChild::UpdateImageClient(RefPtr aContainer) { if (!aContainer) { return; } if (!InImageBridgeChildThread()) { RefPtr runnable = WrapRunnable(RefPtr(this), &ImageBridgeChild::UpdateImageClient, aContainer); GetMessageLoop()->PostTask(runnable.forget()); return; } if (!CanSend()) { return; } RefPtr 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, Layer::CONTENT_OPAQUE, Nothing()); EndTransaction(); } void ImageBridgeChild::UpdateAsyncCanvasRendererSync( SynchronousTask* aTask, AsyncCanvasRenderer* aWrapper) { AutoCompleteTask complete(aTask); UpdateAsyncCanvasRendererNow(aWrapper); } void ImageBridgeChild::UpdateAsyncCanvasRenderer( AsyncCanvasRenderer* aWrapper) { aWrapper->GetCanvasClient()->UpdateAsync(aWrapper); if (InImageBridgeChildThread()) { UpdateAsyncCanvasRendererNow(aWrapper); return; } SynchronousTask task("UpdateAsyncCanvasRenderer Lock"); RefPtr runnable = WrapRunnable( RefPtr(this), &ImageBridgeChild::UpdateAsyncCanvasRendererSync, &task, aWrapper); GetMessageLoop()->PostTask(runnable.forget()); task.Wait(); } void ImageBridgeChild::UpdateAsyncCanvasRendererNow( AsyncCanvasRenderer* aWrapper) { MOZ_ASSERT(aWrapper); if (!CanSend()) { return; } BeginTransaction(); // TODO wr::RenderRoot::Unknown aWrapper->GetCanvasClient()->Updated(wr::RenderRoot::Default); 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 = WrapRunnable( RefPtr(this), &ImageBridgeChild::FlushAllImagesSync, &task, aClient, aContainer); GetMessageLoop()->PostTask(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 cset; cset.SetCapacity(mTxn->mOperations.size()); if (!mTxn->mOperations.empty()) { cset.AppendElements(&mTxn->mOperations.front(), mTxn->mOperations.size()); } if (!IsSameProcess()) { ShadowLayerForwarder::PlatformSyncBeforeUpdate(); } if (!SendUpdate(cset, mTxn->mDestroyedActors, GetFwdTransactionId())) { NS_WARNING("could not send async texture transaction"); return; } } bool ImageBridgeChild::InitForContent(Endpoint&& aEndpoint, uint32_t aNamespace) { MOZ_ASSERT(NS_IsMainThread()); gfxPlatform::GetPlatform(); if (!sImageBridgeChildThread) { sImageBridgeChildThread = new Thread("ImageBridgeChild"); bool success = sImageBridgeChildThread->Start(); MOZ_RELEASE_ASSERT(success, "Failed to start ImageBridgeChild thread!"); } RefPtr child = new ImageBridgeChild(aNamespace); RefPtr runnable = NewRunnableMethod&&>( "layers::ImageBridgeChild::Bind", child, &ImageBridgeChild::Bind, std::move(aEndpoint)); child->GetMessageLoop()->PostTask(runnable.forget()); // 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&& 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&& aEndpoint) { if (!aEndpoint.Bind(this)) { return; } // This reference is dropped in DeallocPImageBridgeChild. this->AddRef(); mCanSend = true; } void ImageBridgeChild::BindSameProcess(RefPtr aParent) { MessageLoop* parentMsgLoop = aParent->GetMessageLoop(); ipc::MessageChannel* parentChannel = aParent->GetIPCChannel(); Open(parentChannel, parentMsgLoop, mozilla::ipc::ChildSide); // This reference is dropped in DeallocPImageBridgeChild. this->AddRef(); mCanSend = true; } /* static */ void ImageBridgeChild::ShutDown() { MOZ_ASSERT(NS_IsMainThread()); ShutdownSingleton(); delete sImageBridgeChildThread; sImageBridgeChildThread = nullptr; } /* static */ void ImageBridgeChild::ShutdownSingleton() { MOZ_ASSERT(NS_IsMainThread()); if (RefPtr child = GetSingleton()) { child->WillShutdown(); StaticMutexAutoLock lock(sImageBridgeSingletonLock); sImageBridgeChildSingleton = nullptr; } } void ImageBridgeChild::WillShutdown() { { SynchronousTask task("ImageBridge ShutdownStep1 lock"); RefPtr runnable = WrapRunnable(RefPtr(this), &ImageBridgeChild::ShutdownStep1, &task); GetMessageLoop()->PostTask(runnable.forget()); task.Wait(); } { SynchronousTask task("ImageBridge ShutdownStep2 lock"); RefPtr runnable = WrapRunnable(RefPtr(this), &ImageBridgeChild::ShutdownStep2, &task); GetMessageLoop()->PostTask(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); sImageBridgeChildThread = new Thread("ImageBridgeChild"); if (!sImageBridgeChildThread->IsRunning()) { sImageBridgeChildThread->Start(); } RefPtr child = new ImageBridgeChild(aNamespace); RefPtr parent = ImageBridgeParent::CreateSameProcess(); RefPtr runnable = WrapRunnable(child, &ImageBridgeChild::BindSameProcess, parent); child->GetMessageLoop()->PostTask(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&& aEndpoint, uint32_t aNamespace) { MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(!sImageBridgeChildSingleton); MOZ_ASSERT(!sImageBridgeChildThread); sImageBridgeChildThread = new Thread("ImageBridgeChild"); if (!sImageBridgeChildThread->IsRunning()) { sImageBridgeChildThread->Start(); } RefPtr child = new ImageBridgeChild(aNamespace); MessageLoop* loop = child->GetMessageLoop(); loop->PostTask(NewRunnableMethod&&>( "layers::ImageBridgeChild::Bind", child, &ImageBridgeChild::Bind, std::move(aEndpoint))); // 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->thread_id() == PlatformThread::CurrentId(); } MessageLoop* ImageBridgeChild::GetMessageLoop() const { return sImageBridgeChildThread ? sImageBridgeChildThread->message_loop() : nullptr; } /* static */ void ImageBridgeChild::IdentifyCompositorTextureHost( const TextureFactoryIdentifier& aIdentifier) { if (RefPtr child = GetSingleton()) { child->UpdateTextureFactoryIdentifier(aIdentifier); } } void ImageBridgeChild::UpdateTextureFactoryIdentifier( const TextureFactoryIdentifier& aIdentifier) { // ImageHost is incompatible between WebRender enabled and WebRender disabled. // Then drop all ImageContainers' ImageClients during disabling WebRender. bool disablingWebRender = GetCompositorBackendType() == LayersBackend::LAYERS_WR && aIdentifier.mParentBackend != LayersBackend::LAYERS_WR; // Do not update TextureFactoryIdentifier if aIdentifier is going to disable // WebRender, but gecko is still using WebRender. Since gecko uses different // incompatible ImageHost and TextureHost between WebRender and non-WebRender. // // Even when WebRender is still in use, if non-accelerated widget is opened, // aIdentifier disables WebRender at ImageBridgeChild. if (disablingWebRender && gfxVars::UseWebRender()) { return; } // D3DTexture might become obsolte. To prevent to use obsoleted D3DTexture, // drop all ImageContainers' ImageClients. // During re-creating GPU process, there was a period that ImageBridgeChild // was re-created, but ImageBridgeChild::UpdateTextureFactoryIdentifier() was // not called yet. In the period, if ImageBridgeChild::CreateImageClient() is // called, ImageBridgeParent creates incompatible ImageHost than // WebRenderImageHost. bool initializingWebRender = GetCompositorBackendType() != LayersBackend::LAYERS_WR && aIdentifier.mParentBackend == LayersBackend::LAYERS_WR; bool needsDrop = disablingWebRender || initializingWebRender; #if defined(XP_WIN) RefPtr device = gfx::DeviceManagerDx::Get()->GetImageDevice(); needsDrop |= !!mImageDevice && mImageDevice != device && GetCompositorBackendType() == LayersBackend::LAYERS_D3D11; mImageDevice = device; #endif IdentifyTextureHost(aIdentifier); if (needsDrop) { nsTArray > listeners; { MutexAutoLock lock(mContainerMapLock); for (const auto& entry : mImageContainerListeners) { listeners.AppendElement(entry.second); } } // Drop ImageContainer's ImageClient whithout holding mContainerMapLock to // avoid deadlock. for (auto container : listeners) { container->DropImageClient(); } } } RefPtr ImageBridgeChild::CreateImageClient( CompositableType aType, ImageContainer* aImageContainer) { if (InImageBridgeChildThread()) { return CreateImageClientNow(aType, aImageContainer); } SynchronousTask task("CreateImageClient Lock"); RefPtr result = nullptr; RefPtr runnable = WrapRunnable( RefPtr(this), &ImageBridgeChild::CreateImageClientSync, &task, &result, aType, aImageContainer); GetMessageLoop()->PostTask(runnable.forget()); task.Wait(); return result; } RefPtr ImageBridgeChild::CreateImageClientNow( CompositableType aType, ImageContainer* aImageContainer) { MOZ_ASSERT(InImageBridgeChildThread()); if (!CanSend()) { return nullptr; } RefPtr client = ImageClient::CreateImageClient(aType, this, TextureFlags::NO_FLAGS); MOZ_ASSERT(client, "failed to create ImageClient"); if (client) { client->Connect(aImageContainer); } return client; } already_AddRefed ImageBridgeChild::CreateCanvasClient( CanvasClient::CanvasClientType aType, TextureFlags aFlag) { if (InImageBridgeChildThread()) { return CreateCanvasClientNow(aType, aFlag); } SynchronousTask task("CreateCanvasClient Lock"); // RefPtrs on arguments are not needed since this dispatches synchronously. RefPtr result = nullptr; RefPtr runnable = WrapRunnable( RefPtr(this), &ImageBridgeChild::CreateCanvasClientSync, &task, aType, aFlag, &result); GetMessageLoop()->PostTask(runnable.forget()); task.Wait(); return result.forget(); } already_AddRefed ImageBridgeChild::CreateCanvasClientNow( CanvasClient::CanvasClientType aType, TextureFlags aFlag) { RefPtr client = CanvasClient::CreateCanvasClient(aType, this, aFlag); MOZ_ASSERT(client, "failed to create CanvasClient"); if (client) { client->Connect(); } return client.forget(); } bool ImageBridgeChild::AllocUnsafeShmem( size_t aSize, ipc::SharedMemory::SharedMemoryType aType, ipc::Shmem* aShmem) { if (!InImageBridgeChildThread()) { return DispatchAllocShmemInternal(aSize, aType, aShmem, true); // true: unsafe } if (!CanSend()) { return false; } return PImageBridgeChild::AllocUnsafeShmem(aSize, aType, aShmem); } bool ImageBridgeChild::AllocShmem(size_t aSize, ipc::SharedMemory::SharedMemoryType aType, ipc::Shmem* aShmem) { if (!InImageBridgeChildThread()) { return DispatchAllocShmemInternal(aSize, aType, aShmem, false); // false: unsafe } if (!CanSend()) { return false; } return PImageBridgeChild::AllocShmem(aSize, aType, aShmem); } void ImageBridgeChild::ProxyAllocShmemNow(SynchronousTask* aTask, size_t aSize, SharedMemory::SharedMemoryType aType, ipc::Shmem* aShmem, bool aUnsafe, bool* aSuccess) { AutoCompleteTask complete(aTask); if (!CanSend()) { return; } bool ok = false; if (aUnsafe) { ok = AllocUnsafeShmem(aSize, aType, aShmem); } else { ok = AllocShmem(aSize, aType, aShmem); } *aSuccess = ok; } bool ImageBridgeChild::DispatchAllocShmemInternal( size_t aSize, SharedMemory::SharedMemoryType aType, ipc::Shmem* aShmem, bool aUnsafe) { SynchronousTask task("AllocatorProxy alloc"); bool success = false; RefPtr runnable = WrapRunnable( RefPtr(this), &ImageBridgeChild::ProxyAllocShmemNow, &task, aSize, aType, aShmem, aUnsafe, &success); GetMessageLoop()->PostTask(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 = WrapRunnable( RefPtr(this), &ImageBridgeChild::ProxyDeallocShmemNow, &task, &aShmem, &result); GetMessageLoop()->PostTask(runnable.forget()); task.Wait(); return result; } PTextureChild* ImageBridgeChild::AllocPTextureChild( const SurfaceDescriptor&, const 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(aActor); return true; } mozilla::ipc::IPCResult ImageBridgeChild::RecvParentAsyncMessages( nsTArray&& 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 ImageBridgeChild::FindListener( const CompositableHandle& aHandle) { RefPtr 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&& aNotifications) { for (auto& n : aNotifications) { RefPtr 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 listener = FindListener(aHandle); if (listener) { listener->NotifyDropped(aFrames); } return IPC_OK(); } PTextureChild* ImageBridgeChild::CreateTexture( const SurfaceDescriptor& aSharedData, const ReadLockDescriptor& aReadLock, LayersBackend aLayersBackend, TextureFlags aFlags, uint64_t aSerial, wr::MaybeExternalImageId& aExternalImageId, nsIEventTarget* aTarget) { MOZ_ASSERT(CanSend()); return SendPTextureConstructor(aSharedData, 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(aTexture)); } bool ImageBridgeChild::DestroyInTransaction(const CompositableHandle& aHandle) { return IBCAddOpDestroy(mTxn, OpDestroy(aHandle)); } void ImageBridgeChild::RemoveTextureFromCompositable( CompositableClient* aCompositable, TextureClient* aTexture, const Maybe& aRenderRoot) { 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(nullptr, 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 = WrapRunnable(RefPtr(this), &ImageBridgeChild::ReleaseCompositable, aHandle); GetMessageLoop()->PostTask(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