gecko/gfx/layers/ipc/ImageBridgeChild.cpp

746 lines
21 KiB
C++

/* -*- 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 "base/thread.h"
#include "CompositorParent.h" // for CompositorParent::CompositorLoop
#include "ImageBridgeChild.h"
#include "ImageBridgeParent.h"
#include "gfxSharedImageSurface.h"
#include "mozilla/Monitor.h"
#include "mozilla/ReentrantMonitor.h"
#include "mozilla/layers/CompositableClient.h"
#include "nsXULAppAPI.h"
#include "mozilla/layers/TextureClient.h"
#include "mozilla/layers/ImageClient.h"
#include "mozilla/layers/LayersTypes.h"
#include "ShadowLayers.h"
using namespace base;
using namespace mozilla::ipc;
namespace mozilla {
namespace layers {
typedef std::vector<CompositableOperation> 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::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::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<ImageClient>* 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<CompositableChild*>(
SendPCompositableConstructor(aCompositable->GetTextureInfo(), &id));
MOZ_ASSERT(child);
child->SetAsyncID(id);
aCompositable->SetIPDLActor(child);
MOZ_ASSERT(child->GetAsyncID() == id);
child->SetClient(aCompositable);
}
PCompositableChild*
ImageBridgeChild::AllocPCompositable(const TextureInfo& aInfo, uint64_t* aID)
{
return new CompositableChild();
}
bool
ImageBridgeChild::DeallocPCompositable(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);
sImageBridgeChildSingleton->EndTransaction();
}
//static
void ImageBridgeChild::DispatchImageClientUpdate(ImageClient* aClient,
ImageContainer* aContainer)
{
sImageBridgeChildSingleton->GetMessageLoop()->PostTask(
FROM_HERE,
NewRunnableFunction(&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<CompositableOperation, 10> cset;
cset.SetCapacity(mTxn->mOperations.size());
if (!mTxn->mOperations.empty()) {
cset.AppendElements(&mTxn->mOperations.front(), mTxn->mOperations.size());
}
ShadowLayerForwarder::PlatformSyncBeforeUpdate();
AutoInfallibleTArray<EditReply, 10> 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<EditReply>::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<CompositableChild*>(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));
}
TemporaryRef<ImageClient>
ImageBridgeChild::CreateImageClient(CompositableType aType)
{
if (InImageBridgeChildThread()) {
return CreateImageClientNow(aType);
}
ReentrantMonitor barrier("CreateImageClient Lock");
ReentrantMonitorAutoEnter autoMon(barrier);
bool done = false;
RefPtr<ImageClient> 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<ImageClient>
ImageBridgeChild::CreateImageClientNow(CompositableType aType)
{
mCompositorBackend = LAYERS_OPENGL;
RefPtr<ImageClient> client
= ImageClient::CreateImageClient(aType, this, 0);
MOZ_ASSERT(client, "failed to create ImageClient");
if (client) {
client->Connect();
}
return client.forget();
}
PGrallocBufferChild*
ImageBridgeChild::AllocPGrallocBuffer(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::DeallocPGrallocBuffer(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<GrallocBufferActor*>(gc);
gba->InitFromHandle(handle.get_MagicGrallocBufferHandle());
*aBuffer = SurfaceDescriptorGralloc(nullptr, gc, aSize, /* external */ 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,
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,
&params,
&barrier,
&done));
while (!done) {
barrier.Wait();
}
return params.mSuccess;
}
static void ProxyDeallocShmemNow(ISurfaceAllocator* aAllocator,
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,
gfxASurface::gfxContentType aContent,
MaybeMagicGrallocBufferHandle* aHandle)
{
#ifdef MOZ_WIDGET_GONK
return SendPGrallocBufferConstructor(aSize,
aContent,
GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
aHandle);
#else
NS_RUNTIMEABORT("not implemented");
return nullptr;
#endif
}
} // layers
} // mozilla