forked from mirrors/gecko-dev
WebGPU uses CompositableInProcessManager to push TextureHost directly from WebGPUParent to WebRender. But CompositableInProcessManager plumbing has a problem and caused Bug 1805209. gecko already has a similar mechanism, called RemoteTextureMap. It is used in oop WebGL. If WebGPU uses RemoteTextureMap instead of CompositableInProcessManager, both WebGPU and oop WebGL use same mechanism. WebGPUParent pushes a new texture to RemoteTextureMap. The RemoteTextureMap notifies the pushed texture to WebRenderImageHost. Before the change, only one TextureHost is used for one swap chain. With the change, multiple TextureHosts are used for one swap chain with recycling. The changes are followings. - Use RemoteTextureMap instead of CompositableInProcessManager. - Use RemoteTextureOwnerId instead of CompositableHandle. - Use WebRenderCanvasData instead of WebRenderInProcessImageData. - Add remote texture pushed callback functionality to RemoteTextureMap. With it, RemoteTextureMap notifies a new pushed remote texture to WebRenderImageHost. - Remove CompositableInProcessManager. Differential Revision: https://phabricator.services.mozilla.com/D164890
982 lines
29 KiB
C++
982 lines
29 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 "SynchronousTask.h"
|
|
#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
|
|
#include "mozilla/Monitor.h" // for Monitor, MonitorAutoLock
|
|
#include "mozilla/ReentrantMonitor.h" // for ReentrantMonitor, etc
|
|
#include "mozilla/StaticMutex.h"
|
|
#include "mozilla/StaticPtr.h" // for StaticRefPtr
|
|
#include "mozilla/dom/ContentChild.h"
|
|
#include "mozilla/gfx/Point.h" // for IntSize
|
|
#include "mozilla/gfx/gfxVars.h"
|
|
#include "mozilla/ipc/Endpoint.h"
|
|
#include "mozilla/ipc/MessageChannel.h" // for MessageChannel, etc
|
|
#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/layers/TextureClient.h"
|
|
#include "mozilla/media/MediaSystemResourceManager.h" // for MediaSystemResourceManager
|
|
#include "mozilla/media/MediaSystemResourceManagerChild.h" // for MediaSystemResourceManagerChild
|
|
#include "mozilla/mozalloc.h" // for operator new, etc
|
|
#include "transport/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
|
|
|
|
#if defined(XP_WIN)
|
|
# include "mozilla/gfx/DeviceManagerDx.h"
|
|
#endif
|
|
|
|
#ifdef MOZ_WIDGET_ANDROID
|
|
# include "mozilla/layers/AndroidHardwareBuffer.h"
|
|
#endif
|
|
|
|
namespace mozilla {
|
|
namespace ipc {
|
|
class Shmem;
|
|
} // namespace ipc
|
|
|
|
namespace layers {
|
|
|
|
using namespace mozilla::ipc;
|
|
using namespace mozilla::gfx;
|
|
using namespace mozilla::media;
|
|
|
|
typedef std::vector<CompositableOperation> OpVector;
|
|
typedef nsTArray<OpDestroy> OpDestroyVector;
|
|
|
|
struct CompositableTransaction {
|
|
CompositableTransaction() : mFinished(true) {}
|
|
~CompositableTransaction() { End(); }
|
|
bool Finished() const { return mFinished; }
|
|
void Begin() {
|
|
MOZ_ASSERT(mFinished);
|
|
mFinished = false;
|
|
}
|
|
void End() {
|
|
mFinished = true;
|
|
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);
|
|
}
|
|
|
|
OpVector mOperations;
|
|
OpDestroyVector mDestroyedActors;
|
|
|
|
bool mFinished;
|
|
};
|
|
|
|
struct AutoEndTransaction final {
|
|
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;
|
|
}
|
|
|
|
bool readLocked = t.mTextureClient->OnForwardedToHost();
|
|
|
|
auto fenceFd = t.mTextureClient->GetInternalData()->GetAcquireFence();
|
|
if (fenceFd.IsValid()) {
|
|
mTxn->AddNoSwapEdit(CompositableOperation(
|
|
aCompositable->GetIPCHandle(),
|
|
OpDeliverAcquireFence(nullptr, t.mTextureClient->GetIPDLActor(),
|
|
fenceFd)));
|
|
}
|
|
|
|
textures.AppendElement(
|
|
TimedTexture(nullptr, t.mTextureClient->GetIPDLActor(), t.mTimeStamp,
|
|
t.mPictureRect, t.mFrameID, t.mProducerID, readLocked));
|
|
|
|
// Wait end of usage on host side if TextureFlags::RECYCLE is set
|
|
HoldUntilCompositableRefReleasedIfNecessary(t.mTextureClient);
|
|
}
|
|
mTxn->AddNoSwapEdit(CompositableOperation(aCompositable->GetIPCHandle(),
|
|
OpUseTexture(textures)));
|
|
}
|
|
|
|
void ImageBridgeChild::UseRemoteTexture(CompositableClient* aCompositable,
|
|
const RemoteTextureId aTextureId,
|
|
const RemoteTextureOwnerId aOwnerId,
|
|
const gfx::IntSize aSize,
|
|
const TextureFlags aFlags) {
|
|
MOZ_ASSERT(aCompositable);
|
|
MOZ_ASSERT(aCompositable->GetIPCHandle());
|
|
MOZ_ASSERT(aCompositable->IsConnected());
|
|
|
|
mTxn->AddNoSwapEdit(CompositableOperation(
|
|
aCompositable->GetIPCHandle(),
|
|
OpUseRemoteTexture(aTextureId, aOwnerId, aSize, aFlags)));
|
|
}
|
|
|
|
void ImageBridgeChild::EnableRemoteTexturePushCallback(
|
|
CompositableClient* aCompositable, const RemoteTextureOwnerId aOwnerId,
|
|
const gfx::IntSize aSize, const TextureFlags aFlags) {
|
|
MOZ_ASSERT(aCompositable);
|
|
MOZ_ASSERT(aCompositable->GetIPCHandle());
|
|
MOZ_ASSERT(aCompositable->IsConnected());
|
|
|
|
mTxn->AddNoSwapEdit(CompositableOperation(
|
|
aCompositable->GetIPCHandle(),
|
|
OpEnableRemoteTexturePushCallback(aOwnerId, aSize, aFlags)));
|
|
}
|
|
|
|
void ImageBridgeChild::HoldUntilCompositableRefReleasedIfNecessary(
|
|
TextureClient* aClient) {
|
|
if (!aClient) {
|
|
return;
|
|
}
|
|
|
|
#ifdef MOZ_WIDGET_ANDROID
|
|
auto bufferId = aClient->GetInternalData()->GetBufferId();
|
|
if (bufferId.isSome()) {
|
|
MOZ_ASSERT(aClient->GetFlags() & TextureFlags::WAIT_HOST_USAGE_END);
|
|
AndroidHardwareBufferManager::Get()->HoldUntilNotifyNotUsed(
|
|
bufferId.ref(), GetFwdTransactionId(), /* aUsesImageBridge */ true);
|
|
}
|
|
#endif
|
|
|
|
// Wait ReleaseCompositableRef only when TextureFlags::RECYCLE or
|
|
// TextureFlags::WAIT_HOST_USAGE_END is set on ImageBridge.
|
|
bool waitNotifyNotUsed =
|
|
aClient->GetFlags() & TextureFlags::RECYCLE ||
|
|
aClient->GetFlags() & TextureFlags::WAIT_HOST_USAGE_END;
|
|
if (!waitNotifyNotUsed) {
|
|
return;
|
|
}
|
|
|
|
aClient->SetLastFwdTransactionId(GetFwdTransactionId());
|
|
mTexturesWaitingNotifyNotUsed.emplace(aClient->GetSerial(), aClient);
|
|
}
|
|
|
|
void ImageBridgeChild::NotifyNotUsed(uint64_t aTextureId,
|
|
uint64_t aFwdTransactionId) {
|
|
auto it = mTexturesWaitingNotifyNotUsed.find(aTextureId);
|
|
if (it != mTexturesWaitingNotifyNotUsed.end()) {
|
|
if (aFwdTransactionId < it->second->GetLastFwdTransactionId()) {
|
|
// Released on host side, but client already requested newer use texture.
|
|
return;
|
|
}
|
|
mTexturesWaitingNotifyNotUsed.erase(it);
|
|
}
|
|
}
|
|
|
|
void ImageBridgeChild::CancelWaitForNotifyNotUsed(uint64_t aTextureId) {
|
|
MOZ_ASSERT(InImageBridgeChildThread());
|
|
mTexturesWaitingNotifyNotUsed.erase(aTextureId);
|
|
}
|
|
|
|
// Singleton
|
|
static StaticMutex sImageBridgeSingletonLock MOZ_UNANNOTATED;
|
|
static StaticRefPtr<ImageBridgeChild> sImageBridgeChildSingleton;
|
|
static StaticRefPtr<nsIThread> sImageBridgeChildThread;
|
|
|
|
// 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
|
|
nsTArray<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::ActorDealloc() { this->Release(); }
|
|
|
|
void ImageBridgeChild::CreateImageClientSync(SynchronousTask* aTask,
|
|
RefPtr<ImageClient>* result,
|
|
CompositableType aType,
|
|
ImageContainer* aImageContainer) {
|
|
AutoCompleteTask complete(aTask);
|
|
*result = CreateImageClientNow(aType, aImageContainer);
|
|
}
|
|
|
|
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() {
|
|
mTexturesWaitingNotifyNotUsed.clear();
|
|
|
|
mCanSend = false;
|
|
}
|
|
|
|
void ImageBridgeChild::Connect(CompositableClient* aCompositable,
|
|
ImageContainer* aImageContainer) {
|
|
MOZ_ASSERT(aCompositable);
|
|
MOZ_ASSERT(InImageBridgeChildThread());
|
|
MOZ_ASSERT(CanSend());
|
|
|
|
CompositableHandle handle = CompositableHandle::GetNext();
|
|
|
|
// ImageClient of ImageContainer provides aImageContainer.
|
|
// But offscreen canvas does not provide it.
|
|
if (aImageContainer) {
|
|
MutexAutoLock lock(mContainerMapLock);
|
|
MOZ_ASSERT(mImageContainerListeners.find(uint64_t(handle)) ==
|
|
mImageContainerListeners.end());
|
|
mImageContainerListeners.emplace(
|
|
uint64_t(handle), aImageContainer->GetImageContainerListener());
|
|
}
|
|
|
|
aCompositable->InitIPDL(handle);
|
|
SendNewCompositable(handle, aCompositable->GetTextureInfo());
|
|
}
|
|
|
|
void ImageBridgeChild::ForgetImageContainer(const CompositableHandle& aHandle) {
|
|
MutexAutoLock lock(mContainerMapLock);
|
|
mImageContainerListeners.erase(aHandle.Value());
|
|
}
|
|
|
|
/* 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);
|
|
GetThread()->Dispatch(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);
|
|
EndTransaction();
|
|
}
|
|
|
|
void ImageBridgeChild::UpdateCompositable(
|
|
const RefPtr<ImageContainer> aContainer, const RemoteTextureId aTextureId,
|
|
const RemoteTextureOwnerId aOwnerId, const gfx::IntSize aSize,
|
|
const TextureFlags aFlags) {
|
|
if (!aContainer) {
|
|
return;
|
|
}
|
|
|
|
if (!InImageBridgeChildThread()) {
|
|
RefPtr<Runnable> runnable = WrapRunnable(
|
|
RefPtr<ImageBridgeChild>(this), &ImageBridgeChild::UpdateCompositable,
|
|
aContainer, aTextureId, aOwnerId, aSize, aFlags);
|
|
GetThread()->Dispatch(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();
|
|
UseRemoteTexture(client, aTextureId, aOwnerId, aSize, aFlags);
|
|
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);
|
|
GetThread()->Dispatch(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 (!SendUpdate(cset, mTxn->mDestroyedActors, GetFwdTransactionId())) {
|
|
NS_WARNING("could not send async texture transaction");
|
|
return;
|
|
}
|
|
}
|
|
|
|
bool ImageBridgeChild::InitForContent(Endpoint<PImageBridgeChild>&& aEndpoint,
|
|
uint32_t aNamespace) {
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
gfxPlatform::GetPlatform();
|
|
|
|
if (!sImageBridgeChildThread) {
|
|
nsCOMPtr<nsIThread> thread;
|
|
nsresult rv = NS_NewNamedThread("ImageBridgeChld", getter_AddRefs(thread));
|
|
MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv),
|
|
"Failed to start ImageBridgeChild thread!");
|
|
sImageBridgeChildThread = thread.forget();
|
|
}
|
|
|
|
RefPtr<ImageBridgeChild> child = new ImageBridgeChild(aNamespace);
|
|
|
|
child->GetThread()->Dispatch(NS_NewRunnableFunction(
|
|
"layers::ImageBridgeChild::Bind",
|
|
[child, endpoint = std::move(aEndpoint)]() mutable {
|
|
child->Bind(std::move(endpoint));
|
|
}));
|
|
|
|
// 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(std::move(aEndpoint), aNamespace);
|
|
}
|
|
|
|
void ImageBridgeChild::Bind(Endpoint<PImageBridgeChild>&& aEndpoint) {
|
|
if (!aEndpoint.Bind(this)) {
|
|
return;
|
|
}
|
|
|
|
// This reference is dropped in DeallocPImageBridgeChild.
|
|
this->AddRef();
|
|
|
|
mCanSend = true;
|
|
}
|
|
|
|
void ImageBridgeChild::BindSameProcess(RefPtr<ImageBridgeParent> aParent) {
|
|
Open(aParent, aParent->GetThread(), mozilla::ipc::ChildSide);
|
|
|
|
// This reference is dropped in DeallocPImageBridgeChild.
|
|
this->AddRef();
|
|
|
|
mCanSend = true;
|
|
}
|
|
|
|
/* static */
|
|
void ImageBridgeChild::ShutDown() {
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
ShutdownSingleton();
|
|
|
|
if (sImageBridgeChildThread) {
|
|
sImageBridgeChildThread->Shutdown();
|
|
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);
|
|
GetThread()->Dispatch(runnable.forget());
|
|
|
|
task.Wait();
|
|
}
|
|
|
|
{
|
|
SynchronousTask task("ImageBridge ShutdownStep2 lock");
|
|
|
|
RefPtr<Runnable> runnable =
|
|
WrapRunnable(RefPtr<ImageBridgeChild>(this),
|
|
&ImageBridgeChild::ShutdownStep2, &task);
|
|
GetThread()->Dispatch(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);
|
|
|
|
nsCOMPtr<nsIThread> thread;
|
|
nsresult rv = NS_NewNamedThread("ImageBridgeChld", getter_AddRefs(thread));
|
|
MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv),
|
|
"Failed to start ImageBridgeChild thread!");
|
|
sImageBridgeChildThread = thread.forget();
|
|
|
|
RefPtr<ImageBridgeChild> child = new ImageBridgeChild(aNamespace);
|
|
RefPtr<ImageBridgeParent> parent = ImageBridgeParent::CreateSameProcess();
|
|
|
|
RefPtr<Runnable> runnable =
|
|
WrapRunnable(child, &ImageBridgeChild::BindSameProcess, parent);
|
|
child->GetThread()->Dispatch(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);
|
|
|
|
nsCOMPtr<nsIThread> thread;
|
|
nsresult rv = NS_NewNamedThread("ImageBridgeChld", getter_AddRefs(thread));
|
|
MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv),
|
|
"Failed to start ImageBridgeChild thread!");
|
|
sImageBridgeChildThread = thread.forget();
|
|
|
|
RefPtr<ImageBridgeChild> child = new ImageBridgeChild(aNamespace);
|
|
|
|
child->GetThread()->Dispatch(NS_NewRunnableFunction(
|
|
"layers::ImageBridgeChild::Bind",
|
|
[child, endpoint = std::move(aEndpoint)]() mutable {
|
|
child->Bind(std::move(endpoint));
|
|
}));
|
|
|
|
// 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->IsOnCurrentThread();
|
|
}
|
|
|
|
nsISerialEventTarget* ImageBridgeChild::GetThread() const {
|
|
return sImageBridgeChildThread;
|
|
}
|
|
|
|
/* static */
|
|
void ImageBridgeChild::IdentifyCompositorTextureHost(
|
|
const TextureFactoryIdentifier& aIdentifier) {
|
|
if (RefPtr<ImageBridgeChild> child = GetSingleton()) {
|
|
child->UpdateTextureFactoryIdentifier(aIdentifier);
|
|
}
|
|
}
|
|
|
|
void ImageBridgeChild::UpdateTextureFactoryIdentifier(
|
|
const TextureFactoryIdentifier& aIdentifier) {
|
|
IdentifyTextureHost(aIdentifier);
|
|
}
|
|
|
|
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);
|
|
GetThread()->Dispatch(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;
|
|
}
|
|
|
|
bool ImageBridgeChild::AllocUnsafeShmem(size_t aSize, ipc::Shmem* aShmem) {
|
|
if (!InImageBridgeChildThread()) {
|
|
return DispatchAllocShmemInternal(aSize, aShmem,
|
|
true); // true: unsafe
|
|
}
|
|
|
|
if (!CanSend()) {
|
|
return false;
|
|
}
|
|
return PImageBridgeChild::AllocUnsafeShmem(aSize, aShmem);
|
|
}
|
|
|
|
bool ImageBridgeChild::AllocShmem(size_t aSize, ipc::Shmem* aShmem) {
|
|
if (!InImageBridgeChildThread()) {
|
|
return DispatchAllocShmemInternal(aSize, aShmem,
|
|
false); // false: unsafe
|
|
}
|
|
|
|
if (!CanSend()) {
|
|
return false;
|
|
}
|
|
return PImageBridgeChild::AllocShmem(aSize, aShmem);
|
|
}
|
|
|
|
void ImageBridgeChild::ProxyAllocShmemNow(SynchronousTask* aTask, size_t aSize,
|
|
ipc::Shmem* aShmem, bool aUnsafe,
|
|
bool* aSuccess) {
|
|
AutoCompleteTask complete(aTask);
|
|
|
|
if (!CanSend()) {
|
|
return;
|
|
}
|
|
|
|
bool ok = false;
|
|
if (aUnsafe) {
|
|
ok = AllocUnsafeShmem(aSize, aShmem);
|
|
} else {
|
|
ok = AllocShmem(aSize, aShmem);
|
|
}
|
|
*aSuccess = ok;
|
|
}
|
|
|
|
bool ImageBridgeChild::DispatchAllocShmemInternal(size_t aSize,
|
|
ipc::Shmem* aShmem,
|
|
bool aUnsafe) {
|
|
SynchronousTask task("AllocatorProxy alloc");
|
|
|
|
bool success = false;
|
|
RefPtr<Runnable> runnable = WrapRunnable(
|
|
RefPtr<ImageBridgeChild>(this), &ImageBridgeChild::ProxyAllocShmemNow,
|
|
&task, aSize, aShmem, aUnsafe, &success);
|
|
GetThread()->Dispatch(runnable.forget());
|
|
|
|
task.Wait();
|
|
|
|
return success;
|
|
}
|
|
|
|
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);
|
|
GetThread()->Dispatch(runnable.forget());
|
|
|
|
task.Wait();
|
|
return result;
|
|
}
|
|
|
|
PTextureChild* ImageBridgeChild::AllocPTextureChild(
|
|
const SurfaceDescriptor&, ReadLockDescriptor&, 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(
|
|
nsTArray<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;
|
|
}
|
|
case AsyncParentMessageData::TOpDeliverReleaseFence: {
|
|
#ifdef MOZ_WIDGET_ANDROID
|
|
const OpDeliverReleaseFence& op = message.get_OpDeliverReleaseFence();
|
|
ipc::FileDescriptor fenceFd;
|
|
if (op.fenceFd().isSome()) {
|
|
fenceFd = *op.fenceFd();
|
|
}
|
|
AndroidHardwareBufferManager::Get()->NotifyNotUsed(
|
|
std::move(fenceFd), op.bufferId(), op.fwdTransactionId(),
|
|
op.usesImageBridge());
|
|
#else
|
|
MOZ_ASSERT_UNREACHABLE("unexpected to be called");
|
|
#endif
|
|
break;
|
|
}
|
|
default:
|
|
NS_ERROR("unknown AsyncParentMessageData type");
|
|
return IPC_FAIL_NO_REASON(this);
|
|
}
|
|
}
|
|
return IPC_OK();
|
|
}
|
|
|
|
RefPtr<ImageContainerListener> ImageBridgeChild::FindListener(
|
|
const CompositableHandle& aHandle) {
|
|
RefPtr<ImageContainerListener> listener;
|
|
MutexAutoLock lock(mContainerMapLock);
|
|
auto it = mImageContainerListeners.find(aHandle.Value());
|
|
if (it != mImageContainerListeners.end()) {
|
|
listener = it->second;
|
|
}
|
|
return listener;
|
|
}
|
|
|
|
mozilla::ipc::IPCResult ImageBridgeChild::RecvDidComposite(
|
|
nsTArray<ImageCompositeNotification>&& aNotifications) {
|
|
for (auto& n : aNotifications) {
|
|
RefPtr<ImageContainerListener> listener = FindListener(n.compositable());
|
|
if (listener) {
|
|
listener->NotifyComposite(n);
|
|
}
|
|
}
|
|
return IPC_OK();
|
|
}
|
|
|
|
mozilla::ipc::IPCResult ImageBridgeChild::RecvReportFramesDropped(
|
|
const CompositableHandle& aHandle, const uint32_t& aFrames) {
|
|
RefPtr<ImageContainerListener> listener = FindListener(aHandle);
|
|
if (listener) {
|
|
listener->NotifyDropped(aFrames);
|
|
}
|
|
|
|
return IPC_OK();
|
|
}
|
|
|
|
PTextureChild* ImageBridgeChild::CreateTexture(
|
|
const SurfaceDescriptor& aSharedData, ReadLockDescriptor&& aReadLock,
|
|
LayersBackend aLayersBackend, TextureFlags aFlags, uint64_t aSerial,
|
|
wr::MaybeExternalImageId& aExternalImageId) {
|
|
MOZ_ASSERT(CanSend());
|
|
return SendPTextureConstructor(aSharedData, std::move(aReadLock),
|
|
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);
|
|
GetThread()->Dispatch(runnable.forget());
|
|
return;
|
|
}
|
|
|
|
if (!CanSend()) {
|
|
return;
|
|
}
|
|
|
|
if (!DestroyInTransaction(aHandle)) {
|
|
SendReleaseCompositable(aHandle);
|
|
}
|
|
|
|
{
|
|
MutexAutoLock lock(mContainerMapLock);
|
|
mImageContainerListeners.erase(aHandle.Value());
|
|
}
|
|
}
|
|
|
|
bool ImageBridgeChild::CanSend() const {
|
|
MOZ_ASSERT(InImageBridgeChildThread());
|
|
return mCanSend;
|
|
}
|
|
|
|
void ImageBridgeChild::HandleFatalError(const char* aMsg) const {
|
|
dom::ContentChild::FatalErrorIfNotUsingGPUProcess(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
|