/* -*- 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 "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/CompositableChild.h" #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/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 "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/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; typedef nsTArray OpDestroyVector; namespace { class ImageBridgeThread : public Thread { public: ImageBridgeThread() : Thread("ImageBridgeChild") { } protected: void Init() { #ifdef MOZ_ENABLE_PROFILER_SPS mPseudoStackHack = mozilla_get_pseudo_stack(); #endif } void CleanUp() { #ifdef MOZ_ENABLE_PROFILER_SPS mPseudoStackHack = nullptr; #endif } private: #ifdef MOZ_ENABLE_PROFILER_SPS // This is needed to avoid a spurious leak report. There's no other // use for it. See bug 1239504 and bug 1215265. MOZ_INIT_OUTSIDE_CTOR PseudoStack* mPseudoStackHack; #endif }; } 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(); mDestroyedActors.Clear(); } bool IsEmpty() const { return mOperations.empty() && mDestroyedActors.IsEmpty(); } void AddNoSwapEdit(const CompositableOperation& op) { MOZ_ASSERT(!Finished(), "forgot BeginTransaction?"); mOperations.push_back(op); } void AddEdit(const CompositableOperation& op) { AddNoSwapEdit(op); MarkSyncTransaction(); } void MarkSyncTransaction() { mSwapRequired = true; } void FallbackDestroyActors() { for (auto& actor : mDestroyedActors) { switch (actor.type()) { case OpDestroy::TPTextureChild: { DebugOnly ok = TextureClient::DestroyFallback(actor.get_PTextureChild()); MOZ_ASSERT(ok); break; } case OpDestroy::TPCompositableChild: { DebugOnly ok = actor.get_PCompositableChild()->SendDestroySync(); MOZ_ASSERT(ok); break; } default: MOZ_CRASH("GFX: IBC Fallback destroy actors"); } } mDestroyedActors.Clear(); } OpVector mOperations; OpDestroyVector mDestroyedActors; bool mSwapRequired; bool mFinished; }; struct AutoEndTransaction { explicit AutoEndTransaction(CompositableTransaction* aTxn) : mTxn(aTxn) {} ~AutoEndTransaction() { mTxn->End(); } CompositableTransaction* mTxn; }; /* static */ Atomic ImageBridgeChild::sIsShutDown(false); void ImageBridgeChild::UseTextures(CompositableClient* aCompositable, const nsTArray& aTextures) { MOZ_ASSERT(aCompositable); MOZ_ASSERT(aCompositable->GetIPDLActor()); MOZ_ASSERT(aCompositable->IsConnected()); AutoTArray textures; for (auto& t : aTextures) { MOZ_ASSERT(t.mTextureClient); MOZ_ASSERT(t.mTextureClient->GetIPDLActor()); if (!t.mTextureClient->IsSharedWithCompositor()) { return; } ReadLockDescriptor readLock; t.mTextureClient->SerializeReadLock(readLock); FenceHandle fence = t.mTextureClient->GetAcquireFenceHandle(); textures.AppendElement(TimedTexture(nullptr, t.mTextureClient->GetIPDLActor(), readLock, fence.IsValid() ? MaybeFence(fence) : MaybeFence(null_t()), t.mTimeStamp, t.mPictureRect, t.mFrameID, t.mProducerID)); // Wait end of usage on host side if TextureFlags::RECYCLE is set or GrallocTextureData case HoldUntilCompositableRefReleasedIfNecessary(t.mTextureClient); } mTxn->AddNoSwapEdit(CompositableOperation(nullptr, aCompositable->GetIPDLActor(), OpUseTexture(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()); ReadLockDescriptor readLockW; ReadLockDescriptor readLockB; aTextureOnBlack->SerializeReadLock(readLockB); aTextureOnWhite->SerializeReadLock(readLockW); HoldUntilCompositableRefReleasedIfNecessary(aTextureOnBlack); HoldUntilCompositableRefReleasedIfNecessary(aTextureOnWhite); mTxn->AddNoSwapEdit( CompositableOperation( nullptr, aCompositable->GetIPDLActor(), OpUseComponentAlphaTextures( nullptr, aTextureOnBlack->GetIPDLActor(), nullptr, aTextureOnWhite->GetIPDLActor(), readLockB, readLockW ) ) ); } #ifdef MOZ_WIDGET_GONK void ImageBridgeChild::UseOverlaySource(CompositableClient* aCompositable, const OverlaySource& aOverlay, const nsIntRect& aPictureRect) { MOZ_ASSERT(aCompositable); MOZ_ASSERT(aCompositable->IsConnected()); CompositableOperation op( nullptr, aCompositable->GetIPDLActor(), OpUseOverlaySource(aOverlay, aPictureRect)); mTxn->AddEdit(op); } #endif void ImageBridgeChild::HoldUntilCompositableRefReleasedIfNecessary(TextureClient* aClient) { // Wait ReleaseCompositableRef only when TextureFlags::RECYCLE is set on ImageBridge. if (!aClient || !(aClient->GetFlags() & TextureFlags::RECYCLE)) { return; } aClient->SetLastFwdTransactionId(GetFwdTransactionId()); mTexturesWaitingRecycled.Put(aClient->GetSerial(), aClient); } void ImageBridgeChild::NotifyNotUsed(uint64_t aTextureId, uint64_t aFwdTransactionId) { RefPtr client = mTexturesWaitingRecycled.Get(aTextureId); if (!client) { return; } if (aFwdTransactionId < client->GetLastFwdTransactionId()) { // Released on host side, but client already requested newer use texture. return; } mTexturesWaitingRecycled.Remove(aTextureId); } void ImageBridgeChild::DeliverFence(uint64_t aTextureId, FenceHandle& aReleaseFenceHandle) { RefPtr client = mTexturesWaitingRecycled.Get(aTextureId); if (!client) { return; } client->SetReleaseFenceHandle(aReleaseFenceHandle); } void ImageBridgeChild::HoldUntilFenceHandleDelivery(TextureClient* aClient, uint64_t aTransactionId) { MOZ_ASSERT(NS_IsMainThread()); // XXX Re-enable fence handling return; #ifdef MOZ_WIDGET_GONK if (!aClient) { return; } MutexAutoLock lock(mWaitingFenceHandleMutex); aClient->SetLastFwdTransactionId(aTransactionId); aClient->WaitFenceHandleOnImageBridge(mWaitingFenceHandleMutex); mTexturesWaitingFenceHandle.Put(aClient->GetSerial(), aClient); #else NS_RUNTIMEABORT("not reached"); #endif } void ImageBridgeChild::DeliverFenceToNonRecycle(uint64_t aTextureId, FenceHandle& aReleaseFenceHandle) { // XXX Re-enable fence handling return; #ifdef MOZ_WIDGET_GONK MutexAutoLock lock(mWaitingFenceHandleMutex); TextureClient* client = mTexturesWaitingFenceHandle.Get(aTextureId).get(); if (!client) { return; } MOZ_ASSERT(aTextureId == client->GetSerial()); client->SetReleaseFenceHandle(aReleaseFenceHandle); #else NS_RUNTIMEABORT("not reached"); #endif } void ImageBridgeChild::NotifyNotUsedToNonRecycle(uint64_t aTextureId, uint64_t aTransactionId) { // XXX Re-enable fence handling return; #ifdef MOZ_WIDGET_GONK MutexAutoLock lock(mWaitingFenceHandleMutex); RefPtr client = mTexturesWaitingFenceHandle.Get(aTextureId); if (!client) { return; } if (aTransactionId < client->GetLastFwdTransactionId()) { return; } MOZ_ASSERT(aTextureId == client->GetSerial()); client->ClearWaitFenceHandleOnImageBridge(mWaitingFenceHandleMutex); mTexturesWaitingFenceHandle.Remove(aTextureId); // Release TextureClient on allocator's message loop. RefPtr task = MakeAndAddRef(client); RefPtr allocator = client->GetAllocator(); client = nullptr; allocator->AsClientAllocator()->GetMessageLoop()->PostTask(task.forget()); #else NS_RUNTIMEABORT("not reached"); #endif } void ImageBridgeChild::CancelWaitFenceHandle(TextureClient* aClient) { // XXX Re-enable fence handling return; #ifdef MOZ_WIDGET_GONK MutexAutoLock lock(mWaitingFenceHandleMutex); aClient->ClearWaitFenceHandleOnImageBridge(mWaitingFenceHandleMutex); mTexturesWaitingFenceHandle.Remove(aClient->GetSerial()); #else NS_RUNTIMEABORT("not reached"); #endif } void ImageBridgeChild::CancelWaitForRecycle(uint64_t aTextureId) { MOZ_ASSERT(InImageBridgeChildThread()); RefPtr client = mTexturesWaitingRecycled.Get(aTextureId); if (!client) { return; } mTexturesWaitingRecycled.Remove(aTextureId); } // Singleton static StaticRefPtr sImageBridgeChildSingleton; static Thread *sImageBridgeChildThread = nullptr; void ImageBridgeChild::FallbackDestroyActors() { if (mTxn && !mTxn->mDestroyedActors.IsEmpty()) { mTxn->FallbackDestroyActors(); } } // 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) { auto compositable = CompositableClient::FromIPDLActor(compositables[i]); if (compositable) { compositable->Destroy(); } } InfallibleTArray textures; sImageBridgeChildSingleton->ManagedPTextureChild(textures); for (int i = textures.Length() - 1; i >= 0; --i) { RefPtr client = TextureClient::AsTextureClient(textures[i]); if (client) { client->Destroy(); } } sImageBridgeChildSingleton->FallbackDestroyActors(); sImageBridgeChildSingleton->SendWillClose(); 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->Close(); *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) , mFwdTransactionId(0) #ifdef MOZ_WIDGET_GONK , mWaitingFenceHandleMutex("ImageBridgeChild::mWaitingFenceHandleMutex") #endif { MOZ_ASSERT(NS_IsMainThread()); mTxn = new CompositableTransaction(); } ImageBridgeChild::~ImageBridgeChild() { delete mTxn; } void ImageBridgeChild::MarkShutDown() { MOZ_ASSERT(!mShuttingDown); mTexturesWaitingRecycled.Clear(); mTrackersHolder.DestroyAsyncTransactionTrackersHolder(); mShuttingDown = true; } void ImageBridgeChild::Connect(CompositableClient* aCompositable, ImageContainer* aImageContainer) { MOZ_ASSERT(aCompositable); MOZ_ASSERT(!mShuttingDown); MOZ_ASSERT(InImageBridgeChildThread()); 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 AsyncCompositableChild::CreateActor(); } bool ImageBridgeChild::DeallocPCompositableChild(PCompositableChild* aActor) { AsyncCompositableChild::DestroyActor(aActor); return true; } Thread* ImageBridgeChild::GetThread() const { return sImageBridgeChildThread; } ImageBridgeChild* ImageBridgeChild::GetSingleton() { return sImageBridgeChildSingleton; } bool ImageBridgeChild::IsCreated() { return GetSingleton() != nullptr; } static void ReleaseImageContainerNow(PImageContainerChild* aChild) { MOZ_ASSERT(InImageBridgeChildThread()); if (aChild) { ImageContainer::AsyncDestroyActor(aChild); } } // static void ImageBridgeChild::DispatchReleaseImageContainer(PImageContainerChild* aChild) { if (!aChild) { return; } if (!IsCreated()) { delete aChild; return; } sImageBridgeChildSingleton->GetMessageLoop()->PostTask( NewRunnableFunction(&ReleaseImageContainerNow, aChild)); } 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( NewRunnableFunction(&ReleaseTextureClientNow, aClient)); } static void UpdateImageClientNow(RefPtr aClient, RefPtr&& aContainer) { if (!ImageBridgeChild::IsCreated() || ImageBridgeChild::IsShutDown()) { NS_WARNING("Something is holding on to graphics resources after the shutdown" "of the graphics subsystem!"); return; } MOZ_ASSERT(aClient); MOZ_ASSERT(aContainer); // If the client has become disconnected before this event was dispatched, // early return now. if (!aClient->IsConnected()) { return; } sImageBridgeChildSingleton->BeginTransaction(); aClient->UpdateImage(aContainer, Layer::CONTENT_OPAQUE); sImageBridgeChildSingleton->EndTransaction(); } // static void ImageBridgeChild::DispatchImageClientUpdate(ImageClient* aClient, ImageContainer* aContainer) { if (!ImageBridgeChild::IsCreated() || ImageBridgeChild::IsShutDown()) { 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( NewRunnableFunction(&UpdateImageClientNow, RefPtr(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( 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, ReentrantMonitor* aBarrier, bool* const outDone) { #ifdef MOZ_WIDGET_GONK MOZ_ASSERT(aWaiter); #else MOZ_ASSERT(!aWaiter); #endif ReentrantMonitorAutoEnter autoMon(*aBarrier); if (!ImageBridgeChild::IsCreated() || ImageBridgeChild::IsShutDown()) { // 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!"); #ifdef MOZ_WIDGET_GONK aWaiter->DecrementWaitCount(); #endif *outDone = true; aBarrier->NotifyAll(); 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. #ifdef MOZ_WIDGET_GONK aWaiter->DecrementWaitCount(); #endif *outDone = true; aBarrier->NotifyAll(); } // static void ImageBridgeChild::FlushAllImages(ImageClient* aClient, ImageContainer* aContainer) { if (!IsCreated() || IsShutDown()) { return; } MOZ_ASSERT(aClient); MOZ_ASSERT(!sImageBridgeChildSingleton->mShuttingDown); MOZ_ASSERT(!InImageBridgeChildThread()); if (InImageBridgeChildThread()) { NS_ERROR("ImageBridgeChild::FlushAllImages() is called on ImageBridge thread."); return; } ReentrantMonitor barrier("FlushAllImages Lock"); ReentrantMonitorAutoEnter autoMon(barrier); bool done = false; RefPtr waiter; #ifdef MOZ_WIDGET_GONK waiter = new AsyncTransactionWaiter(); // This increment is balanced by the decrement in FlushAllImagesSync waiter->IncrementWaitCount(); #endif sImageBridgeChildSingleton->GetMessageLoop()->PostTask( NewRunnableFunction(&FlushAllImagesSync, aClient, aContainer, waiter, &barrier, &done)); while (!done) { barrier.Wait(); } #ifdef MOZ_WIDGET_GONK waiter->WaitComplete(); #endif } void ImageBridgeChild::BeginTransaction() { MOZ_ASSERT(!mShuttingDown); MOZ_ASSERT(mTxn->Finished(), "uncommitted txn?"); UpdateFwdTransactionId(); mTxn->Begin(); } void ImageBridgeChild::EndTransaction() { MOZ_ASSERT(!mShuttingDown); 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(); } AutoTArray replies; if (mTxn->mSwapRequired) { if (!SendUpdate(cset, mTxn->mDestroyedActors, GetFwdTransactionId(), &replies)) { NS_WARNING("could not send async texture transaction"); mTxn->FallbackDestroyActors(); return; } } else { // If we don't require a swap we can call SendUpdateNoSwap which // assumes that aReplies is empty (DEBUG assertion) if (!SendUpdateNoSwap(cset, mTxn->mDestroyedActors, GetFwdTransactionId())) { NS_WARNING("could not send async texture transaction (no swap)"); mTxn->FallbackDestroyActors(); return; } } for (nsTArray::size_type i = 0; i < replies.Length(); ++i) { NS_RUNTIMEABORT("not reached"); } } void ImageBridgeChild::SendImageBridgeThreadId() { #ifdef MOZ_WIDGET_GONK SendImageBridgeThreadId(gettid()); #endif } static void CallSendImageBridgeThreadId(ImageBridgeChild* aImageBridgeChild) { MOZ_ASSERT(InImageBridgeChildThread()); aImageBridgeChild->SendImageBridgeThreadId(); } bool ImageBridgeChild::InitForContent(Endpoint&& aEndpoint) { MOZ_ASSERT(NS_IsMainThread()); gfxPlatform::GetPlatform(); sImageBridgeChildThread = new ImageBridgeThread(); if (!sImageBridgeChildThread->Start()) { return false; } sImageBridgeChildSingleton = new ImageBridgeChild(); MessageLoop* loop = sImageBridgeChildSingleton->GetMessageLoop(); loop->PostTask(NewRunnableMethod&&>( sImageBridgeChildSingleton, &ImageBridgeChild::Bind, Move(aEndpoint))); loop->PostTask(NewRunnableFunction( CallSendImageBridgeThreadId, sImageBridgeChildSingleton.get())); return sImageBridgeChildSingleton; } void ImageBridgeChild::Bind(Endpoint&& aEndpoint) { aEndpoint.Bind(this); } void ImageBridgeChild::ShutDown() { MOZ_ASSERT(NS_IsMainThread()); sIsShutDown = true; if (ImageBridgeChild::IsCreated()) { MOZ_ASSERT(!sImageBridgeChildSingleton->mShuttingDown); { ReentrantMonitor barrier("ImageBridge ShutdownStep1 lock"); ReentrantMonitorAutoEnter autoMon(barrier); bool done = false; sImageBridgeChildSingleton->GetMessageLoop()->PostTask( NewRunnableFunction(&ImageBridgeShutdownStep1, &barrier, &done)); while (!done) { barrier.Wait(); } } { ReentrantMonitor barrier("ImageBridge ShutdownStep2 lock"); ReentrantMonitorAutoEnter autoMon(barrier); bool done = false; sImageBridgeChildSingleton->GetMessageLoop()->PostTask( NewRunnableFunction(&ImageBridgeShutdownStep2, &barrier, &done)); while (!done) { barrier.Wait(); } } sImageBridgeChildSingleton = nullptr; delete sImageBridgeChildThread; sImageBridgeChildThread = nullptr; } } void ImageBridgeChild::InitSameProcess() { NS_ASSERTION(NS_IsMainThread(), "Should be on the main Thread!"); MOZ_ASSERT(!sImageBridgeChildSingleton); MOZ_ASSERT(!sImageBridgeChildThread); sImageBridgeChildThread = new ImageBridgeThread(); if (!sImageBridgeChildThread->IsRunning()) { sImageBridgeChildThread->Start(); } sImageBridgeChildSingleton = new ImageBridgeChild(); RefPtr parent = ImageBridgeParent::CreateSameProcess(); sImageBridgeChildSingleton->ConnectAsync(parent); sImageBridgeChildSingleton->GetMessageLoop()->PostTask( NewRunnableFunction(CallSendImageBridgeThreadId, sImageBridgeChildSingleton.get())); } /* static */ void ImageBridgeChild::InitWithGPUProcess(Endpoint&& aEndpoint) { MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(!sImageBridgeChildSingleton); MOZ_ASSERT(!sImageBridgeChildThread); sImageBridgeChildThread = new ImageBridgeThread(); if (!sImageBridgeChildThread->IsRunning()) { sImageBridgeChildThread->Start(); } sImageBridgeChildSingleton = new ImageBridgeChild(); MessageLoop* loop = sImageBridgeChildSingleton->GetMessageLoop(); loop->PostTask(NewRunnableMethod&&>( sImageBridgeChildSingleton, &ImageBridgeChild::Bind, Move(aEndpoint))); } bool InImageBridgeChildThread() { return ImageBridgeChild::IsCreated() && sImageBridgeChildThread->thread_id() == PlatformThread::CurrentId(); } MessageLoop * ImageBridgeChild::GetMessageLoop() const { return sImageBridgeChildThread ? sImageBridgeChildThread->message_loop() : nullptr; } void ImageBridgeChild::ConnectAsync(ImageBridgeParent* aParent) { GetMessageLoop()->PostTask(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( 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(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); auto shmAllocator = aParams->mAllocator->AsShmemAllocator(); if (aParams->mUnsafe) { aParams->mSuccess = shmAllocator->AllocUnsafeShmem(aParams->mSize, aParams->mType, aParams->mShmem); } else { aParams->mSuccess = shmAllocator->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(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->AsShmemAllocator()->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(NewRunnableFunction(&ProxyDeallocShmemNow, this, &aShmem, &barrier, &done)); while (!done) { barrier.Wait(); } } } PTextureChild* ImageBridgeChild::AllocPTextureChild(const SurfaceDescriptor&, const LayersBackend&, const TextureFlags&, const uint64_t& aSerial) { 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) { ImageContainer::DeallocActor(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(); DeliverFence(op.TextureId(), fence); break; } case AsyncParentMessageData::TOpDeliverFenceToNonRecycle: { // Notify ReleaseCompositableRef to a TextureClient that belongs to // LayerTransactionChild. It is used only on gonk to deliver fence to // a TextureClient that does not have TextureFlags::RECYCLE. // In this case, LayerTransactionChild's ipc could not be used to deliver fence. const OpDeliverFenceToNonRecycle& op = message.get_OpDeliverFenceToNonRecycle(); FenceHandle fence = op.fence(); DeliverFenceToNonRecycle(op.TextureId(), fence); break; } case AsyncParentMessageData::TOpNotifyNotUsed: { const OpNotifyNotUsed& op = message.get_OpNotifyNotUsed(); NotifyNotUsed(op.TextureId(), op.fwdTransactionId()); break; } case AsyncParentMessageData::TOpNotifyNotUsedToNonRecycle: { // Notify ReleaseCompositableRef to a TextureClient that belongs to // LayerTransactionChild. It is used only on gonk to deliver fence to // a TextureClient that does not have TextureFlags::RECYCLE. // In this case, LayerTransactionChild's ipc could not be used to deliver fence. const OpNotifyNotUsedToNonRecycle& op = message.get_OpNotifyNotUsedToNonRecycle(); NotifyNotUsedToNonRecycle(op.TextureId(), op.fwdTransactionId()); break; } case AsyncParentMessageData::TOpReplyRemoveTexture: { const OpReplyRemoveTexture& op = message.get_OpReplyRemoveTexture(); MOZ_ASSERT(mTrackersHolder.GetId() == op.holderId()); mTrackersHolder.TransactionCompleteted(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, uint64_t aSerial) { MOZ_ASSERT(!mShuttingDown); return SendPTextureConstructor(aSharedData, aLayersBackend, aFlags, aSerial); } static bool IBCAddOpDestroy(CompositableTransaction* aTxn, const OpDestroy& op, bool synchronously) { if (aTxn->Finished()) { return false; } aTxn->mDestroyedActors.AppendElement(op); if (synchronously) { aTxn->MarkSyncTransaction(); } return true; } bool ImageBridgeChild::DestroyInTransaction(PTextureChild* aTexture, bool synchronously) { return IBCAddOpDestroy(mTxn, OpDestroy(aTexture), synchronously); } bool ImageBridgeChild::DestroyInTransaction(PCompositableChild* aCompositable, bool synchronously) { return IBCAddOpDestroy(mTxn, OpDestroy(aCompositable), synchronously); } 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; } CompositableOperation op( nullptr, aCompositable->GetIPDLActor(), OpRemoveTexture(nullptr, aTexture->GetIPDLActor())); if (aTexture->GetFlags() & TextureFlags::DEALLOCATE_CLIENT) { mTxn->AddEdit(op); } else { mTxn->AddNoSwapEdit(op); } } 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; } CompositableOperation op( nullptr, aCompositable->GetIPDLActor(), OpRemoveTextureAsync( mTrackersHolder.GetId(), aAsyncTransactionTracker->GetId(), nullptr, aCompositable->GetIPDLActor(), nullptr, aTexture->GetIPDLActor())); mTxn->AddNoSwapEdit(op); // Hold AsyncTransactionTracker until receving reply mTrackersHolder.HoldUntilComplete(aAsyncTransactionTracker); } bool ImageBridgeChild::IsSameProcess() const { return OtherPid() == base::GetCurrentProcId(); } static void DestroyCompositableNow(RefPtr aImageBridge, RefPtr aCompositable) { aImageBridge->Destroy(aCompositable); } void ImageBridgeChild::Destroy(CompositableChild* aCompositable) { if (!InImageBridgeChildThread()) { RefPtr self = this; RefPtr compositable = aCompositable; GetMessageLoop()->PostTask( NewRunnableFunction(&DestroyCompositableNow, self, compositable)); return; } CompositableForwarder::Destroy(aCompositable); } } // namespace layers } // namespace mozilla