/* -*- 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/AsyncChannel.h" // for AsyncChannel, etc #include "mozilla/ipc/Transport.h" // for Transport #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/LayerTransaction.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 struct nsIntRect; using namespace base; using namespace mozilla::ipc; 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::AddTexture(CompositableClient* aCompositable, TextureClient* aTexture) { SurfaceDescriptor descriptor; if (!aTexture->ToSurfaceDescriptor(descriptor)) { NS_WARNING("ImageBridge: Failed to serialize a TextureClient"); return; } mTxn->AddEdit(OpAddTexture(nullptr, aCompositable->GetIPDLActor(), aTexture->GetID(), descriptor, aTexture->GetFlags())); } void ImageBridgeChild::RemoveTexture(CompositableClient* aCompositable, uint64_t aTexture, TextureFlags aFlags) { mTxn->AddNoSwapEdit(OpRemoveTexture(nullptr, aCompositable->GetIPDLActor(), aTexture, aFlags)); } void ImageBridgeChild::UseTexture(CompositableClient* aCompositable, TextureClient* aTexture) { mTxn->AddNoSwapEdit(OpUseTexture(nullptr, aCompositable->GetIPDLActor(), aTexture->GetID())); } void ImageBridgeChild::UpdatedTexture(CompositableClient* aCompositable, TextureClient* aTexture, nsIntRegion* aRegion) { MaybeRegion region = aRegion ? MaybeRegion(*aRegion) : MaybeRegion(null_t()); mTxn->AddNoSwapEdit(OpUpdateTexture(nullptr, aCompositable->GetIPDLActor(), aTexture->GetID(), region)); } void ImageBridgeChild::UpdateTexture(CompositableClient* aCompositable, TextureIdentifier aTextureId, SurfaceDescriptor* aDescriptor) { if (aDescriptor->type() != SurfaceDescriptor::T__None && aDescriptor->type() != SurfaceDescriptor::Tnull_t) { MOZ_ASSERT(aCompositable); MOZ_ASSERT(aCompositable->GetIPDLActor()); mTxn->AddEdit(OpPaintTexture(nullptr, aCompositable->GetIPDLActor(), 1, SurfaceDescriptor(*aDescriptor))); *aDescriptor = SurfaceDescriptor(); } else { NS_WARNING("Trying to send a null SurfaceDescriptor."); } } void ImageBridgeChild::UpdateTextureNoSwap(CompositableClient* aCompositable, TextureIdentifier aTextureId, SurfaceDescriptor* aDescriptor) { if (aDescriptor->type() != SurfaceDescriptor::T__None && aDescriptor->type() != SurfaceDescriptor::Tnull_t) { MOZ_ASSERT(aCompositable); MOZ_ASSERT(aCompositable->GetIPDLActor()); mTxn->AddNoSwapEdit(OpPaintTexture(nullptr, aCompositable->GetIPDLActor(), 1, SurfaceDescriptor(*aDescriptor))); *aDescriptor = SurfaceDescriptor(); } else { NS_WARNING("Trying to send a null SurfaceDescriptor."); } } void ImageBridgeChild::UpdatePictureRect(CompositableClient* aCompositable, const nsIntRect& aRect) { mTxn->AddNoSwapEdit(OpUpdatePictureRect(nullptr, aCompositable->GetIPDLActor(), aRect)); } // Singleton static ImageBridgeChild *sImageBridgeChildSingleton = nullptr; static Thread *sImageBridgeChildThread = nullptr; // dispatched function static void StopImageBridgeSync(ReentrantMonitor *aBarrier, bool *aDone) { ReentrantMonitorAutoEnter autoMon(*aBarrier); NS_ABORT_IF_FALSE(InImageBridgeChildThread(), "Should be in ImageBridgeChild thread."); if (sImageBridgeChildSingleton) { sImageBridgeChildSingleton->SendStop(); } *aDone = true; aBarrier->NotifyAll(); } // dispatched function static void DeleteImageBridgeSync(ReentrantMonitor *aBarrier, bool *aDone) { ReentrantMonitorAutoEnter autoMon(*aBarrier); NS_ABORT_IF_FALSE(InImageBridgeChildThread(), "Should be in ImageBridgeChild thread."); delete sImageBridgeChildSingleton; 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 { gfxIntSize size; uint32_t format; uint32_t usage; SurfaceDescriptor* buffer; GrallocParam(const gfxIntSize& aSize, const uint32_t& aFormat, const uint32_t& aUsage, SurfaceDescriptor* aBuffer) : size(aSize) , format(aFormat) , usage(aUsage) , buffer(aBuffer) {} }; // dispatched function static void AllocSurfaceDescriptorGrallocSync(const GrallocParam& aParam, Monitor* aBarrier, bool* aDone) { MonitorAutoLock autoMon(*aBarrier); sImageBridgeChildSingleton->AllocSurfaceDescriptorGrallocNow(aParam.size, aParam.format, aParam.usage, aParam.buffer); *aDone = true; aBarrier->NotifyAll(); } // dispatched function static void DeallocSurfaceDescriptorGrallocSync(const SurfaceDescriptor& aBuffer, Monitor* aBarrier, bool* aDone) { MonitorAutoLock autoMon(*aBarrier); sImageBridgeChildSingleton->DeallocSurfaceDescriptorGrallocNow(aBuffer); *aDone = true; aBarrier->NotifyAll(); } // dispatched function static void ConnectImageBridge(ImageBridgeChild * child, ImageBridgeParent * parent) { MessageLoop *parentMsgLoop = parent->GetMessageLoop(); ipc::AsyncChannel *parentChannel = parent->GetIPCChannel(); child->Open(parentChannel, parentMsgLoop, mozilla::ipc::AsyncChannel::Child); } ImageBridgeChild::ImageBridgeChild() { mTxn = new CompositableTransaction(); } ImageBridgeChild::~ImageBridgeChild() { delete mTxn; } void ImageBridgeChild::Connect(CompositableClient* aCompositable) { MOZ_ASSERT(aCompositable); uint64_t id = 0; CompositableChild* child = static_cast( SendPCompositableConstructor(aCompositable->GetTextureInfo(), &id)); MOZ_ASSERT(child); child->SetAsyncID(id); aCompositable->SetIPDLActor(child); MOZ_ASSERT(child->GetAsyncID() == id); child->SetClient(aCompositable); } PCompositableChild* ImageBridgeChild::AllocPCompositableChild(const TextureInfo& aInfo, uint64_t* aID) { return new CompositableChild(); } bool ImageBridgeChild::DeallocPCompositableChild(PCompositableChild* aActor) { delete aActor; return true; } 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")); } static void ConnectImageBridgeInChildProcess(Transport* aTransport, ProcessHandle aOtherProcess) { // Bind the IPC channel to the image bridge thread. sImageBridgeChildSingleton->Open(aTransport, aOtherProcess, XRE_GetIOMessageLoop(), AsyncChannel::Child); } static void ReleaseImageClientNow(ImageClient* aClient) { MOZ_ASSERT(InImageBridgeChildThread()); aClient->Release(); } // static void ImageBridgeChild::DispatchReleaseImageClient(ImageClient* aClient) { sImageBridgeChildSingleton->GetMessageLoop()->PostTask( FROM_HERE, NewRunnableFunction(&ReleaseImageClientNow, 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 (InImageBridgeChildThread()) { UpdateImageClientNow(aClient, aContainer); return; } sImageBridgeChildSingleton->GetMessageLoop()->PostTask( FROM_HERE, NewRunnableFunction< void (*)(ImageClient*, ImageContainer*), ImageClient*, nsRefPtr >(&UpdateImageClientNow, aClient, aContainer)); } void ImageBridgeChild::BeginTransaction() { MOZ_ASSERT(mTxn->Finished(), "uncommitted txn?"); mTxn->Begin(); } void ImageBridgeChild::EndTransaction() { MOZ_ASSERT(!mTxn->Finished(), "forgot BeginTransaction?"); AutoEndTransaction _(mTxn); 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(); CompositableChild* compositableChild = static_cast(ots.compositableChild()); MOZ_ASSERT(compositableChild); compositableChild->GetCompositableClient() ->SetDescriptorFromReply(ots.textureId(), ots.image()); 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() { NS_ASSERTION(NS_IsMainThread(), "Should be on the main Thread!"); if (ImageBridgeChild::IsCreated()) { ImageBridgeChild::DestroyBridge(); 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(); ImageBridgeParent* imageBridgeParent = new ImageBridgeParent( CompositorParent::CompositorLoop(), nullptr); sImageBridgeChildSingleton->ConnectAsync(imageBridgeParent); return true; } else { return false; } } void ImageBridgeChild::DestroyBridge() { NS_ABORT_IF_FALSE(!InImageBridgeChildThread(), "This method must not be called in this thread."); // ...because we are about to dispatch synchronous messages to the // ImageBridgeChild thread. if (!IsCreated()) { return; } ReentrantMonitor barrier("ImageBridgeDestroyTask lock"); ReentrantMonitorAutoEnter autoMon(barrier); bool done = false; sImageBridgeChildSingleton->GetMessageLoop()->PostTask(FROM_HERE, NewRunnableFunction(&StopImageBridgeSync, &barrier, &done)); while (!done) { barrier.Wait(); } done = false; sImageBridgeChildSingleton->GetMessageLoop()->PostTask(FROM_HERE, NewRunnableFunction(&DeleteImageBridgeSync, &barrier, &done)); while (!done) { barrier.Wait(); } } 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) { 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 gfxIntSize&, 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::AllocSurfaceDescriptorGralloc(const gfxIntSize& aSize, const uint32_t& aFormat, const uint32_t& aUsage, SurfaceDescriptor* aBuffer) { if (InImageBridgeChildThread()) { return ImageBridgeChild::AllocSurfaceDescriptorGrallocNow(aSize, aFormat, aUsage, aBuffer); } Monitor barrier("AllocSurfaceDescriptorGralloc Lock"); MonitorAutoLock autoMon(barrier); bool done = false; GetMessageLoop()->PostTask( FROM_HERE, NewRunnableFunction(&AllocSurfaceDescriptorGrallocSync, GrallocParam(aSize, aFormat, aUsage, aBuffer), &barrier, &done)); while (!done) { barrier.Wait(); } return true; } bool ImageBridgeChild::AllocSurfaceDescriptorGrallocNow(const gfxIntSize& aSize, const uint32_t& aFormat, const uint32_t& aUsage, SurfaceDescriptor* aBuffer) { #ifdef MOZ_HAVE_SURFACEDESCRIPTORGRALLOC MaybeMagicGrallocBufferHandle handle; PGrallocBufferChild* gc = SendPGrallocBufferConstructor(aSize, aFormat, aUsage, &handle); if (handle.Tnull_t == handle.type()) { PGrallocBufferChild::Send__delete__(gc); return false; } GrallocBufferActor* gba = static_cast(gc); gba->InitFromHandle(handle.get_MagicGrallocBufferHandle()); *aBuffer = SurfaceDescriptorGralloc(nullptr, gc, aSize, /* external */ false, /* swapRB */ false); return true; #else NS_RUNTIMEABORT("No gralloc buffers for you"); return false; #endif } bool ImageBridgeChild::DeallocSurfaceDescriptorGralloc(const SurfaceDescriptor& aBuffer) { if (InImageBridgeChildThread()) { return ImageBridgeChild::DeallocSurfaceDescriptorGrallocNow(aBuffer); } Monitor barrier("DeallocSurfaceDescriptor Lock"); MonitorAutoLock autoMon(barrier); bool done = false; GetMessageLoop()->PostTask(FROM_HERE, NewRunnableFunction(&DeallocSurfaceDescriptorGrallocSync, aBuffer, &barrier, &done)); while (!done) { barrier.Wait(); } return true; } bool ImageBridgeChild::DeallocSurfaceDescriptorGrallocNow(const SurfaceDescriptor& aBuffer) { #ifdef MOZ_HAVE_SURFACEDESCRIPTORGRALLOC PGrallocBufferChild* gbp = aBuffer.get_SurfaceDescriptorGralloc().bufferChild(); PGrallocBufferChild::Send__delete__(gbp); 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) { 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 { ISurfaceAllocator* 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 gfxIntSize& aSize, uint32_t aFormat, uint32_t aUsage, MaybeMagicGrallocBufferHandle* aHandle) { #ifdef MOZ_WIDGET_GONK return SendPGrallocBufferConstructor(aSize, aFormat, aUsage, aHandle); #else NS_RUNTIMEABORT("not implemented"); return nullptr; #endif } } // layers } // mozilla