зеркало из https://github.com/mozilla/gecko-dev.git
1169 строки
31 KiB
C++
1169 строки
31 KiB
C++
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#include "ImageBridgeChild.h"
|
|
#include <vector> // 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/CompositableClient.h" // for CompositableChild, etc
|
|
#include "mozilla/layers/CompositorThread.h"
|
|
#include "mozilla/layers/ISurfaceAllocator.h" // for ISurfaceAllocator
|
|
#include "mozilla/layers/ImageClient.h" // for ImageClient
|
|
#include "mozilla/layers/LayersMessages.h" // for CompositableOperation
|
|
#include "mozilla/layers/TextureClient.h" // for TextureClient
|
|
#include "mozilla/dom/ContentChild.h"
|
|
#include "mozilla/mozalloc.h" // for operator new, etc
|
|
#include "mtransport/runnable_utils.h"
|
|
#include "nsContentUtils.h"
|
|
#include "nsISupportsImpl.h" // for ImageContainer::AddRef, etc
|
|
#include "nsTArray.h" // for AutoTArray, nsTArray, etc
|
|
#include "nsTArrayForwardDeclare.h" // for AutoTArray
|
|
#include "nsThreadUtils.h" // for NS_IsMainThread
|
|
#include "nsXULAppAPI.h" // for XRE_GetIOMessageLoop
|
|
#include "mozilla/StaticMutex.h"
|
|
#include "mozilla/StaticPtr.h" // for StaticRefPtr
|
|
#include "mozilla/layers/TextureClient.h"
|
|
#include "SynchronousTask.h"
|
|
|
|
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<CompositableOperation> OpVector;
|
|
typedef nsTArray<OpDestroy> OpDestroyVector;
|
|
typedef nsTArray<ReadLockInit> ReadLockVector;
|
|
|
|
struct CompositableTransaction
|
|
{
|
|
CompositableTransaction()
|
|
: mReadLockSequenceNumber(0)
|
|
, mFinished(true)
|
|
{}
|
|
~CompositableTransaction()
|
|
{
|
|
End();
|
|
}
|
|
bool Finished() const
|
|
{
|
|
return mFinished;
|
|
}
|
|
void Begin()
|
|
{
|
|
MOZ_ASSERT(mFinished);
|
|
mFinished = false;
|
|
mReadLockSequenceNumber = 0;
|
|
mReadLocks.AppendElement();
|
|
}
|
|
void End()
|
|
{
|
|
mFinished = true;
|
|
mOperations.clear();
|
|
mDestroyedActors.Clear();
|
|
mReadLocks.Clear();
|
|
}
|
|
bool IsEmpty() const
|
|
{
|
|
return mOperations.empty() && mDestroyedActors.IsEmpty();
|
|
}
|
|
void AddNoSwapEdit(const CompositableOperation& op)
|
|
{
|
|
MOZ_ASSERT(!Finished(), "forgot BeginTransaction?");
|
|
mOperations.push_back(op);
|
|
}
|
|
|
|
ReadLockHandle AddReadLock(const ReadLockDescriptor& aReadLock)
|
|
{
|
|
ReadLockHandle handle(++mReadLockSequenceNumber);
|
|
if (mReadLocks.LastElement().Length() >= CompositableForwarder::GetMaxFileDescriptorsPerMessage()) {
|
|
mReadLocks.AppendElement();
|
|
}
|
|
mReadLocks.LastElement().AppendElement(ReadLockInit(aReadLock, handle));
|
|
return handle;
|
|
}
|
|
|
|
OpVector mOperations;
|
|
OpDestroyVector mDestroyedActors;
|
|
nsTArray<ReadLockVector> mReadLocks;
|
|
uint64_t mReadLockSequenceNumber;
|
|
|
|
bool mFinished;
|
|
};
|
|
|
|
struct AutoEndTransaction {
|
|
explicit AutoEndTransaction(CompositableTransaction* aTxn) : mTxn(aTxn) {}
|
|
~AutoEndTransaction() { mTxn->End(); }
|
|
CompositableTransaction* mTxn;
|
|
};
|
|
|
|
void
|
|
ImageBridgeChild::UseTextures(CompositableClient* aCompositable,
|
|
const nsTArray<TimedTextureClient>& aTextures)
|
|
{
|
|
MOZ_ASSERT(aCompositable);
|
|
MOZ_ASSERT(aCompositable->GetIPCHandle());
|
|
MOZ_ASSERT(aCompositable->IsConnected());
|
|
|
|
AutoTArray<TimedTexture,4> textures;
|
|
|
|
for (auto& t : aTextures) {
|
|
MOZ_ASSERT(t.mTextureClient);
|
|
MOZ_ASSERT(t.mTextureClient->GetIPDLActor());
|
|
|
|
if (!t.mTextureClient->IsSharedWithCompositor()) {
|
|
return;
|
|
}
|
|
|
|
ReadLockDescriptor readLock;
|
|
ReadLockHandle readLockHandle;
|
|
if (t.mTextureClient->SerializeReadLock(readLock)) {
|
|
readLockHandle = mTxn->AddReadLock(readLock);
|
|
}
|
|
|
|
textures.AppendElement(TimedTexture(nullptr, t.mTextureClient->GetIPDLActor(),
|
|
readLockHandle,
|
|
t.mTimeStamp, t.mPictureRect,
|
|
t.mFrameID, t.mProducerID));
|
|
|
|
// Wait end of usage on host side if TextureFlags::RECYCLE is set
|
|
HoldUntilCompositableRefReleasedIfNecessary(t.mTextureClient);
|
|
}
|
|
mTxn->AddNoSwapEdit(CompositableOperation(aCompositable->GetIPCHandle(),
|
|
OpUseTexture(textures)));
|
|
}
|
|
|
|
void
|
|
ImageBridgeChild::UseComponentAlphaTextures(CompositableClient* aCompositable,
|
|
TextureClient* aTextureOnBlack,
|
|
TextureClient* aTextureOnWhite)
|
|
{
|
|
MOZ_CRASH("should not be called");
|
|
}
|
|
|
|
void
|
|
ImageBridgeChild::HoldUntilCompositableRefReleasedIfNecessary(TextureClient* aClient)
|
|
{
|
|
// 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)
|
|
{
|
|
if (auto entry = mTexturesWaitingRecycled.Lookup(aTextureId)) {
|
|
if (aFwdTransactionId < entry.Data()->GetLastFwdTransactionId()) {
|
|
// Released on host side, but client already requested newer use texture.
|
|
return;
|
|
}
|
|
entry.Remove();
|
|
}
|
|
}
|
|
|
|
void
|
|
ImageBridgeChild::CancelWaitForRecycle(uint64_t aTextureId)
|
|
{
|
|
MOZ_ASSERT(InImageBridgeChildThread());
|
|
mTexturesWaitingRecycled.Remove(aTextureId);
|
|
}
|
|
|
|
// Singleton
|
|
static StaticMutex sImageBridgeSingletonLock;
|
|
static StaticRefPtr<ImageBridgeChild> sImageBridgeChildSingleton;
|
|
static Thread *sImageBridgeChildThread = nullptr;
|
|
|
|
// dispatched function
|
|
void
|
|
ImageBridgeChild::ShutdownStep1(SynchronousTask* aTask)
|
|
{
|
|
AutoCompleteTask complete(aTask);
|
|
|
|
MOZ_ASSERT(InImageBridgeChildThread(),
|
|
"Should be in ImageBridgeChild thread.");
|
|
|
|
MediaSystemResourceManager::Shutdown();
|
|
|
|
// Force all managed protocols to shut themselves down cleanly
|
|
InfallibleTArray<PTextureChild*> textures;
|
|
ManagedPTextureChild(textures);
|
|
for (int i = textures.Length() - 1; i >= 0; --i) {
|
|
RefPtr<TextureClient> client = TextureClient::AsTextureClient(textures[i]);
|
|
if (client) {
|
|
client->Destroy();
|
|
}
|
|
}
|
|
|
|
if (mCanSend) {
|
|
SendWillClose();
|
|
}
|
|
MarkShutDown();
|
|
|
|
// From now on, no message can be sent through the image bridge from the
|
|
// client side except the final Stop message.
|
|
}
|
|
|
|
// dispatched function
|
|
void
|
|
ImageBridgeChild::ShutdownStep2(SynchronousTask* aTask)
|
|
{
|
|
AutoCompleteTask complete(aTask);
|
|
|
|
MOZ_ASSERT(InImageBridgeChildThread(),
|
|
"Should be in ImageBridgeChild thread.");
|
|
if (!mDestroyed) {
|
|
Close();
|
|
}
|
|
}
|
|
|
|
void
|
|
ImageBridgeChild::ActorDestroy(ActorDestroyReason aWhy)
|
|
{
|
|
mCanSend = false;
|
|
mDestroyed = true;
|
|
{
|
|
MutexAutoLock lock(mContainerMapLock);
|
|
mImageContainerListeners.Clear();
|
|
}
|
|
}
|
|
|
|
void
|
|
ImageBridgeChild::DeallocPImageBridgeChild()
|
|
{
|
|
this->Release();
|
|
}
|
|
|
|
void
|
|
ImageBridgeChild::CreateImageClientSync(SynchronousTask* aTask,
|
|
RefPtr<ImageClient>* result,
|
|
CompositableType aType,
|
|
ImageContainer* aImageContainer)
|
|
{
|
|
AutoCompleteTask complete(aTask);
|
|
*result = CreateImageClientNow(aType, aImageContainer);
|
|
}
|
|
|
|
// dispatched function
|
|
void
|
|
ImageBridgeChild::CreateCanvasClientSync(SynchronousTask* aTask,
|
|
CanvasClient::CanvasClientType aType,
|
|
TextureFlags aFlags,
|
|
RefPtr<CanvasClient>* const outResult)
|
|
{
|
|
AutoCompleteTask complete(aTask);
|
|
*outResult = CreateCanvasClientNow(aType, aFlags);
|
|
}
|
|
|
|
ImageBridgeChild::ImageBridgeChild(uint32_t aNamespace)
|
|
: mNamespace(aNamespace)
|
|
, mCanSend(false)
|
|
, mDestroyed(false)
|
|
, mFwdTransactionId(0)
|
|
, mContainerMapLock("ImageBridgeChild.mContainerMapLock")
|
|
{
|
|
MOZ_ASSERT(mNamespace);
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
mTxn = new CompositableTransaction();
|
|
}
|
|
|
|
ImageBridgeChild::~ImageBridgeChild()
|
|
{
|
|
delete mTxn;
|
|
}
|
|
|
|
void
|
|
ImageBridgeChild::MarkShutDown()
|
|
{
|
|
mTexturesWaitingRecycled.Clear();
|
|
|
|
mCanSend = false;
|
|
}
|
|
|
|
void
|
|
ImageBridgeChild::Connect(CompositableClient* aCompositable,
|
|
ImageContainer* aImageContainer)
|
|
{
|
|
MOZ_ASSERT(aCompositable);
|
|
MOZ_ASSERT(InImageBridgeChildThread());
|
|
MOZ_ASSERT(CanSend());
|
|
|
|
// Note: this is static, rather than per-IBC, so IDs are not re-used across
|
|
// ImageBridgeChild instances. This is relevant for the GPU process, where
|
|
// we don't want old IDs to potentially leak into a recreated ImageBridge.
|
|
static uint64_t sNextID = 1;
|
|
uint64_t id = sNextID++;
|
|
|
|
{
|
|
MutexAutoLock lock(mContainerMapLock);
|
|
MOZ_ASSERT(!mImageContainerListeners.Contains(id));
|
|
mImageContainerListeners.Put(id, aImageContainer->GetImageContainerListener());
|
|
}
|
|
|
|
CompositableHandle handle(id);
|
|
aCompositable->InitIPDL(handle);
|
|
SendNewCompositable(handle, aCompositable->GetTextureInfo(), GetCompositorBackendType());
|
|
}
|
|
|
|
void
|
|
ImageBridgeChild::ForgetImageContainer(const CompositableHandle& aHandle)
|
|
{
|
|
MutexAutoLock lock(mContainerMapLock);
|
|
mImageContainerListeners.Remove(aHandle.Value());
|
|
}
|
|
|
|
Thread* ImageBridgeChild::GetThread() const
|
|
{
|
|
return sImageBridgeChildThread;
|
|
}
|
|
|
|
/* static */ RefPtr<ImageBridgeChild>
|
|
ImageBridgeChild::GetSingleton()
|
|
{
|
|
StaticMutexAutoLock lock(sImageBridgeSingletonLock);
|
|
return sImageBridgeChildSingleton;
|
|
}
|
|
|
|
void
|
|
ImageBridgeChild::UpdateImageClient(RefPtr<ImageContainer> aContainer)
|
|
{
|
|
if (!aContainer) {
|
|
return;
|
|
}
|
|
|
|
if (!InImageBridgeChildThread()) {
|
|
RefPtr<Runnable> runnable = WrapRunnable(
|
|
RefPtr<ImageBridgeChild>(this),
|
|
&ImageBridgeChild::UpdateImageClient,
|
|
aContainer);
|
|
GetMessageLoop()->PostTask(runnable.forget());
|
|
return;
|
|
}
|
|
|
|
if (!CanSend()) {
|
|
return;
|
|
}
|
|
|
|
RefPtr<ImageClient> client = aContainer->GetImageClient();
|
|
if (NS_WARN_IF(!client)) {
|
|
return;
|
|
}
|
|
|
|
// If the client has become disconnected before this event was dispatched,
|
|
// early return now.
|
|
if (!client->IsConnected()) {
|
|
return;
|
|
}
|
|
|
|
BeginTransaction();
|
|
client->UpdateImage(aContainer, Layer::CONTENT_OPAQUE);
|
|
EndTransaction();
|
|
}
|
|
|
|
void
|
|
ImageBridgeChild::UpdateAsyncCanvasRendererSync(SynchronousTask* aTask, AsyncCanvasRenderer* aWrapper)
|
|
{
|
|
AutoCompleteTask complete(aTask);
|
|
|
|
UpdateAsyncCanvasRendererNow(aWrapper);
|
|
}
|
|
|
|
void
|
|
ImageBridgeChild::UpdateAsyncCanvasRenderer(AsyncCanvasRenderer* aWrapper)
|
|
{
|
|
aWrapper->GetCanvasClient()->UpdateAsync(aWrapper);
|
|
|
|
if (InImageBridgeChildThread()) {
|
|
UpdateAsyncCanvasRendererNow(aWrapper);
|
|
return;
|
|
}
|
|
|
|
SynchronousTask task("UpdateAsyncCanvasRenderer Lock");
|
|
|
|
RefPtr<Runnable> runnable = WrapRunnable(
|
|
RefPtr<ImageBridgeChild>(this),
|
|
&ImageBridgeChild::UpdateAsyncCanvasRendererSync,
|
|
&task,
|
|
aWrapper);
|
|
GetMessageLoop()->PostTask(runnable.forget());
|
|
|
|
task.Wait();
|
|
}
|
|
|
|
void
|
|
ImageBridgeChild::UpdateAsyncCanvasRendererNow(AsyncCanvasRenderer* aWrapper)
|
|
{
|
|
MOZ_ASSERT(aWrapper);
|
|
|
|
if (!CanSend()) {
|
|
return;
|
|
}
|
|
|
|
BeginTransaction();
|
|
aWrapper->GetCanvasClient()->Updated();
|
|
EndTransaction();
|
|
}
|
|
|
|
void
|
|
ImageBridgeChild::FlushAllImagesSync(SynchronousTask* aTask,
|
|
ImageClient* aClient,
|
|
ImageContainer* aContainer)
|
|
{
|
|
AutoCompleteTask complete(aTask);
|
|
|
|
if (!CanSend()) {
|
|
return;
|
|
}
|
|
|
|
MOZ_ASSERT(aClient);
|
|
BeginTransaction();
|
|
if (aContainer) {
|
|
aContainer->ClearImagesFromImageBridge();
|
|
}
|
|
aClient->FlushAllImages();
|
|
EndTransaction();
|
|
}
|
|
|
|
void
|
|
ImageBridgeChild::FlushAllImages(ImageClient* aClient, ImageContainer* aContainer)
|
|
{
|
|
MOZ_ASSERT(aClient);
|
|
MOZ_ASSERT(!InImageBridgeChildThread());
|
|
|
|
if (InImageBridgeChildThread()) {
|
|
NS_ERROR("ImageBridgeChild::FlushAllImages() is called on ImageBridge thread.");
|
|
return;
|
|
}
|
|
|
|
SynchronousTask task("FlushAllImages Lock");
|
|
|
|
// RefPtrs on arguments are not needed since this dispatches synchronously.
|
|
RefPtr<Runnable> runnable = WrapRunnable(
|
|
RefPtr<ImageBridgeChild>(this),
|
|
&ImageBridgeChild::FlushAllImagesSync,
|
|
&task,
|
|
aClient,
|
|
aContainer);
|
|
GetMessageLoop()->PostTask(runnable.forget());
|
|
|
|
task.Wait();
|
|
}
|
|
|
|
void
|
|
ImageBridgeChild::BeginTransaction()
|
|
{
|
|
MOZ_ASSERT(CanSend());
|
|
MOZ_ASSERT(mTxn->Finished(), "uncommitted txn?");
|
|
UpdateFwdTransactionId();
|
|
mTxn->Begin();
|
|
}
|
|
|
|
void
|
|
ImageBridgeChild::EndTransaction()
|
|
{
|
|
MOZ_ASSERT(CanSend());
|
|
MOZ_ASSERT(!mTxn->Finished(), "forgot BeginTransaction?");
|
|
|
|
AutoEndTransaction _(mTxn);
|
|
|
|
if (mTxn->IsEmpty()) {
|
|
return;
|
|
}
|
|
|
|
AutoTArray<CompositableOperation, 10> cset;
|
|
cset.SetCapacity(mTxn->mOperations.size());
|
|
if (!mTxn->mOperations.empty()) {
|
|
cset.AppendElements(&mTxn->mOperations.front(), mTxn->mOperations.size());
|
|
}
|
|
|
|
if (!IsSameProcess()) {
|
|
ShadowLayerForwarder::PlatformSyncBeforeUpdate();
|
|
}
|
|
|
|
for (ReadLockVector& locks : mTxn->mReadLocks) {
|
|
if (locks.Length()) {
|
|
if (!SendInitReadLocks(locks)) {
|
|
NS_WARNING("[LayersForwarder] WARNING: sending read locks failed!");
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!SendUpdate(cset, mTxn->mDestroyedActors, GetFwdTransactionId())) {
|
|
NS_WARNING("could not send async texture transaction");
|
|
return;
|
|
}
|
|
}
|
|
|
|
void
|
|
ImageBridgeChild::SendImageBridgeThreadId()
|
|
{
|
|
}
|
|
|
|
bool
|
|
ImageBridgeChild::InitForContent(Endpoint<PImageBridgeChild>&& aEndpoint, uint32_t aNamespace)
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
gfxPlatform::GetPlatform();
|
|
|
|
if (!sImageBridgeChildThread) {
|
|
sImageBridgeChildThread = new Thread("ImageBridgeChild");
|
|
if (!sImageBridgeChildThread->Start()) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
RefPtr<ImageBridgeChild> child = new ImageBridgeChild(aNamespace);
|
|
|
|
RefPtr<Runnable> runnable = NewRunnableMethod<Endpoint<PImageBridgeChild>&&>(
|
|
"layers::ImageBridgeChild::Bind",
|
|
child,
|
|
&ImageBridgeChild::Bind,
|
|
Move(aEndpoint));
|
|
child->GetMessageLoop()->PostTask(runnable.forget());
|
|
|
|
// Assign this after so other threads can't post messages before we connect to IPDL.
|
|
{
|
|
StaticMutexAutoLock lock(sImageBridgeSingletonLock);
|
|
sImageBridgeChildSingleton = child;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
ImageBridgeChild::ReinitForContent(Endpoint<PImageBridgeChild>&& aEndpoint, uint32_t aNamespace)
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
// Note that at this point, ActorDestroy may not have been called yet,
|
|
// meaning mCanSend is still true. In this case we will try to send a
|
|
// synchronous WillClose message to the parent, and will certainly get a
|
|
// false result and a MsgDropped processing error. This is okay.
|
|
ShutdownSingleton();
|
|
|
|
return InitForContent(Move(aEndpoint), aNamespace);
|
|
}
|
|
|
|
void
|
|
ImageBridgeChild::Bind(Endpoint<PImageBridgeChild>&& aEndpoint)
|
|
{
|
|
if (!aEndpoint.Bind(this)) {
|
|
return;
|
|
}
|
|
|
|
// This reference is dropped in DeallocPImageBridgeChild.
|
|
this->AddRef();
|
|
|
|
mCanSend = true;
|
|
SendImageBridgeThreadId();
|
|
}
|
|
|
|
void
|
|
ImageBridgeChild::BindSameProcess(RefPtr<ImageBridgeParent> aParent)
|
|
{
|
|
MessageLoop *parentMsgLoop = aParent->GetMessageLoop();
|
|
ipc::MessageChannel *parentChannel = aParent->GetIPCChannel();
|
|
Open(parentChannel, parentMsgLoop, mozilla::ipc::ChildSide);
|
|
|
|
// This reference is dropped in DeallocPImageBridgeChild.
|
|
this->AddRef();
|
|
|
|
mCanSend = true;
|
|
SendImageBridgeThreadId();
|
|
}
|
|
|
|
/* static */ void
|
|
ImageBridgeChild::ShutDown()
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
ShutdownSingleton();
|
|
|
|
delete sImageBridgeChildThread;
|
|
sImageBridgeChildThread = nullptr;
|
|
}
|
|
|
|
/* static */ void
|
|
ImageBridgeChild::ShutdownSingleton()
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
if (RefPtr<ImageBridgeChild> child = GetSingleton()) {
|
|
child->WillShutdown();
|
|
|
|
StaticMutexAutoLock lock(sImageBridgeSingletonLock);
|
|
sImageBridgeChildSingleton = nullptr;
|
|
}
|
|
}
|
|
|
|
void
|
|
ImageBridgeChild::WillShutdown()
|
|
{
|
|
{
|
|
SynchronousTask task("ImageBridge ShutdownStep1 lock");
|
|
|
|
RefPtr<Runnable> runnable = WrapRunnable(
|
|
RefPtr<ImageBridgeChild>(this),
|
|
&ImageBridgeChild::ShutdownStep1,
|
|
&task);
|
|
GetMessageLoop()->PostTask(runnable.forget());
|
|
|
|
task.Wait();
|
|
}
|
|
|
|
{
|
|
SynchronousTask task("ImageBridge ShutdownStep2 lock");
|
|
|
|
RefPtr<Runnable> runnable = WrapRunnable(
|
|
RefPtr<ImageBridgeChild>(this),
|
|
&ImageBridgeChild::ShutdownStep2,
|
|
&task);
|
|
GetMessageLoop()->PostTask(runnable.forget());
|
|
|
|
task.Wait();
|
|
}
|
|
}
|
|
|
|
void
|
|
ImageBridgeChild::InitSameProcess(uint32_t aNamespace)
|
|
{
|
|
NS_ASSERTION(NS_IsMainThread(), "Should be on the main Thread!");
|
|
|
|
MOZ_ASSERT(!sImageBridgeChildSingleton);
|
|
MOZ_ASSERT(!sImageBridgeChildThread);
|
|
|
|
sImageBridgeChildThread = new Thread("ImageBridgeChild");
|
|
if (!sImageBridgeChildThread->IsRunning()) {
|
|
sImageBridgeChildThread->Start();
|
|
}
|
|
|
|
RefPtr<ImageBridgeChild> child = new ImageBridgeChild(aNamespace);
|
|
RefPtr<ImageBridgeParent> parent = ImageBridgeParent::CreateSameProcess();
|
|
|
|
RefPtr<Runnable> runnable = WrapRunnable(
|
|
child,
|
|
&ImageBridgeChild::BindSameProcess,
|
|
parent);
|
|
child->GetMessageLoop()->PostTask(runnable.forget());
|
|
|
|
// Assign this after so other threads can't post messages before we connect to IPDL.
|
|
{
|
|
StaticMutexAutoLock lock(sImageBridgeSingletonLock);
|
|
sImageBridgeChildSingleton = child;
|
|
}
|
|
}
|
|
|
|
/* static */ void
|
|
ImageBridgeChild::InitWithGPUProcess(Endpoint<PImageBridgeChild>&& aEndpoint, uint32_t aNamespace)
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
MOZ_ASSERT(!sImageBridgeChildSingleton);
|
|
MOZ_ASSERT(!sImageBridgeChildThread);
|
|
|
|
sImageBridgeChildThread = new Thread("ImageBridgeChild");
|
|
if (!sImageBridgeChildThread->IsRunning()) {
|
|
sImageBridgeChildThread->Start();
|
|
}
|
|
|
|
RefPtr<ImageBridgeChild> child = new ImageBridgeChild(aNamespace);
|
|
|
|
MessageLoop* loop = child->GetMessageLoop();
|
|
loop->PostTask(NewRunnableMethod<Endpoint<PImageBridgeChild>&&>(
|
|
"layers::ImageBridgeChild::Bind",
|
|
child,
|
|
&ImageBridgeChild::Bind,
|
|
Move(aEndpoint)));
|
|
|
|
// Assign this after so other threads can't post messages before we connect to IPDL.
|
|
{
|
|
StaticMutexAutoLock lock(sImageBridgeSingletonLock);
|
|
sImageBridgeChildSingleton = child;
|
|
}
|
|
}
|
|
|
|
bool InImageBridgeChildThread()
|
|
{
|
|
return sImageBridgeChildThread &&
|
|
sImageBridgeChildThread->thread_id() == PlatformThread::CurrentId();
|
|
}
|
|
|
|
MessageLoop * ImageBridgeChild::GetMessageLoop() const
|
|
{
|
|
return sImageBridgeChildThread ? sImageBridgeChildThread->message_loop() : nullptr;
|
|
}
|
|
|
|
/* static */ void
|
|
ImageBridgeChild::IdentifyCompositorTextureHost(const TextureFactoryIdentifier& aIdentifier)
|
|
{
|
|
if (RefPtr<ImageBridgeChild> child = GetSingleton()) {
|
|
child->UpdateTextureFactoryIdentifier(aIdentifier);
|
|
}
|
|
}
|
|
|
|
void
|
|
ImageBridgeChild::UpdateTextureFactoryIdentifier(const TextureFactoryIdentifier& aIdentifier)
|
|
{
|
|
bool disablingWebRender = GetCompositorBackendType() == LayersBackend::LAYERS_WR &&
|
|
aIdentifier.mParentBackend != LayersBackend::LAYERS_WR;
|
|
IdentifyTextureHost(aIdentifier);
|
|
if (disablingWebRender) {
|
|
// ImageHost is incompatible between WebRender enabled and WebRender disabled.
|
|
// Then drop all ImageContainers' ImageClients during disabling WebRender.
|
|
nsTArray<RefPtr<ImageContainerListener> > listeners;
|
|
{
|
|
MutexAutoLock lock(mContainerMapLock);
|
|
for (auto iter = mImageContainerListeners.Iter(); !iter.Done(); iter.Next()) {
|
|
RefPtr<ImageContainerListener>& listener = iter.Data();
|
|
listeners.AppendElement(listener);
|
|
}
|
|
}
|
|
// Drop ImageContainer's ImageClient whithout holding mContainerMapLock to avoid deadlock.
|
|
for (auto container : listeners) {
|
|
container->DropImageClient();
|
|
}
|
|
}
|
|
}
|
|
|
|
RefPtr<ImageClient>
|
|
ImageBridgeChild::CreateImageClient(CompositableType aType,
|
|
ImageContainer* aImageContainer)
|
|
{
|
|
if (InImageBridgeChildThread()) {
|
|
return CreateImageClientNow(aType, aImageContainer);
|
|
}
|
|
|
|
SynchronousTask task("CreateImageClient Lock");
|
|
|
|
RefPtr<ImageClient> result = nullptr;
|
|
|
|
RefPtr<Runnable> runnable = WrapRunnable(
|
|
RefPtr<ImageBridgeChild>(this),
|
|
&ImageBridgeChild::CreateImageClientSync,
|
|
&task,
|
|
&result,
|
|
aType,
|
|
aImageContainer);
|
|
GetMessageLoop()->PostTask(runnable.forget());
|
|
|
|
task.Wait();
|
|
|
|
return result;
|
|
}
|
|
|
|
RefPtr<ImageClient>
|
|
ImageBridgeChild::CreateImageClientNow(CompositableType aType,
|
|
ImageContainer* aImageContainer)
|
|
{
|
|
MOZ_ASSERT(InImageBridgeChildThread());
|
|
if (!CanSend()) {
|
|
return nullptr;
|
|
}
|
|
|
|
RefPtr<ImageClient> client = ImageClient::CreateImageClient(aType, this, TextureFlags::NO_FLAGS);
|
|
MOZ_ASSERT(client, "failed to create ImageClient");
|
|
if (client) {
|
|
client->Connect(aImageContainer);
|
|
}
|
|
return client;
|
|
}
|
|
|
|
already_AddRefed<CanvasClient>
|
|
ImageBridgeChild::CreateCanvasClient(CanvasClient::CanvasClientType aType,
|
|
TextureFlags aFlag)
|
|
{
|
|
if (InImageBridgeChildThread()) {
|
|
return CreateCanvasClientNow(aType, aFlag);
|
|
}
|
|
|
|
SynchronousTask task("CreateCanvasClient Lock");
|
|
|
|
// RefPtrs on arguments are not needed since this dispatches synchronously.
|
|
RefPtr<CanvasClient> result = nullptr;
|
|
RefPtr<Runnable> runnable = WrapRunnable(
|
|
RefPtr<ImageBridgeChild>(this),
|
|
&ImageBridgeChild::CreateCanvasClientSync,
|
|
&task,
|
|
aType,
|
|
aFlag,
|
|
&result);
|
|
GetMessageLoop()->PostTask(runnable.forget());
|
|
|
|
task.Wait();
|
|
|
|
return result.forget();
|
|
}
|
|
|
|
already_AddRefed<CanvasClient>
|
|
ImageBridgeChild::CreateCanvasClientNow(CanvasClient::CanvasClientType aType,
|
|
TextureFlags aFlag)
|
|
{
|
|
RefPtr<CanvasClient> client
|
|
= CanvasClient::CreateCanvasClient(aType, this, aFlag);
|
|
MOZ_ASSERT(client, "failed to create CanvasClient");
|
|
if (client) {
|
|
client->Connect();
|
|
}
|
|
return client.forget();
|
|
}
|
|
|
|
bool
|
|
ImageBridgeChild::AllocUnsafeShmem(size_t aSize,
|
|
ipc::SharedMemory::SharedMemoryType aType,
|
|
ipc::Shmem* aShmem)
|
|
{
|
|
if (!InImageBridgeChildThread()) {
|
|
return DispatchAllocShmemInternal(aSize, aType, aShmem, true); // true: unsafe
|
|
}
|
|
|
|
if (!CanSend()) {
|
|
return false;
|
|
}
|
|
return PImageBridgeChild::AllocUnsafeShmem(aSize, aType, aShmem);
|
|
}
|
|
|
|
bool
|
|
ImageBridgeChild::AllocShmem(size_t aSize,
|
|
ipc::SharedMemory::SharedMemoryType aType,
|
|
ipc::Shmem* aShmem)
|
|
{
|
|
if (!InImageBridgeChildThread()) {
|
|
return DispatchAllocShmemInternal(aSize, aType, aShmem, false); // false: unsafe
|
|
}
|
|
|
|
if (!CanSend()) {
|
|
return false;
|
|
}
|
|
return PImageBridgeChild::AllocShmem(aSize, aType, aShmem);
|
|
}
|
|
|
|
// NewRunnableFunction accepts a limited number of parameters so we need a
|
|
// struct here
|
|
struct AllocShmemParams {
|
|
size_t mSize;
|
|
ipc::SharedMemory::SharedMemoryType mType;
|
|
ipc::Shmem* mShmem;
|
|
bool mUnsafe;
|
|
bool mSuccess;
|
|
};
|
|
|
|
void
|
|
ImageBridgeChild::ProxyAllocShmemNow(SynchronousTask* aTask, AllocShmemParams* aParams)
|
|
{
|
|
AutoCompleteTask complete(aTask);
|
|
|
|
if (!CanSend()) {
|
|
return;
|
|
}
|
|
|
|
bool ok = false;
|
|
if (aParams->mUnsafe) {
|
|
ok = AllocUnsafeShmem(aParams->mSize, aParams->mType, aParams->mShmem);
|
|
} else {
|
|
ok = AllocShmem(aParams->mSize, aParams->mType, aParams->mShmem);
|
|
}
|
|
aParams->mSuccess = ok;
|
|
}
|
|
|
|
bool
|
|
ImageBridgeChild::DispatchAllocShmemInternal(size_t aSize,
|
|
SharedMemory::SharedMemoryType aType,
|
|
ipc::Shmem* aShmem,
|
|
bool aUnsafe)
|
|
{
|
|
SynchronousTask task("AllocatorProxy alloc");
|
|
|
|
AllocShmemParams params = {
|
|
aSize, aType, aShmem, aUnsafe, false
|
|
};
|
|
|
|
RefPtr<Runnable> runnable = WrapRunnable(
|
|
RefPtr<ImageBridgeChild>(this),
|
|
&ImageBridgeChild::ProxyAllocShmemNow,
|
|
&task,
|
|
¶ms);
|
|
GetMessageLoop()->PostTask(runnable.forget());
|
|
|
|
task.Wait();
|
|
|
|
return params.mSuccess;
|
|
}
|
|
|
|
void
|
|
ImageBridgeChild::ProxyDeallocShmemNow(SynchronousTask* aTask,
|
|
ipc::Shmem* aShmem,
|
|
bool* aResult)
|
|
{
|
|
AutoCompleteTask complete(aTask);
|
|
|
|
if (!CanSend()) {
|
|
return;
|
|
}
|
|
*aResult = DeallocShmem(*aShmem);
|
|
}
|
|
|
|
bool
|
|
ImageBridgeChild::DeallocShmem(ipc::Shmem& aShmem)
|
|
{
|
|
if (InImageBridgeChildThread()) {
|
|
if (!CanSend()) {
|
|
return false;
|
|
}
|
|
return PImageBridgeChild::DeallocShmem(aShmem);
|
|
}
|
|
|
|
// If we can't post a task, then we definitely cannot send, so there's
|
|
// no reason to queue up this send.
|
|
if (!CanPostTask()) {
|
|
return false;
|
|
}
|
|
|
|
SynchronousTask task("AllocatorProxy Dealloc");
|
|
bool result = false;
|
|
|
|
RefPtr<Runnable> runnable = WrapRunnable(
|
|
RefPtr<ImageBridgeChild>(this),
|
|
&ImageBridgeChild::ProxyDeallocShmemNow,
|
|
&task,
|
|
&aShmem,
|
|
&result);
|
|
GetMessageLoop()->PostTask(runnable.forget());
|
|
|
|
task.Wait();
|
|
return result;
|
|
}
|
|
|
|
PTextureChild*
|
|
ImageBridgeChild::AllocPTextureChild(const SurfaceDescriptor&,
|
|
const LayersBackend&,
|
|
const TextureFlags&,
|
|
const uint64_t& aSerial,
|
|
const wr::MaybeExternalImageId& aExternalImageId)
|
|
{
|
|
MOZ_ASSERT(CanSend());
|
|
return TextureClient::CreateIPDLActor();
|
|
}
|
|
|
|
bool
|
|
ImageBridgeChild::DeallocPTextureChild(PTextureChild* actor)
|
|
{
|
|
return TextureClient::DestroyIPDLActor(actor);
|
|
}
|
|
|
|
PMediaSystemResourceManagerChild*
|
|
ImageBridgeChild::AllocPMediaSystemResourceManagerChild()
|
|
{
|
|
MOZ_ASSERT(CanSend());
|
|
return new mozilla::media::MediaSystemResourceManagerChild();
|
|
}
|
|
|
|
bool
|
|
ImageBridgeChild::DeallocPMediaSystemResourceManagerChild(PMediaSystemResourceManagerChild* aActor)
|
|
{
|
|
MOZ_ASSERT(aActor);
|
|
delete static_cast<mozilla::media::MediaSystemResourceManagerChild*>(aActor);
|
|
return true;
|
|
}
|
|
|
|
mozilla::ipc::IPCResult
|
|
ImageBridgeChild::RecvParentAsyncMessages(InfallibleTArray<AsyncParentMessageData>&& aMessages)
|
|
{
|
|
for (AsyncParentMessageArray::index_type i = 0; i < aMessages.Length(); ++i) {
|
|
const AsyncParentMessageData& message = aMessages[i];
|
|
|
|
switch (message.type()) {
|
|
case AsyncParentMessageData::TOpNotifyNotUsed: {
|
|
const OpNotifyNotUsed& op = message.get_OpNotifyNotUsed();
|
|
NotifyNotUsed(op.TextureId(), op.fwdTransactionId());
|
|
break;
|
|
}
|
|
default:
|
|
NS_ERROR("unknown AsyncParentMessageData type");
|
|
return IPC_FAIL_NO_REASON(this);
|
|
}
|
|
}
|
|
return IPC_OK();
|
|
}
|
|
|
|
mozilla::ipc::IPCResult
|
|
ImageBridgeChild::RecvDidComposite(InfallibleTArray<ImageCompositeNotification>&& aNotifications)
|
|
{
|
|
for (auto& n : aNotifications) {
|
|
RefPtr<ImageContainerListener> listener;
|
|
{
|
|
MutexAutoLock lock(mContainerMapLock);
|
|
if (auto entry = mImageContainerListeners.Lookup(n.compositable().Value())) {
|
|
listener = entry.Data();
|
|
}
|
|
}
|
|
if (listener) {
|
|
listener->NotifyComposite(n);
|
|
}
|
|
}
|
|
return IPC_OK();
|
|
}
|
|
|
|
PTextureChild*
|
|
ImageBridgeChild::CreateTexture(const SurfaceDescriptor& aSharedData,
|
|
LayersBackend aLayersBackend,
|
|
TextureFlags aFlags,
|
|
uint64_t aSerial,
|
|
wr::MaybeExternalImageId& aExternalImageId,
|
|
nsIEventTarget* aTarget)
|
|
{
|
|
MOZ_ASSERT(CanSend());
|
|
return SendPTextureConstructor(aSharedData, aLayersBackend, aFlags, aSerial, aExternalImageId);
|
|
}
|
|
|
|
static bool
|
|
IBCAddOpDestroy(CompositableTransaction* aTxn, const OpDestroy& op)
|
|
{
|
|
if (aTxn->Finished()) {
|
|
return false;
|
|
}
|
|
|
|
aTxn->mDestroyedActors.AppendElement(op);
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
ImageBridgeChild::DestroyInTransaction(PTextureChild* aTexture)
|
|
{
|
|
return IBCAddOpDestroy(mTxn, OpDestroy(aTexture));
|
|
}
|
|
|
|
bool
|
|
ImageBridgeChild::DestroyInTransaction(const CompositableHandle& aHandle)
|
|
{
|
|
return IBCAddOpDestroy(mTxn, OpDestroy(aHandle));
|
|
}
|
|
|
|
void
|
|
ImageBridgeChild::RemoveTextureFromCompositable(CompositableClient* aCompositable,
|
|
TextureClient* aTexture)
|
|
{
|
|
MOZ_ASSERT(CanSend());
|
|
MOZ_ASSERT(aTexture);
|
|
MOZ_ASSERT(aTexture->IsSharedWithCompositor());
|
|
MOZ_ASSERT(aCompositable->IsConnected());
|
|
if (!aTexture || !aTexture->IsSharedWithCompositor() || !aCompositable->IsConnected()) {
|
|
return;
|
|
}
|
|
|
|
mTxn->AddNoSwapEdit(CompositableOperation(
|
|
aCompositable->GetIPCHandle(),
|
|
OpRemoveTexture(nullptr, aTexture->GetIPDLActor())));
|
|
}
|
|
|
|
bool ImageBridgeChild::IsSameProcess() const
|
|
{
|
|
return OtherPid() == base::GetCurrentProcId();
|
|
}
|
|
|
|
bool
|
|
ImageBridgeChild::CanPostTask() const
|
|
{
|
|
// During shutdown, the cycle collector may free objects that are holding a
|
|
// reference to ImageBridgeChild. Since this happens on the main thread,
|
|
// ImageBridgeChild will attempt to post a task to the ImageBridge thread.
|
|
// However the thread manager has already been shut down, so the task cannot
|
|
// post.
|
|
//
|
|
// It's okay if this races. We only care about the shutdown case where
|
|
// everything's happening on the main thread. Even if it races outside of
|
|
// shutdown, it's still harmless to post the task, since the task must
|
|
// check CanSend().
|
|
return !mDestroyed;
|
|
}
|
|
|
|
void
|
|
ImageBridgeChild::ReleaseCompositable(const CompositableHandle& aHandle)
|
|
{
|
|
if (!InImageBridgeChildThread()) {
|
|
// If we can't post a task, then we definitely cannot send, so there's
|
|
// no reason to queue up this send.
|
|
if (!CanPostTask()) {
|
|
return;
|
|
}
|
|
|
|
RefPtr<Runnable> runnable = WrapRunnable(
|
|
RefPtr<ImageBridgeChild>(this),
|
|
&ImageBridgeChild::ReleaseCompositable,
|
|
aHandle);
|
|
GetMessageLoop()->PostTask(runnable.forget());
|
|
return;
|
|
}
|
|
|
|
if (!CanSend()) {
|
|
return;
|
|
}
|
|
|
|
if (!DestroyInTransaction(aHandle)) {
|
|
SendReleaseCompositable(aHandle);
|
|
}
|
|
|
|
{
|
|
MutexAutoLock lock(mContainerMapLock);
|
|
mImageContainerListeners.Remove(aHandle.Value());
|
|
}
|
|
}
|
|
|
|
bool
|
|
ImageBridgeChild::CanSend() const
|
|
{
|
|
MOZ_ASSERT(InImageBridgeChildThread());
|
|
return mCanSend;
|
|
}
|
|
|
|
void
|
|
ImageBridgeChild::HandleFatalError(const char* aName, const char* aMsg) const
|
|
{
|
|
dom::ContentChild::FatalErrorIfNotUsingGPUProcess(aName, aMsg, OtherPid());
|
|
}
|
|
|
|
wr::MaybeExternalImageId
|
|
ImageBridgeChild::GetNextExternalImageId()
|
|
{
|
|
static uint32_t sNextID = 1;
|
|
++sNextID;
|
|
MOZ_RELEASE_ASSERT(sNextID != UINT32_MAX);
|
|
|
|
uint64_t imageId = mNamespace;
|
|
imageId = imageId << 32 | sNextID;
|
|
return Some(wr::ToExternalImageId(imageId));
|
|
}
|
|
|
|
} // namespace layers
|
|
} // namespace mozilla
|