/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- * 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 "base/tracked.h" // for FROM_HERE #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/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/CompositorParent.h" // for CompositorParent #include "mozilla/layers/ISurfaceAllocator.h" // for ISurfaceAllocator #include "mozilla/layers/ImageClient.h" // for ImageClient #include "mozilla/layers/LayersMessages.h" // for CompositableOperation #include "mozilla/layers/PCompositableChild.h" // for PCompositableChild #include "mozilla/layers/PImageContainerChild.h" #include "mozilla/layers/TextureClient.h" // for TextureClient #include "mozilla/mozalloc.h" // for operator new, etc #include "nsAutoPtr.h" // for nsRefPtr #include "nsISupportsImpl.h" // for ImageContainer::AddRef, etc #include "nsTArray.h" // for nsAutoTArray, nsTArray, etc #include "nsTArrayForwardDeclare.h" // for AutoInfallibleTArray #include "nsThreadUtils.h" // for NS_IsMainThread #include "nsXULAppAPI.h" // for XRE_GetIOMessageLoop #include "mozilla/StaticPtr.h" // for StaticRefPtr #include "mozilla/layers/TextureClient.h" namespace mozilla { namespace ipc { class Shmem; } // namespace ipc namespace layers { using base::Thread; using base::ProcessId; using namespace mozilla::ipc; using namespace mozilla::gfx; using namespace mozilla::media; typedef std::vector OpVector; struct CompositableTransaction { CompositableTransaction() : mSwapRequired(false) , mFinished(true) {} ~CompositableTransaction() { End(); } bool Finished() const { return mFinished; } void Begin() { MOZ_ASSERT(mFinished); mFinished = false; } void End() { mFinished = true; mSwapRequired = false; mOperations.clear(); } bool IsEmpty() const { return mOperations.empty(); } void AddNoSwapEdit(const CompositableOperation& op) { MOZ_ASSERT(!Finished(), "forgot BeginTransaction?"); mOperations.push_back(op); } void AddEdit(const CompositableOperation& op) { AddNoSwapEdit(op); mSwapRequired = true; } OpVector mOperations; bool mSwapRequired; bool mFinished; }; struct AutoEndTransaction { explicit AutoEndTransaction(CompositableTransaction* aTxn) : mTxn(aTxn) {} ~AutoEndTransaction() { mTxn->End(); } CompositableTransaction* mTxn; }; void ImageBridgeChild::UseTextures(CompositableClient* aCompositable, const nsTArray& aTextures) { MOZ_ASSERT(aCompositable); MOZ_ASSERT(aCompositable->GetIPDLActor()); MOZ_ASSERT(aCompositable->IsConnected()); nsAutoTArray textures; for (auto& t : aTextures) { MOZ_ASSERT(t.mTextureClient); MOZ_ASSERT(t.mTextureClient->GetIPDLActor()); if (!t.mTextureClient->IsSharedWithCompositor()) { return; } FenceHandle fence = t.mTextureClient->GetAcquireFenceHandle(); textures.AppendElement(TimedTexture(nullptr, t.mTextureClient->GetIPDLActor(), fence.IsValid() ? MaybeFence(fence) : MaybeFence(null_t()), t.mTimeStamp, t.mPictureRect, t.mFrameID, t.mProducerID)); } mTxn->AddNoSwapEdit(OpUseTexture(nullptr, aCompositable->GetIPDLActor(), textures)); } void ImageBridgeChild::UseComponentAlphaTextures(CompositableClient* aCompositable, TextureClient* aTextureOnBlack, TextureClient* aTextureOnWhite) { MOZ_ASSERT(aCompositable); MOZ_ASSERT(aTextureOnWhite); MOZ_ASSERT(aTextureOnBlack); MOZ_ASSERT(aCompositable->IsConnected()); MOZ_ASSERT(aTextureOnWhite->GetIPDLActor()); MOZ_ASSERT(aTextureOnBlack->GetIPDLActor()); MOZ_ASSERT(aTextureOnBlack->GetSize() == aTextureOnWhite->GetSize()); mTxn->AddNoSwapEdit(OpUseComponentAlphaTextures(nullptr, aCompositable->GetIPDLActor(), nullptr, aTextureOnBlack->GetIPDLActor(), nullptr, aTextureOnWhite->GetIPDLActor())); } #ifdef MOZ_WIDGET_GONK void ImageBridgeChild::UseOverlaySource(CompositableClient* aCompositable, const OverlaySource& aOverlay, const nsIntRect& aPictureRect) { MOZ_ASSERT(aCompositable); MOZ_ASSERT(aCompositable->IsConnected()); mTxn->AddEdit(OpUseOverlaySource(nullptr, aCompositable->GetIPDLActor(), aOverlay, aPictureRect)); } #endif // Singleton static StaticRefPtr sImageBridgeChildSingleton; static StaticRefPtr sImageBridgeParentSingleton; static Thread *sImageBridgeChildThread = nullptr; void ReleaseImageBridgeParentSingleton() { sImageBridgeParentSingleton = nullptr; } // dispatched function static void ImageBridgeShutdownStep1(ReentrantMonitor *aBarrier, bool *aDone) { ReentrantMonitorAutoEnter autoMon(*aBarrier); MOZ_ASSERT(InImageBridgeChildThread(), "Should be in ImageBridgeChild thread."); MediaSystemResourceManager::Shutdown(); if (sImageBridgeChildSingleton) { // Force all managed protocols to shut themselves down cleanly InfallibleTArray compositables; sImageBridgeChildSingleton->ManagedPCompositableChild(compositables); for (int i = compositables.Length() - 1; i >= 0; --i) { CompositableClient::FromIPDLActor(compositables[i])->Destroy(); } InfallibleTArray textures; sImageBridgeChildSingleton->ManagedPTextureChild(textures); for (int i = textures.Length() - 1; i >= 0; --i) { RefPtr client = TextureClient::AsTextureClient(textures[i]); if (client) { client->ForceRemove(); } } sImageBridgeChildSingleton->SendWillStop(); sImageBridgeChildSingleton->MarkShutDown(); // From now on, no message can be sent through the image bridge from the // client side except the final Stop message. } *aDone = true; aBarrier->NotifyAll(); } // dispatched function static void ImageBridgeShutdownStep2(ReentrantMonitor *aBarrier, bool *aDone) { ReentrantMonitorAutoEnter autoMon(*aBarrier); MOZ_ASSERT(InImageBridgeChildThread(), "Should be in ImageBridgeChild thread."); sImageBridgeChildSingleton->SendStop(); *aDone = true; aBarrier->NotifyAll(); } // dispatched function static void CreateImageClientSync(RefPtr* result, ReentrantMonitor* barrier, CompositableType aType, ImageContainer* aImageContainer, bool *aDone) { ReentrantMonitorAutoEnter autoMon(*barrier); *result = sImageBridgeChildSingleton->CreateImageClientNow( aType, aImageContainer); *aDone = true; barrier->NotifyAll(); } // dispatched function static void CreateCanvasClientSync(ReentrantMonitor* aBarrier, CanvasClient::CanvasClientType aType, TextureFlags aFlags, RefPtr* const outResult, bool* aDone) { ReentrantMonitorAutoEnter autoMon(*aBarrier); *outResult = sImageBridgeChildSingleton->CreateCanvasClientNow(aType, aFlags); *aDone = true; aBarrier->NotifyAll(); } static void ConnectImageBridge(ImageBridgeChild * child, ImageBridgeParent * parent) { MessageLoop *parentMsgLoop = parent->GetMessageLoop(); ipc::MessageChannel *parentChannel = parent->GetIPCChannel(); child->Open(parentChannel, parentMsgLoop, mozilla::ipc::ChildSide); } ImageBridgeChild::ImageBridgeChild() : mShuttingDown(false) { MOZ_ASSERT(NS_IsMainThread()); // Always run destructor on the main thread SetMessageLoopToPostDestructionTo(MessageLoop::current()); mTxn = new CompositableTransaction(); } ImageBridgeChild::~ImageBridgeChild() { MOZ_ASSERT(NS_IsMainThread()); delete mTxn; } void ImageBridgeChild::MarkShutDown() { MOZ_ASSERT(!mShuttingDown); mShuttingDown = true; } void ImageBridgeChild::Connect(CompositableClient* aCompositable, ImageContainer* aImageContainer) { MOZ_ASSERT(aCompositable); MOZ_ASSERT(!mShuttingDown); uint64_t id = 0; PImageContainerChild* imageContainerChild = nullptr; if (aImageContainer) imageContainerChild = aImageContainer->GetPImageContainerChild(); PCompositableChild* child = SendPCompositableConstructor(aCompositable->GetTextureInfo(), imageContainerChild, &id); MOZ_ASSERT(child); aCompositable->InitIPDLActor(child, id); } PCompositableChild* ImageBridgeChild::AllocPCompositableChild(const TextureInfo& aInfo, PImageContainerChild* aChild, uint64_t* aID) { MOZ_ASSERT(!mShuttingDown); return CompositableClient::CreateIPDLActor(); } bool ImageBridgeChild::DeallocPCompositableChild(PCompositableChild* aActor) { return CompositableClient::DestroyIPDLActor(aActor); } Thread* ImageBridgeChild::GetThread() const { return sImageBridgeChildThread; } ImageBridgeChild* ImageBridgeChild::GetSingleton() { return sImageBridgeChildSingleton; } bool ImageBridgeChild::IsCreated() { return GetSingleton() != nullptr; } void ImageBridgeChild::StartUp() { NS_ASSERTION(NS_IsMainThread(), "Should be on the main Thread!"); ImageBridgeChild::StartUpOnThread(new Thread("ImageBridgeChild")); } #ifdef MOZ_NUWA_PROCESS #include "ipc/Nuwa.h" #endif static void ConnectImageBridgeInChildProcess(Transport* aTransport, ProcessId aOtherPid) { // Bind the IPC channel to the image bridge thread. sImageBridgeChildSingleton->Open(aTransport, aOtherPid, XRE_GetIOMessageLoop(), ipc::ChildSide); #ifdef MOZ_NUWA_PROCESS if (IsNuwaProcess()) { sImageBridgeChildThread ->message_loop()->PostTask(FROM_HERE, NewRunnableFunction(NuwaMarkCurrentThread, (void (*)(void *))nullptr, (void *)nullptr)); } #endif } static void ReleaseImageClientNow(ImageClient* aClient, PImageContainerChild* aChild) { MOZ_ASSERT(InImageBridgeChildThread()); if (aClient) { aClient->Release(); } if (aChild && ImageBridgeChild::IsCreated()) { aChild->SendAsyncDelete(); } } // static void ImageBridgeChild::DispatchReleaseImageClient(ImageClient* aClient, PImageContainerChild* aChild) { if (!aClient && !aChild) { return; } if (!IsCreated()) { if (aClient) { // CompositableClient::Release should normally happen in the ImageBridgeChild // thread because it usually generate some IPDL messages. // However, if we take this branch it means that the ImageBridgeChild // has already shut down, along with the CompositableChild, which means no // message will be sent and it is safe to run this code from any thread. MOZ_ASSERT(aClient->GetIPDLActor() == nullptr); aClient->Release(); } delete aChild; return; } sImageBridgeChildSingleton->GetMessageLoop()->PostTask( FROM_HERE, NewRunnableFunction(&ReleaseImageClientNow, aClient, aChild)); } static void ReleaseCanvasClientNow(CanvasClient* aClient) { MOZ_ASSERT(InImageBridgeChildThread()); aClient->Release(); } // static void ImageBridgeChild::DispatchReleaseCanvasClient(CanvasClient* aClient) { if (!aClient) { return; } if (!IsCreated()) { // CompositableClient::Release should normally happen in the ImageBridgeChild // thread because it usually generate some IPDL messages. // However, if we take this branch it means that the ImageBridgeChild // has already shut down, along with the CompositableChild, which means no // message will be sent and it is safe to run this code from any thread. MOZ_ASSERT(aClient->GetIPDLActor() == nullptr); aClient->Release(); return; } sImageBridgeChildSingleton->GetMessageLoop()->PostTask( FROM_HERE, NewRunnableFunction(&ReleaseCanvasClientNow, aClient)); } static void ReleaseTextureClientNow(TextureClient* aClient) { MOZ_ASSERT(InImageBridgeChildThread()); RELEASE_MANUALLY(aClient); } // static void ImageBridgeChild::DispatchReleaseTextureClient(TextureClient* aClient) { if (!aClient) { return; } if (!IsCreated()) { // TextureClient::Release should normally happen in the ImageBridgeChild // thread because it usually generate some IPDL messages. // However, if we take this branch it means that the ImageBridgeChild // has already shut down, along with the TextureChild, which means no // message will be sent and it is safe to run this code from any thread. MOZ_ASSERT(aClient->GetIPDLActor() == nullptr); RELEASE_MANUALLY(aClient); return; } sImageBridgeChildSingleton->GetMessageLoop()->PostTask( FROM_HERE, NewRunnableFunction(&ReleaseTextureClientNow, aClient)); } static void UpdateImageClientNow(ImageClient* aClient, RefPtr&& aContainer) { if (!ImageBridgeChild::IsCreated()) { NS_WARNING("Something is holding on to graphics resources after the shutdown" "of the graphics subsystem!"); return; } MOZ_ASSERT(aClient); MOZ_ASSERT(aContainer); sImageBridgeChildSingleton->BeginTransaction(); aClient->UpdateImage(aContainer, Layer::CONTENT_OPAQUE); sImageBridgeChildSingleton->EndTransaction(); } // static void ImageBridgeChild::DispatchImageClientUpdate(ImageClient* aClient, ImageContainer* aContainer) { if (!ImageBridgeChild::IsCreated()) { NS_WARNING("Something is holding on to graphics resources after the shutdown" "of the graphics subsystem!"); return; } if (!aClient || !aContainer || !IsCreated()) { return; } if (InImageBridgeChildThread()) { UpdateImageClientNow(aClient, aContainer); return; } sImageBridgeChildSingleton->GetMessageLoop()->PostTask( FROM_HERE, NewRunnableFunction(&UpdateImageClientNow, aClient, RefPtr(aContainer))); } static void UpdateAsyncCanvasRendererSync(AsyncCanvasRenderer* aWrapper, ReentrantMonitor* aBarrier, bool* const outDone) { ImageBridgeChild::UpdateAsyncCanvasRendererNow(aWrapper); ReentrantMonitorAutoEnter autoMon(*aBarrier); *outDone = true; aBarrier->NotifyAll(); } // static void ImageBridgeChild::UpdateAsyncCanvasRenderer(AsyncCanvasRenderer* aWrapper) { aWrapper->GetCanvasClient()->UpdateAsync(aWrapper); if (InImageBridgeChildThread()) { UpdateAsyncCanvasRendererNow(aWrapper); return; } ReentrantMonitor barrier("UpdateAsyncCanvasRenderer Lock"); ReentrantMonitorAutoEnter autoMon(barrier); bool done = false; sImageBridgeChildSingleton->GetMessageLoop()->PostTask( FROM_HERE, NewRunnableFunction(&UpdateAsyncCanvasRendererSync, aWrapper, &barrier, &done)); // should stop the thread until the CanvasClient has been created on // the other thread while (!done) { barrier.Wait(); } } // static void ImageBridgeChild::UpdateAsyncCanvasRendererNow(AsyncCanvasRenderer* aWrapper) { MOZ_ASSERT(aWrapper); sImageBridgeChildSingleton->BeginTransaction(); aWrapper->GetCanvasClient()->Updated(); sImageBridgeChildSingleton->EndTransaction(); } static void FlushAllImagesSync(ImageClient* aClient, ImageContainer* aContainer, RefPtr&& aWaiter) { if (!ImageBridgeChild::IsCreated()) { // How sad. If we get into this branch it means that the ImageBridge // got destroyed between the time we ImageBridgeChild::FlushAllImage // was called on some thread, and the time this function was proxied // to the ImageBridge thread. ImageBridge gets destroyed way to late // in the shutdown of gecko for this to be happening for a good reason. NS_WARNING("Something is holding on to graphics resources after the shutdown" "of the graphics subsystem!"); aWaiter->DecrementWaitCount(); return; } MOZ_ASSERT(aClient); sImageBridgeChildSingleton->BeginTransaction(); if (aContainer) { aContainer->ClearImagesFromImageBridge(); } aClient->FlushAllImages(aWaiter); sImageBridgeChildSingleton->EndTransaction(); // This decrement is balanced by the increment in FlushAllImages. // If any AsyncTransactionTrackers were created by FlushAllImages and attached // to aWaiter, aWaiter will not complete until those trackers all complete. // Otherwise, aWaiter will be ready to complete now. aWaiter->DecrementWaitCount(); } // static void ImageBridgeChild::FlushAllImages(ImageClient* aClient, ImageContainer* aContainer) { if (!IsCreated()) { return; } MOZ_ASSERT(aClient); MOZ_ASSERT(!sImageBridgeChildSingleton->mShuttingDown); MOZ_ASSERT(!InImageBridgeChildThread()); if (InImageBridgeChildThread()) { NS_ERROR("ImageBridgeChild::FlushAllImages() is called on ImageBridge thread."); return; } RefPtr waiter = new AsyncTransactionWaiter(); // This increment is balanced by the decrement in FlushAllImagesSync waiter->IncrementWaitCount(); sImageBridgeChildSingleton->GetMessageLoop()->PostTask( FROM_HERE, NewRunnableFunction(&FlushAllImagesSync, aClient, aContainer, waiter)); waiter->WaitComplete(); } void ImageBridgeChild::BeginTransaction() { MOZ_ASSERT(!mShuttingDown); MOZ_ASSERT(mTxn->Finished(), "uncommitted txn?"); mTxn->Begin(); } class MOZ_STACK_CLASS AutoRemoveTexturesFromImageBridge { public: explicit AutoRemoveTexturesFromImageBridge(ImageBridgeChild* aImageBridge) : mImageBridge(aImageBridge) {} ~AutoRemoveTexturesFromImageBridge() { mImageBridge->RemoveTexturesIfNecessary(); } private: ImageBridgeChild* mImageBridge; }; void ImageBridgeChild::EndTransaction() { MOZ_ASSERT(!mShuttingDown); MOZ_ASSERT(!mTxn->Finished(), "forgot BeginTransaction?"); AutoEndTransaction _(mTxn); AutoRemoveTexturesFromImageBridge autoRemoveTextures(this); if (mTxn->IsEmpty()) { return; } AutoInfallibleTArray cset; cset.SetCapacity(mTxn->mOperations.size()); if (!mTxn->mOperations.empty()) { cset.AppendElements(&mTxn->mOperations.front(), mTxn->mOperations.size()); } if (!IsSameProcess()) { ShadowLayerForwarder::PlatformSyncBeforeUpdate(); } AutoInfallibleTArray replies; if (mTxn->mSwapRequired) { if (!SendUpdate(cset, &replies)) { NS_WARNING("could not send async texture transaction"); return; } } else { // If we don't require a swap we can call SendUpdateNoSwap which // assumes that aReplies is empty (DEBUG assertion) if (!SendUpdateNoSwap(cset)) { NS_WARNING("could not send async texture transaction (no swap)"); return; } } for (nsTArray::size_type i = 0; i < replies.Length(); ++i) { NS_RUNTIMEABORT("not reached"); } SendPendingAsyncMessges(); } void ImageBridgeChild::SendImageBridgeThreadId() { #ifdef MOZ_WIDGET_GONK SendImageBridgeThreadId(gettid()); #endif } static void CallSendImageBridgeThreadId(ImageBridgeChild* aImageBridgeChild) { MOZ_ASSERT(InImageBridgeChildThread()); aImageBridgeChild->SendImageBridgeThreadId(); } PImageBridgeChild* ImageBridgeChild::StartUpInChildProcess(Transport* aTransport, ProcessId aOtherPid) { MOZ_ASSERT(NS_IsMainThread()); gfxPlatform::GetPlatform(); sImageBridgeChildThread = new Thread("ImageBridgeChild"); if (!sImageBridgeChildThread->Start()) { return nullptr; } sImageBridgeChildSingleton = new ImageBridgeChild(); sImageBridgeChildSingleton->GetMessageLoop()->PostTask( FROM_HERE, NewRunnableFunction(ConnectImageBridgeInChildProcess, aTransport, aOtherPid)); sImageBridgeChildSingleton->GetMessageLoop()->PostTask( FROM_HERE, NewRunnableFunction(CallSendImageBridgeThreadId, sImageBridgeChildSingleton.get())); return sImageBridgeChildSingleton; } void ImageBridgeChild::ShutDown() { MOZ_ASSERT(NS_IsMainThread()); if (ImageBridgeChild::IsCreated()) { MOZ_ASSERT(!sImageBridgeChildSingleton->mShuttingDown); { ReentrantMonitor barrier("ImageBridge ShutdownStep1 lock"); ReentrantMonitorAutoEnter autoMon(barrier); bool done = false; sImageBridgeChildSingleton->GetMessageLoop()->PostTask(FROM_HERE, NewRunnableFunction(&ImageBridgeShutdownStep1, &barrier, &done)); while (!done) { barrier.Wait(); } } { ReentrantMonitor barrier("ImageBridge ShutdownStep2 lock"); ReentrantMonitorAutoEnter autoMon(barrier); bool done = false; sImageBridgeChildSingleton->GetMessageLoop()->PostTask(FROM_HERE, NewRunnableFunction(&ImageBridgeShutdownStep2, &barrier, &done)); while (!done) { barrier.Wait(); } } sImageBridgeChildSingleton = nullptr; delete sImageBridgeChildThread; sImageBridgeChildThread = nullptr; } } bool ImageBridgeChild::StartUpOnThread(Thread* aThread) { MOZ_ASSERT(aThread, "ImageBridge needs a thread."); if (sImageBridgeChildSingleton == nullptr) { sImageBridgeChildThread = aThread; if (!aThread->IsRunning()) { aThread->Start(); } sImageBridgeChildSingleton = new ImageBridgeChild(); sImageBridgeParentSingleton = new ImageBridgeParent( CompositorParent::CompositorLoop(), nullptr, base::GetCurrentProcId()); sImageBridgeChildSingleton->ConnectAsync(sImageBridgeParentSingleton); sImageBridgeChildSingleton->GetMessageLoop()->PostTask( FROM_HERE, NewRunnableFunction(CallSendImageBridgeThreadId, sImageBridgeChildSingleton.get())); return true; } else { return false; } } bool InImageBridgeChildThread() { return ImageBridgeChild::IsCreated() && sImageBridgeChildThread->thread_id() == PlatformThread::CurrentId(); } MessageLoop * ImageBridgeChild::GetMessageLoop() const { return sImageBridgeChildThread->message_loop(); } void ImageBridgeChild::ConnectAsync(ImageBridgeParent* aParent) { GetMessageLoop()->PostTask(FROM_HERE, NewRunnableFunction(&ConnectImageBridge, this, aParent)); } void ImageBridgeChild::IdentifyCompositorTextureHost(const TextureFactoryIdentifier& aIdentifier) { if (sImageBridgeChildSingleton) { sImageBridgeChildSingleton->IdentifyTextureHost(aIdentifier); } } already_AddRefed ImageBridgeChild::CreateImageClient(CompositableType aType, ImageContainer* aImageContainer) { if (InImageBridgeChildThread()) { return CreateImageClientNow(aType, aImageContainer); } ReentrantMonitor barrier("CreateImageClient Lock"); ReentrantMonitorAutoEnter autoMon(barrier); bool done = false; RefPtr result = nullptr; GetMessageLoop()->PostTask(FROM_HERE, NewRunnableFunction(&CreateImageClientSync, &result, &barrier, aType, aImageContainer, &done)); // should stop the thread until the ImageClient has been created on // the other thread while (!done) { barrier.Wait(); } return result.forget(); } already_AddRefed ImageBridgeChild::CreateImageClientNow(CompositableType aType, ImageContainer* aImageContainer) { MOZ_ASSERT(!sImageBridgeChildSingleton->mShuttingDown); if (aImageContainer) { SendPImageContainerConstructor(aImageContainer->GetPImageContainerChild()); } RefPtr client = ImageClient::CreateImageClient(aType, this, TextureFlags::NO_FLAGS); MOZ_ASSERT(client, "failed to create ImageClient"); if (client) { client->Connect(aImageContainer); } return client.forget(); } already_AddRefed ImageBridgeChild::CreateCanvasClient(CanvasClient::CanvasClientType aType, TextureFlags aFlag) { if (InImageBridgeChildThread()) { return CreateCanvasClientNow(aType, aFlag); } ReentrantMonitor barrier("CreateCanvasClient Lock"); ReentrantMonitorAutoEnter autoMon(barrier); bool done = false; RefPtr result = nullptr; GetMessageLoop()->PostTask(FROM_HERE, NewRunnableFunction(&CreateCanvasClientSync, &barrier, aType, aFlag, &result, &done)); // should stop the thread until the CanvasClient has been created on the // other thread while (!done) { barrier.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) { MOZ_ASSERT(!mShuttingDown); if (InImageBridgeChildThread()) { return PImageBridgeChild::AllocUnsafeShmem(aSize, aType, aShmem); } else { return DispatchAllocShmemInternal(aSize, aType, aShmem, true); // true: unsafe } } bool ImageBridgeChild::AllocShmem(size_t aSize, ipc::SharedMemory::SharedMemoryType aType, ipc::Shmem* aShmem) { MOZ_ASSERT(!mShuttingDown); if (InImageBridgeChildThread()) { return PImageBridgeChild::AllocShmem(aSize, aType, aShmem); } else { return DispatchAllocShmemInternal(aSize, aType, aShmem, false); // false: unsafe } } // NewRunnableFunction accepts a limited number of parameters so we need a // struct here struct AllocShmemParams { RefPtr mAllocator; size_t mSize; ipc::SharedMemory::SharedMemoryType mType; ipc::Shmem* mShmem; bool mUnsafe; bool mSuccess; }; static void ProxyAllocShmemNow(AllocShmemParams* aParams, ReentrantMonitor* aBarrier, bool* aDone) { MOZ_ASSERT(aParams); MOZ_ASSERT(aDone); MOZ_ASSERT(aBarrier); if (aParams->mUnsafe) { aParams->mSuccess = aParams->mAllocator->AllocUnsafeShmem(aParams->mSize, aParams->mType, aParams->mShmem); } else { aParams->mSuccess = aParams->mAllocator->AllocShmem(aParams->mSize, aParams->mType, aParams->mShmem); } ReentrantMonitorAutoEnter autoMon(*aBarrier); *aDone = true; aBarrier->NotifyAll(); } bool ImageBridgeChild::DispatchAllocShmemInternal(size_t aSize, SharedMemory::SharedMemoryType aType, ipc::Shmem* aShmem, bool aUnsafe) { ReentrantMonitor barrier("AllocatorProxy alloc"); ReentrantMonitorAutoEnter autoMon(barrier); AllocShmemParams params = { this, aSize, aType, aShmem, aUnsafe, true }; bool done = false; GetMessageLoop()->PostTask(FROM_HERE, NewRunnableFunction(&ProxyAllocShmemNow, ¶ms, &barrier, &done)); while (!done) { barrier.Wait(); } return params.mSuccess; } static void ProxyDeallocShmemNow(ISurfaceAllocator* aAllocator, ipc::Shmem* aShmem, ReentrantMonitor* aBarrier, bool* aDone) { MOZ_ASSERT(aShmem); MOZ_ASSERT(aDone); MOZ_ASSERT(aBarrier); aAllocator->DeallocShmem(*aShmem); ReentrantMonitorAutoEnter autoMon(*aBarrier); *aDone = true; aBarrier->NotifyAll(); } void ImageBridgeChild::DeallocShmem(ipc::Shmem& aShmem) { if (InImageBridgeChildThread()) { PImageBridgeChild::DeallocShmem(aShmem); } else { ReentrantMonitor barrier("AllocatorProxy Dealloc"); ReentrantMonitorAutoEnter autoMon(barrier); bool done = false; GetMessageLoop()->PostTask(FROM_HERE, NewRunnableFunction(&ProxyDeallocShmemNow, this, &aShmem, &barrier, &done)); while (!done) { barrier.Wait(); } } } PTextureChild* ImageBridgeChild::AllocPTextureChild(const SurfaceDescriptor&, const LayersBackend&, const TextureFlags&) { MOZ_ASSERT(!mShuttingDown); return TextureClient::CreateIPDLActor(); } bool ImageBridgeChild::DeallocPTextureChild(PTextureChild* actor) { return TextureClient::DestroyIPDLActor(actor); } PMediaSystemResourceManagerChild* ImageBridgeChild::AllocPMediaSystemResourceManagerChild() { MOZ_ASSERT(!mShuttingDown); return new mozilla::media::MediaSystemResourceManagerChild(); } bool ImageBridgeChild::DeallocPMediaSystemResourceManagerChild(PMediaSystemResourceManagerChild* aActor) { MOZ_ASSERT(aActor); delete static_cast(aActor); return true; } PImageContainerChild* ImageBridgeChild::AllocPImageContainerChild() { // we always use the "power-user" ctor NS_RUNTIMEABORT("not reached"); return nullptr; } bool ImageBridgeChild::DeallocPImageContainerChild(PImageContainerChild* actor) { delete actor; return true; } bool ImageBridgeChild::RecvParentAsyncMessages(InfallibleTArray&& aMessages) { for (AsyncParentMessageArray::index_type i = 0; i < aMessages.Length(); ++i) { const AsyncParentMessageData& message = aMessages[i]; switch (message.type()) { case AsyncParentMessageData::TOpDeliverFence: { const OpDeliverFence& op = message.get_OpDeliverFence(); FenceHandle fence = op.fence(); PTextureChild* child = op.textureChild(); RefPtr texture = TextureClient::AsTextureClient(child); if (texture) { texture->SetReleaseFenceHandle(fence); } break; } case AsyncParentMessageData::TOpDeliverFenceToTracker: { const OpDeliverFenceToTracker& op = message.get_OpDeliverFenceToTracker(); FenceHandle fence = op.fence(); AsyncTransactionTrackersHolder::SetReleaseFenceHandle(fence, op.destHolderId(), op.destTransactionId()); break; } case AsyncParentMessageData::TOpReplyRemoveTexture: { const OpReplyRemoveTexture& op = message.get_OpReplyRemoveTexture(); AsyncTransactionTrackersHolder::TransactionCompleteted(op.holderId(), op.transactionId()); break; } default: NS_ERROR("unknown AsyncParentMessageData type"); return false; } } return true; } bool ImageBridgeChild::RecvDidComposite(InfallibleTArray&& aNotifications) { for (auto& n : aNotifications) { ImageContainer::NotifyComposite(n); } return true; } PTextureChild* ImageBridgeChild::CreateTexture(const SurfaceDescriptor& aSharedData, LayersBackend aLayersBackend, TextureFlags aFlags) { MOZ_ASSERT(!mShuttingDown); return SendPTextureConstructor(aSharedData, aLayersBackend, aFlags); } void ImageBridgeChild::RemoveTextureFromCompositable(CompositableClient* aCompositable, TextureClient* aTexture) { MOZ_ASSERT(!mShuttingDown); MOZ_ASSERT(aTexture); MOZ_ASSERT(aTexture->IsSharedWithCompositor()); MOZ_ASSERT(aCompositable->IsConnected()); if (!aTexture || !aTexture->IsSharedWithCompositor() || !aCompositable->IsConnected()) { return; } if (aTexture->GetFlags() & TextureFlags::DEALLOCATE_CLIENT) { mTxn->AddEdit(OpRemoveTexture(nullptr, aCompositable->GetIPDLActor(), nullptr, aTexture->GetIPDLActor())); } else { mTxn->AddNoSwapEdit(OpRemoveTexture(nullptr, aCompositable->GetIPDLActor(), nullptr, aTexture->GetIPDLActor())); } // Hold texture until transaction complete. HoldUntilTransaction(aTexture); } void ImageBridgeChild::RemoveTextureFromCompositableAsync(AsyncTransactionTracker* aAsyncTransactionTracker, CompositableClient* aCompositable, TextureClient* aTexture) { MOZ_ASSERT(!mShuttingDown); MOZ_ASSERT(aTexture); MOZ_ASSERT(aTexture->IsSharedWithCompositor()); MOZ_ASSERT(aCompositable->IsConnected()); if (!aTexture || !aTexture->IsSharedWithCompositor() || !aCompositable->IsConnected()) { return; } mTxn->AddNoSwapEdit(OpRemoveTextureAsync(CompositableClient::GetTrackersHolderId(aCompositable->GetIPDLActor()), aAsyncTransactionTracker->GetId(), nullptr, aCompositable->GetIPDLActor(), nullptr, aTexture->GetIPDLActor())); // Hold AsyncTransactionTracker until receving reply CompositableClient::HoldUntilComplete(aCompositable->GetIPDLActor(), aAsyncTransactionTracker); } static void RemoveTextureSync(TextureClient* aTexture, ReentrantMonitor* aBarrier, bool* aDone) { aTexture->ForceRemove(); ReentrantMonitorAutoEnter autoMon(*aBarrier); *aDone = true; aBarrier->NotifyAll(); } void ImageBridgeChild::RemoveTexture(TextureClient* aTexture) { if (InImageBridgeChildThread()) { MOZ_ASSERT(!mShuttingDown); aTexture->ForceRemove(); return; } ReentrantMonitor barrier("RemoveTexture Lock"); ReentrantMonitorAutoEnter autoMon(barrier); bool done = false; sImageBridgeChildSingleton->GetMessageLoop()->PostTask( FROM_HERE, NewRunnableFunction(&RemoveTextureSync, aTexture, &barrier, &done)); // should stop the thread until the ImageClient has been created on // the other thread while (!done) { barrier.Wait(); } } bool ImageBridgeChild::IsSameProcess() const { return OtherPid() == base::GetCurrentProcId(); } void ImageBridgeChild::SendPendingAsyncMessges() { } } // namespace layers } // namespace mozilla