diff -r 000000000000 -r 6474c204b198 gfx/layers/ipc/ImageBridgeChild.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gfx/layers/ipc/ImageBridgeChild.cpp Wed Dec 31 06:09:35 2014 +0100 @@ -0,0 +1,1034 @@ +/* -*- 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 "CompositorParent.h" // for CompositorParent +#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 ProcessHandle +#include "base/process_util.h" // for OpenProcessHandle +#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/CompositableClient.h" // for CompositableChild, etc +#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/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" + +struct nsIntRect; + +using namespace base; +using namespace mozilla::ipc; +using namespace mozilla::gfx; + +namespace mozilla { +namespace ipc { +class Shmem; +} + +namespace layers { + +class PGrallocBufferChild; +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) + { + NS_ABORT_IF_FALSE(!Finished(), "forgot BeginTransaction?"); + mOperations.push_back(op); + } + void AddEdit(const CompositableOperation& op) + { + AddNoSwapEdit(op); + mSwapRequired = true; + } + + OpVector mOperations; + bool mSwapRequired; + bool mFinished; +}; + +struct AutoEndTransaction { + AutoEndTransaction(CompositableTransaction* aTxn) : mTxn(aTxn) {} + ~AutoEndTransaction() { mTxn->End(); } + CompositableTransaction* mTxn; +}; + +void +ImageBridgeChild::UseTexture(CompositableClient* aCompositable, + TextureClient* aTexture) +{ + MOZ_ASSERT(aCompositable); + MOZ_ASSERT(aTexture); + MOZ_ASSERT(aCompositable->GetIPDLActor()); + MOZ_ASSERT(aTexture->GetIPDLActor()); + mTxn->AddNoSwapEdit(OpUseTexture(nullptr, aCompositable->GetIPDLActor(), + nullptr, aTexture->GetIPDLActor())); +} + +void +ImageBridgeChild::UseComponentAlphaTextures(CompositableClient* aCompositable, + TextureClient* aTextureOnBlack, + TextureClient* aTextureOnWhite) +{ + MOZ_ASSERT(aCompositable); + MOZ_ASSERT(aTextureOnWhite); + MOZ_ASSERT(aTextureOnBlack); + MOZ_ASSERT(aCompositable->GetIPDLActor()); + 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())); +} + +void +ImageBridgeChild::UpdatedTexture(CompositableClient* aCompositable, + TextureClient* aTexture, + nsIntRegion* aRegion) +{ + MOZ_ASSERT(aCompositable); + MOZ_ASSERT(aTexture); + MOZ_ASSERT(aCompositable->GetIPDLActor()); + MOZ_ASSERT(aTexture->GetIPDLActor()); + MaybeRegion region = aRegion ? MaybeRegion(*aRegion) + : MaybeRegion(null_t()); + mTxn->AddNoSwapEdit(OpUpdateTexture(nullptr, aCompositable->GetIPDLActor(), + nullptr, aTexture->GetIPDLActor(), + region)); +} + +void +ImageBridgeChild::UpdatePictureRect(CompositableClient* aCompositable, + const nsIntRect& aRect) +{ + MOZ_ASSERT(aCompositable); + MOZ_ASSERT(aCompositable->GetIPDLActor()); + mTxn->AddNoSwapEdit(OpUpdatePictureRect(nullptr, aCompositable->GetIPDLActor(), aRect)); +} + +// 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); + + NS_ABORT_IF_FALSE(InImageBridgeChildThread(), + "Should be in ImageBridgeChild thread."); + 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) { + TextureClient::AsTextureClient(textures[i])->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); + + NS_ABORT_IF_FALSE(InImageBridgeChildThread(), + "Should be in ImageBridgeChild thread."); + + sImageBridgeChildSingleton->SendStop(); + + sImageBridgeChildSingleton = nullptr; + + *aDone = true; + aBarrier->NotifyAll(); +} + +// dispatched function +static void CreateImageClientSync(RefPtr* result, + ReentrantMonitor* barrier, + CompositableType aType, + bool *aDone) +{ + ReentrantMonitorAutoEnter autoMon(*barrier); + *result = sImageBridgeChildSingleton->CreateImageClientNow(aType); + *aDone = true; + barrier->NotifyAll(); +} + + +struct GrallocParam { + IntSize size; + uint32_t format; + uint32_t usage; + MaybeMagicGrallocBufferHandle* handle; + PGrallocBufferChild** child; + + GrallocParam(const IntSize& aSize, + const uint32_t& aFormat, + const uint32_t& aUsage, + MaybeMagicGrallocBufferHandle* aHandle, + PGrallocBufferChild** aChild) + : size(aSize) + , format(aFormat) + , usage(aUsage) + , handle(aHandle) + , child(aChild) + {} +}; + +// dispatched function +static void AllocGrallocBufferSync(const GrallocParam& aParam, + Monitor* aBarrier, + bool* aDone) +{ + MonitorAutoLock autoMon(*aBarrier); + + sImageBridgeChildSingleton->AllocGrallocBufferNow(aParam.size, + aParam.format, + aParam.usage, + aParam.handle, + aParam.child); + *aDone = true; + aBarrier->NotifyAll(); +} + +// dispatched function +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) +{ + mTxn = new CompositableTransaction(); +} +ImageBridgeChild::~ImageBridgeChild() +{ + delete mTxn; +} + +void +ImageBridgeChild::MarkShutDown() +{ + MOZ_ASSERT(!mShuttingDown); + mShuttingDown = true; +} + +void +ImageBridgeChild::Connect(CompositableClient* aCompositable) +{ + MOZ_ASSERT(aCompositable); + MOZ_ASSERT(!mShuttingDown); + uint64_t id = 0; + PCompositableChild* child = + SendPCompositableConstructor(aCompositable->GetTextureInfo(), &id); + MOZ_ASSERT(child); + aCompositable->InitIPDLActor(child, id); +} + +PCompositableChild* +ImageBridgeChild::AllocPCompositableChild(const TextureInfo& aInfo, 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, + ProcessHandle aOtherProcess) +{ + // Bind the IPC channel to the image bridge thread. + sImageBridgeChildSingleton->Open(aTransport, aOtherProcess, + 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) +{ + MOZ_ASSERT(InImageBridgeChildThread()); + aClient->Release(); +} + +// static +void ImageBridgeChild::DispatchReleaseImageClient(ImageClient* aClient) +{ + 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(&ReleaseImageClientNow, aClient)); +} + +static void ReleaseTextureClientNow(TextureClient* aClient) +{ + MOZ_ASSERT(InImageBridgeChildThread()); + aClient->Release(); +} + +// static +void ImageBridgeChild::DispatchReleaseTextureClient(TextureClient* aClient) +{ + 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); + aClient->Release(); + return; + } + + sImageBridgeChildSingleton->GetMessageLoop()->PostTask( + FROM_HERE, + NewRunnableFunction(&ReleaseTextureClientNow, aClient)); +} + +static void UpdateImageClientNow(ImageClient* aClient, ImageContainer* aContainer) +{ + MOZ_ASSERT(aClient); + MOZ_ASSERT(aContainer); + sImageBridgeChildSingleton->BeginTransaction(); + aClient->UpdateImage(aContainer, Layer::CONTENT_OPAQUE); + aClient->OnTransaction(); + sImageBridgeChildSingleton->EndTransaction(); +} + +//static +void ImageBridgeChild::DispatchImageClientUpdate(ImageClient* aClient, + ImageContainer* aContainer) +{ + if (!IsCreated()) { + return; + } + + if (InImageBridgeChildThread()) { + UpdateImageClientNow(aClient, aContainer); + return; + } + sImageBridgeChildSingleton->GetMessageLoop()->PostTask( + FROM_HERE, + NewRunnableFunction< + void (*)(ImageClient*, ImageContainer*), + ImageClient*, + nsRefPtr >(&UpdateImageClientNow, aClient, aContainer)); +} + +static void FlushAllImagesSync(ImageClient* aClient, ImageContainer* aContainer, bool aExceptFront, ReentrantMonitor* aBarrier, bool* aDone) +{ + ImageBridgeChild::FlushAllImagesNow(aClient, aContainer, aExceptFront); + + ReentrantMonitorAutoEnter autoMon(*aBarrier); + *aDone = true; + aBarrier->NotifyAll(); +} + +//static +void ImageBridgeChild::FlushAllImages(ImageClient* aClient, ImageContainer* aContainer, bool aExceptFront) +{ + if (!IsCreated()) { + return; + } + + MOZ_ASSERT(!sImageBridgeChildSingleton->mShuttingDown); + + if (InImageBridgeChildThread()) { + FlushAllImagesNow(aClient, aContainer, aExceptFront); + return; + } + + ReentrantMonitor barrier("CreateImageClient Lock"); + ReentrantMonitorAutoEnter autoMon(barrier); + bool done = false; + + sImageBridgeChildSingleton->GetMessageLoop()->PostTask( + FROM_HERE, + NewRunnableFunction(&FlushAllImagesSync, aClient, aContainer, aExceptFront, &barrier, &done)); + + // should stop the thread until the ImageClient has been created on + // the other thread + while (!done) { + barrier.Wait(); + } +} + +//static +void ImageBridgeChild::FlushAllImagesNow(ImageClient* aClient, ImageContainer* aContainer, bool aExceptFront) +{ + MOZ_ASSERT(aClient); + sImageBridgeChildSingleton->BeginTransaction(); + if (aContainer && !aExceptFront) { + aContainer->ClearCurrentImage(); + } + aClient->FlushAllImages(aExceptFront); + aClient->OnTransaction(); + sImageBridgeChildSingleton->EndTransaction(); +} + +void +ImageBridgeChild::BeginTransaction() +{ + MOZ_ASSERT(!mShuttingDown); + MOZ_ASSERT(mTxn->Finished(), "uncommitted txn?"); + mTxn->Begin(); +} + +class MOZ_STACK_CLASS AutoRemoveTextures +{ +public: + AutoRemoveTextures(ImageBridgeChild* aImageBridge) + : mImageBridge(aImageBridge) {} + + ~AutoRemoveTextures() + { + mImageBridge->RemoveTexturesIfNecessary(); + } +private: + ImageBridgeChild* mImageBridge; +}; + +void +ImageBridgeChild::EndTransaction() +{ + MOZ_ASSERT(!mShuttingDown); + MOZ_ASSERT(!mTxn->Finished(), "forgot BeginTransaction?"); + + AutoEndTransaction _(mTxn); + AutoRemoveTextures 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()); + } + 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) { + const EditReply& reply = replies[i]; + switch (reply.type()) { + case EditReply::TOpTextureSwap: { + const OpTextureSwap& ots = reply.get_OpTextureSwap(); + + CompositableClient* compositable = + CompositableClient::FromIPDLActor(ots.compositableChild()); + + MOZ_ASSERT(compositable); + + compositable->SetDescriptorFromReply(ots.textureId(), ots.image()); + break; + } + case EditReply::TReturnReleaseFence: { + const ReturnReleaseFence& rep = reply.get_ReturnReleaseFence(); + FenceHandle fence = rep.fence(); + PTextureChild* child = rep.textureChild(); + + if (!fence.IsValid() || !child) { + break; + } + RefPtr texture = TextureClient::AsTextureClient(child); + if (texture) { + texture->SetReleaseFenceHandle(fence); + } + break; + } + default: + NS_RUNTIMEABORT("not reached"); + } + } +} + + +PImageBridgeChild* +ImageBridgeChild::StartUpInChildProcess(Transport* aTransport, + ProcessId aOtherProcess) +{ + NS_ASSERTION(NS_IsMainThread(), "Should be on the main Thread!"); + + ProcessHandle processHandle; + if (!base::OpenProcessHandle(aOtherProcess, &processHandle)) { + return nullptr; + } + + sImageBridgeChildThread = new Thread("ImageBridgeChild"); + if (!sImageBridgeChildThread->Start()) { + return nullptr; + } + + sImageBridgeChildSingleton = new ImageBridgeChild(); + sImageBridgeChildSingleton->GetMessageLoop()->PostTask( + FROM_HERE, + NewRunnableFunction(ConnectImageBridgeInChildProcess, + aTransport, processHandle)); + + return sImageBridgeChildSingleton; +} + +void ImageBridgeChild::ShutDown() +{ + MOZ_ASSERT(NS_IsMainThread(), "Should be on the main Thread!"); + 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(); + } + } + + delete sImageBridgeChildThread; + sImageBridgeChildThread = nullptr; + } +} + +bool ImageBridgeChild::StartUpOnThread(Thread* aThread) +{ + NS_ABORT_IF_FALSE(aThread, "ImageBridge needs a thread."); + if (sImageBridgeChildSingleton == nullptr) { + sImageBridgeChildThread = aThread; + if (!aThread->IsRunning()) { + aThread->Start(); + } + sImageBridgeChildSingleton = new ImageBridgeChild(); + sImageBridgeParentSingleton = new ImageBridgeParent( + CompositorParent::CompositorLoop(), nullptr); + sImageBridgeChildSingleton->ConnectAsync(sImageBridgeParentSingleton); + return true; + } else { + return false; + } +} + +bool InImageBridgeChildThread() +{ + return 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); + } +} + +TemporaryRef +ImageBridgeChild::CreateImageClient(CompositableType aType) +{ + if (InImageBridgeChildThread()) { + return CreateImageClientNow(aType); + } + ReentrantMonitor barrier("CreateImageClient Lock"); + ReentrantMonitorAutoEnter autoMon(barrier); + bool done = false; + + RefPtr result = nullptr; + GetMessageLoop()->PostTask(FROM_HERE, NewRunnableFunction(&CreateImageClientSync, + &result, &barrier, aType, &done)); + // should stop the thread until the ImageClient has been created on + // the other thread + while (!done) { + barrier.Wait(); + } + return result.forget(); +} + +TemporaryRef +ImageBridgeChild::CreateImageClientNow(CompositableType aType) +{ + MOZ_ASSERT(!sImageBridgeChildSingleton->mShuttingDown); + RefPtr client + = ImageClient::CreateImageClient(aType, this, 0); + MOZ_ASSERT(client, "failed to create ImageClient"); + if (client) { + client->Connect(); + } + return client.forget(); +} + +PGrallocBufferChild* +ImageBridgeChild::AllocPGrallocBufferChild(const IntSize&, const uint32_t&, const uint32_t&, + MaybeMagicGrallocBufferHandle*) +{ +#ifdef MOZ_HAVE_SURFACEDESCRIPTORGRALLOC + return GrallocBufferActor::Create(); +#else + NS_RUNTIMEABORT("No gralloc buffers for you"); + return nullptr; +#endif +} + +bool +ImageBridgeChild::DeallocPGrallocBufferChild(PGrallocBufferChild* actor) +{ +#ifdef MOZ_HAVE_SURFACEDESCRIPTORGRALLOC + delete actor; + return true; +#else + NS_RUNTIMEABORT("Um, how did we get here?"); + return false; +#endif +} + +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) +{ + 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(); + } + } +} + +PGrallocBufferChild* +ImageBridgeChild::AllocGrallocBuffer(const IntSize& aSize, + uint32_t aFormat, + uint32_t aUsage, + MaybeMagicGrallocBufferHandle* aHandle) +{ + if (InImageBridgeChildThread()) { + PGrallocBufferChild* child = nullptr; + ImageBridgeChild::AllocGrallocBufferNow(aSize, aFormat, aUsage, aHandle, &child); + return child; + } + + Monitor barrier("AllocGrallocBuffer Lock"); + MonitorAutoLock autoMon(barrier); + bool done = false; + PGrallocBufferChild* child = nullptr; + + GetMessageLoop()->PostTask( + FROM_HERE, + NewRunnableFunction(&AllocGrallocBufferSync, + GrallocParam(aSize, aFormat, aUsage, aHandle, &child), &barrier, &done)); + + while (!done) { + barrier.Wait(); + } + + return child; +} + +void +ImageBridgeChild::AllocGrallocBufferNow(const gfx::IntSize& aSize, + uint32_t aFormat, uint32_t aUsage, + MaybeMagicGrallocBufferHandle* aHandle, + PGrallocBufferChild** aChild) +{ +#ifdef MOZ_WIDGET_GONK + *aChild = SendPGrallocBufferConstructor(aSize, + aFormat, + aUsage, + aHandle); +#else + NS_RUNTIMEABORT("not implemented"); + aChild = nullptr; +#endif +} + +static void ProxyDeallocGrallocBufferNow(ISurfaceAllocator* aAllocator, + PGrallocBufferChild* aChild, + ReentrantMonitor* aBarrier, + bool* aDone) +{ + MOZ_ASSERT(aChild); + MOZ_ASSERT(aDone); + MOZ_ASSERT(aBarrier); + +#ifdef MOZ_WIDGET_GONK + PGrallocBufferChild::Send__delete__(aChild); +#else + NS_RUNTIMEABORT("not implemented"); +#endif + + ReentrantMonitorAutoEnter autoMon(*aBarrier); + *aDone = true; + aBarrier->NotifyAll(); +} + +void +ImageBridgeChild::DeallocGrallocBuffer(PGrallocBufferChild* aChild) +{ + MOZ_ASSERT(aChild); + if (InImageBridgeChildThread()) { +#ifdef MOZ_WIDGET_GONK + PGrallocBufferChild::Send__delete__(aChild); +#else + NS_RUNTIMEABORT("not implemented"); +#endif + } else { + ReentrantMonitor barrier("AllocatorProxy Dealloc"); + ReentrantMonitorAutoEnter autoMon(barrier); + + bool done = false; + GetMessageLoop()->PostTask(FROM_HERE, + NewRunnableFunction(&ProxyDeallocGrallocBufferNow, + this, + aChild, + &barrier, + &done)); + while (!done) { + barrier.Wait(); + } + } +} + +PTextureChild* +ImageBridgeChild::AllocPTextureChild(const SurfaceDescriptor&, + const TextureFlags&) +{ + MOZ_ASSERT(!mShuttingDown); + return TextureClient::CreateIPDLActor(); +} + +bool +ImageBridgeChild::DeallocPTextureChild(PTextureChild* actor) +{ + return TextureClient::DestroyIPDLActor(actor); +} + +PTextureChild* +ImageBridgeChild::CreateTexture(const SurfaceDescriptor& aSharedData, + TextureFlags aFlags) +{ + MOZ_ASSERT(!mShuttingDown); + return SendPTextureConstructor(aSharedData, aFlags); +} + +void +ImageBridgeChild::RemoveTextureFromCompositable(CompositableClient* aCompositable, + TextureClient* aTexture) +{ + MOZ_ASSERT(!mShuttingDown); + if (aTexture->GetFlags() & TEXTURE_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); +} + +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 OtherProcess() == ipc::kInvalidProcessHandle; +} + +} // layers +} // mozilla