diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
commit | 26a029d407be480d791972afb5975cf62c9360a6 (patch) | |
tree | f435a8308119effd964b339f76abb83a57c29483 /gfx/ipc | |
parent | Initial commit. (diff) | |
download | firefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz firefox-26a029d407be480d791972afb5975cf62c9360a6.zip |
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'gfx/ipc')
45 files changed, 8826 insertions, 0 deletions
diff --git a/gfx/ipc/CanvasManagerChild.cpp b/gfx/ipc/CanvasManagerChild.cpp new file mode 100644 index 0000000000..eca803bc4b --- /dev/null +++ b/gfx/ipc/CanvasManagerChild.cpp @@ -0,0 +1,338 @@ +/* -*- 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 "CanvasManagerChild.h" +#include "mozilla/AppShutdown.h" +#include "mozilla/dom/CanvasRenderingContext2D.h" +#include "mozilla/dom/WorkerPrivate.h" +#include "mozilla/dom/WorkerRef.h" +#include "mozilla/gfx/2D.h" +#include "mozilla/gfx/Swizzle.h" +#include "mozilla/ipc/Endpoint.h" +#include "mozilla/layers/ActiveResource.h" +#include "mozilla/layers/CanvasChild.h" +#include "mozilla/layers/CompositorManagerChild.h" +#include "mozilla/webgpu/WebGPUChild.h" + +using namespace mozilla::dom; +using namespace mozilla::layers; + +namespace mozilla::gfx { + +// The IPDL actor holds a strong reference to CanvasManagerChild which we use +// to keep it alive. The owning thread will tell us to close when it is +// shutdown, either via CanvasManagerChild::Shutdown for the main thread, or +// via a shutdown callback from ThreadSafeWorkerRef for worker threads. +MOZ_THREAD_LOCAL(CanvasManagerChild*) CanvasManagerChild::sLocalManager; + +Atomic<uint32_t> CanvasManagerChild::sNextId(1); + +CanvasManagerChild::CanvasManagerChild(uint32_t aId) : mId(aId) {} +CanvasManagerChild::~CanvasManagerChild() = default; + +void CanvasManagerChild::ActorDestroy(ActorDestroyReason aReason) { + DestroyInternal(); + if (sLocalManager.get() == this) { + sLocalManager.set(nullptr); + } + mWorkerRef = nullptr; +} + +void CanvasManagerChild::DestroyInternal() { + std::set<CanvasRenderingContext2D*> activeCanvas = std::move(mActiveCanvas); + for (const auto& i : activeCanvas) { + i->OnShutdown(); + } + + if (mActiveResourceTracker) { + mActiveResourceTracker->AgeAllGenerations(); + mActiveResourceTracker.reset(); + } + + if (mCanvasChild) { + mCanvasChild->Destroy(); + mCanvasChild = nullptr; + } +} + +void CanvasManagerChild::Destroy() { + DestroyInternal(); + + // The caller has a strong reference. ActorDestroy will clear sLocalManager + // and mWorkerRef. + Close(); +} + +/* static */ void CanvasManagerChild::Shutdown() { + MOZ_ASSERT(NS_IsMainThread()); + + // The worker threads should destroy their own CanvasManagerChild instances + // during their shutdown sequence. We just need to take care of the main + // thread. We need to init here because we may have never created a + // CanvasManagerChild for the main thread in the first place. + if (sLocalManager.init()) { + RefPtr<CanvasManagerChild> manager = sLocalManager.get(); + if (manager) { + manager->Destroy(); + } + } +} + +/* static */ bool CanvasManagerChild::CreateParent( + ipc::Endpoint<PCanvasManagerParent>&& aEndpoint) { + MOZ_ASSERT(NS_IsMainThread()); + + CompositorManagerChild* manager = CompositorManagerChild::GetInstance(); + if (!manager || !manager->CanSend()) { + return false; + } + + return manager->SendInitCanvasManager(std::move(aEndpoint)); +} + +/* static */ CanvasManagerChild* CanvasManagerChild::Get() { + if (NS_WARN_IF(!sLocalManager.init())) { + return nullptr; + } + + CanvasManagerChild* managerWeak = sLocalManager.get(); + if (managerWeak) { + return managerWeak; + } + + // We are only used on the main thread, or on worker threads. + WorkerPrivate* worker = GetCurrentThreadWorkerPrivate(); + MOZ_ASSERT_IF(!worker, NS_IsMainThread()); + + ipc::Endpoint<PCanvasManagerParent> parentEndpoint; + ipc::Endpoint<PCanvasManagerChild> childEndpoint; + + auto compositorPid = CompositorManagerChild::GetOtherPid(); + if (NS_WARN_IF(!compositorPid)) { + return nullptr; + } + + nsresult rv = PCanvasManager::CreateEndpoints( + compositorPid, base::GetCurrentProcId(), &parentEndpoint, &childEndpoint); + if (NS_WARN_IF(NS_FAILED(rv))) { + return nullptr; + } + + auto manager = MakeRefPtr<CanvasManagerChild>(sNextId++); + + if (worker) { + // The ThreadSafeWorkerRef will let us know when the worker is shutting + // down. This will let us clear our threadlocal reference and close the + // actor. We rely upon an explicit shutdown for the main thread. + RefPtr<StrongWorkerRef> workerRef = StrongWorkerRef::Create( + worker, "CanvasManager", [manager]() { manager->Destroy(); }); + if (NS_WARN_IF(!workerRef)) { + return nullptr; + } + + manager->mWorkerRef = new ThreadSafeWorkerRef(workerRef); + } else if (NS_IsMainThread()) { + if (NS_WARN_IF( + AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed))) { + return nullptr; + } + } else { + MOZ_ASSERT_UNREACHABLE("Can only be used on main or DOM worker threads!"); + return nullptr; + } + + if (NS_WARN_IF(!childEndpoint.Bind(manager))) { + return nullptr; + } + + // We can't talk to the compositor process directly from worker threads, but + // the main thread can via CompositorManagerChild. + if (worker) { + worker->DispatchToMainThread(NS_NewRunnableFunction( + "CanvasManagerChild::CreateParent", + [parentEndpoint = std::move(parentEndpoint)]() { + CreateParent( + std::move(const_cast<ipc::Endpoint<PCanvasManagerParent>&&>( + parentEndpoint))); + })); + } else if (NS_WARN_IF(!CreateParent(std::move(parentEndpoint)))) { + return nullptr; + } + + manager->SendInitialize(manager->Id()); + sLocalManager.set(manager); + return manager; +} + +/* static */ CanvasManagerChild* CanvasManagerChild::MaybeGet() { + if (!sLocalManager.initialized()) { + return nullptr; + } + + return sLocalManager.get(); +} + +void CanvasManagerChild::AddShutdownObserver( + dom::CanvasRenderingContext2D* aCanvas) { + mActiveCanvas.insert(aCanvas); +} + +void CanvasManagerChild::RemoveShutdownObserver( + dom::CanvasRenderingContext2D* aCanvas) { + mActiveCanvas.erase(aCanvas); +} + +void CanvasManagerChild::EndCanvasTransaction() { + if (!mCanvasChild) { + return; + } + + mCanvasChild->EndTransaction(); + if (mCanvasChild->ShouldBeCleanedUp()) { + mCanvasChild->Destroy(); + mCanvasChild = nullptr; + } +} + +void CanvasManagerChild::ClearCachedResources() { + if (mCanvasChild) { + mCanvasChild->ClearCachedResources(); + } +} + +void CanvasManagerChild::DeactivateCanvas() { + mActive = false; + if (mCanvasChild) { + mCanvasChild->Destroy(); + mCanvasChild = nullptr; + } +} + +void CanvasManagerChild::BlockCanvas() { mBlocked = true; } + +RefPtr<layers::CanvasChild> CanvasManagerChild::GetCanvasChild() { + if (mBlocked) { + return nullptr; + } + + if (!mActive) { + MOZ_ASSERT(!mCanvasChild); + return nullptr; + } + + if (!mCanvasChild) { + mCanvasChild = MakeAndAddRef<layers::CanvasChild>(); + if (!SendPCanvasConstructor(mCanvasChild)) { + mCanvasChild = nullptr; + } + } + + return mCanvasChild; +} + +RefPtr<webgpu::WebGPUChild> CanvasManagerChild::GetWebGPUChild() { + if (!mWebGPUChild) { + mWebGPUChild = MakeAndAddRef<webgpu::WebGPUChild>(); + if (!SendPWebGPUConstructor(mWebGPUChild)) { + mWebGPUChild = nullptr; + } + } + + return mWebGPUChild; +} + +layers::ActiveResourceTracker* CanvasManagerChild::GetActiveResourceTracker() { + if (!mActiveResourceTracker) { + mActiveResourceTracker = MakeUnique<ActiveResourceTracker>( + 1000, "CanvasManagerChild", GetCurrentSerialEventTarget()); + } + return mActiveResourceTracker.get(); +} + +already_AddRefed<DataSourceSurface> CanvasManagerChild::GetSnapshot( + uint32_t aManagerId, int32_t aProtocolId, + const Maybe<RemoteTextureOwnerId>& aOwnerId, SurfaceFormat aFormat, + bool aPremultiply, bool aYFlip) { + if (!CanSend()) { + return nullptr; + } + + webgl::FrontBufferSnapshotIpc res; + if (!SendGetSnapshot(aManagerId, aProtocolId, aOwnerId, &res)) { + return nullptr; + } + + if (!res.shmem || !res.shmem->IsReadable()) { + return nullptr; + } + + auto guard = MakeScopeExit([&] { DeallocShmem(res.shmem.ref()); }); + + if (!res.surfSize.x || !res.surfSize.y || res.surfSize.x > INT32_MAX || + res.surfSize.y > INT32_MAX) { + return nullptr; + } + + IntSize size(res.surfSize.x, res.surfSize.y); + CheckedInt32 stride = CheckedInt32(size.width) * sizeof(uint32_t); + if (!stride.isValid()) { + return nullptr; + } + + CheckedInt32 length = stride * size.height; + if (!length.isValid() || + size_t(length.value()) != res.shmem->Size<uint8_t>()) { + return nullptr; + } + + SurfaceFormat format = + IsOpaque(aFormat) ? SurfaceFormat::B8G8R8X8 : SurfaceFormat::B8G8R8A8; + RefPtr<DataSourceSurface> surface = + Factory::CreateDataSourceSurfaceWithStride(size, format, stride.value(), + /* aZero */ false); + if (!surface) { + return nullptr; + } + + gfx::DataSourceSurface::ScopedMap map(surface, + gfx::DataSourceSurface::READ_WRITE); + if (!map.IsMapped()) { + return nullptr; + } + + // The buffer we may readback from the canvas could be R8G8B8A8, not + // premultiplied, and/or has its rows iverted. For the general case, we want + // surfaces represented as premultiplied B8G8R8A8, with its rows ordered top + // to bottom. Given this path is used for screenshots/SurfaceFromElement, + // that's the representation we need. + if (aYFlip) { + if (aPremultiply) { + if (!PremultiplyYFlipData(res.shmem->get<uint8_t>(), stride.value(), + aFormat, map.GetData(), map.GetStride(), format, + size)) { + return nullptr; + } + } else { + if (!SwizzleYFlipData(res.shmem->get<uint8_t>(), stride.value(), aFormat, + map.GetData(), map.GetStride(), format, size)) { + return nullptr; + } + } + } else if (aPremultiply) { + if (!PremultiplyData(res.shmem->get<uint8_t>(), stride.value(), aFormat, + map.GetData(), map.GetStride(), format, size)) { + return nullptr; + } + } else { + if (!SwizzleData(res.shmem->get<uint8_t>(), stride.value(), aFormat, + map.GetData(), map.GetStride(), format, size)) { + return nullptr; + } + } + return surface.forget(); +} + +} // namespace mozilla::gfx diff --git a/gfx/ipc/CanvasManagerChild.h b/gfx/ipc/CanvasManagerChild.h new file mode 100644 index 0000000000..6369d87ada --- /dev/null +++ b/gfx/ipc/CanvasManagerChild.h @@ -0,0 +1,88 @@ +/* -*- 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/. */ +#ifndef _include_gfx_ipc_CanvasManagerChild_h__ +#define _include_gfx_ipc_CanvasManagerChild_h__ + +#include "mozilla/Atomics.h" +#include "mozilla/gfx/PCanvasManagerChild.h" +#include "mozilla/gfx/Types.h" +#include "mozilla/ThreadLocal.h" +#include <set> + +namespace mozilla { +namespace dom { +class CanvasRenderingContext2D; +class ThreadSafeWorkerRef; +class WorkerPrivate; +} // namespace dom + +namespace layers { +class CanvasChild; +class ActiveResourceTracker; +} // namespace layers + +namespace webgpu { +class WebGPUChild; +} // namespace webgpu + +namespace gfx { +class DataSourceSurface; + +class CanvasManagerChild final : public PCanvasManagerChild { + public: + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CanvasManagerChild, override); + + explicit CanvasManagerChild(uint32_t aId); + uint32_t Id() const { return mId; } + already_AddRefed<DataSourceSurface> GetSnapshot( + uint32_t aManagerId, int32_t aProtocolId, + const Maybe<RemoteTextureOwnerId>& aOwnerId, SurfaceFormat aFormat, + bool aPremultiply, bool aYFlip); + void ActorDestroy(ActorDestroyReason aReason) override; + + static CanvasManagerChild* Get(); + static CanvasManagerChild* MaybeGet(); + static void Shutdown(); + static bool CreateParent( + mozilla::ipc::Endpoint<PCanvasManagerParent>&& aEndpoint); + + void AddShutdownObserver(dom::CanvasRenderingContext2D* aCanvas); + void RemoveShutdownObserver(dom::CanvasRenderingContext2D* aCanvas); + + bool IsCanvasActive() { return mActive; } + void EndCanvasTransaction(); + void ClearCachedResources(); + void DeactivateCanvas(); + void BlockCanvas(); + + RefPtr<layers::CanvasChild> GetCanvasChild(); + + RefPtr<webgpu::WebGPUChild> GetWebGPUChild(); + + layers::ActiveResourceTracker* GetActiveResourceTracker(); + + private: + ~CanvasManagerChild(); + void DestroyInternal(); + void Destroy(); + + RefPtr<mozilla::dom::ThreadSafeWorkerRef> mWorkerRef; + RefPtr<layers::CanvasChild> mCanvasChild; + RefPtr<webgpu::WebGPUChild> mWebGPUChild; + UniquePtr<layers::ActiveResourceTracker> mActiveResourceTracker; + std::set<dom::CanvasRenderingContext2D*> mActiveCanvas; + const uint32_t mId; + bool mActive = true; + bool mBlocked = false; + + static MOZ_THREAD_LOCAL(CanvasManagerChild*) sLocalManager; + static Atomic<uint32_t> sNextId; +}; + +} // namespace gfx +} // namespace mozilla + +#endif // _include_gfx_ipc_CanvasManagerChild_h__ diff --git a/gfx/ipc/CanvasManagerParent.cpp b/gfx/ipc/CanvasManagerParent.cpp new file mode 100644 index 0000000000..50fdb6e104 --- /dev/null +++ b/gfx/ipc/CanvasManagerParent.cpp @@ -0,0 +1,236 @@ +/* -*- 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 "CanvasManagerParent.h" +#include "gfxPlatform.h" +#include "mozilla/dom/WebGLParent.h" +#include "mozilla/gfx/CanvasRenderThread.h" +#include "mozilla/gfx/gfxVars.h" +#include "mozilla/gfx/GPUParent.h" +#include "mozilla/ipc/Endpoint.h" +#include "mozilla/layers/CanvasTranslator.h" +#include "mozilla/layers/CompositorThread.h" +#include "mozilla/layers/ISurfaceAllocator.h" +#include "mozilla/layers/SharedSurfacesParent.h" +#include "mozilla/StaticPrefs_gfx.h" +#include "mozilla/StaticPrefs_webgl.h" +#include "mozilla/webgpu/WebGPUParent.h" +#include "nsIThread.h" +#include "nsThreadUtils.h" + +namespace mozilla::gfx { + +CanvasManagerParent::ManagerSet CanvasManagerParent::sManagers; + +/* static */ void CanvasManagerParent::Init( + Endpoint<PCanvasManagerParent>&& aEndpoint, + layers::SharedSurfacesHolder* aSharedSurfacesHolder, + const dom::ContentParentId& aContentId) { + MOZ_ASSERT(layers::CompositorThreadHolder::IsInCompositorThread()); + + auto manager = + MakeRefPtr<CanvasManagerParent>(aSharedSurfacesHolder, aContentId); + + nsCOMPtr<nsIThread> owningThread = + gfx::CanvasRenderThread::GetCanvasRenderThread(); + MOZ_ASSERT(owningThread); + + owningThread->Dispatch(NewRunnableMethod<Endpoint<PCanvasManagerParent>&&>( + "CanvasManagerParent::Bind", manager, &CanvasManagerParent::Bind, + std::move(aEndpoint))); +} + +/* static */ void CanvasManagerParent::Shutdown() { + MOZ_ASSERT(NS_IsMainThread()); + + nsCOMPtr<nsIThread> owningThread = + gfx::CanvasRenderThread::GetCanvasRenderThread(); + MOZ_ASSERT(owningThread); + + NS_DispatchAndSpinEventLoopUntilComplete( + "CanvasManagerParent::Shutdown"_ns, owningThread, + NS_NewRunnableFunction("CanvasManagerParent::Shutdown", []() -> void { + CanvasManagerParent::ShutdownInternal(); + })); +} + +/* static */ void CanvasManagerParent::ShutdownInternal() { + nsTArray<RefPtr<CanvasManagerParent>> actors(sManagers.Count()); + // Do a copy since Close will remove the entry from the set. + for (const auto& actor : sManagers) { + actors.AppendElement(actor); + } + + for (auto const& actor : actors) { + actor->Close(); + } +} + +/* static */ void CanvasManagerParent::DisableRemoteCanvas() { + NS_DispatchToMainThread( + NS_NewRunnableFunction("CanvasManagerParent::DisableRemoteCanvas", [] { + if (XRE_IsGPUProcess()) { + GPUParent::GetSingleton()->NotifyDisableRemoteCanvas(); + } else { + gfxPlatform::DisableRemoteCanvas(); + } + })); + + if (CanvasRenderThread::IsInCanvasRenderThread()) { + DisableRemoteCanvasInternal(); + return; + } + + CanvasRenderThread::Dispatch(NS_NewRunnableFunction( + "CanvasManagerParent::DisableRemoteCanvas", + [] { CanvasManagerParent::DisableRemoteCanvasInternal(); })); +} + +/* static */ void CanvasManagerParent::DisableRemoteCanvasInternal() { + MOZ_ASSERT(CanvasRenderThread::IsInCanvasRenderThread()); + + AutoTArray<RefPtr<layers::CanvasTranslator>, 16> actors; + for (const auto& manager : sManagers) { + for (const auto& canvas : manager->ManagedPCanvasParent()) { + actors.AppendElement(static_cast<layers::CanvasTranslator*>(canvas)); + } + } + + for (const auto& actor : actors) { + Unused << NS_WARN_IF(!actor->SendDeactivate()); + } +} + +CanvasManagerParent::CanvasManagerParent( + layers::SharedSurfacesHolder* aSharedSurfacesHolder, + const dom::ContentParentId& aContentId) + : mSharedSurfacesHolder(aSharedSurfacesHolder), mContentId(aContentId) {} + +CanvasManagerParent::~CanvasManagerParent() = default; + +void CanvasManagerParent::Bind(Endpoint<PCanvasManagerParent>&& aEndpoint) { + if (!aEndpoint.Bind(this)) { + NS_WARNING("Failed to bind CanvasManagerParent!"); + return; + } + +#ifdef DEBUG + for (CanvasManagerParent* i : sManagers) { + MOZ_ASSERT_IF(i->mContentId == mContentId, + i->OtherPidMaybeInvalid() == OtherPidMaybeInvalid()); + } +#endif + + sManagers.Insert(this); +} + +void CanvasManagerParent::ActorDestroy(ActorDestroyReason aWhy) { + sManagers.Remove(this); +} + +already_AddRefed<dom::PWebGLParent> CanvasManagerParent::AllocPWebGLParent() { + if (NS_WARN_IF(!gfxVars::AllowWebglOop() && + !StaticPrefs::webgl_out_of_process_force())) { + MOZ_ASSERT_UNREACHABLE("AllocPWebGLParent without remote WebGL"); + return nullptr; + } + return MakeAndAddRef<dom::WebGLParent>(mContentId); +} + +already_AddRefed<webgpu::PWebGPUParent> +CanvasManagerParent::AllocPWebGPUParent() { + if (NS_WARN_IF(!gfxVars::AllowWebGPU())) { + MOZ_ASSERT_UNREACHABLE("AllocPWebGPUParent without WebGPU"); + return nullptr; + } + + return MakeAndAddRef<webgpu::WebGPUParent>(); +} + +mozilla::ipc::IPCResult CanvasManagerParent::RecvInitialize( + const uint32_t& aId) { + if (!aId) { + return IPC_FAIL(this, "invalid id"); + } + if (mId) { + return IPC_FAIL(this, "already initialized"); + } + mId = aId; + return IPC_OK(); +} + +already_AddRefed<layers::PCanvasParent> +CanvasManagerParent::AllocPCanvasParent() { + if (NS_WARN_IF(!gfx::gfxVars::RemoteCanvasEnabled() && + !gfx::gfxVars::UseAcceleratedCanvas2D())) { + MOZ_ASSERT_UNREACHABLE("AllocPCanvasParent without remote canvas"); + return nullptr; + } + if (NS_WARN_IF(!mId)) { + MOZ_ASSERT_UNREACHABLE("AllocPCanvasParent without ID"); + return nullptr; + } + return MakeAndAddRef<layers::CanvasTranslator>(mSharedSurfacesHolder, + mContentId, mId); +} + +mozilla::ipc::IPCResult CanvasManagerParent::RecvGetSnapshot( + const uint32_t& aManagerId, const int32_t& aProtocolId, + const Maybe<RemoteTextureOwnerId>& aOwnerId, + webgl::FrontBufferSnapshotIpc* aResult) { + if (!aManagerId) { + return IPC_FAIL(this, "invalid id"); + } + + IProtocol* actor = nullptr; + for (CanvasManagerParent* i : sManagers) { + if (i->mContentId == mContentId && i->mId == aManagerId) { + actor = i->Lookup(aProtocolId); + break; + } + } + + if (!actor) { + return IPC_FAIL(this, "invalid actor"); + } + + if (actor->GetSide() != mozilla::ipc::Side::ParentSide) { + return IPC_FAIL(this, "unsupported actor"); + } + + webgl::FrontBufferSnapshotIpc buffer; + switch (actor->GetProtocolId()) { + case ProtocolId::PWebGLMsgStart: { + RefPtr<dom::WebGLParent> webgl = static_cast<dom::WebGLParent*>(actor); + mozilla::ipc::IPCResult rv = webgl->GetFrontBufferSnapshot(&buffer, this); + if (!rv) { + return rv; + } + } break; + case ProtocolId::PWebGPUMsgStart: { + RefPtr<webgpu::WebGPUParent> webgpu = + static_cast<webgpu::WebGPUParent*>(actor); + IntSize size; + if (aOwnerId.isNothing()) { + return IPC_FAIL(this, "invalid OwnerId"); + } + mozilla::ipc::IPCResult rv = + webgpu->GetFrontBufferSnapshot(this, *aOwnerId, buffer.shmem, size); + if (!rv) { + return rv; + } + buffer.surfSize.x = static_cast<uint32_t>(size.width); + buffer.surfSize.y = static_cast<uint32_t>(size.height); + } break; + default: + return IPC_FAIL(this, "unsupported protocol"); + } + + *aResult = std::move(buffer); + return IPC_OK(); +} + +} // namespace mozilla::gfx diff --git a/gfx/ipc/CanvasManagerParent.h b/gfx/ipc/CanvasManagerParent.h new file mode 100644 index 0000000000..1eaadc3ad5 --- /dev/null +++ b/gfx/ipc/CanvasManagerParent.h @@ -0,0 +1,71 @@ +/* -*- 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/. */ +#ifndef _include_gfx_ipc_CanvasManagerParent_h__ +#define _include_gfx_ipc_CanvasManagerParent_h__ + +#include "mozilla/gfx/PCanvasManagerParent.h" +#include "mozilla/dom/ipc/IdType.h" +#include "mozilla/StaticMonitor.h" +#include "mozilla/UniquePtr.h" +#include "nsHashtablesFwd.h" +#include "nsTArray.h" + +namespace mozilla { +namespace layers { +class CanvasTranslator; +class HostIPCAllocator; +class SharedSurfacesHolder; +class SurfaceDescriptor; +} // namespace layers + +namespace gfx { + +class CanvasManagerParent final : public PCanvasManagerParent { + public: + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CanvasManagerParent, override); + + static void Init(Endpoint<PCanvasManagerParent>&& aEndpoint, + layers::SharedSurfacesHolder* aSharedSurfacesHolder, + const dom::ContentParentId& aContentId); + + static void Shutdown(); + + static void DisableRemoteCanvas(); + + CanvasManagerParent(layers::SharedSurfacesHolder* aSharedSurfacesHolder, + const dom::ContentParentId& aContentId); + + void Bind(Endpoint<PCanvasManagerParent>&& aEndpoint); + void ActorDestroy(ActorDestroyReason aWhy) override; + + already_AddRefed<dom::PWebGLParent> AllocPWebGLParent(); + already_AddRefed<webgpu::PWebGPUParent> AllocPWebGPUParent(); + already_AddRefed<layers::PCanvasParent> AllocPCanvasParent(); + + mozilla::ipc::IPCResult RecvInitialize(const uint32_t& aId); + mozilla::ipc::IPCResult RecvGetSnapshot( + const uint32_t& aManagerId, const int32_t& aProtocolId, + const Maybe<RemoteTextureOwnerId>& aOwnerId, + webgl::FrontBufferSnapshotIpc* aResult); + + private: + static void ShutdownInternal(); + static void DisableRemoteCanvasInternal(); + + ~CanvasManagerParent() override; + + RefPtr<layers::SharedSurfacesHolder> mSharedSurfacesHolder; + const dom::ContentParentId mContentId; + uint32_t mId = 0; + + using ManagerSet = nsTHashSet<RefPtr<CanvasManagerParent>>; + static ManagerSet sManagers; +}; + +} // namespace gfx +} // namespace mozilla + +#endif // _include_gfx_ipc_CanvasManagerParent_h__ diff --git a/gfx/ipc/CanvasRenderThread.cpp b/gfx/ipc/CanvasRenderThread.cpp new file mode 100644 index 0000000000..853806a222 --- /dev/null +++ b/gfx/ipc/CanvasRenderThread.cpp @@ -0,0 +1,279 @@ +/* -*- 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 "CanvasRenderThread.h" + +#include "mozilla/BackgroundHangMonitor.h" +#include "mozilla/SharedThreadPool.h" +#include "mozilla/StaticPrefs_gfx.h" +#include "mozilla/TaskQueue.h" +#include "mozilla/gfx/CanvasManagerParent.h" +#include "mozilla/gfx/gfxVars.h" +#include "mozilla/layers/CanvasTranslator.h" +#include "mozilla/layers/CompositorThread.h" +#include "mozilla/webrender/RenderThread.h" +#include "nsThread.h" +#include "prsystem.h" +#include "transport/runnable_utils.h" + +bool NS_IsInCanvasThreadOrWorker() { + return mozilla::gfx::CanvasRenderThread::IsInCanvasRenderOrWorkerThread(); +} + +namespace mozilla::gfx { + +static StaticRefPtr<CanvasRenderThread> sCanvasRenderThread; +static mozilla::BackgroundHangMonitor* sBackgroundHangMonitor; +#ifdef DEBUG +static bool sCanvasRenderThreadEverStarted = false; +#endif + +CanvasRenderThread::CanvasRenderThread(nsCOMPtr<nsIThread>&& aThread, + nsCOMPtr<nsIThreadPool>&& aWorkers, + bool aCreatedThread) + : mMutex("CanvasRenderThread::mMutex"), + mThread(std::move(aThread)), + mWorkers(std::move(aWorkers)), + mCreatedThread(aCreatedThread) {} + +CanvasRenderThread::~CanvasRenderThread() = default; + +// static +void CanvasRenderThread::Start() { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(!sCanvasRenderThread); + +#ifdef DEBUG + // Check to ensure nobody will try to ever start us more than once during + // the process' lifetime (in particular after Stop). + MOZ_ASSERT(!sCanvasRenderThreadEverStarted); + sCanvasRenderThreadEverStarted = true; +#endif + + int32_t threadPref = + StaticPrefs::gfx_canvas_remote_worker_threads_AtStartup(); + uint32_t threadLimit; + if (threadPref < 0) { + // Given that the canvas workers are receiving instructions from + // content processes, it probably doesn't make sense to have more than + // half the number of processors doing canvas drawing. We set the + // lower limit to 2, so that even on single processor systems, if + // there is more than one window with canvas drawing, the OS can + // manage the load between them. + threadLimit = std::max(2, PR_GetNumberOfProcessors() / 2); + } else { + threadLimit = uint32_t(threadPref); + } + + // We don't spawn any workers if the user set the limit to 0. Instead we will + // use the CanvasRenderThread virtual thread. + nsCOMPtr<nsIThreadPool> workers; + if (threadLimit > 0) { + workers = SharedThreadPool::Get("CanvasWorkers"_ns, threadLimit); + if (NS_WARN_IF(!workers)) { + return; + } + } + + nsCOMPtr<nsIThread> thread; + if (!gfxVars::SupportsThreadsafeGL()) { + thread = wr::RenderThread::GetRenderThread(); + MOZ_ASSERT(thread); + } else if (!gfxVars::UseCanvasRenderThread()) { + thread = layers::CompositorThread(); + MOZ_ASSERT(thread); + } + + if (thread) { + sCanvasRenderThread = new CanvasRenderThread( + std::move(thread), std::move(workers), /* aCreatedThread */ false); + return; + } + + // This is 4M, which is higher than the default 256K. + // Increased with bug 1753349 to accommodate the `chromium/5359` branch of + // ANGLE, which has large peak stack usage for some pathological shader + // compilations. + // + // Previously increased to 512K to accommodate Mesa in bug 1753340. + // + // Previously increased to 320K to avoid a stack overflow in the + // Intel Vulkan driver initialization in bug 1716120. + // + // Note: we only override it if it's limited already. + const uint32_t stackSize = + nsIThreadManager::DEFAULT_STACK_SIZE ? 4096 << 10 : 0; + + nsresult rv = NS_NewNamedThread( + "CanvasRenderer", getter_AddRefs(thread), + NS_NewRunnableFunction( + "CanvasRender::BackgroundHangSetup", + []() { + sBackgroundHangMonitor = new mozilla::BackgroundHangMonitor( + "CanvasRendererBHM", + /* Timeout values are powers-of-two to enable us get better + data. 128ms is chosen for transient hangs because 8Hz should + be the minimally acceptable goal for Compositor + responsiveness (normal goal is 60Hz). */ + 128, + /* 2048ms is chosen for permanent hangs because it's longer than + * most Compositor hangs seen in the wild, but is short enough + * to not miss getting native hang stacks. */ + 2048); + nsCOMPtr<nsIThread> thread = NS_GetCurrentThread(); + nsThread* nsthread = static_cast<nsThread*>(thread.get()); + nsthread->SetUseHangMonitor(true); + nsthread->SetPriority(nsISupportsPriority::PRIORITY_HIGH); + }), + {.stackSize = stackSize}); + + if (NS_WARN_IF(NS_FAILED(rv))) { + return; + } + + sCanvasRenderThread = new CanvasRenderThread( + std::move(thread), std::move(workers), /* aCreatedThread */ true); +} + +// static +void CanvasRenderThread::Shutdown() { + MOZ_ASSERT(NS_IsMainThread()); + + // It is possible we never initialized this thread in the parent process, + // because we used the GPU process instead. + if (!sCanvasRenderThread) { + MOZ_ASSERT(XRE_IsParentProcess()); + return; + } + + // This closes all of the IPDL actors with possibly active task queues. + CanvasManagerParent::Shutdown(); + + // Any task queues that are in the process of shutting down are tracked in + // mPendingShutdownTaskQueues. We need to block on each one until all events + // are flushed so that we can safely teardown RemoteTextureMap afterwards. + while (true) { + RefPtr<TaskQueue> taskQueue; + { + MutexAutoLock lock(sCanvasRenderThread->mMutex); + + auto& pendingQueues = sCanvasRenderThread->mPendingShutdownTaskQueues; + if (pendingQueues.IsEmpty()) { + break; + } + + taskQueue = pendingQueues.PopLastElement(); + } + taskQueue->AwaitShutdownAndIdle(); + } + + bool createdThread = sCanvasRenderThread->mCreatedThread; + nsCOMPtr<nsIThread> oldThread = sCanvasRenderThread->GetCanvasRenderThread(); + nsCOMPtr<nsIThreadPool> oldWorkers = sCanvasRenderThread->mWorkers; + + // Ensure that we flush the CanvasRenderThread event queue before clearing our + // singleton. + NS_DispatchAndSpinEventLoopUntilComplete( + "CanvasRenderThread::Shutdown"_ns, oldThread, + NS_NewRunnableFunction("CanvasRenderThread::Shutdown", []() -> void {})); + + // Null out sCanvasRenderThread before we enter synchronous Shutdown, + // from here on we are to be considered shut down for our consumers. + sCanvasRenderThread = nullptr; + + if (oldWorkers) { + oldWorkers->Shutdown(); + } + + // We do a synchronous shutdown here while spinning the MT event loop, but + // only if we created a dedicated CanvasRender thread. + if (createdThread) { + oldThread->Shutdown(); + } +} + +// static +bool CanvasRenderThread::IsInCanvasRenderThread() { + return sCanvasRenderThread && + sCanvasRenderThread->mThread == NS_GetCurrentThread(); +} + +/* static */ bool CanvasRenderThread::IsInCanvasWorkerThread() { + // It is possible there are no worker threads, and the worker is the same as + // the CanvasRenderThread itself. + return sCanvasRenderThread && + ((sCanvasRenderThread->mWorkers && + sCanvasRenderThread->mWorkers->IsOnCurrentThread()) || + (!sCanvasRenderThread->mWorkers && + sCanvasRenderThread->mThread == NS_GetCurrentThread())); +} + +/* static */ bool CanvasRenderThread::IsInCanvasRenderOrWorkerThread() { + // It is possible there are no worker threads, and the worker is the same as + // the CanvasRenderThread itself. + return sCanvasRenderThread && + (sCanvasRenderThread->mThread == NS_GetCurrentThread() || + (sCanvasRenderThread->mWorkers && + sCanvasRenderThread->mWorkers->IsOnCurrentThread())); +} + +// static +already_AddRefed<nsIThread> CanvasRenderThread::GetCanvasRenderThread() { + nsCOMPtr<nsIThread> thread; + if (sCanvasRenderThread) { + thread = sCanvasRenderThread->mThread; + } + return thread.forget(); +} + +/* static */ already_AddRefed<TaskQueue> +CanvasRenderThread::CreateWorkerTaskQueue() { + if (!sCanvasRenderThread || !sCanvasRenderThread->mWorkers) { + return nullptr; + } + + return TaskQueue::Create(do_AddRef(sCanvasRenderThread->mWorkers), + "CanvasWorker") + .forget(); +} + +/* static */ void CanvasRenderThread::ShutdownWorkerTaskQueue( + TaskQueue* aTaskQueue) { + MOZ_ASSERT(aTaskQueue); + + aTaskQueue->BeginShutdown(); + + if (!sCanvasRenderThread) { + MOZ_ASSERT_UNREACHABLE("No CanvasRenderThread!"); + return; + } + + MutexAutoLock lock(sCanvasRenderThread->mMutex); + auto& pendingQueues = sCanvasRenderThread->mPendingShutdownTaskQueues; + pendingQueues.AppendElement(aTaskQueue); +} + +/* static */ void CanvasRenderThread::FinishShutdownWorkerTaskQueue( + TaskQueue* aTaskQueue) { + if (!sCanvasRenderThread) { + return; + } + + MutexAutoLock lock(sCanvasRenderThread->mMutex); + sCanvasRenderThread->mPendingShutdownTaskQueues.RemoveElement(aTaskQueue); +} + +/* static */ void CanvasRenderThread::Dispatch( + already_AddRefed<nsIRunnable> aRunnable) { + if (!sCanvasRenderThread) { + MOZ_DIAGNOSTIC_ASSERT(false, + "Dispatching after CanvasRenderThread shutdown!"); + return; + } + sCanvasRenderThread->mThread->Dispatch(std::move(aRunnable)); +} + +} // namespace mozilla::gfx diff --git a/gfx/ipc/CanvasRenderThread.h b/gfx/ipc/CanvasRenderThread.h new file mode 100644 index 0000000000..d94b320ad3 --- /dev/null +++ b/gfx/ipc/CanvasRenderThread.h @@ -0,0 +1,85 @@ +/* -*- 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/. */ + +#ifndef _include_gfx_ipc_CanvasRenderThread_h__ +#define _include_gfx_ipc_CanvasRenderThread_h__ + +#include "mozilla/AlreadyAddRefed.h" +#include "mozilla/Mutex.h" +#include "nsCOMPtr.h" +#include "nsISupportsImpl.h" +#include "nsTArray.h" + +class nsIRunnable; +class nsIThread; +class nsIThreadPool; + +namespace mozilla { +class TaskQueue; + +namespace gfx { + +/** + * This class represents the virtual thread for canvas rendering. Depending on + * platform requirements and user configuration, canvas rendering may happen on + * the Compositor thread, Render thread or the CanvasRender thread. + */ +class CanvasRenderThread final { + NS_INLINE_DECL_THREADSAFE_REFCOUNTING_WITH_DELETE_ON_MAIN_THREAD( + CanvasRenderThread) + + public: + /// Can only be called from the main thread, expected to be called at most + /// once during a process' lifetime. Must be called after the Compositor and + /// Render threads are initialized. + static void Start(); + + /// Can only be called from the main thread. Must be called before the + /// Compositor and Render threads are shutdown. + static void Shutdown(); + + /// Can be called from any thread. + static bool IsInCanvasRenderThread(); + + /// Can be called from any thread. + static bool IsInCanvasWorkerThread(); + + /// Can be called from any thread. + static bool IsInCanvasRenderOrWorkerThread(); + + /// Can be called from any thread, may return nullptr late in shutdown. + static already_AddRefed<nsIThread> GetCanvasRenderThread(); + + static already_AddRefed<TaskQueue> CreateWorkerTaskQueue(); + + static void ShutdownWorkerTaskQueue(TaskQueue* aTaskQueue); + + static void FinishShutdownWorkerTaskQueue(TaskQueue* aTaskQueue); + + static void Dispatch(already_AddRefed<nsIRunnable> aRunnable); + + private: + CanvasRenderThread(nsCOMPtr<nsIThread>&& aThread, + nsCOMPtr<nsIThreadPool>&& aWorkers, bool aCreatedThread); + ~CanvasRenderThread(); + + Mutex mMutex; + + nsCOMPtr<nsIThread> const mThread; + + nsCOMPtr<nsIThreadPool> const mWorkers; + + nsTArray<RefPtr<TaskQueue>> mPendingShutdownTaskQueues MOZ_GUARDED_BY(mMutex); + + // True if mThread points to CanvasRender thread, false if mThread points to + // Compositor/Render thread. + const bool mCreatedThread; +}; + +} // namespace gfx +} // namespace mozilla + +#endif // _include_gfx_ipc_CanvasRenderThread_h__ diff --git a/gfx/ipc/CompositorOptions.h b/gfx/ipc/CompositorOptions.h new file mode 100644 index 0000000000..42b7e361d6 --- /dev/null +++ b/gfx/ipc/CompositorOptions.h @@ -0,0 +1,94 @@ +/* -*- 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/. */ +#ifndef _include_mozilla_gfx_ipc_CompositorOptions_h_ +#define _include_mozilla_gfx_ipc_CompositorOptions_h_ + +namespace IPC { +template <typename> +struct ParamTraits; +} // namespace IPC + +namespace mozilla { +namespace layers { + +/** + * This class holds options that are "per compositor" - that is, these options + * affect a particular CompositorBridgeParent and all the content that it + * renders. + * + * This class is intended to be created by a platform widget (but NOT + * PuppetWidget) and passed to the graphics code during initialization of the + * top level compositor associated with that widget. The options are immutable + * after creation. The CompositorBridgeParent holds the canonical version of + * the options, but they may be accessed by other parts of the code as needed, + * and are accessible to content processes over PCompositorBridge as well. + */ +class CompositorOptions { + public: + // This constructor needed for IPDL purposes, don't use it anywhere else. + CompositorOptions() = default; + + CompositorOptions(bool aUseAPZ, bool aUseSoftwareWebRender) + : mUseAPZ(aUseAPZ), mUseSoftwareWebRender(aUseSoftwareWebRender) {} + + bool UseAPZ() const { return mUseAPZ; } + bool UseSoftwareWebRender() const { return mUseSoftwareWebRender; } + bool AllowSoftwareWebRenderD3D11() const { + return mAllowSoftwareWebRenderD3D11; + } + bool AllowSoftwareWebRenderOGL() const { return mAllowSoftwareWebRenderOGL; } + bool InitiallyPaused() const { return mInitiallyPaused; } + bool NeedFastSnaphot() const { return mNeedFastSnaphot; } + + void SetUseAPZ(bool aUseAPZ) { mUseAPZ = aUseAPZ; } + + void SetAllowSoftwareWebRenderD3D11(bool aAllowSoftwareWebRenderD3D11) { + mAllowSoftwareWebRenderD3D11 = aAllowSoftwareWebRenderD3D11; + } + + void SetAllowSoftwareWebRenderOGL(bool aAllowSoftwareWebRenderOGL) { + mAllowSoftwareWebRenderOGL = aAllowSoftwareWebRenderOGL; + } + + void SetInitiallyPaused(bool aPauseAtStartup) { + mInitiallyPaused = aPauseAtStartup; + } + + void SetNeedFastSnaphot(bool aNeedFastSnaphot) { + mNeedFastSnaphot = aNeedFastSnaphot; + } + + bool EqualsIgnoringApzEnablement(const CompositorOptions& aOther) const { + return mUseSoftwareWebRender == aOther.mUseSoftwareWebRender && + mAllowSoftwareWebRenderD3D11 == + aOther.mAllowSoftwareWebRenderD3D11 && + mAllowSoftwareWebRenderOGL == aOther.mAllowSoftwareWebRenderOGL && + mInitiallyPaused == aOther.mInitiallyPaused && + mNeedFastSnaphot == aOther.mNeedFastSnaphot; + } + + bool operator==(const CompositorOptions& aOther) const { + return mUseAPZ == aOther.mUseAPZ && EqualsIgnoringApzEnablement(aOther); + } + + friend struct IPC::ParamTraits<CompositorOptions>; + + private: + bool mUseAPZ = false; + bool mUseSoftwareWebRender = false; + bool mAllowSoftwareWebRenderD3D11 = false; + bool mAllowSoftwareWebRenderOGL = false; + bool mInitiallyPaused = false; + bool mNeedFastSnaphot = false; + + // Make sure to add new fields to the ParamTraits implementation + // in LayersMessageUtils.h +}; + +} // namespace layers +} // namespace mozilla + +#endif // _include_mozilla_gfx_ipc_CompositorOptions_h_ diff --git a/gfx/ipc/CompositorSession.cpp b/gfx/ipc/CompositorSession.cpp new file mode 100644 index 0000000000..bdc7ce7019 --- /dev/null +++ b/gfx/ipc/CompositorSession.cpp @@ -0,0 +1,35 @@ +/* -*- 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 "CompositorSession.h" +#include "base/process_util.h" +#include "GPUChild.h" +#include "mozilla/gfx/Logging.h" +#include "mozilla/gfx/GPUProcessHost.h" +#include "mozilla/layers/CompositorBridgeChild.h" +#include "mozilla/layers/CompositorBridgeParent.h" + +namespace mozilla { +namespace layers { + +using namespace gfx; + +CompositorSession::CompositorSession(nsBaseWidget* aWidget, + CompositorWidgetDelegate* aDelegate, + CompositorBridgeChild* aChild, + const LayersId& aRootLayerTreeId) + : mWidget(aWidget), + mCompositorWidgetDelegate(aDelegate), + mCompositorBridgeChild(aChild), + mRootLayerTreeId(aRootLayerTreeId) {} + +CompositorSession::~CompositorSession() = default; + +CompositorBridgeChild* CompositorSession::GetCompositorBridgeChild() { + return mCompositorBridgeChild; +} + +} // namespace layers +} // namespace mozilla diff --git a/gfx/ipc/CompositorSession.h b/gfx/ipc/CompositorSession.h new file mode 100644 index 0000000000..88b2231546 --- /dev/null +++ b/gfx/ipc/CompositorSession.h @@ -0,0 +1,105 @@ +/* -*- 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/. */ +#ifndef _include_mozilla_gfx_ipc_CompositorSession_h_ +#define _include_mozilla_gfx_ipc_CompositorSession_h_ + +#include "base/basictypes.h" +#include "mozilla/layers/LayersTypes.h" +#include "mozilla/layers/CompositorTypes.h" +#include "nsISupportsImpl.h" +#if defined(MOZ_WIDGET_ANDROID) +# include "mozilla/layers/UiCompositorControllerChild.h" +#endif // defined(MOZ_WIDGET_ANDROID) + +class nsBaseWidget; + +namespace mozilla { +namespace widget { +class CompositorWidget; +class CompositorWidgetDelegate; +} // namespace widget +namespace gfx { +class GPUProcessHost; +class GPUProcessManager; +} // namespace gfx +namespace layers { + +class GeckoContentController; +class IAPZCTreeManager; +class CompositorBridgeParent; +class CompositorBridgeChild; +class ClientLayerManager; + +// A CompositorSession provides access to a compositor without exposing whether +// or not it's in-process or out-of-process. +class CompositorSession { + friend class gfx::GPUProcessManager; + + protected: + typedef gfx::GPUProcessHost GPUProcessHost; + typedef widget::CompositorWidget CompositorWidget; + typedef widget::CompositorWidgetDelegate CompositorWidgetDelegate; + + public: + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CompositorSession) + + virtual void Shutdown() = 0; + + // This returns a CompositorBridgeParent if the compositor resides in the same + // process. + virtual CompositorBridgeParent* GetInProcessBridge() const = 0; + + // Set the GeckoContentController for the root of the layer tree. + virtual void SetContentController(GeckoContentController* aController) = 0; + + // Return the Async Pan/Zoom Tree Manager for this compositor. + virtual RefPtr<IAPZCTreeManager> GetAPZCTreeManager() const = 0; + + // Return the child end of the compositor IPC bridge. + CompositorBridgeChild* GetCompositorBridgeChild(); + + // Return the proxy for accessing the compositor's widget. + CompositorWidgetDelegate* GetCompositorWidgetDelegate() { + return mCompositorWidgetDelegate; + } + + // Return the id of the root layer tree. + LayersId RootLayerTreeId() const { return mRootLayerTreeId; } + +#if defined(MOZ_WIDGET_ANDROID) + // Set the UiCompositorControllerChild after Session creation so the Session + // constructor doesn't get mucked up for other platforms. + void SetUiCompositorControllerChild( + RefPtr<UiCompositorControllerChild> aUiController) { + mUiCompositorControllerChild = aUiController; + } + + RefPtr<UiCompositorControllerChild> GetUiCompositorControllerChild() { + return mUiCompositorControllerChild; + } +#endif // defined(MOZ_WIDGET_ANDROID) + protected: + CompositorSession(nsBaseWidget* aWidget, CompositorWidgetDelegate* aDelegate, + CompositorBridgeChild* aChild, + const LayersId& aRootLayerTreeId); + virtual ~CompositorSession(); + + protected: + nsBaseWidget* mWidget; + CompositorWidgetDelegate* mCompositorWidgetDelegate; + RefPtr<CompositorBridgeChild> mCompositorBridgeChild; + LayersId mRootLayerTreeId; +#if defined(MOZ_WIDGET_ANDROID) + RefPtr<UiCompositorControllerChild> mUiCompositorControllerChild; +#endif // defined(MOZ_WIDGET_ANDROID) + private: + DISALLOW_COPY_AND_ASSIGN(CompositorSession); +}; + +} // namespace layers +} // namespace mozilla + +#endif // _include_mozilla_gfx_ipc_CompositorSession_h_ diff --git a/gfx/ipc/CompositorWidgetVsyncObserver.cpp b/gfx/ipc/CompositorWidgetVsyncObserver.cpp new file mode 100644 index 0000000000..a552e02ae6 --- /dev/null +++ b/gfx/ipc/CompositorWidgetVsyncObserver.cpp @@ -0,0 +1,32 @@ +/* -*- 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 "CompositorWidgetVsyncObserver.h" +#include "mozilla/gfx/VsyncBridgeChild.h" + +namespace mozilla { +namespace widget { + +CompositorWidgetVsyncObserver::CompositorWidgetVsyncObserver( + RefPtr<VsyncBridgeChild> aVsyncBridge, + const layers::LayersId& aRootLayerTreeId) + : mVsyncBridge(aVsyncBridge), mRootLayerTreeId(aRootLayerTreeId) { + MOZ_ASSERT(XRE_IsParentProcess()); + MOZ_ASSERT(NS_IsMainThread()); +} + +CompositorWidgetVsyncObserver::~CompositorWidgetVsyncObserver() = default; + +void CompositorWidgetVsyncObserver::NotifyVsync(const VsyncEvent& aVsync) { + // Vsync notifications should only arrive on the vsync thread. + MOZ_ASSERT(XRE_IsParentProcess()); + MOZ_ASSERT(!NS_IsMainThread()); + + mVsyncBridge->NotifyVsync(aVsync, mRootLayerTreeId); +} + +} // namespace widget +} // namespace mozilla diff --git a/gfx/ipc/CompositorWidgetVsyncObserver.h b/gfx/ipc/CompositorWidgetVsyncObserver.h new file mode 100644 index 0000000000..880536c572 --- /dev/null +++ b/gfx/ipc/CompositorWidgetVsyncObserver.h @@ -0,0 +1,41 @@ +/* -*- 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/. */ + +#ifndef mozilla_gfx_ipc_CompositorWidgetVsyncObserver_h +#define mozilla_gfx_ipc_CompositorWidgetVsyncObserver_h + +#include "mozilla/layers/LayersTypes.h" +#include "mozilla/VsyncDispatcher.h" + +namespace mozilla { +namespace gfx { +class VsyncBridgeChild; +} // namespace gfx + +namespace widget { + +class CompositorWidgetVsyncObserver : public VsyncObserver { + typedef gfx::VsyncBridgeChild VsyncBridgeChild; + + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CompositorWidgetVsyncObserver, override) + + public: + CompositorWidgetVsyncObserver(RefPtr<VsyncBridgeChild> aVsyncBridge, + const layers::LayersId& aRootLayerTreeId); + + void NotifyVsync(const VsyncEvent& aVsync) override; + + private: + ~CompositorWidgetVsyncObserver() override; + + RefPtr<VsyncBridgeChild> mVsyncBridge; + layers::LayersId mRootLayerTreeId; +}; + +} // namespace widget +} // namespace mozilla + +#endif // mozilla_gfx_ipc_CompositorWidgetVsyncObserver_h diff --git a/gfx/ipc/CrossProcessPaint.cpp b/gfx/ipc/CrossProcessPaint.cpp new file mode 100644 index 0000000000..e8a2f4474e --- /dev/null +++ b/gfx/ipc/CrossProcessPaint.cpp @@ -0,0 +1,529 @@ +/* -*- 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 "CrossProcessPaint.h" + +#include "mozilla/dom/CanonicalBrowsingContext.h" +#include "mozilla/dom/ContentProcessManager.h" +#include "mozilla/dom/ImageBitmap.h" +#include "mozilla/dom/BrowserParent.h" +#include "mozilla/dom/PWindowGlobalParent.h" +#include "mozilla/dom/Promise.h" +#include "mozilla/dom/WindowGlobalParent.h" +#include "mozilla/dom/WindowGlobalChild.h" +#include "mozilla/dom/WindowGlobalActorsBinding.h" +#include "mozilla/gfx/DrawEventRecorder.h" +#include "mozilla/gfx/InlineTranslator.h" +#include "mozilla/Logging.h" +#include "mozilla/PresShell.h" + +#include "gfxPlatform.h" + +#include "nsContentUtils.h" +#include "nsIDocShell.h" +#include "nsPresContext.h" + +static mozilla::LazyLogModule gCrossProcessPaintLog("CrossProcessPaint"); +static mozilla::LazyLogModule gPaintFragmentLog("PaintFragment"); + +#define CPP_LOG(msg, ...) \ + MOZ_LOG(gCrossProcessPaintLog, LogLevel::Debug, (msg, ##__VA_ARGS__)) +#define PF_LOG(msg, ...) \ + MOZ_LOG(gPaintFragmentLog, LogLevel::Debug, (msg, ##__VA_ARGS__)) + +namespace mozilla { +namespace gfx { + +using namespace mozilla::ipc; + +/// The minimum scale we allow tabs to be rasterized at. +static const float kMinPaintScale = 0.05f; + +/* static */ +PaintFragment PaintFragment::Record(dom::BrowsingContext* aBc, + const Maybe<IntRect>& aRect, float aScale, + nscolor aBackgroundColor, + CrossProcessPaintFlags aFlags) { + nsIDocShell* ds = aBc->GetDocShell(); + if (!ds) { + PF_LOG("Couldn't find docshell.\n"); + return PaintFragment{}; + } + + RefPtr<nsPresContext> presContext = ds->GetPresContext(); + if (!presContext) { + PF_LOG("Couldn't find PresContext.\n"); + return PaintFragment{}; + } + + CSSIntRect rect; + if (!aRect) { + nsCOMPtr<nsIWidget> widget = + nsContentUtils::WidgetForDocument(presContext->Document()); + + // TODO: Apply some sort of clipping to visible bounds here (Bug 1562720) + LayoutDeviceIntRect boundsDevice = widget->GetBounds(); + boundsDevice.MoveTo(0, 0); + nsRect boundsAu = LayoutDevicePixel::ToAppUnits( + boundsDevice, presContext->AppUnitsPerDevPixel()); + rect = gfx::RoundedOut(CSSPixel::FromAppUnits(boundsAu)); + } else { + rect = CSSIntRect::FromUnknownRect(*aRect); + } + + if (rect.IsEmpty()) { + // TODO: Should we return an empty surface here? + PF_LOG("Empty rect to paint.\n"); + return PaintFragment{}; + } + + // FIXME: Shouldn't the surface size be in device rather than CSS pixels? + CSSIntSize surfaceSize = rect.Size(); + surfaceSize.width *= aScale; + surfaceSize.height *= aScale; + + CPP_LOG( + "Recording " + "[browsingContext=%p, " + "rect=(%d, %d) x (%d, %d), " + "scale=%f, " + "color=(%u, %u, %u, %u)]\n", + aBc, rect.x, rect.y, rect.width, rect.height, aScale, + NS_GET_R(aBackgroundColor), NS_GET_G(aBackgroundColor), + NS_GET_B(aBackgroundColor), NS_GET_A(aBackgroundColor)); + + // Check for invalid sizes + if (surfaceSize.width <= 0 || surfaceSize.height <= 0 || + !Factory::CheckSurfaceSize(surfaceSize.ToUnknownSize())) { + PF_LOG("Invalid surface size of (%d x %d).\n", surfaceSize.width, + surfaceSize.height); + return PaintFragment{}; + } + + // Flush any pending notifications + nsContentUtils::FlushLayoutForTree(ds->GetWindow()); + + // Initialize the recorder + SurfaceFormat format = SurfaceFormat::B8G8R8A8; + RefPtr<DrawTarget> referenceDt = Factory::CreateDrawTarget( + gfxPlatform::GetPlatform()->GetSoftwareBackend(), IntSize(1, 1), format); + + // TODO: This may OOM crash if the content is complex enough + RefPtr<DrawEventRecorderMemory> recorder = + MakeAndAddRef<DrawEventRecorderMemory>(nullptr); + RefPtr<DrawTarget> dt = Factory::CreateRecordingDrawTarget( + recorder, referenceDt, + IntRect(IntPoint(0, 0), surfaceSize.ToUnknownSize())); + if (!dt || !dt->IsValid()) { + PF_LOG("Failed to create drawTarget.\n"); + return PaintFragment{}; + } + + RenderDocumentFlags renderDocFlags = RenderDocumentFlags::None; + if (!(aFlags & CrossProcessPaintFlags::DrawView)) { + renderDocFlags |= RenderDocumentFlags::IgnoreViewportScrolling | + RenderDocumentFlags::DocumentRelative; + if (aFlags & CrossProcessPaintFlags::ResetScrollPosition) { + renderDocFlags |= RenderDocumentFlags::ResetViewportScrolling; + } + } + if (aFlags & CrossProcessPaintFlags::UseHighQualityScaling) { + renderDocFlags |= RenderDocumentFlags::UseHighQualityScaling; + } + + // Perform the actual rendering + { + nsRect r = CSSPixel::ToAppUnits(rect); + + // This matches what nsDeviceContext::CreateRenderingContext does. + if (presContext->IsPrintingOrPrintPreview()) { + dt->AddUserData(&sDisablePixelSnapping, (void*)0x1, nullptr); + } + + gfxContext thebes(dt); + thebes.SetMatrix(Matrix::Scaling(aScale, aScale)); + thebes.SetCrossProcessPaintScale(aScale); + RefPtr<PresShell> presShell = presContext->PresShell(); + Unused << presShell->RenderDocument(r, renderDocFlags, aBackgroundColor, + &thebes); + } + + if (!recorder->mOutputStream.mValid) { + recorder->DetachResources(); + return PaintFragment{}; + } + + ByteBuf recording = ByteBuf((uint8_t*)recorder->mOutputStream.mData, + recorder->mOutputStream.mLength, + recorder->mOutputStream.mCapacity); + recorder->mOutputStream.mData = nullptr; + recorder->mOutputStream.mLength = 0; + recorder->mOutputStream.mCapacity = 0; + + PaintFragment fragment{ + surfaceSize.ToUnknownSize(), + std::move(recording), + std::move(recorder->TakeDependentSurfaces()), + }; + + recorder->DetachResources(); + return fragment; +} + +bool PaintFragment::IsEmpty() const { + return !mRecording.mData || mRecording.mLen == 0 || mSize == IntSize(0, 0); +} + +PaintFragment::PaintFragment(IntSize aSize, ByteBuf&& aRecording, + nsTHashSet<uint64_t>&& aDependencies) + : mSize(aSize), + mRecording(std::move(aRecording)), + mDependencies(std::move(aDependencies)) {} + +static dom::TabId GetTabId(dom::WindowGlobalParent* aWGP) { + // There is no unique TabId for a given WindowGlobalParent, as multiple + // WindowGlobalParents share the same PBrowser actor. However, we only + // ever queue one paint per PBrowser by just using the current + // WindowGlobalParent for a PBrowser. So we can interchange TabId and + // WindowGlobalParent when dealing with resolving surfaces. + RefPtr<dom::BrowserParent> browserParent = aWGP->GetBrowserParent(); + return browserParent ? browserParent->GetTabId() : dom::TabId(0); +} + +/* static */ +bool CrossProcessPaint::Start(dom::WindowGlobalParent* aRoot, + const dom::DOMRect* aRect, float aScale, + nscolor aBackgroundColor, + CrossProcessPaintFlags aFlags, + dom::Promise* aPromise) { + MOZ_RELEASE_ASSERT(XRE_IsParentProcess()); + aScale = std::max(aScale, kMinPaintScale); + + CPP_LOG( + "Starting paint. " + "[wgp=%p, " + "scale=%f, " + "color=(%u, %u, %u, %u)]\n", + aRoot, aScale, NS_GET_R(aBackgroundColor), NS_GET_G(aBackgroundColor), + NS_GET_B(aBackgroundColor), NS_GET_A(aBackgroundColor)); + + Maybe<IntRect> rect; + if (aRect) { + rect = + Some(IntRect::RoundOut((float)aRect->X(), (float)aRect->Y(), + (float)aRect->Width(), (float)aRect->Height())); + } + + if (rect && rect->IsEmpty()) { + return false; + } + + dom::TabId rootId = GetTabId(aRoot); + + RefPtr<CrossProcessPaint> resolver = + new CrossProcessPaint(aScale, rootId, aFlags); + RefPtr<CrossProcessPaint::ResolvePromise> promise; + if (aRoot->IsInProcess()) { + RefPtr<dom::WindowGlobalChild> childActor = aRoot->GetChildActor(); + if (!childActor) { + return false; + } + + // `BrowsingContext()` cannot be nullptr. + RefPtr<dom::BrowsingContext> bc = childActor->BrowsingContext(); + + promise = resolver->Init(); + resolver->mPendingFragments += 1; + resolver->ReceiveFragment( + aRoot, + PaintFragment::Record(bc, rect, aScale, aBackgroundColor, aFlags)); + } else { + promise = resolver->Init(); + resolver->QueuePaint(aRoot, rect, aBackgroundColor, aFlags); + } + + promise->Then( + GetMainThreadSerialEventTarget(), __func__, + [promise = RefPtr{aPromise}, rootId](ResolvedFragmentMap&& aFragments) { + RefPtr<RecordedDependentSurface> root = aFragments.Get(rootId); + CPP_LOG("Resolved all fragments.\n"); + + // Create the destination draw target + RefPtr<DrawTarget> drawTarget = + gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget( + root->mSize, SurfaceFormat::B8G8R8A8); + if (!drawTarget || !drawTarget->IsValid()) { + CPP_LOG("Couldn't create (%d x %d) surface for fragment %" PRIu64 + ".\n", + root->mSize.width, root->mSize.height, (uint64_t)rootId); + promise->MaybeReject(NS_ERROR_FAILURE); + return; + } + + // Translate the recording using our child tabs + { + InlineTranslator translator(drawTarget, nullptr); + translator.SetDependentSurfaces(&aFragments); + if (!translator.TranslateRecording((char*)root->mRecording.mData, + root->mRecording.mLen)) { + CPP_LOG("Couldn't translate recording for fragment %" PRIu64 ".\n", + (uint64_t)rootId); + promise->MaybeReject(NS_ERROR_FAILURE); + return; + } + } + + RefPtr<SourceSurface> snapshot = drawTarget->Snapshot(); + if (!snapshot) { + promise->MaybeReject(NS_ERROR_FAILURE); + return; + } + + ErrorResult rv; + RefPtr<dom::ImageBitmap> bitmap = + dom::ImageBitmap::CreateFromSourceSurface( + promise->GetParentObject(), snapshot, rv); + + if (!rv.Failed()) { + CPP_LOG("Success, fulfilling promise.\n"); + promise->MaybeResolve(bitmap); + } else { + CPP_LOG("Couldn't create ImageBitmap for SourceSurface.\n"); + promise->MaybeReject(std::move(rv)); + } + }, + [promise = RefPtr{aPromise}](const nsresult& aRv) { + promise->MaybeReject(aRv); + }); + + return true; +} + +/* static */ +RefPtr<CrossProcessPaint::ResolvePromise> CrossProcessPaint::Start( + nsTHashSet<uint64_t>&& aDependencies) { + MOZ_ASSERT(!aDependencies.IsEmpty()); + RefPtr<CrossProcessPaint> resolver = + new CrossProcessPaint(1.0, dom::TabId(0), CrossProcessPaintFlags::None); + + RefPtr<CrossProcessPaint::ResolvePromise> promise = resolver->Init(); + + PaintFragment rootFragment; + rootFragment.mDependencies = std::move(aDependencies); + + resolver->QueueDependencies(rootFragment.mDependencies); + resolver->mReceivedFragments.InsertOrUpdate(dom::TabId(0), + std::move(rootFragment)); + + resolver->MaybeResolve(); + + return promise; +} + +CrossProcessPaint::CrossProcessPaint(float aScale, dom::TabId aRoot, + CrossProcessPaintFlags aFlags) + : mRoot{aRoot}, mScale{aScale}, mPendingFragments{0}, mFlags{aFlags} {} + +CrossProcessPaint::~CrossProcessPaint() { Clear(NS_ERROR_ABORT); } + +void CrossProcessPaint::ReceiveFragment(dom::WindowGlobalParent* aWGP, + PaintFragment&& aFragment) { + if (IsCleared()) { + CPP_LOG("Ignoring fragment from %p.\n", aWGP); + return; + } + + dom::TabId surfaceId = GetTabId(aWGP); + + MOZ_ASSERT(mPendingFragments > 0); + MOZ_ASSERT(!mReceivedFragments.Contains(surfaceId)); + + // Double check our invariants to protect against a compromised content + // process + if (mPendingFragments == 0 || mReceivedFragments.Contains(surfaceId) || + aFragment.IsEmpty()) { + CPP_LOG("Dropping invalid fragment from %p.\n", aWGP); + LostFragment(aWGP); + return; + } + + CPP_LOG("Receiving fragment from %p(%" PRIu64 ").\n", aWGP, + (uint64_t)surfaceId); + + // Queue paints for child tabs + QueueDependencies(aFragment.mDependencies); + + mReceivedFragments.InsertOrUpdate(surfaceId, std::move(aFragment)); + mPendingFragments -= 1; + + // Resolve this paint if we have received all pending fragments + MaybeResolve(); +} + +void CrossProcessPaint::LostFragment(dom::WindowGlobalParent* aWGP) { + if (IsCleared()) { + CPP_LOG("Ignoring lost fragment from %p.\n", aWGP); + return; + } + + Clear(NS_ERROR_LOSS_OF_SIGNIFICANT_DATA); +} + +void CrossProcessPaint::QueueDependencies( + const nsTHashSet<uint64_t>& aDependencies) { + dom::ContentProcessManager* cpm = dom::ContentProcessManager::GetSingleton(); + if (!cpm) { + CPP_LOG( + "Skipping QueueDependencies with no" + " current ContentProcessManager.\n"); + return; + } + for (const auto& key : aDependencies) { + auto dependency = dom::TabId(key); + + // Get the current BrowserParent of the remote browser that was marked + // as a dependency + dom::ContentParentId cpId = cpm->GetTabProcessId(dependency); + RefPtr<dom::BrowserParent> browser = + cpm->GetBrowserParentByProcessAndTabId(cpId, dependency); + if (!browser) { + CPP_LOG("Skipping dependency %" PRIu64 + " with no current BrowserParent.\n", + (uint64_t)dependency); + continue; + } + + // Note that if the remote document is currently being cloned, it's possible + // that the BrowserParent isn't the one for the cloned document, but the + // BrowsingContext should be persisted/consistent. + QueuePaint(browser->GetBrowsingContext()); + } +} + +void CrossProcessPaint::QueuePaint(dom::WindowGlobalParent* aWGP, + const Maybe<IntRect>& aRect, + nscolor aBackgroundColor, + CrossProcessPaintFlags aFlags) { + MOZ_ASSERT(!mReceivedFragments.Contains(GetTabId(aWGP))); + + CPP_LOG("Queueing paint for WindowGlobalParent(%p).\n", aWGP); + + aWGP->DrawSnapshotInternal(this, aRect, mScale, aBackgroundColor, + (uint32_t)aFlags); + mPendingFragments += 1; +} + +void CrossProcessPaint::QueuePaint(dom::CanonicalBrowsingContext* aBc) { + RefPtr<GenericNonExclusivePromise> clonePromise = aBc->GetClonePromise(); + + if (!clonePromise) { + RefPtr<dom::WindowGlobalParent> wgp = aBc->GetCurrentWindowGlobal(); + if (!wgp) { + CPP_LOG("Skipping BrowsingContext(%p) with no current WGP.\n", aBc); + return; + } + + // TODO: Apply some sort of clipping to visible bounds here (Bug 1562720) + QueuePaint(wgp, Nothing(), NS_RGBA(0, 0, 0, 0), GetFlagsForDependencies()); + return; + } + + CPP_LOG("Queueing paint for BrowsingContext(%p).\n", aBc); + // In the case it's still in the process of cloning the remote document, we + // should defer the snapshot request after the cloning has been finished. + mPendingFragments += 1; + clonePromise->Then( + GetMainThreadSerialEventTarget(), __func__, + [self = RefPtr{this}, bc = RefPtr{aBc}]() { + RefPtr<dom::WindowGlobalParent> wgp = bc->GetCurrentWindowGlobal(); + if (!wgp) { + CPP_LOG("Skipping BrowsingContext(%p) with no current WGP.\n", + bc.get()); + return; + } + MOZ_ASSERT(!self->mReceivedFragments.Contains(GetTabId(wgp))); + + // TODO: Apply some sort of clipping to visible bounds here (Bug + // 1562720) + wgp->DrawSnapshotInternal(self, Nothing(), self->mScale, + NS_RGBA(0, 0, 0, 0), + (uint32_t)self->GetFlagsForDependencies()); + }, + [self = RefPtr{this}]() { + CPP_LOG( + "Abort painting for BrowsingContext(%p) because cloning remote " + "document failed.\n", + self.get()); + self->Clear(NS_ERROR_FAILURE); + }); +} + +void CrossProcessPaint::Clear(nsresult aStatus) { + mPendingFragments = 0; + mReceivedFragments.Clear(); + mPromise.RejectIfExists(aStatus, __func__); +} + +bool CrossProcessPaint::IsCleared() const { return mPromise.IsEmpty(); } + +void CrossProcessPaint::MaybeResolve() { + // Don't do anything if we aren't ready, experienced an error, or already + // resolved this paint + if (IsCleared() || mPendingFragments > 0) { + CPP_LOG("Not ready to resolve yet, have %u fragments left.\n", + mPendingFragments); + return; + } + + CPP_LOG("Starting to resolve fragments.\n"); + + // Resolve the paint fragments from the bottom up + ResolvedFragmentMap resolved; + { + nsresult rv = ResolveInternal(mRoot, &resolved); + if (NS_FAILED(rv)) { + CPP_LOG("Couldn't resolve.\n"); + Clear(rv); + return; + } + } + + CPP_LOG("Resolved all fragments.\n"); + + mPromise.ResolveIfExists(std::move(resolved), __func__); + Clear(NS_OK); +} + +nsresult CrossProcessPaint::ResolveInternal(dom::TabId aTabId, + ResolvedFragmentMap* aResolved) { + // We should not have resolved this paint already + MOZ_ASSERT(!aResolved->GetWeak(aTabId)); + + CPP_LOG("Resolving fragment %" PRIu64 ".\n", (uint64_t)aTabId); + + Maybe<PaintFragment> fragment = mReceivedFragments.Extract(aTabId); + if (!fragment) { + return NS_ERROR_LOSS_OF_SIGNIFICANT_DATA; + } + + // Rasterize all the dependencies first so that we can resolve this fragment + for (const auto& key : fragment->mDependencies) { + auto dependency = dom::TabId(key); + + nsresult rv = ResolveInternal(dependency, aResolved); + if (NS_FAILED(rv)) { + return rv; + } + } + + RefPtr<RecordedDependentSurface> surface = new RecordedDependentSurface{ + fragment->mSize, std::move(fragment->mRecording)}; + aResolved->InsertOrUpdate(aTabId, std::move(surface)); + return NS_OK; +} + +} // namespace gfx +} // namespace mozilla diff --git a/gfx/ipc/CrossProcessPaint.h b/gfx/ipc/CrossProcessPaint.h new file mode 100644 index 0000000000..55dc9a8b04 --- /dev/null +++ b/gfx/ipc/CrossProcessPaint.h @@ -0,0 +1,189 @@ +/* -*- 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/. */ +#ifndef _include_mozilla_gfx_ipc_CrossProcessPaint_h_ +#define _include_mozilla_gfx_ipc_CrossProcessPaint_h_ + +#include "nsISupportsImpl.h" + +#include "mozilla/dom/ipc/IdType.h" +#include "mozilla/gfx/Point.h" +#include "mozilla/gfx/RecordedEvent.h" +#include "mozilla/gfx/Rect.h" +#include "mozilla/MozPromise.h" +#include "mozilla/ipc/ByteBuf.h" +#include "nsColor.h" +#include "nsTHashMap.h" +#include "nsHashKeys.h" +#include "nsRefPtrHashtable.h" +#include "nsTHashSet.h" + +class nsIDocShell; + +namespace IPC { +template <typename T> +struct ParamTraits; +} // namespace IPC + +namespace mozilla { + +namespace dom { +class CanonicalBrowsingContext; +class DOMRect; +class Promise; +class WindowGlobalParent; +} // namespace dom + +namespace gfx { + +class CrossProcessPaint; + +enum class CrossProcessPaintFlags { + None = 0, + DrawView = 1 << 1, + ResetScrollPosition = 1 << 2, + UseHighQualityScaling = 1 << 3, +}; + +MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(CrossProcessPaintFlags) + +/** + * A fragment of a paint of a cross process document tree. + */ +class PaintFragment final { + public: + /// Initializes an empty PaintFragment + PaintFragment() = default; + + /** + * Creates a paint fragment by recording the draw commands and dependent tabs + * for a BrowsingContext. + * + * @param aBrowsingContext The frame to record. + * @param aRect The rectangle relative to the viewport to use. If no + * rectangle is specified, then the whole viewport will be used. + * @param aScale The coordinate scale to use. The size of the resolved + * surface will be `aRect.Size() * aScale`, with aScale clamped to + * at least kMinPaintScale. + * @param aBackgroundColor The background color to use. + * + * @return A paint fragment. The paint fragment may be `empty` if rendering + * was unable to be accomplished for some reason. + */ + static PaintFragment Record(dom::BrowsingContext* aBc, + const Maybe<IntRect>& aRect, float aScale, + nscolor aBackgroundColor, + CrossProcessPaintFlags aFlags); + + /// Returns whether this paint fragment contains a valid recording. + bool IsEmpty() const; + + PaintFragment(PaintFragment&&) = default; + PaintFragment& operator=(PaintFragment&&) = default; + + protected: + friend struct mozilla::ipc::IPDLParamTraits<PaintFragment>; + friend CrossProcessPaint; + + typedef mozilla::ipc::ByteBuf ByteBuf; + + PaintFragment(IntSize, ByteBuf&&, nsTHashSet<uint64_t>&&); + + IntSize mSize; + ByteBuf mRecording; + nsTHashSet<uint64_t> mDependencies; +}; + +/** + * An object for painting a cross process document tree. + */ +class CrossProcessPaint final { + NS_INLINE_DECL_REFCOUNTING(CrossProcessPaint); + + public: + typedef nsRefPtrHashtable<nsUint64HashKey, RecordedDependentSurface> + ResolvedFragmentMap; + typedef MozPromise<ResolvedFragmentMap, nsresult, true> ResolvePromise; + /** + * Begin an asynchronous paint of a cross process document tree starting at + * a WindowGlobalParent. A maybe-async paint for the root WGP will be done, + * then async paints will be recursively queued for remote subframes. Once + * all subframes have been recorded, the final image will be resolved, and + * the promise will be resolved with a dom::ImageBitmap. + * + * @param aRoot The WindowGlobalParent to paint. + * @param aRect The rectangle relative to the viewport to use, or null to + * render the whole viewport. + * @param aScale The coordinate scale to use. The size of the resolved + * surface will be `aRect.Size() * aScale`, with aScale clamped to + * at least kMinPaintScale. See the implementation for the current + * minimum value. + * @param aBackgroundColor The background color to use. + * @param aPromise The promise to resolve with a dom::ImageBitmap. + * + * @returns Whether the paint was able to be initiated or not. + */ + static bool Start(dom::WindowGlobalParent* aRoot, const dom::DOMRect* aRect, + float aScale, nscolor aBackgroundColor, + CrossProcessPaintFlags aFlags, dom::Promise* aPromise); + + static RefPtr<ResolvePromise> Start(nsTHashSet<uint64_t>&& aDependencies); + + void ReceiveFragment(dom::WindowGlobalParent* aWGP, + PaintFragment&& aFragment); + void LostFragment(dom::WindowGlobalParent* aWGP); + + private: + typedef nsTHashMap<nsUint64HashKey, PaintFragment> ReceivedFragmentMap; + + CrossProcessPaint(float aScale, dom::TabId aRoot, + CrossProcessPaintFlags aFlags); + ~CrossProcessPaint(); + + void QueueDependencies(const nsTHashSet<uint64_t>& aDependencies); + + void QueuePaint( + dom::WindowGlobalParent* aWGP, const Maybe<IntRect>& aRect, + nscolor aBackgroundColor = NS_RGBA(0, 0, 0, 0), + CrossProcessPaintFlags aFlags = CrossProcessPaintFlags::DrawView); + + void QueuePaint(dom::CanonicalBrowsingContext* aBc); + + /// Clear the state of this paint so that it cannot be resolved or receive + /// any paint fragments. + void Clear(nsresult aStatus); + + /// Returns if this paint has been cleared. + bool IsCleared() const; + + /// Resolves the paint fragments if we have none pending and resolves the + /// promise. + void MaybeResolve(); + nsresult ResolveInternal(dom::TabId aTabId, ResolvedFragmentMap* aResolved); + + RefPtr<ResolvePromise> Init() { + MOZ_ASSERT(mPromise.IsEmpty()); + return mPromise.Ensure(__func__); + } + + // UseHighQualityScaling is the only flag that dependencies inherit, and we + // always want to use DrawView for dependencies. + CrossProcessPaintFlags GetFlagsForDependencies() const { + return (mFlags & CrossProcessPaintFlags::UseHighQualityScaling) | + CrossProcessPaintFlags::DrawView; + } + + MozPromiseHolder<ResolvePromise> mPromise; + dom::TabId mRoot; + float mScale; + uint32_t mPendingFragments; + ReceivedFragmentMap mReceivedFragments; + CrossProcessPaintFlags mFlags; +}; + +} // namespace gfx +} // namespace mozilla + +#endif // _include_mozilla_gfx_ipc_CrossProcessPaint_h_ diff --git a/gfx/ipc/D3DMessageUtils.cpp b/gfx/ipc/D3DMessageUtils.cpp new file mode 100644 index 0000000000..0d18d2ed00 --- /dev/null +++ b/gfx/ipc/D3DMessageUtils.cpp @@ -0,0 +1,71 @@ +/* -*- 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 "D3DMessageUtils.h" +#if defined(XP_WIN) +# include "gfxWindowsPlatform.h" +#endif + +bool DxgiAdapterDesc::operator==(const DxgiAdapterDesc& aOther) const { + return memcmp(&aOther, this, sizeof(*this)) == 0; +} + +#if defined(XP_WIN) +static_assert(sizeof(DxgiAdapterDesc) == sizeof(DXGI_ADAPTER_DESC), + "DXGI_ADAPTER_DESC doe snot match DxgiAdapterDesc"); + +const DxgiAdapterDesc& DxgiAdapterDesc::From(const DXGI_ADAPTER_DESC& aDesc) { + return reinterpret_cast<const DxgiAdapterDesc&>(aDesc); +} + +const DXGI_ADAPTER_DESC& DxgiAdapterDesc::ToDesc() const { + return reinterpret_cast<const DXGI_ADAPTER_DESC&>(*this); +} +#endif + +namespace IPC { + +void ParamTraits<DxgiAdapterDesc>::Write(MessageWriter* aWriter, + const paramType& aParam) { +#if defined(XP_WIN) + aWriter->WriteBytes(aParam.Description, sizeof(aParam.Description)); + WriteParam(aWriter, aParam.VendorId); + WriteParam(aWriter, aParam.DeviceId); + WriteParam(aWriter, aParam.SubSysId); + WriteParam(aWriter, aParam.Revision); + WriteParam(aWriter, aParam.DedicatedVideoMemory); + WriteParam(aWriter, aParam.DedicatedSystemMemory); + WriteParam(aWriter, aParam.SharedSystemMemory); + WriteParam(aWriter, aParam.AdapterLuid.LowPart); + WriteParam(aWriter, aParam.AdapterLuid.HighPart); +#endif +} + +bool ParamTraits<DxgiAdapterDesc>::Read(MessageReader* aReader, + paramType* aResult) { +#if defined(XP_WIN) + if (!aReader->ReadBytesInto(aResult->Description, + sizeof(aResult->Description))) { + return false; + } + + if (ReadParam(aReader, &aResult->VendorId) && + ReadParam(aReader, &aResult->DeviceId) && + ReadParam(aReader, &aResult->SubSysId) && + ReadParam(aReader, &aResult->Revision) && + ReadParam(aReader, &aResult->DedicatedVideoMemory) && + ReadParam(aReader, &aResult->DedicatedSystemMemory) && + ReadParam(aReader, &aResult->SharedSystemMemory) && + ReadParam(aReader, &aResult->AdapterLuid.LowPart) && + ReadParam(aReader, &aResult->AdapterLuid.HighPart)) { + return true; + } + return false; +#else + return true; +#endif +} + +} // namespace IPC diff --git a/gfx/ipc/D3DMessageUtils.h b/gfx/ipc/D3DMessageUtils.h new file mode 100644 index 0000000000..60f4d24452 --- /dev/null +++ b/gfx/ipc/D3DMessageUtils.h @@ -0,0 +1,46 @@ +/* -*- 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/. */ +#ifndef _include_gfx_ipc_D3DMessageUtils_h__ +#define _include_gfx_ipc_D3DMessageUtils_h__ + +#include "chrome/common/ipc_message_utils.h" +#include "ipc/IPCMessageUtils.h" + +// Can't include dxgi.h, since it leaks random identifiers and #defines, and +// IPDL causes this file to be #included all over. +typedef struct DXGI_ADAPTER_DESC DXGI_ADAPTER_DESC; + +struct DxgiAdapterDesc { +#if defined(XP_WIN) + WCHAR Description[128]; + UINT VendorId; + UINT DeviceId; + UINT SubSysId; + UINT Revision; + SIZE_T DedicatedVideoMemory; + SIZE_T DedicatedSystemMemory; + SIZE_T SharedSystemMemory; + LUID AdapterLuid; + + static const DxgiAdapterDesc& From(const DXGI_ADAPTER_DESC& aDesc); + const DXGI_ADAPTER_DESC& ToDesc() const; +#endif + + bool operator==(const DxgiAdapterDesc& aOther) const; +}; + +namespace IPC { + +template <> +struct ParamTraits<DxgiAdapterDesc> { + typedef DxgiAdapterDesc paramType; + static void Write(MessageWriter* aWriter, const paramType& aParam); + static bool Read(MessageReader* aReader, paramType* aResult); +}; + +} // namespace IPC + +#endif // _include_gfx_ipc_D3DMessageUtils_h__ diff --git a/gfx/ipc/FileHandleWrapper.cpp b/gfx/ipc/FileHandleWrapper.cpp new file mode 100644 index 0000000000..7597e27c1d --- /dev/null +++ b/gfx/ipc/FileHandleWrapper.cpp @@ -0,0 +1,27 @@ +/* -*- 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 "FileHandleWrapper.h" + +#include "mozilla/ipc/FileDescriptor.h" + +namespace mozilla::gfx { + +FileHandleWrapper::FileHandleWrapper(mozilla::UniqueFileHandle&& aHandle) + : mHandle(std::move(aHandle)) {} + +FileHandleWrapper::~FileHandleWrapper() {} + +mozilla::detail::FileHandleType FileHandleWrapper::GetHandle() { + return mHandle.get(); +} + +mozilla::UniqueFileHandle FileHandleWrapper::ClonePlatformHandle() { + auto handle = ipc::FileDescriptor(GetHandle()); + return handle.TakePlatformHandle(); +} + +} // namespace mozilla::gfx diff --git a/gfx/ipc/FileHandleWrapper.h b/gfx/ipc/FileHandleWrapper.h new file mode 100644 index 0000000000..6ca905bd61 --- /dev/null +++ b/gfx/ipc/FileHandleWrapper.h @@ -0,0 +1,63 @@ +/* -*- 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/. */ + +#ifndef _include_gfx_ipc_FileHandleWrapper_h__ +#define _include_gfx_ipc_FileHandleWrapper_h__ + +#include "mozilla/UniquePtrExtensions.h" +#include "nsISupportsImpl.h" + +namespace mozilla { + +namespace ipc { +template <typename T> +struct IPDLParamTraits; +} // namespace ipc + +namespace gfx { + +// +// A class for sharing file handle or shared handle among multiple clients. +// +// The file handles or the shared handles consume system resources. The class +// could reduce the number of shared handles in a process. +// +class FileHandleWrapper { + friend struct mozilla::ipc::IPDLParamTraits<gfx::FileHandleWrapper*>; + + public: + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(FileHandleWrapper); + + explicit FileHandleWrapper(mozilla::UniqueFileHandle&& aHandle); + + mozilla::detail::FileHandleType GetHandle(); + + mozilla::UniqueFileHandle ClonePlatformHandle(); + + protected: + ~FileHandleWrapper(); + + const mozilla::UniqueFileHandle mHandle; +}; + +struct FenceInfo { + FenceInfo() = default; + FenceInfo(FileHandleWrapper* aFenceHandle, uint64_t aFenceValue) + : mFenceHandle(aFenceHandle), mFenceValue(aFenceValue) {} + + bool operator==(const FenceInfo& aOther) const { + return mFenceHandle == aOther.mFenceHandle && + mFenceValue == aOther.mFenceValue; + } + + RefPtr<FileHandleWrapper> mFenceHandle; + uint64_t mFenceValue = 0; +}; + +} // namespace gfx +} // namespace mozilla + +#endif // _include_gfx_ipc_FileHandleWrapper_h__ diff --git a/gfx/ipc/GPUChild.cpp b/gfx/ipc/GPUChild.cpp new file mode 100644 index 0000000000..afc9f61ee4 --- /dev/null +++ b/gfx/ipc/GPUChild.cpp @@ -0,0 +1,371 @@ +/* -*- 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 "GPUChild.h" + +#include "GPUProcessHost.h" +#include "GPUProcessManager.h" +#include "GfxInfoBase.h" +#include "VRProcessManager.h" +#include "gfxConfig.h" +#include "gfxPlatform.h" +#include "mozilla/Components.h" +#include "mozilla/FOGIPC.h" +#include "mozilla/StaticPrefs_dom.h" +#include "mozilla/Telemetry.h" +#include "mozilla/TelemetryIPC.h" +#include "mozilla/dom/CheckerboardReportService.h" +#include "mozilla/dom/ContentParent.h" +#include "mozilla/dom/MemoryReportRequest.h" +#include "mozilla/gfx/Logging.h" +#include "mozilla/gfx/gfxVars.h" +#if defined(XP_WIN) +# include "mozilla/gfx/DeviceManagerDx.h" +#endif +#include "mozilla/HangDetails.h" +#include "mozilla/RemoteDecoderManagerChild.h" // For RemoteDecodeIn +#include "mozilla/Unused.h" +#include "mozilla/ipc/Endpoint.h" +#include "mozilla/layers/APZInputBridgeChild.h" +#include "mozilla/layers/LayerTreeOwnerTracker.h" +#include "nsHashPropertyBag.h" +#include "nsIGfxInfo.h" +#include "nsIObserverService.h" +#include "nsIPropertyBag2.h" +#include "ProfilerParent.h" + +namespace mozilla { +namespace gfx { + +using namespace layers; + +GPUChild::GPUChild(GPUProcessHost* aHost) : mHost(aHost), mGPUReady(false) {} + +GPUChild::~GPUChild() = default; + +void GPUChild::Init() { + nsTArray<GfxVarUpdate> updates = gfxVars::FetchNonDefaultVars(); + + DevicePrefs devicePrefs; + devicePrefs.hwCompositing() = gfxConfig::GetValue(Feature::HW_COMPOSITING); + devicePrefs.d3d11Compositing() = + gfxConfig::GetValue(Feature::D3D11_COMPOSITING); + devicePrefs.oglCompositing() = + gfxConfig::GetValue(Feature::OPENGL_COMPOSITING); + devicePrefs.useD2D1() = gfxConfig::GetValue(Feature::DIRECT2D); + devicePrefs.d3d11HwAngle() = gfxConfig::GetValue(Feature::D3D11_HW_ANGLE); + + nsTArray<LayerTreeIdMapping> mappings; + LayerTreeOwnerTracker::Get()->Iterate( + [&](LayersId aLayersId, base::ProcessId aProcessId) { + mappings.AppendElement(LayerTreeIdMapping(aLayersId, aProcessId)); + }); + + nsCOMPtr<nsIGfxInfo> gfxInfo = components::GfxInfo::Service(); + nsTArray<GfxInfoFeatureStatus> features; + if (gfxInfo) { + auto* gfxInfoRaw = static_cast<widget::GfxInfoBase*>(gfxInfo.get()); + features = gfxInfoRaw->GetAllFeatures(); + } + + SendInit(updates, devicePrefs, mappings, features, + GPUProcessManager::Get()->AllocateNamespace()); + + gfxVars::AddReceiver(this); + + Unused << SendInitProfiler(ProfilerParent::CreateForProcess(OtherPid())); +} + +void GPUChild::OnVarChanged(const GfxVarUpdate& aVar) { SendUpdateVar(aVar); } + +bool GPUChild::EnsureGPUReady() { + // On our initial process launch, we want to block on the GetDeviceStatus + // message. Additionally, we may have updated our compositor configuration + // through the gfxVars after fallback, in which case we want to ensure the + // GPU process has handled any updates before creating compositor sessions. + if (mGPUReady && !mWaitForVarUpdate) { + return true; + } + + GPUDeviceData data; + if (!SendGetDeviceStatus(&data)) { + return false; + } + + // Only import and collect telemetry for the initial GPU process launch. + if (!mGPUReady) { + gfxPlatform::GetPlatform()->ImportGPUDeviceData(data); + Telemetry::AccumulateTimeDelta(Telemetry::GPU_PROCESS_LAUNCH_TIME_MS_2, + mHost->GetLaunchTime()); + mGPUReady = true; + } + + mWaitForVarUpdate = false; + return true; +} + +void GPUChild::OnUnexpectedShutdown() { mUnexpectedShutdown = true; } + +mozilla::ipc::IPCResult GPUChild::RecvInitComplete(const GPUDeviceData& aData) { + // We synchronously requested GPU parameters before this arrived. + if (mGPUReady) { + return IPC_OK(); + } + + gfxPlatform::GetPlatform()->ImportGPUDeviceData(aData); + Telemetry::AccumulateTimeDelta(Telemetry::GPU_PROCESS_LAUNCH_TIME_MS_2, + mHost->GetLaunchTime()); + mGPUReady = true; + return IPC_OK(); +} + +mozilla::ipc::IPCResult GPUChild::RecvDeclareStable() { + mHost->mListener->OnProcessDeclaredStable(); + return IPC_OK(); +} + +mozilla::ipc::IPCResult GPUChild::RecvReportCheckerboard( + const uint32_t& aSeverity, const nsCString& aLog) { + layers::CheckerboardEventStorage::Report(aSeverity, std::string(aLog.get())); + return IPC_OK(); +} + +mozilla::ipc::IPCResult GPUChild::RecvGraphicsError(const nsCString& aError) { + gfx::LogForwarder* lf = gfx::Factory::GetLogForwarder(); + if (lf) { + std::stringstream message; + message << "GP+" << aError.get(); + lf->UpdateStringsVector(message.str()); + } + return IPC_OK(); +} + +mozilla::ipc::IPCResult GPUChild::RecvCreateVRProcess() { + // Make sure create VR process at the main process + MOZ_ASSERT(XRE_IsParentProcess()); + if (StaticPrefs::dom_vr_process_enabled_AtStartup()) { + VRProcessManager::Initialize(); + VRProcessManager* vr = VRProcessManager::Get(); + MOZ_ASSERT(vr, "VRProcessManager must be initialized first."); + + if (vr) { + vr->LaunchVRProcess(); + } + } + + return IPC_OK(); +} + +mozilla::ipc::IPCResult GPUChild::RecvShutdownVRProcess() { + // Make sure stopping VR process at the main process + MOZ_ASSERT(XRE_IsParentProcess()); + if (StaticPrefs::dom_vr_process_enabled_AtStartup()) { + VRProcessManager::Shutdown(); + } + + return IPC_OK(); +} + +mozilla::ipc::IPCResult GPUChild::RecvNotifyUiObservers( + const nsCString& aTopic) { + nsCOMPtr<nsIObserverService> obsSvc = mozilla::services::GetObserverService(); + MOZ_ASSERT(obsSvc); + if (obsSvc) { + obsSvc->NotifyObservers(nullptr, aTopic.get(), nullptr); + } + return IPC_OK(); +} + +mozilla::ipc::IPCResult GPUChild::RecvAccumulateChildHistograms( + nsTArray<HistogramAccumulation>&& aAccumulations) { + TelemetryIPC::AccumulateChildHistograms(Telemetry::ProcessID::Gpu, + aAccumulations); + return IPC_OK(); +} + +mozilla::ipc::IPCResult GPUChild::RecvAccumulateChildKeyedHistograms( + nsTArray<KeyedHistogramAccumulation>&& aAccumulations) { + TelemetryIPC::AccumulateChildKeyedHistograms(Telemetry::ProcessID::Gpu, + aAccumulations); + return IPC_OK(); +} + +mozilla::ipc::IPCResult GPUChild::RecvUpdateChildScalars( + nsTArray<ScalarAction>&& aScalarActions) { + TelemetryIPC::UpdateChildScalars(Telemetry::ProcessID::Gpu, aScalarActions); + return IPC_OK(); +} + +mozilla::ipc::IPCResult GPUChild::RecvUpdateChildKeyedScalars( + nsTArray<KeyedScalarAction>&& aScalarActions) { + TelemetryIPC::UpdateChildKeyedScalars(Telemetry::ProcessID::Gpu, + aScalarActions); + return IPC_OK(); +} + +mozilla::ipc::IPCResult GPUChild::RecvRecordChildEvents( + nsTArray<mozilla::Telemetry::ChildEventData>&& aEvents) { + TelemetryIPC::RecordChildEvents(Telemetry::ProcessID::Gpu, aEvents); + return IPC_OK(); +} + +mozilla::ipc::IPCResult GPUChild::RecvRecordDiscardedData( + const mozilla::Telemetry::DiscardedData& aDiscardedData) { + TelemetryIPC::RecordDiscardedData(Telemetry::ProcessID::Gpu, aDiscardedData); + return IPC_OK(); +} + +mozilla::ipc::IPCResult GPUChild::RecvNotifyDeviceReset( + const GPUDeviceData& aData) { + gfxPlatform::GetPlatform()->ImportGPUDeviceData(aData); + mHost->mListener->OnRemoteProcessDeviceReset(mHost); + return IPC_OK(); +} + +mozilla::ipc::IPCResult GPUChild::RecvNotifyOverlayInfo( + const OverlayInfo aInfo) { + gfxPlatform::GetPlatform()->SetOverlayInfo(aInfo); + return IPC_OK(); +} + +mozilla::ipc::IPCResult GPUChild::RecvNotifySwapChainInfo( + const SwapChainInfo aInfo) { + gfxPlatform::GetPlatform()->SetSwapChainInfo(aInfo); + return IPC_OK(); +} + +mozilla::ipc::IPCResult GPUChild::RecvNotifyDisableRemoteCanvas() { + gfxPlatform::DisableRemoteCanvas(); + return IPC_OK(); +} + +mozilla::ipc::IPCResult GPUChild::RecvFlushMemory(const nsString& aReason) { + nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService(); + if (os) { + os->NotifyObservers(nullptr, "memory-pressure", aReason.get()); + } + return IPC_OK(); +} + +bool GPUChild::SendRequestMemoryReport(const uint32_t& aGeneration, + const bool& aAnonymize, + const bool& aMinimizeMemoryUsage, + const Maybe<FileDescriptor>& aDMDFile) { + mMemoryReportRequest = MakeUnique<MemoryReportRequestHost>(aGeneration); + + PGPUChild::SendRequestMemoryReport( + aGeneration, aAnonymize, aMinimizeMemoryUsage, aDMDFile, + [&](const uint32_t& aGeneration2) { + if (GPUProcessManager* gpm = GPUProcessManager::Get()) { + if (GPUChild* child = gpm->GetGPUChild()) { + if (child->mMemoryReportRequest) { + child->mMemoryReportRequest->Finish(aGeneration2); + child->mMemoryReportRequest = nullptr; + } + } + } + }, + [&](mozilla::ipc::ResponseRejectReason) { + if (GPUProcessManager* gpm = GPUProcessManager::Get()) { + if (GPUChild* child = gpm->GetGPUChild()) { + child->mMemoryReportRequest = nullptr; + } + } + }); + + return true; +} + +mozilla::ipc::IPCResult GPUChild::RecvAddMemoryReport( + const MemoryReport& aReport) { + if (mMemoryReportRequest) { + mMemoryReportRequest->RecvReport(aReport); + } + return IPC_OK(); +} + +void GPUChild::ActorDestroy(ActorDestroyReason aWhy) { + if (aWhy == AbnormalShutdown || mUnexpectedShutdown) { + nsAutoString dumpId; + GenerateCrashReport(OtherPid(), &dumpId); + + Telemetry::Accumulate( + Telemetry::SUBPROCESS_ABNORMAL_ABORT, + nsDependentCString(XRE_GeckoProcessTypeToString(GeckoProcessType_GPU)), + 1); + + // Notify the Telemetry environment so that we can refresh and do a + // subsession split. This also notifies the crash reporter on geckoview. + if (nsCOMPtr<nsIObserverService> obsvc = services::GetObserverService()) { + RefPtr<nsHashPropertyBag> props = new nsHashPropertyBag(); + props->SetPropertyAsBool(u"abnormal"_ns, true); + props->SetPropertyAsAString(u"dumpID"_ns, dumpId); + obsvc->NotifyObservers((nsIPropertyBag2*)props, + "compositor:process-aborted", nullptr); + } + } + + gfxVars::RemoveReceiver(this); + mHost->OnChannelClosed(); +} + +mozilla::ipc::IPCResult GPUChild::RecvUpdateFeature( + const Feature& aFeature, const FeatureFailure& aChange) { + gfxConfig::SetFailed(aFeature, aChange.status(), aChange.message().get(), + aChange.failureId()); + return IPC_OK(); +} + +mozilla::ipc::IPCResult GPUChild::RecvUsedFallback(const Fallback& aFallback, + const nsCString& aMessage) { + gfxConfig::EnableFallback(aFallback, aMessage.get()); + return IPC_OK(); +} + +mozilla::ipc::IPCResult GPUChild::RecvBHRThreadHang( + const HangDetails& aDetails) { + nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); + if (obs) { + // Copy the HangDetails recieved over the network into a nsIHangDetails, and + // then fire our own observer notification. + // XXX: We should be able to avoid this potentially expensive copy here by + // moving our deserialized argument. + nsCOMPtr<nsIHangDetails> hangDetails = + new nsHangDetails(HangDetails(aDetails), PersistedToDisk::No); + obs->NotifyObservers(hangDetails, "bhr-thread-hang", nullptr); + } + return IPC_OK(); +} + +mozilla::ipc::IPCResult GPUChild::RecvUpdateMediaCodecsSupported( + const media::MediaCodecsSupported& aSupported) { + dom::ContentParent::BroadcastMediaCodecsSupportedUpdate( + RemoteDecodeIn::GpuProcess, aSupported); + return IPC_OK(); +} + +mozilla::ipc::IPCResult GPUChild::RecvFOGData(ByteBuf&& aBuf) { + glean::FOGData(std::move(aBuf)); + return IPC_OK(); +} + +class DeferredDeleteGPUChild : public Runnable { + public: + explicit DeferredDeleteGPUChild(RefPtr<GPUChild>&& aChild) + : Runnable("gfx::DeferredDeleteGPUChild"), mChild(std::move(aChild)) {} + + NS_IMETHODIMP Run() override { return NS_OK; } + + private: + RefPtr<GPUChild> mChild; +}; + +/* static */ +void GPUChild::Destroy(RefPtr<GPUChild>&& aChild) { + NS_DispatchToMainThread(new DeferredDeleteGPUChild(std::move(aChild))); +} + +} // namespace gfx +} // namespace mozilla diff --git a/gfx/ipc/GPUChild.h b/gfx/ipc/GPUChild.h new file mode 100644 index 0000000000..6c2765fcc7 --- /dev/null +++ b/gfx/ipc/GPUChild.h @@ -0,0 +1,108 @@ +/* -*- 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/. */ +#ifndef _include_mozilla_gfx_ipc_GPUChild_h_ +#define _include_mozilla_gfx_ipc_GPUChild_h_ + +#include "mozilla/RefPtr.h" +#include "mozilla/UniquePtr.h" +#include "mozilla/ipc/CrashReporterHelper.h" +#include "mozilla/gfx/PGPUChild.h" +#include "mozilla/gfx/gfxVarReceiver.h" + +namespace mozilla { + +namespace dom { +class MemoryReportRequestHost; +} // namespace dom +namespace gfx { + +class GPUProcessHost; + +class GPUChild final : public ipc::CrashReporterHelper<GeckoProcessType_GPU>, + public PGPUChild, + public gfxVarReceiver { + typedef mozilla::dom::MemoryReportRequestHost MemoryReportRequestHost; + + public: + NS_INLINE_DECL_REFCOUNTING(GPUChild, final) + + explicit GPUChild(GPUProcessHost* aHost); + + void Init(); + + bool EnsureGPUReady(); + void MarkWaitForVarUpdate() { mWaitForVarUpdate = true; } + + // Notifies that an unexpected GPU process shutdown has been noticed by a + // different IPDL actor, and the GPU process is being torn down as a result. + // ActorDestroy may receive either NormalShutdown or AbnormalShutdown as a + // reason, depending on timings, but either way we treat the shutdown as + // abnormal. + void OnUnexpectedShutdown(); + + // gfxVarReceiver overrides. + void OnVarChanged(const GfxVarUpdate& aVar) override; + + // PGPUChild overrides. + mozilla::ipc::IPCResult RecvInitComplete(const GPUDeviceData& aData); + mozilla::ipc::IPCResult RecvDeclareStable(); + mozilla::ipc::IPCResult RecvReportCheckerboard(const uint32_t& aSeverity, + const nsCString& aLog); + mozilla::ipc::IPCResult RecvCreateVRProcess(); + mozilla::ipc::IPCResult RecvShutdownVRProcess(); + + mozilla::ipc::IPCResult RecvAccumulateChildHistograms( + nsTArray<HistogramAccumulation>&& aAccumulations); + mozilla::ipc::IPCResult RecvAccumulateChildKeyedHistograms( + nsTArray<KeyedHistogramAccumulation>&& aAccumulations); + mozilla::ipc::IPCResult RecvUpdateChildScalars( + nsTArray<ScalarAction>&& aScalarActions); + mozilla::ipc::IPCResult RecvUpdateChildKeyedScalars( + nsTArray<KeyedScalarAction>&& aScalarActions); + mozilla::ipc::IPCResult RecvRecordChildEvents( + nsTArray<ChildEventData>&& events); + mozilla::ipc::IPCResult RecvRecordDiscardedData( + const DiscardedData& aDiscardedData); + + void ActorDestroy(ActorDestroyReason aWhy) override; + mozilla::ipc::IPCResult RecvGraphicsError(const nsCString& aError); + mozilla::ipc::IPCResult RecvNotifyUiObservers(const nsCString& aTopic); + mozilla::ipc::IPCResult RecvNotifyDeviceReset(const GPUDeviceData& aData); + mozilla::ipc::IPCResult RecvNotifyOverlayInfo(const OverlayInfo aInfo); + mozilla::ipc::IPCResult RecvNotifySwapChainInfo(const SwapChainInfo aInfo); + mozilla::ipc::IPCResult RecvNotifyDisableRemoteCanvas(); + mozilla::ipc::IPCResult RecvFlushMemory(const nsString& aReason); + mozilla::ipc::IPCResult RecvAddMemoryReport(const MemoryReport& aReport); + mozilla::ipc::IPCResult RecvUpdateFeature(const Feature& aFeature, + const FeatureFailure& aChange); + mozilla::ipc::IPCResult RecvUsedFallback(const Fallback& aFallback, + const nsCString& aMessage); + mozilla::ipc::IPCResult RecvBHRThreadHang(const HangDetails& aDetails); + mozilla::ipc::IPCResult RecvUpdateMediaCodecsSupported( + const media::MediaCodecsSupported& aSupported); + mozilla::ipc::IPCResult RecvFOGData(ByteBuf&& aBuf); + + bool SendRequestMemoryReport(const uint32_t& aGeneration, + const bool& aAnonymize, + const bool& aMinimizeMemoryUsage, + const Maybe<ipc::FileDescriptor>& aDMDFile); + + static void Destroy(RefPtr<GPUChild>&& aChild); + + private: + virtual ~GPUChild(); + + GPUProcessHost* mHost; + UniquePtr<MemoryReportRequestHost> mMemoryReportRequest; + bool mGPUReady; + bool mWaitForVarUpdate = false; + bool mUnexpectedShutdown = false; +}; + +} // namespace gfx +} // namespace mozilla + +#endif // _include_mozilla_gfx_ipc_GPUChild_h_ diff --git a/gfx/ipc/GPUParent.cpp b/gfx/ipc/GPUParent.cpp new file mode 100644 index 0000000000..283fb87ee9 --- /dev/null +++ b/gfx/ipc/GPUParent.cpp @@ -0,0 +1,831 @@ +/* -*- 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/. */ + +#ifdef XP_WIN +# include "WMF.h" +# include "WMFDecoderModule.h" +#endif +#include "GLContextProvider.h" +#include "GPUParent.h" +#include "GPUProcessHost.h" +#include "GPUProcessManager.h" +#include "gfxGradientCache.h" +#include "GfxInfoBase.h" +#include "VRGPUChild.h" +#include "VRManager.h" +#include "VRManagerParent.h" +#include "VsyncBridgeParent.h" +#include "cairo.h" +#include "gfxConfig.h" +#include "gfxCrashReporterUtils.h" +#include "gfxPlatform.h" +#include "mozilla/Assertions.h" +#include "mozilla/Components.h" +#include "mozilla/FOGIPC.h" +#include "mozilla/HangDetails.h" +#include "mozilla/PerfStats.h" +#include "mozilla/Preferences.h" +#include "mozilla/ProcessPriorityManager.h" +#include "mozilla/RemoteDecoderManagerChild.h" +#include "mozilla/RemoteDecoderManagerParent.h" +#include "mozilla/ScopeExit.h" +#include "mozilla/StaticPrefs_dom.h" +#include "mozilla/StaticPrefs_media.h" +#include "mozilla/Telemetry.h" +#include "mozilla/TimeStamp.h" +#include "mozilla/dom/MemoryReportRequest.h" +#include "mozilla/gfx/2D.h" +#include "mozilla/gfx/CanvasRenderThread.h" +#include "mozilla/gfx/gfxVars.h" +#include "mozilla/glean/GleanMetrics.h" +#include "mozilla/image/ImageMemoryReporter.h" +#include "mozilla/ipc/CrashReporterClient.h" +#include "mozilla/ipc/ProcessChild.h" +#include "mozilla/ipc/ProcessUtils.h" +#include "mozilla/layers/APZInputBridgeParent.h" +#include "mozilla/layers/APZPublicUtils.h" // for apz::InitializeGlobalState +#include "mozilla/layers/APZThreadUtils.h" +#include "mozilla/layers/CompositorBridgeParent.h" +#include "mozilla/layers/CompositorManagerParent.h" +#include "mozilla/layers/CompositorThread.h" +#include "mozilla/layers/ImageBridgeParent.h" +#include "mozilla/layers/LayerTreeOwnerTracker.h" +#include "mozilla/layers/RemoteTextureMap.h" +#include "mozilla/layers/UiCompositorControllerParent.h" +#include "mozilla/layers/VideoBridgeParent.h" +#include "mozilla/webrender/RenderThread.h" +#include "mozilla/webrender/WebRenderAPI.h" +#include "nsDebugImpl.h" +#include "nsIGfxInfo.h" +#include "nsIXULRuntime.h" +#include "nsThreadManager.h" +#include "nscore.h" +#include "prenv.h" +#include "skia/include/core/SkGraphics.h" +#if defined(XP_WIN) +# include <dwrite.h> +# include <process.h> +# include <windows.h> + +# include "gfxDWriteFonts.h" +# include "gfxWindowsPlatform.h" +# include "mozilla/WindowsVersion.h" +# include "mozilla/gfx/DeviceManagerDx.h" +# include "mozilla/layers/GpuProcessD3D11TextureMap.h" +# include "mozilla/layers/GpuProcessD3D11QueryMap.h" +# include "mozilla/layers/TextureD3D11.h" +# include "mozilla/widget/WinCompositorWindowThread.h" +# include "MediaCodecsSupport.h" +# include "WMFDecoderModule.h" +#else +# include <unistd.h> +#endif +#ifdef MOZ_WIDGET_GTK +# include <gtk/gtk.h> + +# include "skia/include/ports/SkTypeface_cairo.h" +#endif +#ifdef ANDROID +# include "mozilla/layers/AndroidHardwareBuffer.h" +# include "skia/include/ports/SkTypeface_cairo.h" +#endif +#include "ChildProfilerController.h" +#include "nsAppRunner.h" + +#if defined(MOZ_SANDBOX) && defined(MOZ_DEBUG) && defined(ENABLE_TESTS) +# include "mozilla/SandboxTestingChild.h" +#endif + +namespace mozilla::gfx { + +using namespace ipc; +using namespace layers; + +static GPUParent* sGPUParent; + +static void ReportHardwareMediaCodecSupportIfNeeded() { + // We only need to report the result once. + static bool sReported = false; + if (sReported) { + return; + } +#if defined(XP_WIN) + NS_GetCurrentThread()->Dispatch(NS_NewRunnableFunction( + "GPUParent:ReportHardwareMediaCodecSupportIfNeeded", []() { + // Only report telemetry when hardware decoding is available. + if (!gfx::gfxVars::IsInitialized() || + !gfx::gfxVars::CanUseHardwareVideoDecoding()) { + return; + } + sReported = true; + + // TODO : we can remove this after HEVC is enabled by default. + // HEVC is not enabled. We need to force to enable it in order to know + // its support as well, and it would be turn off later. + if (StaticPrefs::media_wmf_hevc_enabled() != 1) { + WMFDecoderModule::Init(WMFDecoderModule::Config::ForceEnableHEVC); + } + const auto support = PDMFactory::Supported(true /* force refresh */); + if (support.contains( + mozilla::media::MediaCodecsSupport::H264HardwareDecode)) { + Telemetry::ScalarSet( + Telemetry::ScalarID::MEDIA_DEVICE_HARDWARE_DECODING_SUPPORT, + u"h264"_ns, true); + } + if (support.contains( + mozilla::media::MediaCodecsSupport::VP8HardwareDecode)) { + Telemetry::ScalarSet( + Telemetry::ScalarID::MEDIA_DEVICE_HARDWARE_DECODING_SUPPORT, + u"vp8"_ns, true); + } + if (support.contains( + mozilla::media::MediaCodecsSupport::VP9HardwareDecode)) { + Telemetry::ScalarSet( + Telemetry::ScalarID::MEDIA_DEVICE_HARDWARE_DECODING_SUPPORT, + u"vp9"_ns, true); + } + if (support.contains( + mozilla::media::MediaCodecsSupport::AV1HardwareDecode)) { + Telemetry::ScalarSet( + Telemetry::ScalarID::MEDIA_DEVICE_HARDWARE_DECODING_SUPPORT, + u"av1"_ns, true); + } + if (support.contains( + mozilla::media::MediaCodecsSupport::HEVCHardwareDecode)) { + Telemetry::ScalarSet( + Telemetry::ScalarID::MEDIA_DEVICE_HARDWARE_DECODING_SUPPORT, + u"hevc"_ns, true); + } + if (StaticPrefs::media_wmf_hevc_enabled() != 1) { + WMFDecoderModule::Init(); + } + })); +#endif + // TODO : in the future, when we have GPU procss on MacOS, then we can report + // HEVC usage as well. +} + +GPUParent::GPUParent() : mLaunchTime(TimeStamp::Now()) { sGPUParent = this; } + +GPUParent::~GPUParent() { sGPUParent = nullptr; } + +/* static */ +GPUParent* GPUParent::GetSingleton() { + MOZ_DIAGNOSTIC_ASSERT(sGPUParent); + return sGPUParent; +} + +/* static */ bool GPUParent::MaybeFlushMemory() { +#if defined(XP_WIN) && !defined(HAVE_64BIT_BUILD) + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); + if (!XRE_IsGPUProcess()) { + return false; + } + + MEMORYSTATUSEX stat; + stat.dwLength = sizeof(stat); + if (!GlobalMemoryStatusEx(&stat)) { + return false; + } + + // We only care about virtual process memory space in the GPU process because + // the UI process is already watching total memory usage. + static const size_t kLowVirtualMemoryThreshold = 384 * 1024 * 1024; + bool lowMemory = stat.ullAvailVirtual < kLowVirtualMemoryThreshold; + + // We suppress more than one low memory notification until we exit the + // condition. The UI process goes through more effort, reporting on-going + // memory pressure, but rather than try to manage a shared state, we just + // send one notification here to try to resolve it. + static bool sLowMemory = false; + if (lowMemory && !sLowMemory) { + NS_DispatchToMainThread( + NS_NewRunnableFunction("gfx::GPUParent::FlushMemory", []() -> void { + Unused << GPUParent::GetSingleton()->SendFlushMemory( + u"low-memory"_ns); + })); + } + sLowMemory = lowMemory; + return lowMemory; +#else + return false; +#endif +} + +bool GPUParent::Init(mozilla::ipc::UntypedEndpoint&& aEndpoint, + const char* aParentBuildID) { + // Initialize the thread manager before starting IPC. Otherwise, messages + // may be posted to the main thread and we won't be able to process them. + if (NS_WARN_IF(NS_FAILED(nsThreadManager::get().Init()))) { + return false; + } + + // Now it's safe to start IPC. + if (NS_WARN_IF(!aEndpoint.Bind(this))) { + return false; + } + + nsDebugImpl::SetMultiprocessMode("GPU"); + + // This must be checked before any IPDL message, which may hit sentinel + // errors due to parent and content processes having different + // versions. + MessageChannel* channel = GetIPCChannel(); + if (channel && !channel->SendBuildIDsMatchMessage(aParentBuildID)) { + // We need to quit this process if the buildID doesn't match the parent's. + // This can occur when an update occurred in the background. + ProcessChild::QuickExit(); + } + + if (NS_FAILED(NS_InitMinimalXPCOM())) { + return false; + } + + // Ensure the observer service exists. + ProcessPriorityManager::Init(); + + // Init crash reporter support. + CrashReporterClient::InitSingleton(this); + + gfxConfig::Init(); + gfxVars::Initialize(); + gfxPlatform::InitNullMetadata(); + // Ensure our Factory is initialised, mainly for gfx logging to work. + gfxPlatform::InitMoz2DLogging(); +#if defined(XP_WIN) + gfxWindowsPlatform::InitMemoryReportersForGPUProcess(); + DeviceManagerDx::Init(); + GpuProcessD3D11TextureMap::Init(); + GpuProcessD3D11QueryMap::Init(); +#endif + + CompositorThreadHolder::Start(); + RemoteTextureMap::Init(); + APZThreadUtils::SetControllerThread(NS_GetCurrentThread()); + apz::InitializeGlobalState(); + LayerTreeOwnerTracker::Initialize(); + CompositorBridgeParent::InitializeStatics(); + mozilla::ipc::SetThisProcessName("GPU Process"); + + return true; +} + +void GPUParent::NotifyDeviceReset() { + if (!NS_IsMainThread()) { + NS_DispatchToMainThread(NS_NewRunnableFunction( + "gfx::GPUParent::NotifyDeviceReset", + []() -> void { GPUParent::GetSingleton()->NotifyDeviceReset(); })); + return; + } + + // Reset and reinitialize the compositor devices +#ifdef XP_WIN + if (!DeviceManagerDx::Get()->MaybeResetAndReacquireDevices()) { + // If the device doesn't need to be reset then the device + // has already been reset by a previous NotifyDeviceReset message. + return; + } +#endif + + // Notify the main process that there's been a device reset + // and that they should reset their compositors and repaint + GPUDeviceData data; + RecvGetDeviceStatus(&data); + Unused << SendNotifyDeviceReset(data); +} + +void GPUParent::NotifyOverlayInfo(layers::OverlayInfo aInfo) { + if (!NS_IsMainThread()) { + NS_DispatchToMainThread(NS_NewRunnableFunction( + "gfx::GPUParent::NotifyOverlayInfo", [aInfo]() -> void { + GPUParent::GetSingleton()->NotifyOverlayInfo(aInfo); + })); + return; + } + Unused << SendNotifyOverlayInfo(aInfo); +} + +void GPUParent::NotifySwapChainInfo(layers::SwapChainInfo aInfo) { + if (!NS_IsMainThread()) { + NS_DispatchToMainThread(NS_NewRunnableFunction( + "gfx::GPUParent::NotifySwapChainInfo", [aInfo]() -> void { + GPUParent::GetSingleton()->NotifySwapChainInfo(aInfo); + })); + return; + } + Unused << SendNotifySwapChainInfo(aInfo); +} + +void GPUParent::NotifyDisableRemoteCanvas() { + if (!NS_IsMainThread()) { + NS_DispatchToMainThread(NS_NewRunnableFunction( + "gfx::GPUParent::NotifyDisableRemoteCanvas", []() -> void { + GPUParent::GetSingleton()->NotifyDisableRemoteCanvas(); + })); + return; + } + Unused << SendNotifyDisableRemoteCanvas(); +} + +mozilla::ipc::IPCResult GPUParent::RecvInit( + nsTArray<GfxVarUpdate>&& vars, const DevicePrefs& devicePrefs, + nsTArray<LayerTreeIdMapping>&& aMappings, + nsTArray<GfxInfoFeatureStatus>&& aFeatures, uint32_t aWrNamespace) { + for (const auto& var : vars) { + gfxVars::ApplyUpdate(var); + } + + // Inherit device preferences. + gfxConfig::Inherit(Feature::HW_COMPOSITING, devicePrefs.hwCompositing()); + gfxConfig::Inherit(Feature::D3D11_COMPOSITING, + devicePrefs.d3d11Compositing()); + gfxConfig::Inherit(Feature::OPENGL_COMPOSITING, devicePrefs.oglCompositing()); + gfxConfig::Inherit(Feature::DIRECT2D, devicePrefs.useD2D1()); + gfxConfig::Inherit(Feature::D3D11_HW_ANGLE, devicePrefs.d3d11HwAngle()); + + { // Let the crash reporter know if we've got WR enabled or not. For other + // processes this happens in gfxPlatform::InitWebRenderConfig. + ScopedGfxFeatureReporter reporter("WR", + gfxPlatform::WebRenderPrefEnabled()); + reporter.SetSuccessful(); + } + + for (const LayerTreeIdMapping& map : aMappings) { + LayerTreeOwnerTracker::Get()->Map(map.layersId(), map.ownerId()); + } + + widget::GfxInfoBase::SetFeatureStatus(std::move(aFeatures)); + + // We bypass gfxPlatform::Init, so we must initialize any relevant libraries + // here that would normally be initialized there. + SkGraphics::Init(); + + bool useRemoteCanvas = + gfxVars::RemoteCanvasEnabled() || gfxVars::UseAcceleratedCanvas2D(); + if (useRemoteCanvas) { + gfxGradientCache::Init(); + } + +#if defined(XP_WIN) + if (gfxConfig::IsEnabled(Feature::D3D11_COMPOSITING)) { + if (DeviceManagerDx::Get()->CreateCompositorDevices() && useRemoteCanvas) { + if (DeviceManagerDx::Get()->CreateCanvasDevice()) { + gfxDWriteFont::InitDWriteSupport(); + } else { + gfxWarning() << "Failed to create canvas device."; + } + } + } + DeviceManagerDx::Get()->CreateDirectCompositionDevice(); + // Ensure to initialize GfxInfo + nsCOMPtr<nsIGfxInfo> gfxInfo = components::GfxInfo::Service(); + Unused << gfxInfo; + + Factory::EnsureDWriteFactory(); +#endif + +#if defined(MOZ_WIDGET_GTK) + char* display_name = PR_GetEnv("MOZ_GDK_DISPLAY"); + if (!display_name) { + bool waylandEnabled = false; +# ifdef MOZ_WAYLAND + waylandEnabled = IsWaylandEnabled(); +# endif + if (!waylandEnabled) { + display_name = PR_GetEnv("DISPLAY"); + } + } + if (display_name) { + int argc = 3; + char option_name[] = "--display"; + char* argv[] = {// argv0 is unused because g_set_prgname() was called in + // XRE_InitChildProcess(). + nullptr, option_name, display_name, nullptr}; + char** argvp = argv; + gtk_init(&argc, &argvp); + } else { + gtk_init(nullptr, nullptr); + } + + // Ensure we have an FT library for font instantiation. + // This would normally be set by gfxPlatform::Init(). + // Since we bypass that, we must do it here instead. + FT_Library library = Factory::NewFTLibrary(); + MOZ_ASSERT(library); + Factory::SetFTLibrary(library); + + // true to match gfxPlatform::FontHintingEnabled(). We must hardcode + // this value because we do not have a gfxPlatform instance. + SkInitCairoFT(true); + + // Ensure that GfxInfo::Init is called on the main thread. + nsCOMPtr<nsIGfxInfo> gfxInfo = components::GfxInfo::Service(); + Unused << gfxInfo; +#endif + +#ifdef ANDROID + // Ensure we have an FT library for font instantiation. + // This would normally be set by gfxPlatform::Init(). + // Since we bypass that, we must do it here instead. + FT_Library library = Factory::NewFTLibrary(); + MOZ_ASSERT(library); + Factory::SetFTLibrary(library); + + // false to match gfxAndroidPlatform::FontHintingEnabled(). We must + // hardcode this value because we do not have a gfxPlatform instance. + SkInitCairoFT(false); + + if (gfxVars::UseAHardwareBufferSharedSurfaceWebglOop()) { + layers::AndroidHardwareBufferApi::Init(); + layers::AndroidHardwareBufferManager::Init(); + } + +#endif + + // Make sure to do this *after* we update gfxVars above. + wr::RenderThread::Start(aWrNamespace); + gfx::CanvasRenderThread::Start(); + image::ImageMemoryReporter::InitForWebRender(); + + VRManager::ManagerInit(); + // Send a message to the UI process that we're done. + GPUDeviceData data; + RecvGetDeviceStatus(&data); + Unused << SendInitComplete(data); + + // Dispatch a task to run when idle that will determine which codecs are + // usable. The primary goal is to determine if the media feature pack is + // installed. + MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThreadQueue( + NS_NewRunnableFunction( + "GPUParent::Supported", + []() { + auto supported = PDMFactory::Supported(); + Unused << GPUParent::GetSingleton()->SendUpdateMediaCodecsSupported( + supported); + ReportHardwareMediaCodecSupportIfNeeded(); + }), + 2000 /* 2 seconds timeout */, EventQueuePriority::Idle)); + + Telemetry::AccumulateTimeDelta(Telemetry::GPU_PROCESS_INITIALIZATION_TIME_MS, + mLaunchTime); + return IPC_OK(); +} + +#if defined(MOZ_SANDBOX) && defined(MOZ_DEBUG) && defined(ENABLE_TESTS) +mozilla::ipc::IPCResult GPUParent::RecvInitSandboxTesting( + Endpoint<PSandboxTestingChild>&& aEndpoint) { + if (!SandboxTestingChild::Initialize(std::move(aEndpoint))) { + return IPC_FAIL( + this, "InitSandboxTesting failed to initialise the child process."); + } + return IPC_OK(); +} +#endif + +mozilla::ipc::IPCResult GPUParent::RecvInitCompositorManager( + Endpoint<PCompositorManagerParent>&& aEndpoint, uint32_t aNamespace) { + CompositorManagerParent::Create(std::move(aEndpoint), ContentParentId(), + aNamespace, /* aIsRoot */ true); + return IPC_OK(); +} + +mozilla::ipc::IPCResult GPUParent::RecvInitVsyncBridge( + Endpoint<PVsyncBridgeParent>&& aVsyncEndpoint) { + mVsyncBridge = VsyncBridgeParent::Start(std::move(aVsyncEndpoint)); + return IPC_OK(); +} + +mozilla::ipc::IPCResult GPUParent::RecvInitImageBridge( + Endpoint<PImageBridgeParent>&& aEndpoint) { + ImageBridgeParent::CreateForGPUProcess(std::move(aEndpoint)); + return IPC_OK(); +} + +mozilla::ipc::IPCResult GPUParent::RecvInitVideoBridge( + Endpoint<PVideoBridgeParent>&& aEndpoint, + const layers::VideoBridgeSource& aSource) { + // For GPU decoding, the video bridge would be opened in + // `VideoBridgeChild::StartupForGPUProcess`. + MOZ_ASSERT(aSource == layers::VideoBridgeSource::RddProcess || + aSource == layers::VideoBridgeSource::MFMediaEngineCDMProcess); + VideoBridgeParent::Open(std::move(aEndpoint), aSource); + return IPC_OK(); +} + +mozilla::ipc::IPCResult GPUParent::RecvInitVRManager( + Endpoint<PVRManagerParent>&& aEndpoint) { + VRManagerParent::CreateForGPUProcess(std::move(aEndpoint)); + return IPC_OK(); +} + +mozilla::ipc::IPCResult GPUParent::RecvInitVR( + Endpoint<PVRGPUChild>&& aEndpoint) { + gfx::VRGPUChild::InitForGPUProcess(std::move(aEndpoint)); + return IPC_OK(); +} + +mozilla::ipc::IPCResult GPUParent::RecvInitUiCompositorController( + const LayersId& aRootLayerTreeId, + Endpoint<PUiCompositorControllerParent>&& aEndpoint) { + UiCompositorControllerParent::Start(aRootLayerTreeId, std::move(aEndpoint)); + return IPC_OK(); +} + +mozilla::ipc::IPCResult GPUParent::RecvInitAPZInputBridge( + const LayersId& aRootLayerTreeId, + Endpoint<PAPZInputBridgeParent>&& aEndpoint) { + APZInputBridgeParent::Create(aRootLayerTreeId, std::move(aEndpoint)); + return IPC_OK(); +} + +mozilla::ipc::IPCResult GPUParent::RecvInitProfiler( + Endpoint<PProfilerChild>&& aEndpoint) { + mProfilerController = ChildProfilerController::Create(std::move(aEndpoint)); + return IPC_OK(); +} + +mozilla::ipc::IPCResult GPUParent::RecvUpdateVar(const GfxVarUpdate& aUpdate) { +#if defined(XP_WIN) + auto scopeExit = MakeScopeExit( + [couldUseHWDecoder = gfx::gfxVars::CanUseHardwareVideoDecoding()] { + if (couldUseHWDecoder != gfx::gfxVars::CanUseHardwareVideoDecoding()) { + // The capabilities of the system may have changed, force a refresh by + // re-initializing the WMF PDM. + WMFDecoderModule::Init(); + Unused << GPUParent::GetSingleton()->SendUpdateMediaCodecsSupported( + PDMFactory::Supported(true /* force refresh */)); + ReportHardwareMediaCodecSupportIfNeeded(); + } + }); +#endif + gfxVars::ApplyUpdate(aUpdate); + return IPC_OK(); +} + +mozilla::ipc::IPCResult GPUParent::RecvPreferenceUpdate(const Pref& aPref) { + Preferences::SetPreference(aPref); + return IPC_OK(); +} + +static void CopyFeatureChange(Feature aFeature, Maybe<FeatureFailure>* aOut) { + FeatureState& feature = gfxConfig::GetFeature(aFeature); + if (feature.DisabledByDefault() || feature.IsEnabled()) { + // No change: + // - Disabled-by-default means the parent process told us not to use this + // feature. + // - Enabled means we were told to use this feature, and we didn't + // discover anything + // that would prevent us from doing so. + *aOut = Nothing(); + return; + } + + MOZ_ASSERT(!feature.IsEnabled()); + + nsCString message; + message.AssignASCII(feature.GetFailureMessage()); + + *aOut = + Some(FeatureFailure(feature.GetValue(), message, feature.GetFailureId())); +} + +mozilla::ipc::IPCResult GPUParent::RecvGetDeviceStatus(GPUDeviceData* aOut) { + CopyFeatureChange(Feature::D3D11_COMPOSITING, &aOut->d3d11Compositing()); + CopyFeatureChange(Feature::OPENGL_COMPOSITING, &aOut->oglCompositing()); + +#if defined(XP_WIN) + if (DeviceManagerDx* dm = DeviceManagerDx::Get()) { + D3D11DeviceStatus deviceStatus; + dm->ExportDeviceInfo(&deviceStatus); + aOut->gpuDevice() = Some(deviceStatus); + } +#else + aOut->gpuDevice() = Nothing(); +#endif + + return IPC_OK(); +} + +mozilla::ipc::IPCResult GPUParent::RecvSimulateDeviceReset() { +#if defined(XP_WIN) + DeviceManagerDx::Get()->ForceDeviceReset( + ForcedDeviceResetReason::COMPOSITOR_UPDATED); +#endif + wr::RenderThread::Get()->SimulateDeviceReset(); + return IPC_OK(); +} + +mozilla::ipc::IPCResult GPUParent::RecvNewContentCompositorManager( + Endpoint<PCompositorManagerParent>&& aEndpoint, + const ContentParentId& aChildId, uint32_t aNamespace) { + CompositorManagerParent::Create(std::move(aEndpoint), aChildId, aNamespace, + /* aIsRoot */ false); + return IPC_OK(); +} + +mozilla::ipc::IPCResult GPUParent::RecvNewContentImageBridge( + Endpoint<PImageBridgeParent>&& aEndpoint, const ContentParentId& aChildId) { + if (!ImageBridgeParent::CreateForContent(std::move(aEndpoint), aChildId)) { + return IPC_FAIL_NO_REASON(this); + } + return IPC_OK(); +} + +mozilla::ipc::IPCResult GPUParent::RecvNewContentVRManager( + Endpoint<PVRManagerParent>&& aEndpoint, const ContentParentId& aChildId) { + if (!VRManagerParent::CreateForContent(std::move(aEndpoint), aChildId)) { + return IPC_FAIL_NO_REASON(this); + } + return IPC_OK(); +} + +mozilla::ipc::IPCResult GPUParent::RecvNewContentRemoteDecoderManager( + Endpoint<PRemoteDecoderManagerParent>&& aEndpoint, + const ContentParentId& aChildId) { + if (!RemoteDecoderManagerParent::CreateForContent(std::move(aEndpoint), + aChildId)) { + return IPC_FAIL_NO_REASON(this); + } + return IPC_OK(); +} + +mozilla::ipc::IPCResult GPUParent::RecvAddLayerTreeIdMapping( + const LayerTreeIdMapping& aMapping) { + LayerTreeOwnerTracker::Get()->Map(aMapping.layersId(), aMapping.ownerId()); + return IPC_OK(); +} + +mozilla::ipc::IPCResult GPUParent::RecvRemoveLayerTreeIdMapping( + const LayerTreeIdMapping& aMapping) { + LayerTreeOwnerTracker::Get()->Unmap(aMapping.layersId(), aMapping.ownerId()); + CompositorBridgeParent::DeallocateLayerTreeId(aMapping.layersId()); + return IPC_OK(); +} + +mozilla::ipc::IPCResult GPUParent::RecvNotifyGpuObservers( + const nsCString& aTopic) { + nsCOMPtr<nsIObserverService> obsSvc = mozilla::services::GetObserverService(); + MOZ_ASSERT(obsSvc); + if (obsSvc) { + obsSvc->NotifyObservers(nullptr, aTopic.get(), nullptr); + } + return IPC_OK(); +} + +/* static */ +void GPUParent::GetGPUProcessName(nsACString& aStr) { + auto processType = XRE_GetProcessType(); + unsigned pid = 0; + if (processType == GeckoProcessType_GPU) { + pid = getpid(); + } else { + MOZ_DIAGNOSTIC_ASSERT(processType == GeckoProcessType_Default); + pid = GPUProcessManager::Get()->GPUProcessPid(); + } + + nsPrintfCString processName("GPU (pid %u)", pid); + aStr.Assign(processName); +} + +mozilla::ipc::IPCResult GPUParent::RecvRequestMemoryReport( + const uint32_t& aGeneration, const bool& aAnonymize, + const bool& aMinimizeMemoryUsage, const Maybe<FileDescriptor>& aDMDFile, + const RequestMemoryReportResolver& aResolver) { + nsAutoCString processName; + GetGPUProcessName(processName); + + mozilla::dom::MemoryReportRequestClient::Start( + aGeneration, aAnonymize, aMinimizeMemoryUsage, aDMDFile, processName, + [&](const MemoryReport& aReport) { + Unused << GetSingleton()->SendAddMemoryReport(aReport); + }, + aResolver); + return IPC_OK(); +} + +mozilla::ipc::IPCResult GPUParent::RecvShutdownVR() { + if (StaticPrefs::dom_vr_process_enabled_AtStartup()) { + VRGPUChild::Shutdown(); + } + return IPC_OK(); +} + +mozilla::ipc::IPCResult GPUParent::RecvUpdatePerfStatsCollectionMask( + const uint64_t& aMask) { + PerfStats::SetCollectionMask(static_cast<PerfStats::MetricMask>(aMask)); + return IPC_OK(); +} + +mozilla::ipc::IPCResult GPUParent::RecvCollectPerfStatsJSON( + CollectPerfStatsJSONResolver&& aResolver) { + aResolver(PerfStats::CollectLocalPerfStatsJSON()); + return IPC_OK(); +} + +mozilla::ipc::IPCResult GPUParent::RecvFlushFOGData( + FlushFOGDataResolver&& aResolver) { + glean::FlushFOGData(std::move(aResolver)); + return IPC_OK(); +} + +mozilla::ipc::IPCResult GPUParent::RecvTestTriggerMetrics( + TestTriggerMetricsResolver&& aResolve) { + mozilla::glean::test_only_ipc::a_counter.Add(nsIXULRuntime::PROCESS_TYPE_GPU); + aResolve(true); + return IPC_OK(); +} + +mozilla::ipc::IPCResult GPUParent::RecvCrashProcess() { + MOZ_CRASH("Deliberate GPU process crash"); + return IPC_OK(); +} + +void GPUParent::ActorDestroy(ActorDestroyReason aWhy) { + if (AbnormalShutdown == aWhy) { + NS_WARNING("Shutting down GPU process early due to a crash!"); + ProcessChild::QuickExit(); + } + + // Send the last bits of Glean data over to the main process. + glean::FlushFOGData( + [](ByteBuf&& aBuf) { glean::SendFOGData(std::move(aBuf)); }); + +#ifndef NS_FREE_PERMANENT_DATA + // No point in going through XPCOM shutdown because we don't keep persistent + // state. + ProcessChild::QuickExit(); +#endif + + // Wait until all RemoteDecoderManagerParent have closed. + mShutdownBlockers.WaitUntilClear(10 * 1000 /* 10s timeout*/) + ->Then(GetCurrentSerialEventTarget(), __func__, [self = RefPtr{this}]() { + if (self->mProfilerController) { + self->mProfilerController->Shutdown(); + self->mProfilerController = nullptr; + } + + if (self->mVsyncBridge) { + self->mVsyncBridge->Shutdown(); + self->mVsyncBridge = nullptr; + } + VideoBridgeParent::Shutdown(); + // This could be running on either the Compositor thread, the Renderer + // thread, or the dedicated CanvasRender thread, so we need to shutdown + // before the former two. + CanvasRenderThread::Shutdown(); + CompositorThreadHolder::Shutdown(); + RemoteTextureMap::Shutdown(); + // There is a case that RenderThread exists when gfxVars::UseWebRender() + // is false. This could happen when WebRender was fallbacked to + // compositor. + if (wr::RenderThread::Get()) { + wr::RenderThread::ShutDown(); + } +#ifdef XP_WIN + if (widget::WinCompositorWindowThread::Get()) { + widget::WinCompositorWindowThread::ShutDown(); + } +#endif + + image::ImageMemoryReporter::ShutdownForWebRender(); + + // Shut down the default GL context provider. + gl::GLContextProvider::Shutdown(); + +#if defined(XP_WIN) + // The above shutdown calls operate on the available context providers + // on most platforms. Windows is a "special snowflake", though, and has + // three context providers available, so we have to shut all of them + // down. We should only support the default GL provider on Windows; + // then, this could go away. Unfortunately, we currently support WGL + // (the default) for WebGL on Optimus. + gl::GLContextProviderEGL::Shutdown(); +#endif + + Factory::ShutDown(); + + // We bypass gfxPlatform shutdown, so we must shutdown any libraries here + // that would normally be handled by it. +#ifdef NS_FREE_PERMANENT_DATA + SkGraphics::PurgeFontCache(); + cairo_debug_reset_static_data(); +#endif + +#if defined(XP_WIN) + GpuProcessD3D11QueryMap::Shutdown(); + GpuProcessD3D11TextureMap::Shutdown(); + DeviceManagerDx::Shutdown(); +#endif + LayerTreeOwnerTracker::Shutdown(); + gfxVars::Shutdown(); + gfxConfig::Shutdown(); + CrashReporterClient::DestroySingleton(); + XRE_ShutdownChildProcess(); + }); +} + +} // namespace mozilla::gfx diff --git a/gfx/ipc/GPUParent.h b/gfx/ipc/GPUParent.h new file mode 100644 index 0000000000..434c9eab79 --- /dev/null +++ b/gfx/ipc/GPUParent.h @@ -0,0 +1,133 @@ +/* -*- 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/. */ +#ifndef _include_gfx_ipc_GPUParent_h__ +#define _include_gfx_ipc_GPUParent_h__ + +#include "mozilla/RefPtr.h" +#include "mozilla/gfx/PGPUParent.h" +#include "mozilla/ipc/AsyncBlockers.h" +#if defined(MOZ_SANDBOX) && defined(MOZ_DEBUG) && defined(ENABLE_TESTS) +# include "mozilla/PSandboxTestingChild.h" +#endif + +namespace mozilla { + +class TimeStamp; +class ChildProfilerController; + +namespace gfx { + +class VsyncBridgeParent; + +class GPUParent final : public PGPUParent { + public: + NS_INLINE_DECL_REFCOUNTING(GPUParent, final) + + GPUParent(); + + static GPUParent* GetSingleton(); + + ipc::AsyncBlockers& AsyncShutdownService() { return mShutdownBlockers; } + + // Gets the name of the GPU process, in the format expected by about:memory. + // There must be a GPU process active, and the caller must be either in that + // process or the parent process. + static void GetGPUProcessName(nsACString& aStr); + + // Check for memory pressure and notify the parent process if necessary. + static bool MaybeFlushMemory(); + + bool Init(mozilla::ipc::UntypedEndpoint&& aEndpoint, + const char* aParentBuildID); + void NotifyDeviceReset(); + void NotifyOverlayInfo(layers::OverlayInfo aInfo); + void NotifySwapChainInfo(layers::SwapChainInfo aInfo); + void NotifyDisableRemoteCanvas(); + + mozilla::ipc::IPCResult RecvInit(nsTArray<GfxVarUpdate>&& vars, + const DevicePrefs& devicePrefs, + nsTArray<LayerTreeIdMapping>&& mappings, + nsTArray<GfxInfoFeatureStatus>&& features, + uint32_t wrNamespace); + mozilla::ipc::IPCResult RecvInitCompositorManager( + Endpoint<PCompositorManagerParent>&& aEndpoint, uint32_t aNamespace); + mozilla::ipc::IPCResult RecvInitVsyncBridge( + Endpoint<PVsyncBridgeParent>&& aVsyncEndpoint); + mozilla::ipc::IPCResult RecvInitImageBridge( + Endpoint<PImageBridgeParent>&& aEndpoint); + mozilla::ipc::IPCResult RecvInitVideoBridge( + Endpoint<PVideoBridgeParent>&& aEndpoint, + const layers::VideoBridgeSource& aSource); + mozilla::ipc::IPCResult RecvInitVRManager( + Endpoint<PVRManagerParent>&& aEndpoint); + mozilla::ipc::IPCResult RecvInitVR(Endpoint<PVRGPUChild>&& aVRGPUChild); + mozilla::ipc::IPCResult RecvInitUiCompositorController( + const LayersId& aRootLayerTreeId, + Endpoint<PUiCompositorControllerParent>&& aEndpoint); + mozilla::ipc::IPCResult RecvInitAPZInputBridge( + const LayersId& aRootLayerTreeId, + Endpoint<PAPZInputBridgeParent>&& aEndpoint); + mozilla::ipc::IPCResult RecvInitProfiler( + Endpoint<PProfilerChild>&& aEndpoint); + mozilla::ipc::IPCResult RecvUpdateVar(const GfxVarUpdate& pref); + mozilla::ipc::IPCResult RecvPreferenceUpdate(const Pref& pref); + mozilla::ipc::IPCResult RecvNewContentCompositorManager( + Endpoint<PCompositorManagerParent>&& aEndpoint, + const ContentParentId& aChildId, uint32_t aNamespace); + mozilla::ipc::IPCResult RecvNewContentImageBridge( + Endpoint<PImageBridgeParent>&& aEndpoint, + const ContentParentId& aChildId); + mozilla::ipc::IPCResult RecvNewContentVRManager( + Endpoint<PVRManagerParent>&& aEndpoint, const ContentParentId& aChildId); + mozilla::ipc::IPCResult RecvNewContentRemoteDecoderManager( + Endpoint<PRemoteDecoderManagerParent>&& aEndpoint, + const ContentParentId& aChildId); + mozilla::ipc::IPCResult RecvGetDeviceStatus(GPUDeviceData* aOutStatus); + mozilla::ipc::IPCResult RecvSimulateDeviceReset(); + mozilla::ipc::IPCResult RecvAddLayerTreeIdMapping( + const LayerTreeIdMapping& aMapping); + mozilla::ipc::IPCResult RecvRemoveLayerTreeIdMapping( + const LayerTreeIdMapping& aMapping); + mozilla::ipc::IPCResult RecvNotifyGpuObservers(const nsCString& aTopic); + mozilla::ipc::IPCResult RecvRequestMemoryReport( + const uint32_t& generation, const bool& anonymize, + const bool& minimizeMemoryUsage, + const Maybe<ipc::FileDescriptor>& DMDFile, + const RequestMemoryReportResolver& aResolver); + mozilla::ipc::IPCResult RecvShutdownVR(); + + mozilla::ipc::IPCResult RecvUpdatePerfStatsCollectionMask( + const uint64_t& aMask); + mozilla::ipc::IPCResult RecvCollectPerfStatsJSON( + CollectPerfStatsJSONResolver&& aResolver); + +#if defined(MOZ_SANDBOX) && defined(MOZ_DEBUG) && defined(ENABLE_TESTS) + mozilla::ipc::IPCResult RecvInitSandboxTesting( + Endpoint<PSandboxTestingChild>&& aEndpoint); +#endif + + mozilla::ipc::IPCResult RecvFlushFOGData(FlushFOGDataResolver&& aResolver); + + mozilla::ipc::IPCResult RecvTestTriggerMetrics( + TestTriggerMetricsResolver&& aResolve); + + mozilla::ipc::IPCResult RecvCrashProcess(); + + void ActorDestroy(ActorDestroyReason aWhy) override; + + private: + ~GPUParent(); + + const TimeStamp mLaunchTime; + RefPtr<VsyncBridgeParent> mVsyncBridge; + RefPtr<ChildProfilerController> mProfilerController; + ipc::AsyncBlockers mShutdownBlockers; +}; + +} // namespace gfx +} // namespace mozilla + +#endif // _include_gfx_ipc_GPUParent_h__ diff --git a/gfx/ipc/GPUProcessHost.cpp b/gfx/ipc/GPUProcessHost.cpp new file mode 100644 index 0000000000..77cfcf300f --- /dev/null +++ b/gfx/ipc/GPUProcessHost.cpp @@ -0,0 +1,249 @@ +/* -*- 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 "GPUProcessHost.h" +#include "chrome/common/process_watcher.h" +#include "gfxPlatform.h" +#include "mozilla/dom/ContentParent.h" +#include "mozilla/gfx/GPUChild.h" +#include "mozilla/gfx/Logging.h" +#include "mozilla/layers/SynchronousTask.h" +#include "mozilla/Preferences.h" +#include "mozilla/StaticPrefs_layers.h" +#include "VRGPUChild.h" +#include "mozilla/ipc/ProcessUtils.h" +#ifdef MOZ_WIDGET_ANDROID +# include "mozilla/java/GeckoProcessManagerWrappers.h" +#endif + +namespace mozilla { +namespace gfx { + +using namespace ipc; + +GPUProcessHost::GPUProcessHost(Listener* aListener) + : GeckoChildProcessHost(GeckoProcessType_GPU), + mListener(aListener), + mTaskFactory(this), + mLaunchPhase(LaunchPhase::Unlaunched), + mProcessToken(0), + mShutdownRequested(false), + mChannelClosed(false) { + MOZ_COUNT_CTOR(GPUProcessHost); +} + +GPUProcessHost::~GPUProcessHost() { MOZ_COUNT_DTOR(GPUProcessHost); } + +bool GPUProcessHost::Launch(StringVector aExtraOpts) { + MOZ_ASSERT(mLaunchPhase == LaunchPhase::Unlaunched); + MOZ_ASSERT(!mGPUChild); + MOZ_ASSERT(!gfxPlatform::IsHeadless()); + + mPrefSerializer = MakeUnique<ipc::SharedPreferenceSerializer>(); + if (!mPrefSerializer->SerializeToSharedMemory(GeckoProcessType_GPU, + /* remoteType */ ""_ns)) { + return false; + } + mPrefSerializer->AddSharedPrefCmdLineArgs(*this, aExtraOpts); + +#if defined(XP_WIN) && defined(MOZ_SANDBOX) + mSandboxLevel = Preferences::GetInt("security.sandbox.gpu.level"); +#endif + + mLaunchPhase = LaunchPhase::Waiting; + mLaunchTime = TimeStamp::Now(); + + if (!GeckoChildProcessHost::AsyncLaunch(aExtraOpts)) { + mLaunchPhase = LaunchPhase::Complete; + mPrefSerializer = nullptr; + return false; + } + return true; +} + +bool GPUProcessHost::WaitForLaunch() { + if (mLaunchPhase == LaunchPhase::Complete) { + return !!mGPUChild; + } + + int32_t timeoutMs = + StaticPrefs::layers_gpu_process_startup_timeout_ms_AtStartup(); + + // If one of the following environment variables are set we can effectively + // ignore the timeout - as we can guarantee the compositor process will be + // terminated + if (PR_GetEnv("MOZ_DEBUG_CHILD_PROCESS") || + PR_GetEnv("MOZ_DEBUG_CHILD_PAUSE")) { + timeoutMs = 0; + } + + // Our caller expects the connection to be finished after we return, so we + // immediately set up the IPDL actor and fire callbacks. The IO thread will + // still dispatch a notification to the main thread - we'll just ignore it. + bool result = GeckoChildProcessHost::WaitUntilConnected(timeoutMs); + InitAfterConnect(result); + return result; +} + +void GPUProcessHost::OnChannelConnected(base::ProcessId peer_pid) { + MOZ_ASSERT(!NS_IsMainThread()); + + GeckoChildProcessHost::OnChannelConnected(peer_pid); + + // Post a task to the main thread. Take the lock because mTaskFactory is not + // thread-safe. + RefPtr<Runnable> runnable; + { + MonitorAutoLock lock(mMonitor); + runnable = + mTaskFactory.NewRunnableMethod(&GPUProcessHost::OnChannelConnectedTask); + } + NS_DispatchToMainThread(runnable); +} + +void GPUProcessHost::OnChannelConnectedTask() { + if (mLaunchPhase == LaunchPhase::Waiting) { + InitAfterConnect(true); + } +} + +void GPUProcessHost::OnChannelErrorTask() { + if (mLaunchPhase == LaunchPhase::Waiting) { + InitAfterConnect(false); + } +} + +static uint64_t sProcessTokenCounter = 0; + +void GPUProcessHost::InitAfterConnect(bool aSucceeded) { + MOZ_ASSERT(mLaunchPhase == LaunchPhase::Waiting); + MOZ_ASSERT(!mGPUChild); + + mLaunchPhase = LaunchPhase::Complete; + mPrefSerializer = nullptr; + + if (aSucceeded) { + mProcessToken = ++sProcessTokenCounter; + mGPUChild = MakeRefPtr<GPUChild>(this); + DebugOnly<bool> rv = TakeInitialEndpoint().Bind(mGPUChild.get()); + MOZ_ASSERT(rv); + + mGPUChild->Init(); + +#ifdef MOZ_WIDGET_ANDROID + nsCOMPtr<nsIEventTarget> launcherThread(GetIPCLauncher()); + MOZ_ASSERT(launcherThread); + layers::SynchronousTask task( + "GeckoProcessManager::GetCompositorSurfaceManager"); + + launcherThread->Dispatch(NS_NewRunnableFunction( + "GeckoProcessManager::GetCompositorSurfaceManager", [&]() { + layers::AutoCompleteTask complete(&task); + mCompositorSurfaceManager = + java::GeckoProcessManager::GetCompositorSurfaceManager(); + })); + + task.Wait(); +#endif + } + + if (mListener) { + mListener->OnProcessLaunchComplete(this); + } +} + +void GPUProcessHost::Shutdown(bool aUnexpectedShutdown) { + MOZ_ASSERT(!mShutdownRequested); + + mListener = nullptr; + + if (mGPUChild) { + // OnChannelClosed uses this to check if the shutdown was expected or + // unexpected. + mShutdownRequested = true; + + if (aUnexpectedShutdown) { + mGPUChild->OnUnexpectedShutdown(); + } + + // The channel might already be closed if we got here unexpectedly. + if (!mChannelClosed) { + if (VRGPUChild::IsCreated()) { + VRGPUChild::Get()->Close(); + } + mGPUChild->SendShutdownVR(); + mGPUChild->Close(); + } + +#ifndef NS_FREE_PERMANENT_DATA + // No need to communicate shutdown, the GPU process doesn't need to + // communicate anything back. + KillHard("NormalShutdown"); +#endif + + // If we're shutting down unexpectedly, we're in the middle of handling an + // ActorDestroy for PGPUChild, which is still on the stack. We'll return + // back to OnChannelClosed. + // + // Otherwise, we'll wait for OnChannelClose to be called whenever PGPUChild + // acknowledges shutdown. + return; + } + + DestroyProcess(); +} + +void GPUProcessHost::OnChannelClosed() { + mChannelClosed = true; + + if (!mShutdownRequested && mListener) { + // This is an unclean shutdown. Notify our listener that we're going away. + mListener->OnProcessUnexpectedShutdown(this); + } else { + DestroyProcess(); + } + + // Release the actor. + GPUChild::Destroy(std::move(mGPUChild)); + MOZ_ASSERT(!mGPUChild); +} + +void GPUProcessHost::KillHard(const char* aReason) { + ProcessHandle handle = GetChildProcessHandle(); + if (!base::KillProcess(handle, base::PROCESS_END_KILLED_BY_USER)) { + NS_WARNING("failed to kill subprocess!"); + } + + SetAlreadyDead(); +} + +uint64_t GPUProcessHost::GetProcessToken() const { return mProcessToken; } + +void GPUProcessHost::KillProcess() { KillHard("DiagnosticKill"); } + +void GPUProcessHost::CrashProcess() { mGPUChild->SendCrashProcess(); } + +void GPUProcessHost::DestroyProcess() { + // Cancel all tasks. We don't want anything triggering after our caller + // expects this to go away. + { + MonitorAutoLock lock(mMonitor); + mTaskFactory.RevokeAll(); + } + + GetCurrentSerialEventTarget()->Dispatch( + NS_NewRunnableFunction("DestroyProcessRunnable", [this] { Destroy(); })); +} + +#ifdef MOZ_WIDGET_ANDROID +java::CompositorSurfaceManager::Param +GPUProcessHost::GetCompositorSurfaceManager() { + return mCompositorSurfaceManager; +} +#endif + +} // namespace gfx +} // namespace mozilla diff --git a/gfx/ipc/GPUProcessHost.h b/gfx/ipc/GPUProcessHost.h new file mode 100644 index 0000000000..4f84d1e296 --- /dev/null +++ b/gfx/ipc/GPUProcessHost.h @@ -0,0 +1,164 @@ +/* -*- 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/. */ + +#ifndef _include_mozilla_gfx_ipc_GPUProcessHost_h_ +#define _include_mozilla_gfx_ipc_GPUProcessHost_h_ + +#include "mozilla/Maybe.h" +#include "mozilla/UniquePtr.h" +#include "mozilla/ipc/GeckoChildProcessHost.h" +#include "mozilla/ipc/ProtocolUtils.h" +#include "mozilla/ipc/TaskFactory.h" + +#ifdef MOZ_WIDGET_ANDROID +# include "mozilla/java/CompositorSurfaceManagerWrappers.h" +#endif + +namespace mozilla { +namespace ipc { +class SharedPreferenceSerializer; +} +} // namespace mozilla +class nsITimer; + +namespace mozilla { +namespace gfx { + +class GPUChild; + +// GPUProcessHost is the "parent process" container for a subprocess handle and +// IPC connection. It owns the parent process IPDL actor, which in this case, +// is a GPUChild. +// +// GPUProcessHosts are allocated and managed by GPUProcessManager. For all +// intents and purposes it is a singleton, though more than one may be allocated +// at a time due to its shutdown being asynchronous. +class GPUProcessHost final : public mozilla::ipc::GeckoChildProcessHost { + friend class GPUChild; + + public: + class Listener { + public: + virtual void OnProcessLaunchComplete(GPUProcessHost* aHost) {} + + // The GPUProcessHost has unexpectedly shutdown or had its connection + // severed. This is not called if an error occurs after calling + // Shutdown(). + virtual void OnProcessUnexpectedShutdown(GPUProcessHost* aHost) {} + + virtual void OnRemoteProcessDeviceReset(GPUProcessHost* aHost) {} + + virtual void OnProcessDeclaredStable() {} + }; + + explicit GPUProcessHost(Listener* listener); + + // Launch the subprocess asynchronously. On failure, false is returned. + // Otherwise, true is returned, and the OnProcessLaunchComplete listener + // callback will be invoked either when a connection has been established, or + // if a connection could not be established due to an asynchronous error. + // + // @param aExtraOpts (StringVector) + // Extra options to pass to the subprocess. + bool Launch(StringVector aExtraOpts); + + // If the process is being launched, block until it has launched and + // connected. If a launch task is pending, it will fire immediately. + // + // Returns true if the process is successfully connected; false otherwise. + bool WaitForLaunch(); + + // Inform the process that it should clean up its resources and shut down. + // This initiates an asynchronous shutdown sequence. After this method + // returns, it is safe for the caller to forget its pointer to the + // GPUProcessHost. + // + // After this returns, the attached Listener is no longer used. + // + // Setting aUnexpectedShutdown = true indicates that this is being called to + // clean up resources in response to an unexpected shutdown having been + // detected. + void Shutdown(bool aUnexpectedShutdown = false); + + // Return the actor for the top-level actor of the process. If the process + // has not connected yet, this returns null. + GPUChild* GetActor() const { return mGPUChild.get(); } + + // Return a unique id for this process, guaranteed not to be shared with any + // past or future instance of GPUProcessHost. + uint64_t GetProcessToken() const; + + bool IsConnected() const { return !!mGPUChild; } + + // Return the time stamp for when we tried to launch the GPU process. This is + // currently used for Telemetry so that we can determine how long GPU + // processes take to spin up. Note this doesn't denote a successful launch, + // just when we attempted launch. + TimeStamp GetLaunchTime() const { return mLaunchTime; } + + // Called on the IO thread. + void OnChannelConnected(base::ProcessId peer_pid) override; + + void SetListener(Listener* aListener); + + // Kills the GPU process. Used for tests and diagnostics + void KillProcess(); + + // Causes the GPU process to crash. Used for tests and diagnostics + void CrashProcess(); + +#ifdef MOZ_WIDGET_ANDROID + java::CompositorSurfaceManager::Param GetCompositorSurfaceManager(); +#endif + + private: + ~GPUProcessHost(); + + // Called on the main thread. + void OnChannelConnectedTask(); + void OnChannelErrorTask(); + + // Called on the main thread after a connection has been established. + void InitAfterConnect(bool aSucceeded); + + // Called on the main thread when the mGPUChild actor is shutting down. + void OnChannelClosed(); + + // Kill the remote process, triggering IPC shutdown. + void KillHard(const char* aReason); + + void DestroyProcess(); + + DISALLOW_COPY_AND_ASSIGN(GPUProcessHost); + + Listener* mListener; + mozilla::ipc::TaskFactory<GPUProcessHost> mTaskFactory; + + enum class LaunchPhase { Unlaunched, Waiting, Complete }; + LaunchPhase mLaunchPhase; + + RefPtr<GPUChild> mGPUChild; + uint64_t mProcessToken; + + UniquePtr<mozilla::ipc::SharedPreferenceSerializer> mPrefSerializer; + + bool mShutdownRequested; + bool mChannelClosed; + + TimeStamp mLaunchTime; + +#ifdef MOZ_WIDGET_ANDROID + // Binder interface used to send compositor surfaces to GPU process. There is + // one instance per GPU process which gets initialized after launch, then + // multiple compositors can take a reference to it. + java::CompositorSurfaceManager::GlobalRef mCompositorSurfaceManager; +#endif +}; + +} // namespace gfx +} // namespace mozilla + +#endif // _include_mozilla_gfx_ipc_GPUProcessHost_h_ diff --git a/gfx/ipc/GPUProcessImpl.cpp b/gfx/ipc/GPUProcessImpl.cpp new file mode 100644 index 0000000000..3ea86843e0 --- /dev/null +++ b/gfx/ipc/GPUProcessImpl.cpp @@ -0,0 +1,48 @@ +/* -*- 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 "GPUProcessImpl.h" +#include "mozilla/ipc/IOThreadChild.h" +#include "nsXPCOM.h" +#include "mozilla/ipc/ProcessUtils.h" +#include "mozilla/GeckoArgs.h" + +#if defined(XP_WIN) && defined(MOZ_SANDBOX) +# include "mozilla/sandboxTarget.h" +#elif defined(__OpenBSD__) && defined(MOZ_SANDBOX) +# include "mozilla/SandboxSettings.h" +#endif + +namespace mozilla { +namespace gfx { + +using namespace ipc; + +GPUProcessImpl::~GPUProcessImpl() = default; + +bool GPUProcessImpl::Init(int aArgc, char* aArgv[]) { +#if defined(MOZ_SANDBOX) && defined(XP_WIN) + mozilla::SandboxTarget::Instance()->StartSandbox(); +#elif defined(__OpenBSD__) && defined(MOZ_SANDBOX) + StartOpenBSDSandbox(GeckoProcessType_GPU); +#endif + + Maybe<const char*> parentBuildID = + geckoargs::sParentBuildID.Get(aArgc, aArgv); + if (parentBuildID.isNothing()) { + return false; + } + + if (!ProcessChild::InitPrefs(aArgc, aArgv)) { + return false; + } + + return mGPU->Init(TakeInitialEndpoint(), *parentBuildID); +} + +void GPUProcessImpl::CleanUp() { NS_ShutdownXPCOM(nullptr); } + +} // namespace gfx +} // namespace mozilla diff --git a/gfx/ipc/GPUProcessImpl.h b/gfx/ipc/GPUProcessImpl.h new file mode 100644 index 0000000000..0f188afe8a --- /dev/null +++ b/gfx/ipc/GPUProcessImpl.h @@ -0,0 +1,41 @@ +/* -*- 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/. */ +#ifndef _include_gfx_ipc_GPUProcessImpl_h__ +#define _include_gfx_ipc_GPUProcessImpl_h__ + +#include "mozilla/ipc/ProcessChild.h" +#include "GPUParent.h" + +#if defined(XP_WIN) +# include "mozilla/mscom/ProcessRuntime.h" +#endif + +namespace mozilla { +namespace gfx { + +// This class owns the subprocess instance of a PGPU - which in this case, +// is a GPUParent. It is instantiated as a singleton in XRE_InitChildProcess. +class GPUProcessImpl final : public ipc::ProcessChild { + public: + using ipc::ProcessChild::ProcessChild; + virtual ~GPUProcessImpl(); + + bool Init(int aArgc, char* aArgv[]) override; + void CleanUp() override; + + private: + RefPtr<GPUParent> mGPU = new GPUParent; + +#if defined(XP_WIN) + // This object initializes and configures COM. + mozilla::mscom::ProcessRuntime mCOMRuntime; +#endif +}; + +} // namespace gfx +} // namespace mozilla + +#endif // _include_gfx_ipc_GPUProcessImpl_h__ diff --git a/gfx/ipc/GPUProcessListener.h b/gfx/ipc/GPUProcessListener.h new file mode 100644 index 0000000000..a54c890ba0 --- /dev/null +++ b/gfx/ipc/GPUProcessListener.h @@ -0,0 +1,28 @@ +/* -*- 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/. */ +#ifndef _include_mozilla_gfx_ipc_GPUProcessListener_h_ +#define _include_mozilla_gfx_ipc_GPUProcessListener_h_ + +namespace mozilla { +namespace gfx { + +class GPUProcessListener { + public: + virtual ~GPUProcessListener() = default; + + // Called when the compositor has died and the rendering stack must be + // recreated. + virtual void OnCompositorUnexpectedShutdown() {} + + // Called when devices have been reset and tabs must throw away their + // layer managers. + virtual void OnCompositorDeviceReset() {} +}; + +} // namespace gfx +} // namespace mozilla + +#endif // _include_mozilla_gfx_ipc_GPUProcessListener_h_ diff --git a/gfx/ipc/GPUProcessManager.cpp b/gfx/ipc/GPUProcessManager.cpp new file mode 100644 index 0000000000..babc523e3a --- /dev/null +++ b/gfx/ipc/GPUProcessManager.cpp @@ -0,0 +1,1646 @@ +/* -*- 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 "GPUProcessManager.h" + +#include "gfxConfig.h" +#include "gfxPlatform.h" +#include "GPUProcessHost.h" +#include "GPUProcessListener.h" +#include "mozilla/AppShutdown.h" +#include "mozilla/MemoryReportingProcess.h" +#include "mozilla/Preferences.h" +#include "mozilla/Sprintf.h" +#include "mozilla/StaticPtr.h" +#include "mozilla/StaticPrefs_gfx.h" +#include "mozilla/StaticPrefs_layers.h" +#include "mozilla/StaticPrefs_media.h" +#include "mozilla/RemoteDecoderManagerChild.h" +#include "mozilla/RemoteDecoderManagerParent.h" +#include "mozilla/Telemetry.h" +#include "mozilla/dom/ContentParent.h" +#include "mozilla/gfx/gfxVars.h" +#include "mozilla/gfx/GPUChild.h" +#include "mozilla/glean/GleanMetrics.h" +#include "mozilla/ipc/Endpoint.h" +#include "mozilla/ipc/ProcessChild.h" +#include "mozilla/layers/APZCTreeManagerChild.h" +#include "mozilla/layers/APZInputBridgeChild.h" +#include "mozilla/layers/CompositorBridgeChild.h" +#include "mozilla/layers/CompositorBridgeParent.h" +#include "mozilla/layers/CompositorManagerChild.h" +#include "mozilla/layers/CompositorManagerParent.h" +#include "mozilla/layers/CompositorOptions.h" +#include "mozilla/layers/ImageBridgeChild.h" +#include "mozilla/layers/ImageBridgeParent.h" +#include "mozilla/layers/InProcessCompositorSession.h" +#include "mozilla/layers/LayerTreeOwnerTracker.h" +#include "mozilla/layers/RemoteCompositorSession.h" +#include "mozilla/webrender/RenderThread.h" +#include "mozilla/widget/PlatformWidgetTypes.h" +#include "nsAppRunner.h" +#include "mozilla/widget/CompositorWidget.h" +#ifdef MOZ_WIDGET_SUPPORTS_OOP_COMPOSITING +# include "mozilla/widget/CompositorWidgetChild.h" +#endif +#include "nsBaseWidget.h" +#include "nsContentUtils.h" +#include "VRManagerChild.h" +#include "VRManagerParent.h" +#include "VsyncBridgeChild.h" +#include "VsyncIOThreadHolder.h" +#include "VsyncSource.h" +#include "nsExceptionHandler.h" +#include "nsPrintfCString.h" + +#if defined(MOZ_WIDGET_ANDROID) +# include "mozilla/java/SurfaceControlManagerWrappers.h" +# include "mozilla/widget/AndroidUiThread.h" +# include "mozilla/layers/UiCompositorControllerChild.h" +#endif // defined(MOZ_WIDGET_ANDROID) + +#if defined(XP_WIN) +# include "gfxWindowsPlatform.h" +#endif + +namespace mozilla { +namespace gfx { + +using namespace mozilla::layers; + +static StaticAutoPtr<GPUProcessManager> sSingleton; + +GPUProcessManager* GPUProcessManager::Get() { return sSingleton; } + +void GPUProcessManager::Initialize() { + MOZ_ASSERT(XRE_IsParentProcess()); + sSingleton = new GPUProcessManager(); +} + +void GPUProcessManager::Shutdown() { sSingleton = nullptr; } + +GPUProcessManager::GPUProcessManager() + : mTaskFactory(this), + mNextNamespace(0), + mIdNamespace(0), + mResourceId(0), + mUnstableProcessAttempts(0), + mTotalProcessAttempts(0), + mDeviceResetCount(0), + mAppInForeground(true), + mProcess(nullptr), + mProcessToken(0), + mProcessStable(true), + mGPUChild(nullptr) { + MOZ_COUNT_CTOR(GPUProcessManager); + + mIdNamespace = AllocateNamespace(); + + mDeviceResetLastTime = TimeStamp::Now(); + + LayerTreeOwnerTracker::Initialize(); + CompositorBridgeParent::InitializeStatics(); +} + +GPUProcessManager::~GPUProcessManager() { + MOZ_COUNT_DTOR(GPUProcessManager); + + LayerTreeOwnerTracker::Shutdown(); + + // The GPU process should have already been shut down. + MOZ_ASSERT(!mProcess && !mGPUChild); + + // We should have already removed observers. + MOZ_ASSERT(!mObserver); +} + +NS_IMPL_ISUPPORTS(GPUProcessManager::Observer, nsIObserver); + +GPUProcessManager::Observer::Observer(GPUProcessManager* aManager) + : mManager(aManager) {} + +NS_IMETHODIMP +GPUProcessManager::Observer::Observe(nsISupports* aSubject, const char* aTopic, + const char16_t* aData) { + if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) { + mManager->OnXPCOMShutdown(); + } else if (!strcmp(aTopic, "nsPref:changed")) { + mManager->OnPreferenceChange(aData); + } else if (!strcmp(aTopic, "application-foreground")) { + mManager->mAppInForeground = true; + if (!mManager->mProcess && gfxConfig::IsEnabled(Feature::GPU_PROCESS)) { + Unused << mManager->LaunchGPUProcess(); + } + } else if (!strcmp(aTopic, "application-background")) { + mManager->mAppInForeground = false; + } + return NS_OK; +} + +void GPUProcessManager::OnXPCOMShutdown() { + if (mObserver) { + nsContentUtils::UnregisterShutdownObserver(mObserver); + Preferences::RemoveObserver(mObserver, ""); + nsCOMPtr<nsIObserverService> obsServ = services::GetObserverService(); + if (obsServ) { + obsServ->RemoveObserver(mObserver, "application-foreground"); + obsServ->RemoveObserver(mObserver, "application-background"); + } + mObserver = nullptr; + } + + CleanShutdown(); +} + +void GPUProcessManager::OnPreferenceChange(const char16_t* aData) { + // We know prefs are ASCII here. + NS_LossyConvertUTF16toASCII strData(aData); + + mozilla::dom::Pref pref(strData, /* isLocked */ false, + /* isSanitized */ false, Nothing(), Nothing()); + + Preferences::GetPreference(&pref, GeckoProcessType_GPU, + /* remoteType */ ""_ns); + if (!!mGPUChild) { + MOZ_ASSERT(mQueuedPrefs.IsEmpty()); + mGPUChild->SendPreferenceUpdate(pref); + } else if (IsGPUProcessLaunching()) { + mQueuedPrefs.AppendElement(pref); + } +} + +void GPUProcessManager::ResetProcessStable() { + mTotalProcessAttempts++; + mProcessStable = false; + mProcessAttemptLastTime = TimeStamp::Now(); +} + +bool GPUProcessManager::IsProcessStable(const TimeStamp& aNow) { + if (mTotalProcessAttempts > 0) { + auto delta = (int32_t)(aNow - mProcessAttemptLastTime).ToMilliseconds(); + if (delta < StaticPrefs::layers_gpu_process_stable_min_uptime_ms()) { + return false; + } + } + return mProcessStable; +} + +bool GPUProcessManager::LaunchGPUProcess() { + if (mProcess) { + return true; + } + + if (AppShutdown::IsInOrBeyond(ShutdownPhase::XPCOMShutdown)) { + return false; + } + + // Start listening for pref changes so we can + // forward them to the process once it is running. + if (!mObserver) { + mObserver = new Observer(this); + nsContentUtils::RegisterShutdownObserver(mObserver); + Preferences::AddStrongObserver(mObserver, ""); + nsCOMPtr<nsIObserverService> obsServ = services::GetObserverService(); + if (obsServ) { + obsServ->AddObserver(mObserver, "application-foreground", false); + obsServ->AddObserver(mObserver, "application-background", false); + } + } + + // Start the Vsync I/O thread so can use it as soon as the process launches. + EnsureVsyncIOThread(); + + // If the previous process didn't live long enough, increment our unstable + // attempts counter so that we don't end up in a restart loop. If the process + // did live long enough, reset the counter so that we don't disable the + // process too eagerly. + auto newTime = TimeStamp::Now(); + if (IsProcessStable(newTime)) { + mUnstableProcessAttempts = 0; + } else { + mUnstableProcessAttempts++; + mozilla::glean::gpu_process::unstable_launch_attempts.Set( + mUnstableProcessAttempts); + } + mTotalProcessAttempts++; + mozilla::glean::gpu_process::total_launch_attempts.Set(mTotalProcessAttempts); + mProcessAttemptLastTime = newTime; + mProcessStable = false; + + // If the process is launched whilst we're in the background it may never get + // a chance to be declared stable before it is killed again. We don't want + // this happening repeatedly to result in the GPU process being disabled, so + // we assume that processes launched whilst in the background are stable. + if (!mAppInForeground) { + gfxCriticalNote + << "GPU process is being launched whilst app is in background"; + mProcessStable = true; + } + + std::vector<std::string> extraArgs; + ipc::ProcessChild::AddPlatformBuildID(extraArgs); + + // The subprocess is launched asynchronously, so we wait for a callback to + // acquire the IPDL actor. + mProcess = new GPUProcessHost(this); + if (!mProcess->Launch(extraArgs)) { + DisableGPUProcess("Failed to launch GPU process"); + } + + return true; +} + +bool GPUProcessManager::IsGPUProcessLaunching() { + MOZ_ASSERT(NS_IsMainThread()); + return !!mProcess && !mGPUChild; +} + +void GPUProcessManager::DisableGPUProcess(const char* aMessage) { + MaybeDisableGPUProcess(aMessage, /* aAllowRestart */ false); +} + +bool GPUProcessManager::MaybeDisableGPUProcess(const char* aMessage, + bool aAllowRestart) { + if (!gfxConfig::IsEnabled(Feature::GPU_PROCESS)) { + return true; + } + + if (!aAllowRestart) { + gfxConfig::SetFailed(Feature::GPU_PROCESS, FeatureStatus::Failed, aMessage); + } + + bool wantRestart; + if (mLastError) { + wantRestart = + FallbackFromAcceleration(mLastError.value(), mLastErrorMsg.ref()); + mLastError.reset(); + mLastErrorMsg.reset(); + } else { + wantRestart = gfxPlatform::FallbackFromAcceleration( + FeatureStatus::Unavailable, aMessage, + "FEATURE_FAILURE_GPU_PROCESS_ERROR"_ns); + } + if (aAllowRestart && wantRestart) { + // The fallback method can make use of the GPU process. + return false; + } + + if (aAllowRestart) { + gfxConfig::SetFailed(Feature::GPU_PROCESS, FeatureStatus::Failed, aMessage); + } + + MOZ_ASSERT(!gfxConfig::IsEnabled(Feature::GPU_PROCESS)); + + gfxCriticalNote << aMessage; + + gfxPlatform::DisableGPUProcess(); + + mozilla::glean::gpu_process::feature_status.Set( + gfxConfig::GetFeature(Feature::GPU_PROCESS) + .GetStatusAndFailureIdString()); + + mozilla::glean::gpu_process::crash_fallbacks.Get("disabled"_ns).Add(1); + + DestroyProcess(); + ShutdownVsyncIOThread(); + + // Now the stability state is based upon the in process compositor session. + ResetProcessStable(); + + // We may have been in the middle of guaranteeing our various services are + // available when one failed. Some callers may fallback to using the same + // process equivalent, and we need to make sure those services are setup + // correctly. We cannot re-enter DisableGPUProcess from this call because we + // know that it is disabled in the config above. + DebugOnly<bool> ready = EnsureProtocolsReady(); + MOZ_ASSERT_IF(!ready, + AppShutdown::IsInOrBeyond(ShutdownPhase::XPCOMShutdown)); + + // If we disable the GPU process during reinitialization after a previous + // crash, then we need to tell the content processes again, because they + // need to rebind to the UI process. + HandleProcessLost(); + return true; +} + +nsresult GPUProcessManager::EnsureGPUReady( + bool aRetryAfterFallback /* = true */) { + MOZ_ASSERT(NS_IsMainThread()); + + // We only wait to fail with NS_ERROR_ILLEGAL_DURING_SHUTDOWN if we would + // cause a state change or if we are in the middle of relaunching the GPU + // process. + bool inShutdown = AppShutdown::IsInOrBeyond(ShutdownPhase::XPCOMShutdown); + + while (true) { + // Launch the GPU process if it is enabled but hasn't been (re-)launched + // yet. + if (!mProcess && gfxConfig::IsEnabled(Feature::GPU_PROCESS)) { + if (NS_WARN_IF(inShutdown)) { + return NS_ERROR_ILLEGAL_DURING_SHUTDOWN; + } + + if (!LaunchGPUProcess()) { + return NS_ERROR_FAILURE; + } + } + + if (mProcess && !mProcess->IsConnected()) { + if (NS_WARN_IF(inShutdown)) { + return NS_ERROR_ILLEGAL_DURING_SHUTDOWN; + } + + if (!mProcess->WaitForLaunch()) { + // If this fails, we should have fired OnProcessLaunchComplete and + // removed the process. + MOZ_ASSERT(!mProcess && !mGPUChild); + return NS_ERROR_FAILURE; + } + } + + // The only scenario this should be possible is if we raced with the + // initialization, which failed, and has already decided to disable the GPU + // process. + if (!mGPUChild) { + MOZ_DIAGNOSTIC_ASSERT(!gfxConfig::IsEnabled(Feature::GPU_PROCESS)); + break; + } + + if (mGPUChild->EnsureGPUReady()) { + return NS_OK; + } + + // If the initialization above fails, we likely have a GPU process teardown + // waiting in our message queue (or will soon). If the fallback wants us to + // give up on the GPU process, we will exit the loop. + if (MaybeDisableGPUProcess("Failed to initialize GPU process", + /* aAllowRestart */ true)) { + MOZ_DIAGNOSTIC_ASSERT(!gfxConfig::IsEnabled(Feature::GPU_PROCESS)); + break; + } + + // Otherwise HandleProcessLost will explicitly teardown the process and + // prevent any pending events from triggering our fallback logic again, and + // we will retry with a different configuration. + MOZ_DIAGNOSTIC_ASSERT(gfxConfig::IsEnabled(Feature::GPU_PROCESS)); + OnBlockingProcessUnexpectedShutdown(); + + // Some callers may need to reconfigure if we fellback. + if (!aRetryAfterFallback) { + return NS_ERROR_NOT_AVAILABLE; + } + } + + // This is the first time we are trying to use the in-process compositor. + if (mTotalProcessAttempts == 0) { + if (NS_WARN_IF(inShutdown)) { + return NS_ERROR_ILLEGAL_DURING_SHUTDOWN; + } + ResetProcessStable(); + } + return NS_ERROR_FAILURE; +} + +bool GPUProcessManager::EnsureProtocolsReady() { + return EnsureCompositorManagerChild() && EnsureImageBridgeChild() && + EnsureVRManager(); +} + +bool GPUProcessManager::EnsureCompositorManagerChild() { + nsresult rv = EnsureGPUReady(); + if (NS_WARN_IF(rv == NS_ERROR_ILLEGAL_DURING_SHUTDOWN)) { + return false; + } + + if (CompositorManagerChild::IsInitialized(mProcessToken)) { + return true; + } + + if (NS_FAILED(rv)) { + CompositorManagerChild::InitSameProcess(AllocateNamespace(), mProcessToken); + return true; + } + + ipc::Endpoint<PCompositorManagerParent> parentPipe; + ipc::Endpoint<PCompositorManagerChild> childPipe; + rv = PCompositorManager::CreateEndpoints( + mGPUChild->OtherPid(), base::GetCurrentProcId(), &parentPipe, &childPipe); + if (NS_FAILED(rv)) { + DisableGPUProcess("Failed to create PCompositorManager endpoints"); + return true; + } + + uint32_t cmNamespace = AllocateNamespace(); + mGPUChild->SendInitCompositorManager(std::move(parentPipe), cmNamespace); + CompositorManagerChild::Init(std::move(childPipe), cmNamespace, + mProcessToken); + return true; +} + +bool GPUProcessManager::EnsureImageBridgeChild() { + if (ImageBridgeChild::GetSingleton()) { + return true; + } + + nsresult rv = EnsureGPUReady(); + if (NS_WARN_IF(rv == NS_ERROR_ILLEGAL_DURING_SHUTDOWN)) { + return false; + } + + if (NS_FAILED(rv)) { + ImageBridgeChild::InitSameProcess(AllocateNamespace()); + return true; + } + + ipc::Endpoint<PImageBridgeParent> parentPipe; + ipc::Endpoint<PImageBridgeChild> childPipe; + rv = PImageBridge::CreateEndpoints( + mGPUChild->OtherPid(), base::GetCurrentProcId(), &parentPipe, &childPipe); + if (NS_FAILED(rv)) { + DisableGPUProcess("Failed to create PImageBridge endpoints"); + return true; + } + + mGPUChild->SendInitImageBridge(std::move(parentPipe)); + ImageBridgeChild::InitWithGPUProcess(std::move(childPipe), + AllocateNamespace()); + return true; +} + +bool GPUProcessManager::EnsureVRManager() { + if (VRManagerChild::IsCreated()) { + return true; + } + + nsresult rv = EnsureGPUReady(); + if (NS_WARN_IF(rv == NS_ERROR_ILLEGAL_DURING_SHUTDOWN)) { + return false; + } + + if (NS_FAILED(rv)) { + VRManagerChild::InitSameProcess(); + return true; + } + + ipc::Endpoint<PVRManagerParent> parentPipe; + ipc::Endpoint<PVRManagerChild> childPipe; + rv = PVRManager::CreateEndpoints( + mGPUChild->OtherPid(), base::GetCurrentProcId(), &parentPipe, &childPipe); + if (NS_FAILED(rv)) { + DisableGPUProcess("Failed to create PVRManager endpoints"); + return true; + } + + mGPUChild->SendInitVRManager(std::move(parentPipe)); + VRManagerChild::InitWithGPUProcess(std::move(childPipe)); + return true; +} + +#if defined(MOZ_WIDGET_ANDROID) +already_AddRefed<UiCompositorControllerChild> +GPUProcessManager::CreateUiCompositorController(nsBaseWidget* aWidget, + const LayersId aId) { + RefPtr<UiCompositorControllerChild> result; + + nsresult rv = EnsureGPUReady(); + if (NS_WARN_IF(rv == NS_ERROR_ILLEGAL_DURING_SHUTDOWN)) { + return nullptr; + } + + if (NS_FAILED(rv)) { + result = UiCompositorControllerChild::CreateForSameProcess(aId, aWidget); + } else { + ipc::Endpoint<PUiCompositorControllerParent> parentPipe; + ipc::Endpoint<PUiCompositorControllerChild> childPipe; + rv = PUiCompositorController::CreateEndpoints(mGPUChild->OtherPid(), + base::GetCurrentProcId(), + &parentPipe, &childPipe); + if (NS_FAILED(rv)) { + DisableGPUProcess("Failed to create PUiCompositorController endpoints"); + return nullptr; + } + + mGPUChild->SendInitUiCompositorController(aId, std::move(parentPipe)); + result = UiCompositorControllerChild::CreateForGPUProcess( + mProcessToken, std::move(childPipe), aWidget); + + if (result) { + result->SetCompositorSurfaceManager( + mProcess->GetCompositorSurfaceManager()); + } + } + return result.forget(); +} +#endif // defined(MOZ_WIDGET_ANDROID) + +void GPUProcessManager::OnProcessLaunchComplete(GPUProcessHost* aHost) { + MOZ_ASSERT(mProcess && mProcess == aHost); + + if (!mProcess->IsConnected()) { + DisableGPUProcess("Failed to connect GPU process"); + return; + } + + mGPUChild = mProcess->GetActor(); + mProcessToken = mProcess->GetProcessToken(); +#if defined(XP_WIN) + if (mAppInForeground) { + SetProcessIsForeground(); + } +#endif + + ipc::Endpoint<PVsyncBridgeParent> vsyncParent; + ipc::Endpoint<PVsyncBridgeChild> vsyncChild; + nsresult rv = PVsyncBridge::CreateEndpoints(mGPUChild->OtherPid(), + base::GetCurrentProcId(), + &vsyncParent, &vsyncChild); + if (NS_FAILED(rv)) { + DisableGPUProcess("Failed to create PVsyncBridge endpoints"); + return; + } + + mVsyncBridge = VsyncBridgeChild::Create(mVsyncIOThread, mProcessToken, + std::move(vsyncChild)); + mGPUChild->SendInitVsyncBridge(std::move(vsyncParent)); + + // Flush any pref updates that happened during launch and weren't + // included in the blobs set up in LaunchGPUProcess. + for (const mozilla::dom::Pref& pref : mQueuedPrefs) { + Unused << NS_WARN_IF(!mGPUChild->SendPreferenceUpdate(pref)); + } + mQueuedPrefs.Clear(); + + CrashReporter::AnnotateCrashReport( + CrashReporter::Annotation::GPUProcessStatus, "Running"_ns); + + CrashReporter::AnnotateCrashReport( + CrashReporter::Annotation::GPUProcessLaunchCount, + static_cast<int>(mTotalProcessAttempts)); + + ReinitializeRendering(); +} + +void GPUProcessManager::OnProcessDeclaredStable() { mProcessStable = true; } + +static bool ShouldLimitDeviceResets(uint32_t count, int32_t deltaMilliseconds) { + // We decide to limit by comparing the amount of resets that have happened + // and time since the last reset to two prefs. + int32_t timeLimit = StaticPrefs::gfx_device_reset_threshold_ms_AtStartup(); + int32_t countLimit = StaticPrefs::gfx_device_reset_limit_AtStartup(); + + bool hasTimeLimit = timeLimit >= 0; + bool hasCountLimit = countLimit >= 0; + + bool triggeredTime = deltaMilliseconds < timeLimit; + bool triggeredCount = count > (uint32_t)countLimit; + + // If we have both prefs set then it needs to trigger both limits, + // otherwise we only test the pref that is set or none + if (hasTimeLimit && hasCountLimit) { + return triggeredTime && triggeredCount; + } else if (hasTimeLimit) { + return triggeredTime; + } else if (hasCountLimit) { + return triggeredCount; + } + + return false; +} + +void GPUProcessManager::ResetCompositors() { + // Note: this will recreate devices in addition to recreating compositors. + // This isn't optimal, but this is only used on linux where acceleration + // isn't enabled by default, and this way we don't need a new code path. + SimulateDeviceReset(); +} + +void GPUProcessManager::SimulateDeviceReset() { + // Make sure we rebuild environment and configuration for accelerated + // features. + gfxPlatform::GetPlatform()->CompositorUpdated(); + + if (mProcess) { + if (mGPUChild) { + mGPUChild->SendSimulateDeviceReset(); + } + } else { + wr::RenderThread::Get()->SimulateDeviceReset(); + } +} + +bool GPUProcessManager::FallbackFromAcceleration(wr::WebRenderError aError, + const nsCString& aMsg) { + if (aError == wr::WebRenderError::INITIALIZE) { + return gfxPlatform::FallbackFromAcceleration( + gfx::FeatureStatus::Unavailable, "WebRender initialization failed", + aMsg); + } else if (aError == wr::WebRenderError::MAKE_CURRENT) { + return gfxPlatform::FallbackFromAcceleration( + gfx::FeatureStatus::Unavailable, + "Failed to make render context current", + "FEATURE_FAILURE_WEBRENDER_MAKE_CURRENT"_ns); + } else if (aError == wr::WebRenderError::RENDER) { + return gfxPlatform::FallbackFromAcceleration( + gfx::FeatureStatus::Unavailable, "Failed to render WebRender", + "FEATURE_FAILURE_WEBRENDER_RENDER"_ns); + } else if (aError == wr::WebRenderError::NEW_SURFACE) { + // If we cannot create a new Surface even in the final fallback + // configuration then force a crash. + return gfxPlatform::FallbackFromAcceleration( + gfx::FeatureStatus::Unavailable, "Failed to create new surface", + "FEATURE_FAILURE_WEBRENDER_NEW_SURFACE"_ns, + /* aCrashAfterFinalFallback */ true); + } else if (aError == wr::WebRenderError::BEGIN_DRAW) { + return gfxPlatform::FallbackFromAcceleration( + gfx::FeatureStatus::Unavailable, "BeginDraw() failed", + "FEATURE_FAILURE_WEBRENDER_BEGIN_DRAW"_ns); + } else if (aError == wr::WebRenderError::EXCESSIVE_RESETS) { + return gfxPlatform::FallbackFromAcceleration( + gfx::FeatureStatus::Unavailable, "Device resets exceeded threshold", + "FEATURE_FAILURE_WEBRENDER_EXCESSIVE_RESETS"_ns); + } else { + MOZ_ASSERT_UNREACHABLE("Invalid value"); + return gfxPlatform::FallbackFromAcceleration( + gfx::FeatureStatus::Unavailable, "Unhandled failure reason", + "FEATURE_FAILURE_WEBRENDER_UNHANDLED"_ns); + } +} + +bool GPUProcessManager::DisableWebRenderConfig(wr::WebRenderError aError, + const nsCString& aMsg) { + // If we have a stable compositor process, this may just be due to an OOM or + // bad driver state. In that case, we should consider restarting the GPU + // process, or simulating a device reset to teardown the compositors to + // hopefully alleviate the situation. + if (IsProcessStable(TimeStamp::Now())) { + if (mProcess) { + mProcess->KillProcess(); + } else { + SimulateDeviceReset(); + } + + mLastError = Some(aError); + mLastErrorMsg = Some(aMsg); + return false; + } + + mLastError.reset(); + mLastErrorMsg.reset(); + + // Disable WebRender + bool wantRestart = FallbackFromAcceleration(aError, aMsg); + gfxVars::SetUseWebRenderDCompVideoHwOverlayWin(false); + gfxVars::SetUseWebRenderDCompVideoSwOverlayWin(false); + + // If we still have the GPU process, and we fallback to a new configuration + // that prefers to have the GPU process, reset the counter. Because we + // updated the gfxVars, we want to flag the GPUChild to wait for the update + // to be processed before creating new compositor sessions, otherwise we risk + // them being out of sync with the content/parent processes. + if (wantRestart && mProcess && mGPUChild) { + mUnstableProcessAttempts = 1; + mGPUChild->MarkWaitForVarUpdate(); + } + + return true; +} + +void GPUProcessManager::DisableWebRender(wr::WebRenderError aError, + const nsCString& aMsg) { + if (DisableWebRenderConfig(aError, aMsg)) { + if (mProcess) { + DestroyRemoteCompositorSessions(); + } else { + DestroyInProcessCompositorSessions(); + } + NotifyListenersOnCompositeDeviceReset(); + } +} + +void GPUProcessManager::NotifyWebRenderError(wr::WebRenderError aError) { + gfxCriticalNote << "Handling webrender error " << (unsigned int)aError; +#ifdef XP_WIN + if (aError == wr::WebRenderError::VIDEO_OVERLAY) { + gfxVars::SetUseWebRenderDCompVideoHwOverlayWin(false); + gfxVars::SetUseWebRenderDCompVideoSwOverlayWin(false); + return; + } + if (aError == wr::WebRenderError::VIDEO_HW_OVERLAY) { + gfxVars::SetUseWebRenderDCompVideoHwOverlayWin(false); + return; + } + if (aError == wr::WebRenderError::VIDEO_SW_OVERLAY) { + gfxVars::SetUseWebRenderDCompVideoSwOverlayWin(false); + return; + } +#else + if (aError == wr::WebRenderError::VIDEO_OVERLAY || + aError == wr::WebRenderError::VIDEO_HW_OVERLAY || + aError == wr::WebRenderError::VIDEO_SW_OVERLAY) { + MOZ_ASSERT_UNREACHABLE("unexpected to be called"); + return; + } +#endif + + DisableWebRender(aError, nsCString()); +} + +/* static */ void GPUProcessManager::RecordDeviceReset( + DeviceResetReason aReason) { + if (aReason != DeviceResetReason::FORCED_RESET) { + Telemetry::Accumulate(Telemetry::DEVICE_RESET_REASON, uint32_t(aReason)); + } + + CrashReporter::AnnotateCrashReport( + CrashReporter::Annotation::DeviceResetReason, int(aReason)); +} + +bool GPUProcessManager::OnDeviceReset(bool aTrackThreshold) { + // Ignore resets for thresholding if requested. + if (!aTrackThreshold) { + return false; + } + + // Detect whether the device is resetting too quickly or too much + // indicating that we should give up and use software + mDeviceResetCount++; + + auto newTime = TimeStamp::Now(); + auto delta = (int32_t)(newTime - mDeviceResetLastTime).ToMilliseconds(); + mDeviceResetLastTime = newTime; + + // Returns true if we should disable acceleration due to the reset. + return ShouldLimitDeviceResets(mDeviceResetCount, delta); +} + +void GPUProcessManager::OnInProcessDeviceReset(bool aTrackThreshold) { + if (OnDeviceReset(aTrackThreshold)) { + gfxCriticalNoteOnce << "In-process device reset threshold exceeded"; +#ifdef MOZ_WIDGET_GTK + // FIXME(aosmond): Should we disable WebRender on other platforms? + DisableWebRenderConfig(wr::WebRenderError::EXCESSIVE_RESETS, nsCString()); +#endif + } +#ifdef XP_WIN + // Ensure device reset handling before re-creating in process sessions. + // Normally nsWindow::OnPaint() already handled it. + gfxWindowsPlatform::GetPlatform()->HandleDeviceReset(); +#endif + DestroyInProcessCompositorSessions(); + NotifyListenersOnCompositeDeviceReset(); +} + +void GPUProcessManager::OnRemoteProcessDeviceReset(GPUProcessHost* aHost) { + if (OnDeviceReset(/* aTrackThreshold */ true) && + !DisableWebRenderConfig(wr::WebRenderError::EXCESSIVE_RESETS, + nsCString())) { + return; + } + + DestroyRemoteCompositorSessions(); + NotifyListenersOnCompositeDeviceReset(); +} + +void GPUProcessManager::NotifyListenersOnCompositeDeviceReset() { + for (const auto& listener : mListeners) { + listener->OnCompositorDeviceReset(); + } +} + +void GPUProcessManager::OnBlockingProcessUnexpectedShutdown() { + if (mProcess) { + CompositorManagerChild::OnGPUProcessLost(mProcess->GetProcessToken()); + } + DestroyProcess(/* aUnexpectedShutdown */ true); + mUnstableProcessAttempts = 0; + HandleProcessLost(); +} + +void GPUProcessManager::OnProcessUnexpectedShutdown(GPUProcessHost* aHost) { + MOZ_ASSERT(mProcess && mProcess == aHost); + + if (StaticPrefs::layers_gpu_process_crash_also_crashes_browser()) { + MOZ_CRASH("GPU process crashed and pref is set to crash the browser."); + } + + CompositorManagerChild::OnGPUProcessLost(aHost->GetProcessToken()); + DestroyProcess(/* aUnexpectedShutdown */ true); + + if (mUnstableProcessAttempts > + uint32_t(StaticPrefs::layers_gpu_process_max_restarts())) { + char disableMessage[64]; + SprintfLiteral(disableMessage, "GPU process disabled after %d attempts", + mTotalProcessAttempts); + if (!MaybeDisableGPUProcess(disableMessage, /* aAllowRestart */ true)) { + // Fallback wants the GPU process. Reset our counter. + MOZ_DIAGNOSTIC_ASSERT(gfxConfig::IsEnabled(Feature::GPU_PROCESS)); + mUnstableProcessAttempts = 0; + HandleProcessLost(); + } else { + MOZ_DIAGNOSTIC_ASSERT(!gfxConfig::IsEnabled(Feature::GPU_PROCESS)); + } + } else if (mUnstableProcessAttempts > + uint32_t(StaticPrefs:: + layers_gpu_process_max_restarts_with_decoder()) && + mDecodeVideoOnGpuProcess) { + mDecodeVideoOnGpuProcess = false; + mozilla::glean::gpu_process::crash_fallbacks.Get("decoding_disabled"_ns) + .Add(1); + HandleProcessLost(); + } else { + mozilla::glean::gpu_process::crash_fallbacks.Get("none"_ns).Add(1); + HandleProcessLost(); + } +} + +void GPUProcessManager::HandleProcessLost() { + MOZ_ASSERT(NS_IsMainThread()); + + // The shutdown and restart sequence for the GPU process is as follows: + // + // (1) The GPU process dies. IPDL will enqueue an ActorDestroy message on + // each channel owning a bridge to the GPU process, on the thread owning + // that channel. + // + // (2) The first channel to process its ActorDestroy message will post a + // message to the main thread to call NotifyRemoteActorDestroyed on the + // GPUProcessManager, which calls OnProcessUnexpectedShutdown if it has + // not handled shutdown for this process yet. OnProcessUnexpectedShutdown + // is responsible for tearing down the old process and deciding whether + // or not to disable the GPU process. It then calls this function, + // HandleProcessLost. + // + // (3) We then notify each widget that its session with the compositor is now + // invalid. The widget is responsible for destroying its layer manager + // and CompositorBridgeChild. Note that at this stage, not all actors may + // have received ActorDestroy yet. CompositorBridgeChild may attempt to + // send messages, and if this happens, it will probably report a + // MsgDropped error. This is okay. + // + // (4) At this point, the UI process has a clean slate: no layers should + // exist for the old compositor. We may make a decision on whether or not + // to re-launch the GPU process. Or, on Android if the app is in the + // background we may decide to wait until it comes to the foreground + // before re-launching. + // + // (5) When we do decide to re-launch, or continue without a GPU process, we + // notify each ContentParent of the lost connection. It will request new + // endpoints from the GPUProcessManager and forward them to its + // ContentChild. The parent-side of these endpoints may come from the + // compositor thread of the UI process, or the compositor thread of the + // GPU process. However, no actual compositors should exist yet. + // + // (6) Each ContentChild will receive new endpoints. It will destroy its + // Compositor/ImageBridgeChild singletons and recreate them, as well + // as invalidate all retained layers. + // + // (7) In addition, each ContentChild will ask each of its BrowserChildren + // to re-request association with the compositor for the window + // owning the tab. The sequence of calls looks like: + // (a) [CONTENT] ContentChild::RecvReinitRendering + // (b) [CONTENT] BrowserChild::ReinitRendering + // (c) [CONTENT] BrowserChild::SendEnsureLayersConnected + // (d) [UI] BrowserParent::RecvEnsureLayersConnected + // (e) [UI] RemoteLayerTreeOwner::EnsureLayersConnected + // (f) [UI] CompositorBridgeChild::SendNotifyChildRecreated + // + // Note that at step (e), RemoteLayerTreeOwner will call + // GetWindowRenderer on the nsIWidget owning the tab. This step ensures + // that a compositor exists for the window. If we decided to launch a new + // GPU Process, at this point we block until the process has launched and + // we're able to create a new window compositor. Otherwise, if + // compositing is now in-process, this will simply create a new + // CompositorBridgeParent in the UI process. If there are multiple tabs + // in the same window, additional tabs will simply return the already- + // established compositor. + // + // Finally, this step serves one other crucial function: tabs must be + // associated with a window compositor or else they can't forward + // layer transactions. So this step both ensures that a compositor + // exists, and that the tab can forward layers. + // + // (8) Last, if the window had no remote tabs, step (7) will not have + // applied, and the window will not have a new compositor just yet. The + // next refresh tick and paint will ensure that one exists, again via + // nsIWidget::GetWindowRenderer. On Android, we called + // nsIWidgetListener::RequestRepaint back in step (3) to ensure this + // tick occurs, but on other platforms this is not necessary. + + DestroyRemoteCompositorSessions(); + +#ifdef MOZ_WIDGET_ANDROID + java::SurfaceControlManager::GetInstance()->OnGpuProcessLoss(); +#endif + + // Re-launch the process if immediately if the GPU process is still enabled. + // Except on Android if the app is in the background, where we want to wait + // until the app is in the foreground again. + if (gfxConfig::IsEnabled(Feature::GPU_PROCESS)) { +#ifdef MOZ_WIDGET_ANDROID + if (mAppInForeground) { +#else + { +#endif + Unused << LaunchGPUProcess(); + } + } else { + // If the GPU process is disabled we can reinitialize rendering immediately. + // This will be handled in OnProcessLaunchComplete() if the GPU process is + // enabled. + ReinitializeRendering(); + } +} + +void GPUProcessManager::ReinitializeRendering() { + // Notify content. This will ensure that each content process re-establishes + // a connection to the compositor thread (whether it's in-process or in a + // newly launched GPU process). + for (const auto& listener : mListeners) { + listener->OnCompositorUnexpectedShutdown(); + } + + // Notify any observers that the compositor has been reinitialized, + // eg the ZoomConstraintsClients for parent process documents. + nsCOMPtr<nsIObserverService> observerService = services::GetObserverService(); + if (observerService) { + observerService->NotifyObservers(nullptr, "compositor-reinitialized", + nullptr); + } +} + +void GPUProcessManager::DestroyRemoteCompositorSessions() { + // Build a list of sessions to notify, since notification might delete + // entries from the list. + nsTArray<RefPtr<RemoteCompositorSession>> sessions; + for (auto& session : mRemoteSessions) { + sessions.AppendElement(session); + } + + // Notify each widget that we have lost the GPU process. This will ensure + // that each widget destroys its layer manager and CompositorBridgeChild. + for (const auto& session : sessions) { + session->NotifySessionLost(); + } +} + +void GPUProcessManager::DestroyInProcessCompositorSessions() { + // Build a list of sessions to notify, since notification might delete + // entries from the list. + nsTArray<RefPtr<InProcessCompositorSession>> sessions; + for (auto& session : mInProcessSessions) { + sessions.AppendElement(session); + } + + // Notify each widget that we have lost the GPU process. This will ensure + // that each widget destroys its layer manager and CompositorBridgeChild. + for (const auto& session : sessions) { + session->NotifySessionLost(); + } + + // Ensure our stablility state is reset so that we don't necessarily crash + // right away on some WebRender errors. + CompositorBridgeParent::ResetStable(); + ResetProcessStable(); +} + +void GPUProcessManager::NotifyRemoteActorDestroyed( + const uint64_t& aProcessToken) { + if (!NS_IsMainThread()) { + RefPtr<Runnable> task = mTaskFactory.NewRunnableMethod( + &GPUProcessManager::NotifyRemoteActorDestroyed, aProcessToken); + NS_DispatchToMainThread(task.forget()); + return; + } + + if (mProcessToken != aProcessToken) { + // This token is for an older process; we can safely ignore it. + return; + } + + // One of the bridged top-level actors for the GPU process has been + // prematurely terminated, and we're receiving a notification. This + // can happen if the ActorDestroy for a bridged protocol fires + // before the ActorDestroy for PGPUChild. + OnProcessUnexpectedShutdown(mProcess); +} + +void GPUProcessManager::CleanShutdown() { + DestroyProcess(); + mVsyncIOThread = nullptr; +} + +void GPUProcessManager::KillProcess() { + if (!mProcess) { + return; + } + + mProcess->KillProcess(); +} + +void GPUProcessManager::CrashProcess() { + if (!mProcess) { + return; + } + + mProcess->CrashProcess(); +} + +void GPUProcessManager::DestroyProcess(bool aUnexpectedShutdown) { + if (!mProcess) { + return; + } + + mProcess->Shutdown(aUnexpectedShutdown); + mProcessToken = 0; + mProcess = nullptr; + mGPUChild = nullptr; + mQueuedPrefs.Clear(); + if (mVsyncBridge) { + mVsyncBridge->Close(); + mVsyncBridge = nullptr; + } + + CrashReporter::AnnotateCrashReport( + CrashReporter::Annotation::GPUProcessStatus, "Destroyed"_ns); +} + +already_AddRefed<CompositorSession> GPUProcessManager::CreateTopLevelCompositor( + nsBaseWidget* aWidget, WebRenderLayerManager* aLayerManager, + CSSToLayoutDeviceScale aScale, const CompositorOptions& aOptions, + bool aUseExternalSurfaceSize, const gfx::IntSize& aSurfaceSize, + uint64_t aInnerWindowId, bool* aRetryOut) { + MOZ_ASSERT(aRetryOut); + + LayersId layerTreeId = AllocateLayerTreeId(); + + RefPtr<CompositorSession> session; + + nsresult rv = EnsureGPUReady(/* aRetryAfterFallback */ false); + if (NS_WARN_IF(rv == NS_ERROR_ILLEGAL_DURING_SHUTDOWN)) { + *aRetryOut = false; + return nullptr; + } + + // If we used fallback, then retry creating the compositor sessions because + // our configuration may have changed. + if (rv == NS_ERROR_NOT_AVAILABLE) { + *aRetryOut = true; + return nullptr; + } + + if (!EnsureProtocolsReady()) { + *aRetryOut = false; + return nullptr; + } + + if (NS_SUCCEEDED(rv)) { + session = CreateRemoteSession(aWidget, aLayerManager, layerTreeId, aScale, + aOptions, aUseExternalSurfaceSize, + aSurfaceSize, aInnerWindowId); + if (NS_WARN_IF(!session)) { + if (!MaybeDisableGPUProcess("Failed to create remote compositor", + /* aAllowRestart */ true)) { + // Fallback wants the GPU process. Reset our counter. + MOZ_DIAGNOSTIC_ASSERT(gfxConfig::IsEnabled(Feature::GPU_PROCESS)); + OnBlockingProcessUnexpectedShutdown(); + } else { + MOZ_DIAGNOSTIC_ASSERT(!gfxConfig::IsEnabled(Feature::GPU_PROCESS)); + } + *aRetryOut = true; + return nullptr; + } + } else { + session = InProcessCompositorSession::Create( + aWidget, aLayerManager, layerTreeId, aScale, aOptions, + aUseExternalSurfaceSize, aSurfaceSize, AllocateNamespace(), + aInnerWindowId); + } + +#if defined(MOZ_WIDGET_ANDROID) + if (session) { + // Nothing to do if controller gets a nullptr + RefPtr<UiCompositorControllerChild> controller = + CreateUiCompositorController(aWidget, session->RootLayerTreeId()); + MOZ_ASSERT(controller); + session->SetUiCompositorControllerChild(controller); + } +#endif // defined(MOZ_WIDGET_ANDROID) + + *aRetryOut = false; + return session.forget(); +} + +RefPtr<CompositorSession> GPUProcessManager::CreateRemoteSession( + nsBaseWidget* aWidget, WebRenderLayerManager* aLayerManager, + const LayersId& aRootLayerTreeId, CSSToLayoutDeviceScale aScale, + const CompositorOptions& aOptions, bool aUseExternalSurfaceSize, + const gfx::IntSize& aSurfaceSize, uint64_t aInnerWindowId) { +#ifdef MOZ_WIDGET_SUPPORTS_OOP_COMPOSITING + widget::CompositorWidgetInitData initData; + aWidget->GetCompositorWidgetInitData(&initData); + + RefPtr<CompositorBridgeChild> child = + CompositorManagerChild::CreateWidgetCompositorBridge( + mProcessToken, aLayerManager, AllocateNamespace(), aScale, aOptions, + aUseExternalSurfaceSize, aSurfaceSize, aInnerWindowId); + if (!child) { + gfxCriticalNote << "Failed to create CompositorBridgeChild"; + return nullptr; + } + + RefPtr<CompositorVsyncDispatcher> dispatcher = + aWidget->GetCompositorVsyncDispatcher(); + RefPtr<widget::CompositorWidgetVsyncObserver> observer = + new widget::CompositorWidgetVsyncObserver(mVsyncBridge, aRootLayerTreeId); + + widget::CompositorWidgetChild* widget = + new widget::CompositorWidgetChild(dispatcher, observer, initData); + if (!child->SendPCompositorWidgetConstructor(widget, initData)) { + return nullptr; + } + if (!widget->Initialize()) { + return nullptr; + } + if (!child->SendInitialize(aRootLayerTreeId)) { + return nullptr; + } + + RefPtr<APZCTreeManagerChild> apz = nullptr; + if (aOptions.UseAPZ()) { + PAPZCTreeManagerChild* papz = + child->SendPAPZCTreeManagerConstructor(LayersId{0}); + if (!papz) { + return nullptr; + } + apz = static_cast<APZCTreeManagerChild*>(papz); + + ipc::Endpoint<PAPZInputBridgeParent> parentPipe; + ipc::Endpoint<PAPZInputBridgeChild> childPipe; + nsresult rv = PAPZInputBridge::CreateEndpoints(mGPUChild->OtherPid(), + base::GetCurrentProcId(), + &parentPipe, &childPipe); + if (NS_FAILED(rv)) { + return nullptr; + } + mGPUChild->SendInitAPZInputBridge(aRootLayerTreeId, std::move(parentPipe)); + + RefPtr<APZInputBridgeChild> inputBridge = + APZInputBridgeChild::Create(mProcessToken, std::move(childPipe)); + if (!inputBridge) { + return nullptr; + } + + apz->SetInputBridge(inputBridge); + } + + return new RemoteCompositorSession(aWidget, child, widget, apz, + aRootLayerTreeId); +#else + gfxCriticalNote << "Platform does not support out-of-process compositing"; + return nullptr; +#endif +} + +bool GPUProcessManager::CreateContentBridges( + base::ProcessId aOtherProcess, + ipc::Endpoint<PCompositorManagerChild>* aOutCompositor, + ipc::Endpoint<PImageBridgeChild>* aOutImageBridge, + ipc::Endpoint<PVRManagerChild>* aOutVRBridge, + ipc::Endpoint<PRemoteDecoderManagerChild>* aOutVideoManager, + dom::ContentParentId aChildId, nsTArray<uint32_t>* aNamespaces) { + const uint32_t cmNamespace = AllocateNamespace(); + if (!CreateContentCompositorManager(aOtherProcess, aChildId, cmNamespace, + aOutCompositor) || + !CreateContentImageBridge(aOtherProcess, aChildId, aOutImageBridge) || + !CreateContentVRManager(aOtherProcess, aChildId, aOutVRBridge)) { + return false; + } + // VideoDeocderManager is only supported in the GPU process, so we allow this + // to be fallible. + CreateContentRemoteDecoderManager(aOtherProcess, aChildId, aOutVideoManager); + // Allocates 3 namespaces(for CompositorManagerChild, CompositorBridgeChild + // and ImageBridgeChild) + aNamespaces->AppendElement(cmNamespace); + aNamespaces->AppendElement(AllocateNamespace()); + aNamespaces->AppendElement(AllocateNamespace()); + return true; +} + +bool GPUProcessManager::CreateContentCompositorManager( + base::ProcessId aOtherProcess, dom::ContentParentId aChildId, + uint32_t aNamespace, ipc::Endpoint<PCompositorManagerChild>* aOutEndpoint) { + ipc::Endpoint<PCompositorManagerParent> parentPipe; + ipc::Endpoint<PCompositorManagerChild> childPipe; + + nsresult rv = EnsureGPUReady(); + if (NS_WARN_IF(rv == NS_ERROR_ILLEGAL_DURING_SHUTDOWN)) { + return false; + } + + base::ProcessId parentPid = + NS_SUCCEEDED(rv) ? mGPUChild->OtherPid() : base::GetCurrentProcId(); + + rv = PCompositorManager::CreateEndpoints(parentPid, aOtherProcess, + &parentPipe, &childPipe); + if (NS_FAILED(rv)) { + gfxCriticalNote << "Could not create content compositor manager: " + << hexa(int(rv)); + return false; + } + + if (mGPUChild) { + mGPUChild->SendNewContentCompositorManager(std::move(parentPipe), aChildId, + aNamespace); + } else if (!CompositorManagerParent::Create(std::move(parentPipe), aChildId, + aNamespace, + /* aIsRoot */ false)) { + return false; + } + + *aOutEndpoint = std::move(childPipe); + return true; +} + +bool GPUProcessManager::CreateContentImageBridge( + base::ProcessId aOtherProcess, dom::ContentParentId aChildId, + ipc::Endpoint<PImageBridgeChild>* aOutEndpoint) { + if (!EnsureImageBridgeChild()) { + return false; + } + + nsresult rv = EnsureGPUReady(); + if (NS_WARN_IF(rv == NS_ERROR_ILLEGAL_DURING_SHUTDOWN)) { + return false; + } + + base::ProcessId parentPid = + NS_SUCCEEDED(rv) ? mGPUChild->OtherPid() : base::GetCurrentProcId(); + + ipc::Endpoint<PImageBridgeParent> parentPipe; + ipc::Endpoint<PImageBridgeChild> childPipe; + rv = PImageBridge::CreateEndpoints(parentPid, aOtherProcess, &parentPipe, + &childPipe); + if (NS_FAILED(rv)) { + gfxCriticalNote << "Could not create content compositor bridge: " + << hexa(int(rv)); + return false; + } + + if (mGPUChild) { + mGPUChild->SendNewContentImageBridge(std::move(parentPipe), aChildId); + } else { + if (!ImageBridgeParent::CreateForContent(std::move(parentPipe), aChildId)) { + return false; + } + } + + *aOutEndpoint = std::move(childPipe); + return true; +} + +base::ProcessId GPUProcessManager::GPUProcessPid() { + base::ProcessId gpuPid = + mGPUChild ? mGPUChild->OtherPid() : base::kInvalidProcessId; + return gpuPid; +} + +bool GPUProcessManager::CreateContentVRManager( + base::ProcessId aOtherProcess, dom::ContentParentId aChildId, + ipc::Endpoint<PVRManagerChild>* aOutEndpoint) { + if (NS_WARN_IF(!EnsureVRManager())) { + return false; + } + + nsresult rv = EnsureGPUReady(); + if (NS_WARN_IF(rv == NS_ERROR_ILLEGAL_DURING_SHUTDOWN)) { + return false; + } + + base::ProcessId parentPid = + NS_SUCCEEDED(rv) ? mGPUChild->OtherPid() : base::GetCurrentProcId(); + + ipc::Endpoint<PVRManagerParent> parentPipe; + ipc::Endpoint<PVRManagerChild> childPipe; + rv = PVRManager::CreateEndpoints(parentPid, aOtherProcess, &parentPipe, + &childPipe); + if (NS_FAILED(rv)) { + gfxCriticalNote << "Could not create content compositor bridge: " + << hexa(int(rv)); + return false; + } + + if (mGPUChild) { + mGPUChild->SendNewContentVRManager(std::move(parentPipe), aChildId); + } else { + if (!VRManagerParent::CreateForContent(std::move(parentPipe), aChildId)) { + return false; + } + } + + *aOutEndpoint = std::move(childPipe); + return true; +} + +void GPUProcessManager::CreateContentRemoteDecoderManager( + base::ProcessId aOtherProcess, dom::ContentParentId aChildId, + ipc::Endpoint<PRemoteDecoderManagerChild>* aOutEndpoint) { + nsresult rv = EnsureGPUReady(); + if (NS_WARN_IF(rv == NS_ERROR_ILLEGAL_DURING_SHUTDOWN)) { + return; + } + + if (NS_FAILED(rv) || !StaticPrefs::media_gpu_process_decoder() || + !mDecodeVideoOnGpuProcess) { + return; + } + + ipc::Endpoint<PRemoteDecoderManagerParent> parentPipe; + ipc::Endpoint<PRemoteDecoderManagerChild> childPipe; + + rv = PRemoteDecoderManager::CreateEndpoints( + mGPUChild->OtherPid(), aOtherProcess, &parentPipe, &childPipe); + if (NS_FAILED(rv)) { + gfxCriticalNote << "Could not create content video decoder: " + << hexa(int(rv)); + return; + } + + mGPUChild->SendNewContentRemoteDecoderManager(std::move(parentPipe), + aChildId); + + *aOutEndpoint = std::move(childPipe); +} + +void GPUProcessManager::InitVideoBridge( + ipc::Endpoint<PVideoBridgeParent>&& aVideoBridge, + layers::VideoBridgeSource aSource) { + nsresult rv = EnsureGPUReady(); + if (NS_WARN_IF(rv == NS_ERROR_ILLEGAL_DURING_SHUTDOWN)) { + return; + } + + if (NS_SUCCEEDED(rv)) { + mGPUChild->SendInitVideoBridge(std::move(aVideoBridge), aSource); + } +} + +void GPUProcessManager::MapLayerTreeId(LayersId aLayersId, + base::ProcessId aOwningId) { + nsresult rv = EnsureGPUReady(); + if (NS_WARN_IF(rv == NS_ERROR_ILLEGAL_DURING_SHUTDOWN)) { + return; + } + + if (NS_SUCCEEDED(rv)) { + mGPUChild->SendAddLayerTreeIdMapping( + LayerTreeIdMapping(aLayersId, aOwningId)); + } + + // Must do this *after* the call to EnsureGPUReady, so that if the + // process is launched as a result then it is initialized without this + // LayersId, meaning it can be successfully mapped. + LayerTreeOwnerTracker::Get()->Map(aLayersId, aOwningId); +} + +void GPUProcessManager::UnmapLayerTreeId(LayersId aLayersId, + base::ProcessId aOwningId) { + // Only call EnsureGPUReady() if we have already launched the process, to + // avoid launching a new process unnecesarily. (eg if we are backgrounded) + nsresult rv = mProcess ? EnsureGPUReady() : NS_ERROR_NOT_AVAILABLE; + if (NS_WARN_IF(rv == NS_ERROR_ILLEGAL_DURING_SHUTDOWN)) { + return; + } + + if (NS_SUCCEEDED(rv)) { + mGPUChild->SendRemoveLayerTreeIdMapping( + LayerTreeIdMapping(aLayersId, aOwningId)); + } else if (!mProcess) { + CompositorBridgeParent::DeallocateLayerTreeId(aLayersId); + } + + // Must do this *after* the call to EnsureGPUReady, so that if the + // process is launched as a result then it is initialized with this + // LayersId, meaning it can be successfully unmapped. + LayerTreeOwnerTracker::Get()->Unmap(aLayersId, aOwningId); +} + +bool GPUProcessManager::IsLayerTreeIdMapped(LayersId aLayersId, + base::ProcessId aRequestingId) { + return LayerTreeOwnerTracker::Get()->IsMapped(aLayersId, aRequestingId); +} + +LayersId GPUProcessManager::AllocateLayerTreeId() { + // Allocate tree id by using id namespace. + // By it, tree id does not conflict with external image id and + // async image pipeline id. + MOZ_ASSERT(NS_IsMainThread()); + ++mResourceId; + if (mResourceId == UINT32_MAX) { + // Move to next id namespace. + mIdNamespace = AllocateNamespace(); + mResourceId = 1; + } + + uint64_t layerTreeId = mIdNamespace; + layerTreeId = (layerTreeId << 32) | mResourceId; + return LayersId{layerTreeId}; +} + +uint32_t GPUProcessManager::AllocateNamespace() { + MOZ_ASSERT(NS_IsMainThread()); + return ++mNextNamespace; +} + +bool GPUProcessManager::AllocateAndConnectLayerTreeId( + PCompositorBridgeChild* aCompositorBridge, base::ProcessId aOtherPid, + LayersId* aOutLayersId, CompositorOptions* aOutCompositorOptions) { + LayersId layersId = AllocateLayerTreeId(); + *aOutLayersId = layersId; + + if (!mGPUChild || !aCompositorBridge) { + // If we're not remoting to another process, or there is no compositor, + // then we'll send at most one message. In this case we can just keep + // the old behavior of making sure the mapping occurs, and maybe sending + // a creation notification. + MapLayerTreeId(layersId, aOtherPid); + if (!aCompositorBridge) { + return false; + } + return aCompositorBridge->SendNotifyChildCreated(layersId, + aOutCompositorOptions); + } + + // Use the combined message path. + LayerTreeOwnerTracker::Get()->Map(layersId, aOtherPid); + return aCompositorBridge->SendMapAndNotifyChildCreated(layersId, aOtherPid, + aOutCompositorOptions); +} + +void GPUProcessManager::EnsureVsyncIOThread() { + if (mVsyncIOThread) { + return; + } + + mVsyncIOThread = new VsyncIOThreadHolder(); + MOZ_RELEASE_ASSERT(mVsyncIOThread->Start()); +} + +void GPUProcessManager::ShutdownVsyncIOThread() { mVsyncIOThread = nullptr; } + +void GPUProcessManager::RegisterRemoteProcessSession( + RemoteCompositorSession* aSession) { + mRemoteSessions.AppendElement(aSession); +} + +void GPUProcessManager::UnregisterRemoteProcessSession( + RemoteCompositorSession* aSession) { + mRemoteSessions.RemoveElement(aSession); +} + +void GPUProcessManager::RegisterInProcessSession( + InProcessCompositorSession* aSession) { + mInProcessSessions.AppendElement(aSession); +} + +void GPUProcessManager::UnregisterInProcessSession( + InProcessCompositorSession* aSession) { + mInProcessSessions.RemoveElement(aSession); +} + +void GPUProcessManager::AddListener(GPUProcessListener* aListener) { + mListeners.AppendElement(aListener); +} + +void GPUProcessManager::RemoveListener(GPUProcessListener* aListener) { + mListeners.RemoveElement(aListener); +} + +bool GPUProcessManager::NotifyGpuObservers(const char* aTopic) { + if (NS_FAILED(EnsureGPUReady())) { + return false; + } + nsCString topic(aTopic); + mGPUChild->SendNotifyGpuObservers(topic); + return true; +} + +class GPUMemoryReporter : public MemoryReportingProcess { + public: + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(GPUMemoryReporter, override) + + bool IsAlive() const override { + if (GPUProcessManager* gpm = GPUProcessManager::Get()) { + return !!gpm->GetGPUChild(); + } + return false; + } + + bool SendRequestMemoryReport( + const uint32_t& aGeneration, const bool& aAnonymize, + const bool& aMinimizeMemoryUsage, + const Maybe<ipc::FileDescriptor>& aDMDFile) override { + GPUChild* child = GetChild(); + if (!child) { + return false; + } + + return child->SendRequestMemoryReport(aGeneration, aAnonymize, + aMinimizeMemoryUsage, aDMDFile); + } + + int32_t Pid() const override { + if (GPUChild* child = GetChild()) { + return (int32_t)child->OtherPid(); + } + return 0; + } + + private: + GPUChild* GetChild() const { + if (GPUProcessManager* gpm = GPUProcessManager::Get()) { + if (GPUChild* child = gpm->GetGPUChild()) { + return child; + } + } + return nullptr; + } + + protected: + ~GPUMemoryReporter() = default; +}; + +RefPtr<MemoryReportingProcess> GPUProcessManager::GetProcessMemoryReporter() { + // Ensure mProcess is non-null before calling EnsureGPUReady, to avoid + // launching the process if it has not already been launched. + if (!mProcess || NS_FAILED(EnsureGPUReady())) { + return nullptr; + } + return new GPUMemoryReporter(); +} + +void GPUProcessManager::SetAppInForeground(bool aInForeground) { + if (mAppInForeground == aInForeground) { + return; + } + + mAppInForeground = aInForeground; +#if defined(XP_WIN) + SetProcessIsForeground(); +#endif +} + +#if defined(XP_WIN) +void GPUProcessManager::SetProcessIsForeground() { + NTSTATUS WINAPI NtSetInformationProcess( + IN HANDLE process_handle, IN ULONG info_class, + IN PVOID process_information, IN ULONG information_length); + constexpr unsigned int NtProcessInformationForeground = 25; + + static bool alreadyInitialized = false; + static decltype(NtSetInformationProcess)* setInformationProcess = nullptr; + if (!alreadyInitialized) { + alreadyInitialized = true; + nsModuleHandle module(LoadLibrary(L"ntdll.dll")); + if (module) { + setInformationProcess = + (decltype(NtSetInformationProcess)*)GetProcAddress( + module, "NtSetInformationProcess"); + } + } + if (MOZ_UNLIKELY(!setInformationProcess)) { + return; + } + + unsigned pid = GPUProcessPid(); + if (pid <= 0) { + return; + } + // Using the handle from mProcess->GetChildProcessHandle() fails; + // the PROCESS_SET_INFORMATION permission is probably missing. + nsAutoHandle processHandle( + ::OpenProcess(PROCESS_SET_INFORMATION, FALSE, pid)); + if (!processHandle) { + return; + } + + BOOLEAN foreground = mAppInForeground; + setInformationProcess(processHandle, NtProcessInformationForeground, + (PVOID)&foreground, sizeof(foreground)); +} +#endif + +RefPtr<PGPUChild::TestTriggerMetricsPromise> +GPUProcessManager::TestTriggerMetrics() { + if (!NS_WARN_IF(!mGPUChild)) { + return mGPUChild->SendTestTriggerMetrics(); + } + + return PGPUChild::TestTriggerMetricsPromise::CreateAndReject( + ipc::ResponseRejectReason::SendError, __func__); +} + +} // namespace gfx +} // namespace mozilla diff --git a/gfx/ipc/GPUProcessManager.h b/gfx/ipc/GPUProcessManager.h new file mode 100644 index 0000000000..bf5b7e6587 --- /dev/null +++ b/gfx/ipc/GPUProcessManager.h @@ -0,0 +1,366 @@ +/* -*- 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/. */ +#ifndef _include_mozilla_gfx_ipc_GPUProcessManager_h_ +#define _include_mozilla_gfx_ipc_GPUProcessManager_h_ + +#include "base/basictypes.h" +#include "base/process.h" +#include "Units.h" +#include "mozilla/UniquePtr.h" +#include "mozilla/dom/ipc/IdType.h" +#include "mozilla/gfx/GPUProcessHost.h" +#include "mozilla/gfx/PGPUChild.h" +#include "mozilla/gfx/Point.h" +#include "mozilla/ipc/ProtocolUtils.h" +#include "mozilla/ipc/TaskFactory.h" +#include "mozilla/layers/LayersTypes.h" +#include "mozilla/webrender/WebRenderTypes.h" +#include "nsIObserver.h" +#include "nsThreadUtils.h" +class nsBaseWidget; +enum class DeviceResetReason; + +namespace mozilla { +class MemoryReportingProcess; +class PRemoteDecoderManagerChild; +namespace layers { +class IAPZCTreeManager; +class CompositorOptions; +class CompositorSession; +class CompositorUpdateObserver; +class PCompositorBridgeChild; +class PCompositorManagerChild; +class PImageBridgeChild; +class PVideoBridgeParent; +class RemoteCompositorSession; +class InProcessCompositorSession; +class UiCompositorControllerChild; +class WebRenderLayerManager; +} // namespace layers +namespace widget { +class CompositorWidget; +} // namespace widget +namespace dom { +class ContentParent; +class BrowserParent; +} // namespace dom +namespace ipc { +class GeckoChildProcessHost; +} // namespace ipc +namespace gfx { + +class GPUChild; +class GPUProcessListener; +class PVRManagerChild; +class VsyncBridgeChild; +class VsyncIOThreadHolder; + +// The GPUProcessManager is a singleton responsible for creating GPU-bound +// objects that may live in another process. Currently, it provides access +// to the compositor via CompositorBridgeParent. +class GPUProcessManager final : public GPUProcessHost::Listener { + friend class layers::RemoteCompositorSession; + friend class layers::InProcessCompositorSession; + + typedef layers::CompositorOptions CompositorOptions; + typedef layers::CompositorSession CompositorSession; + typedef layers::CompositorUpdateObserver CompositorUpdateObserver; + typedef layers::IAPZCTreeManager IAPZCTreeManager; + typedef layers::WebRenderLayerManager WebRenderLayerManager; + typedef layers::LayersId LayersId; + typedef layers::PCompositorBridgeChild PCompositorBridgeChild; + typedef layers::PCompositorManagerChild PCompositorManagerChild; + typedef layers::PImageBridgeChild PImageBridgeChild; + typedef layers::PVideoBridgeParent PVideoBridgeParent; + typedef layers::RemoteCompositorSession RemoteCompositorSession; + typedef layers::InProcessCompositorSession InProcessCompositorSession; + typedef layers::UiCompositorControllerChild UiCompositorControllerChild; + + public: + static void Initialize(); + static void Shutdown(); + static GPUProcessManager* Get(); + + ~GPUProcessManager(); + + // If not using a GPU process, launch a new GPU process asynchronously. + bool LaunchGPUProcess(); + bool IsGPUProcessLaunching(); + + // Ensure that GPU-bound methods can be used. If no GPU process is being + // used, or one is launched and ready, this function returns immediately. + // Otherwise it blocks until the GPU process has finished launching. + // If the GPU process is enabled but has not yet been launched then this will + // launch the process. If that is not desired then check that return value of + // Process() is non-null before calling. + nsresult EnsureGPUReady(bool aRetryAfterFallback = true); + + already_AddRefed<CompositorSession> CreateTopLevelCompositor( + nsBaseWidget* aWidget, WebRenderLayerManager* aLayerManager, + CSSToLayoutDeviceScale aScale, const CompositorOptions& aOptions, + bool aUseExternalSurfaceSize, const gfx::IntSize& aSurfaceSize, + uint64_t aInnerWindowId, bool* aRetry); + + bool CreateContentBridges( + base::ProcessId aOtherProcess, + mozilla::ipc::Endpoint<PCompositorManagerChild>* aOutCompositor, + mozilla::ipc::Endpoint<PImageBridgeChild>* aOutImageBridge, + mozilla::ipc::Endpoint<PVRManagerChild>* aOutVRBridge, + mozilla::ipc::Endpoint<PRemoteDecoderManagerChild>* aOutVideoManager, + dom::ContentParentId aChildId, nsTArray<uint32_t>* aNamespaces); + + // Initialize GPU process with consuming end of PVideoBridge. + void InitVideoBridge( + mozilla::ipc::Endpoint<PVideoBridgeParent>&& aVideoBridge, + layers::VideoBridgeSource aSource); + + // Maps the layer tree and process together so that aOwningPID is allowed + // to access aLayersId across process. + void MapLayerTreeId(LayersId aLayersId, base::ProcessId aOwningId); + + // Release compositor-thread resources referred to by |aID|. + // + // Must run on the content main thread. + void UnmapLayerTreeId(LayersId aLayersId, base::ProcessId aOwningId); + + // Checks to see if aLayersId and aRequestingPID have been mapped by + // MapLayerTreeId + bool IsLayerTreeIdMapped(LayersId aLayersId, base::ProcessId aRequestingId); + + // Allocate an ID that can be used to refer to a layer tree and + // associated resources that live only on the compositor thread. + // + // Must run on the browser main thread. + LayersId AllocateLayerTreeId(); + + // Allocate an ID that can be used as Namespace and + // Must run on the browser main thread. + uint32_t AllocateNamespace(); + + // Allocate a layers ID and connect it to a compositor. If the compositor is + // null, the connect operation will not be performed, but an ID will still be + // allocated. This must be called from the browser main thread. + // + // Note that a layer tree id is always allocated, even if this returns false. + bool AllocateAndConnectLayerTreeId(PCompositorBridgeChild* aCompositorBridge, + base::ProcessId aOtherPid, + LayersId* aOutLayersId, + CompositorOptions* aOutCompositorOptions); + + // Destroy and recreate all of the compositors + void ResetCompositors(); + + // Record the device reset in telemetry / annotate the crash report. + static void RecordDeviceReset(DeviceResetReason aReason); + + void OnProcessLaunchComplete(GPUProcessHost* aHost) override; + void OnProcessUnexpectedShutdown(GPUProcessHost* aHost) override; + void SimulateDeviceReset(); + void DisableWebRender(wr::WebRenderError aError, const nsCString& aMsg); + void NotifyWebRenderError(wr::WebRenderError aError); + void OnInProcessDeviceReset(bool aTrackThreshold); + void OnRemoteProcessDeviceReset(GPUProcessHost* aHost) override; + void OnProcessDeclaredStable() override; + void NotifyListenersOnCompositeDeviceReset(); + + // Notify the GPUProcessManager that a top-level PGPU protocol has been + // terminated. This may be called from any thread. + void NotifyRemoteActorDestroyed(const uint64_t& aProcessToken); + + void AddListener(GPUProcessListener* aListener); + void RemoveListener(GPUProcessListener* aListener); + + // Send a message to the GPU process observer service to broadcast. Returns + // true if the message was sent, false if not. + bool NotifyGpuObservers(const char* aTopic); + + // Kills the GPU process. Used for tests and diagnostics + void KillProcess(); + + // Causes the GPU process to crash. Used for tests and diagnostics + void CrashProcess(); + + // Returns -1 if there is no GPU process, or the platform pid for it. + base::ProcessId GPUProcessPid(); + + // If a GPU process is present, create a MemoryReportingProcess object. + // Otherwise, return null. + RefPtr<MemoryReportingProcess> GetProcessMemoryReporter(); + + // Returns access to the PGPU protocol if a GPU process is present. + GPUChild* GetGPUChild() { return mGPUChild; } + + // Returns whether or not a GPU process was ever launched. + bool AttemptedGPUProcess() const { return mTotalProcessAttempts > 0; } + + // Returns the process host + GPUProcessHost* Process() { return mProcess; } + + // Sets the value of mAppInForeground, and (on Windows) adjusts the priority + // of the GPU process accordingly. + void SetAppInForeground(bool aInForeground); + + /* + * ** Test-only Method ** + * + * Trigger GPU-process test metric instrumentation. + */ + RefPtr<PGPUChild::TestTriggerMetricsPromise> TestTriggerMetrics(); + + private: + // Called from our xpcom-shutdown observer. + void OnXPCOMShutdown(); + void OnPreferenceChange(const char16_t* aData); + + bool CreateContentCompositorManager( + base::ProcessId aOtherProcess, dom::ContentParentId aChildId, + uint32_t aNamespace, + mozilla::ipc::Endpoint<PCompositorManagerChild>* aOutEndpoint); + bool CreateContentImageBridge( + base::ProcessId aOtherProcess, dom::ContentParentId aChildId, + mozilla::ipc::Endpoint<PImageBridgeChild>* aOutEndpoint); + bool CreateContentVRManager( + base::ProcessId aOtherProcess, dom::ContentParentId aChildId, + mozilla::ipc::Endpoint<PVRManagerChild>* aOutEndpoint); + void CreateContentRemoteDecoderManager( + base::ProcessId aOtherProcess, dom::ContentParentId aChildId, + mozilla::ipc::Endpoint<PRemoteDecoderManagerChild>* aOutEndPoint); + + // Called from RemoteCompositorSession. We track remote sessions so we can + // notify their owning widgets that the session must be restarted. + void RegisterRemoteProcessSession(RemoteCompositorSession* aSession); + void UnregisterRemoteProcessSession(RemoteCompositorSession* aSession); + + // Called from InProcessCompositorSession. We track in process sessino so we + // can notify their owning widgets that the session must be restarted + void RegisterInProcessSession(InProcessCompositorSession* aSession); + void UnregisterInProcessSession(InProcessCompositorSession* aSession); + + void DestroyRemoteCompositorSessions(); + void DestroyInProcessCompositorSessions(); + + void OnBlockingProcessUnexpectedShutdown(); + + // Returns true if we crossed the threshold such that we should disable + // acceleration. + bool OnDeviceReset(bool aTrackThreshold); + + // Returns true if WebRender was enabled and is now disabled. + bool DisableWebRenderConfig(wr::WebRenderError aError, const nsCString& aMsg); + + void FallbackToSoftware(const char* aMessage); + + private: + GPUProcessManager(); + + // Permanently disable the GPU process and record a message why. + void DisableGPUProcess(const char* aMessage); + + // May permanently disable the GPU process and record a message why. May + // return false if the fallback process decided we should retry the GPU + // process, but only if aAllowRestart is also true. + bool MaybeDisableGPUProcess(const char* aMessage, bool aAllowRestart); + + bool FallbackFromAcceleration(wr::WebRenderError aError, + const nsCString& aMsg); + + void ResetProcessStable(); + + // Returns true if the composting pocess is currently considered to be stable. + bool IsProcessStable(const TimeStamp& aNow); + + // Shutdown the GPU process. + void CleanShutdown(); + // Destroy the process and clean up resources. + // Setting aUnexpectedShutdown = true indicates that this is being called to + // clean up resources in response to an unexpected shutdown having been + // detected. + void DestroyProcess(bool aUnexpectedShutdown = false); + + void HandleProcessLost(); + // Reinitialize rendering following a GPU process loss. + void ReinitializeRendering(); + + void EnsureVsyncIOThread(); + void ShutdownVsyncIOThread(); + + bool EnsureProtocolsReady(); + bool EnsureCompositorManagerChild(); + bool EnsureImageBridgeChild(); + bool EnsureVRManager(); + +#if defined(XP_WIN) + void SetProcessIsForeground(); +#endif + +#if defined(MOZ_WIDGET_ANDROID) + already_AddRefed<UiCompositorControllerChild> CreateUiCompositorController( + nsBaseWidget* aWidget, const LayersId aId); +#endif // defined(MOZ_WIDGET_ANDROID) + + RefPtr<CompositorSession> CreateRemoteSession( + nsBaseWidget* aWidget, WebRenderLayerManager* aLayerManager, + const LayersId& aRootLayerTreeId, CSSToLayoutDeviceScale aScale, + const CompositorOptions& aOptions, bool aUseExternalSurfaceSize, + const gfx::IntSize& aSurfaceSize, uint64_t aInnerWindowId); + + DISALLOW_COPY_AND_ASSIGN(GPUProcessManager); + + class Observer final : public nsIObserver { + public: + NS_DECL_ISUPPORTS + NS_DECL_NSIOBSERVER + explicit Observer(GPUProcessManager* aManager); + + protected: + virtual ~Observer() = default; + + GPUProcessManager* mManager; + }; + friend class Observer; + + private: + bool mDecodeVideoOnGpuProcess = true; + + RefPtr<Observer> mObserver; + mozilla::ipc::TaskFactory<GPUProcessManager> mTaskFactory; + RefPtr<VsyncIOThreadHolder> mVsyncIOThread; + uint32_t mNextNamespace; + uint32_t mIdNamespace; + uint32_t mResourceId; + + uint32_t mUnstableProcessAttempts; + uint32_t mTotalProcessAttempts; + TimeStamp mProcessAttemptLastTime; + + nsTArray<RefPtr<RemoteCompositorSession>> mRemoteSessions; + nsTArray<RefPtr<InProcessCompositorSession>> mInProcessSessions; + nsTArray<GPUProcessListener*> mListeners; + + uint32_t mDeviceResetCount; + TimeStamp mDeviceResetLastTime; + + // Keeps track of whether not the application is in the foreground on android. + bool mAppInForeground; + + // Fields that are associated with the current GPU process. + GPUProcessHost* mProcess; + uint64_t mProcessToken; + bool mProcessStable; + Maybe<wr::WebRenderError> mLastError; + Maybe<nsCString> mLastErrorMsg; + GPUChild* mGPUChild; + RefPtr<VsyncBridgeChild> mVsyncBridge; + // Collects any pref changes that occur during process launch (after + // the initial map is passed in command-line arguments) to be sent + // when the process can receive IPC messages. + nsTArray<mozilla::dom::Pref> mQueuedPrefs; +}; + +} // namespace gfx +} // namespace mozilla + +#endif // _include_mozilla_gfx_ipc_GPUProcessManager_h_ diff --git a/gfx/ipc/GfxMessageUtils.h b/gfx/ipc/GfxMessageUtils.h new file mode 100644 index 0000000000..5375550bc7 --- /dev/null +++ b/gfx/ipc/GfxMessageUtils.h @@ -0,0 +1,1303 @@ +/* -*- 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/. */ + +#ifndef __GFXMESSAGEUTILS_H__ +#define __GFXMESSAGEUTILS_H__ + +#include "FilterSupport.h" +#include "ImageTypes.h" +#include "RegionBuilder.h" +#include "chrome/common/ipc_message_utils.h" +#include "gfxFeature.h" +#include "gfxFontUtils.h" +#include "gfxFallback.h" +#include "gfxPoint.h" +#include "gfxRect.h" +#include "gfxTelemetry.h" +#include "gfxTypes.h" +#include "ipc/EnumSerializer.h" +#include "ipc/IPCMessageUtilsSpecializations.h" +#include "mozilla/gfx/CrossProcessPaint.h" +#include "mozilla/gfx/FileHandleWrapper.h" +#include "mozilla/gfx/Matrix.h" +#include "mozilla/gfx/ScaleFactor.h" +#include "mozilla/gfx/ScaleFactors2D.h" +#include "SharedFontList.h" +#include "nsRect.h" +#include "nsRegion.h" +#include "mozilla/Array.h" +#include "mozilla/ipc/FileDescriptor.h" +#include "mozilla/ipc/IPDLParamTraits.h" +#include "mozilla/ipc/ProtocolMessageUtils.h" +#include "mozilla/ipc/ProtocolUtils.h" +#include "mozilla/ipc/ShmemMessageUtils.h" + +#include <stdint.h> + +#ifdef _MSC_VER +# pragma warning(disable : 4800) +#endif + +namespace mozilla { + +typedef gfxImageFormat PixelFormat; + +} // namespace mozilla + +namespace IPC { + +template <> +struct ParamTraits<mozilla::gfx::Matrix> { + typedef mozilla::gfx::Matrix paramType; + + static void Write(MessageWriter* aWriter, const paramType& aParam) { + WriteParam(aWriter, aParam._11); + WriteParam(aWriter, aParam._12); + WriteParam(aWriter, aParam._21); + WriteParam(aWriter, aParam._22); + WriteParam(aWriter, aParam._31); + WriteParam(aWriter, aParam._32); + } + + static bool Read(MessageReader* aReader, paramType* aResult) { + if (ReadParam(aReader, &aResult->_11) && + ReadParam(aReader, &aResult->_12) && + ReadParam(aReader, &aResult->_21) && + ReadParam(aReader, &aResult->_22) && + ReadParam(aReader, &aResult->_31) && ReadParam(aReader, &aResult->_32)) + return true; + + return false; + } +}; + +template <class SourceUnits, class TargetUnits, class T> +struct ParamTraits<mozilla::gfx::Matrix4x4Typed<SourceUnits, TargetUnits, T>> { + typedef mozilla::gfx::Matrix4x4Typed<SourceUnits, TargetUnits, T> paramType; + + static void Write(MessageWriter* writer, const paramType& param) { +#define Wr(_f) WriteParam(writer, param._f) + Wr(_11); + Wr(_12); + Wr(_13); + Wr(_14); + Wr(_21); + Wr(_22); + Wr(_23); + Wr(_24); + Wr(_31); + Wr(_32); + Wr(_33); + Wr(_34); + Wr(_41); + Wr(_42); + Wr(_43); + Wr(_44); +#undef Wr + } + + static bool Read(MessageReader* reader, paramType* result) { +#define Rd(_f) ReadParam(reader, &result->_f) + return (Rd(_11) && Rd(_12) && Rd(_13) && Rd(_14) && Rd(_21) && Rd(_22) && + Rd(_23) && Rd(_24) && Rd(_31) && Rd(_32) && Rd(_33) && Rd(_34) && + Rd(_41) && Rd(_42) && Rd(_43) && Rd(_44)); +#undef Rd + } +}; + +template <> +struct ParamTraits<mozilla::gfx::Matrix5x4> { + typedef mozilla::gfx::Matrix5x4 paramType; + + static void Write(MessageWriter* writer, const paramType& param) { +#define Wr(_f) WriteParam(writer, param._f) + Wr(_11); + Wr(_12); + Wr(_13); + Wr(_14); + Wr(_21); + Wr(_22); + Wr(_23); + Wr(_24); + Wr(_31); + Wr(_32); + Wr(_33); + Wr(_34); + Wr(_41); + Wr(_42); + Wr(_43); + Wr(_44); + Wr(_51); + Wr(_52); + Wr(_53); + Wr(_54); +#undef Wr + } + + static bool Read(MessageReader* reader, paramType* result) { +#define Rd(_f) ReadParam(reader, &result->_f) + return (Rd(_11) && Rd(_12) && Rd(_13) && Rd(_14) && Rd(_21) && Rd(_22) && + Rd(_23) && Rd(_24) && Rd(_31) && Rd(_32) && Rd(_33) && Rd(_34) && + Rd(_41) && Rd(_42) && Rd(_43) && Rd(_44) && Rd(_51) && Rd(_52) && + Rd(_53) && Rd(_54)); +#undef Rd + } +}; + +template <> +struct ParamTraits<gfxPoint> { + typedef gfxPoint paramType; + + static void Write(MessageWriter* aWriter, const paramType& aParam) { + WriteParam(aWriter, aParam.x.value); + WriteParam(aWriter, aParam.y.value); + } + + static bool Read(MessageReader* aReader, paramType* aResult) { + return (ReadParam(aReader, &aResult->x.value) && + ReadParam(aReader, &aResult->y.value)); + } +}; + +template <> +struct ParamTraits<gfxSize> { + typedef gfxSize paramType; + + static void Write(MessageWriter* aWriter, const paramType& aParam) { + WriteParam(aWriter, aParam.width); + WriteParam(aWriter, aParam.height); + } + + static bool Read(MessageReader* aReader, paramType* aResult) { + if (ReadParam(aReader, &aResult->width) && + ReadParam(aReader, &aResult->height)) + return true; + + return false; + } +}; + +template <> +struct ParamTraits<gfxRect> { + typedef gfxRect paramType; + + static void Write(MessageWriter* aWriter, const paramType& aParam) { + WriteParam(aWriter, aParam.X()); + WriteParam(aWriter, aParam.Y()); + WriteParam(aWriter, aParam.Width()); + WriteParam(aWriter, aParam.Height()); + } + + static bool Read(MessageReader* aReader, paramType* aResult) { + auto x = aResult->X(); + auto y = aResult->Y(); + auto w = aResult->Width(); + auto h = aResult->Height(); + + bool retVal = (ReadParam(aReader, &x) && ReadParam(aReader, &y) && + ReadParam(aReader, &w) && ReadParam(aReader, &h)); + aResult->SetRect(x, y, w, h); + return retVal; + } +}; + +template <> +struct ParamTraits<gfxContentType> + : public ContiguousEnumSerializer<gfxContentType, gfxContentType::COLOR, + gfxContentType::SENTINEL> {}; + +template <> +struct ParamTraits<gfxSurfaceType> + : public ContiguousEnumSerializer<gfxSurfaceType, gfxSurfaceType::Image, + gfxSurfaceType::Max> {}; + +template <> +struct ParamTraits<mozilla::gfx::SamplingFilter> + : public ContiguousEnumSerializer<mozilla::gfx::SamplingFilter, + mozilla::gfx::SamplingFilter::GOOD, + mozilla::gfx::SamplingFilter::SENTINEL> { +}; + +template <> +struct ParamTraits<mozilla::gfx::BackendType> + : public ContiguousEnumSerializer<mozilla::gfx::BackendType, + mozilla::gfx::BackendType::NONE, + mozilla::gfx::BackendType::BACKEND_LAST> { +}; + +template <> +struct ParamTraits<mozilla::gfx::Feature> + : public ContiguousEnumSerializer<mozilla::gfx::Feature, + mozilla::gfx::Feature::HW_COMPOSITING, + mozilla::gfx::Feature::NumValues> {}; + +template <> +struct ParamTraits<mozilla::gfx::Fallback> + : public ContiguousEnumSerializer< + mozilla::gfx::Fallback, + mozilla::gfx::Fallback::NO_CONSTANT_BUFFER_OFFSETTING, + mozilla::gfx::Fallback::NumValues> {}; + +template <> +struct ParamTraits<mozilla::gfx::FeatureStatus> + : public ContiguousEnumSerializer<mozilla::gfx::FeatureStatus, + mozilla::gfx::FeatureStatus::Unused, + mozilla::gfx::FeatureStatus::LAST> {}; + +template <> +struct ParamTraits<mozilla::gfx::LightType> + : public ContiguousEnumSerializer<mozilla::gfx::LightType, + mozilla::gfx::LightType::None, + mozilla::gfx::LightType::Max> {}; + +template <> +struct ParamTraits<mozilla::gfx::ColorSpace> + : public ContiguousEnumSerializer<mozilla::gfx::ColorSpace, + mozilla::gfx::ColorSpace::SRGB, + mozilla::gfx::ColorSpace::Max> {}; + +template <> +struct ParamTraits<mozilla::gfx::CompositionOp> + : public ContiguousEnumSerializerInclusive< + mozilla::gfx::CompositionOp, mozilla::gfx::CompositionOp::OP_OVER, + mozilla::gfx::CompositionOp::OP_COUNT> {}; + +/* +template <> +struct ParamTraits<mozilla::PixelFormat> + : public EnumSerializer<mozilla::PixelFormat, + SurfaceFormat::A8R8G8B8_UINT32, + SurfaceFormat::UNKNOWN> +{}; +*/ + +template <> +struct ParamTraits<mozilla::gfx::sRGBColor> { + typedef mozilla::gfx::sRGBColor paramType; + + static void Write(MessageWriter* writer, const paramType& param) { + WriteParam(writer, param.r); + WriteParam(writer, param.g); + WriteParam(writer, param.b); + WriteParam(writer, param.a); + } + + static bool Read(MessageReader* reader, paramType* result) { + return (ReadParam(reader, &result->r) && ReadParam(reader, &result->g) && + ReadParam(reader, &result->b) && ReadParam(reader, &result->a)); + } +}; + +template <> +struct ParamTraits<mozilla::gfx::DeviceColor> { + typedef mozilla::gfx::DeviceColor paramType; + + static void Write(MessageWriter* writer, const paramType& param) { + WriteParam(writer, param.r); + WriteParam(writer, param.g); + WriteParam(writer, param.b); + WriteParam(writer, param.a); + } + + static bool Read(MessageReader* reader, paramType* result) { + return (ReadParam(reader, &result->r) && ReadParam(reader, &result->g) && + ReadParam(reader, &result->b) && ReadParam(reader, &result->a)); + } +}; + +template <> +struct ParamTraits<nsPoint> { + typedef nsPoint paramType; + + static void Write(MessageWriter* writer, const paramType& param) { + WriteParam(writer, param.x); + WriteParam(writer, param.y); + } + + static bool Read(MessageReader* reader, paramType* result) { + return (ReadParam(reader, &result->x) && ReadParam(reader, &result->y)); + } +}; + +template <> +struct ParamTraits<nsIntPoint> { + typedef nsIntPoint paramType; + + static void Write(MessageWriter* writer, const paramType& param) { + WriteParam(writer, param.x); + WriteParam(writer, param.y); + } + + static bool Read(MessageReader* reader, paramType* result) { + return (ReadParam(reader, &result->x) && ReadParam(reader, &result->y)); + } +}; + +template <typename T> +struct ParamTraits<mozilla::gfx::IntSizeTyped<T>> { + typedef mozilla::gfx::IntSizeTyped<T> paramType; + + static void Write(MessageWriter* writer, const paramType& param) { + WriteParam(writer, param.width); + WriteParam(writer, param.height); + } + + static bool Read(MessageReader* reader, paramType* result) { + return (ReadParam(reader, &result->width) && + ReadParam(reader, &result->height)); + } +}; + +template <typename Region, typename Rect, typename Iter> +struct RegionParamTraits { + typedef Region paramType; + + static void Write(MessageWriter* writer, const paramType& param) { + for (auto iter = param.RectIter(); !iter.Done(); iter.Next()) { + const Rect& r = iter.Get(); + MOZ_RELEASE_ASSERT(!r.IsEmpty(), "GFX: rect is empty."); + WriteParam(writer, r); + } + // empty rects are sentinel values because nsRegions will never + // contain them + WriteParam(writer, Rect()); + } + + static bool Read(MessageReader* reader, paramType* result) { + RegionBuilder<Region> builder; + Rect rect; + while (ReadParam(reader, &rect)) { + if (rect.IsEmpty()) { + *result = builder.ToRegion(); + return true; + } + builder.OrWith(rect); + } + + return false; + } +}; + +template <class Units> +struct ParamTraits<mozilla::gfx::IntRegionTyped<Units>> + : RegionParamTraits< + mozilla::gfx::IntRegionTyped<Units>, + mozilla::gfx::IntRectTyped<Units>, + typename mozilla::gfx::IntRegionTyped<Units>::RectIterator> {}; + +template <> +struct ParamTraits<mozilla::gfx::IntSize> { + typedef mozilla::gfx::IntSize paramType; + + static void Write(MessageWriter* writer, const paramType& param) { + WriteParam(writer, param.width); + WriteParam(writer, param.height); + } + + static bool Read(MessageReader* reader, paramType* result) { + return (ReadParam(reader, &result->width) && + ReadParam(reader, &result->height)); + } +}; + +template <class T> +struct ParamTraits<mozilla::gfx::CoordTyped<T>> { + typedef mozilla::gfx::CoordTyped<T> paramType; + + static void Write(MessageWriter* writer, const paramType& param) { + WriteParam(writer, param.value); + } + + static bool Read(MessageReader* reader, paramType* result) { + return (ReadParam(reader, &result->value)); + } +}; + +template <class T> +struct ParamTraits<mozilla::gfx::IntCoordTyped<T>> { + typedef mozilla::gfx::IntCoordTyped<T> paramType; + + static void Write(MessageWriter* writer, const paramType& param) { + WriteParam(writer, param.value); + } + + static bool Read(MessageReader* reader, paramType* result) { + return (ReadParam(reader, &result->value)); + } +}; + +template <class T, class U> +struct ParamTraits<mozilla::gfx::ScaleFactor<T, U>> { + typedef mozilla::gfx::ScaleFactor<T, U> paramType; + + static void Write(MessageWriter* writer, const paramType& param) { + WriteParam(writer, param.scale); + } + + static bool Read(MessageReader* reader, paramType* result) { + return (ReadParam(reader, &result->scale)); + } +}; + +template <class T, class U> +struct ParamTraits<mozilla::gfx::ScaleFactors2D<T, U>> { + typedef mozilla::gfx::ScaleFactors2D<T, U> paramType; + + static void Write(MessageWriter* writer, const paramType& param) { + WriteParam(writer, param.xScale); + WriteParam(writer, param.yScale); + } + + static bool Read(MessageReader* reader, paramType* result) { + return (ReadParam(reader, &result->xScale) && + ReadParam(reader, &result->yScale)); + } +}; + +template <class T> +struct ParamTraits<mozilla::gfx::PointTyped<T>> { + typedef mozilla::gfx::PointTyped<T> paramType; + + static void Write(MessageWriter* writer, const paramType& param) { + WriteParam(writer, param.x); + WriteParam(writer, param.y); + } + + static bool Read(MessageReader* reader, paramType* result) { + return (ReadParam(reader, &result->x) && ReadParam(reader, &result->y)); + } +}; + +template <class F, class T> +struct ParamTraits<mozilla::gfx::Point3DTyped<F, T>> { + typedef mozilla::gfx::Point3DTyped<F, T> paramType; + + static void Write(MessageWriter* writer, const paramType& param) { + WriteParam(writer, param.x); + WriteParam(writer, param.y); + WriteParam(writer, param.z); + } + + static bool Read(MessageReader* reader, paramType* result) { + return (ReadParam(reader, &result->x) && ReadParam(reader, &result->y) && + ReadParam(reader, &result->z)); + } +}; + +template <class T> +struct ParamTraits<mozilla::gfx::IntPointTyped<T>> { + typedef mozilla::gfx::IntPointTyped<T> paramType; + + static void Write(MessageWriter* writer, const paramType& param) { + WriteParam(writer, param.x); + WriteParam(writer, param.y); + } + + static bool Read(MessageReader* reader, paramType* result) { + return (ReadParam(reader, &result->x) && ReadParam(reader, &result->y)); + } +}; + +template <class T> +struct ParamTraits<mozilla::gfx::SizeTyped<T>> { + typedef mozilla::gfx::SizeTyped<T> paramType; + + static void Write(MessageWriter* writer, const paramType& param) { + WriteParam(writer, param.width); + WriteParam(writer, param.height); + } + + static bool Read(MessageReader* reader, paramType* result) { + return (ReadParam(reader, &result->width) && + ReadParam(reader, &result->height)); + } +}; + +template <class T> +struct ParamTraits<mozilla::gfx::RectTyped<T>> { + typedef mozilla::gfx::RectTyped<T> paramType; + + static void Write(MessageWriter* writer, const paramType& param) { + WriteParam(writer, param.X()); + WriteParam(writer, param.Y()); + WriteParam(writer, param.Width()); + WriteParam(writer, param.Height()); + } + + static bool Read(MessageReader* reader, paramType* result) { + auto x = result->X(); + auto y = result->Y(); + auto w = result->Width(); + auto h = result->Height(); + + bool retVal = (ReadParam(reader, &x) && ReadParam(reader, &y) && + ReadParam(reader, &w) && ReadParam(reader, &h)); + result->SetRect(x, y, w, h); + return retVal; + } +}; + +template <class T> +struct ParamTraits<mozilla::gfx::RectAbsoluteTyped<T>> { + typedef mozilla::gfx::RectAbsoluteTyped<T> paramType; + + static void Write(MessageWriter* writer, const paramType& param) { + WriteParam(writer, param.Left()); + WriteParam(writer, param.Top()); + WriteParam(writer, param.Right()); + WriteParam(writer, param.Bottom()); + } + + static bool Read(MessageReader* reader, paramType* result) { + auto l = result->Left(); + auto t = result->Top(); + auto r = result->Right(); + auto b = result->Bottom(); + + bool retVal = (ReadParam(reader, &l) && ReadParam(reader, &t) && + ReadParam(reader, &r) && ReadParam(reader, &b)); + result->SetBox(l, t, r, b); + return retVal; + } +}; + +template <class T> +struct ParamTraits<mozilla::gfx::IntRectTyped<T>> { + typedef mozilla::gfx::IntRectTyped<T> paramType; + + static void Write(MessageWriter* writer, const paramType& param) { + WriteParam(writer, param.X()); + WriteParam(writer, param.Y()); + WriteParam(writer, param.Width()); + WriteParam(writer, param.Height()); + } + + static bool Read(MessageReader* reader, paramType* result) { + auto x = result->X(); + auto y = result->Y(); + auto w = result->Width(); + auto h = result->Height(); + + bool retVal = (ReadParam(reader, &x) && ReadParam(reader, &y) && + ReadParam(reader, &w) && ReadParam(reader, &h)); + result->SetRect(x, y, w, h); + return retVal; + } +}; + +template <> +struct ParamTraits<mozilla::gfx::Margin> { + typedef mozilla::gfx::Margin paramType; + + static void Write(MessageWriter* writer, const paramType& param) { + WriteParam(writer, param.top); + WriteParam(writer, param.right); + WriteParam(writer, param.bottom); + WriteParam(writer, param.left); + } + + static bool Read(MessageReader* reader, paramType* result) { + return ( + ReadParam(reader, &result->top) && ReadParam(reader, &result->right) && + ReadParam(reader, &result->bottom) && ReadParam(reader, &result->left)); + } +}; + +template <class T> +struct ParamTraits<mozilla::gfx::MarginTyped<T>> { + typedef mozilla::gfx::MarginTyped<T> paramType; + + static void Write(MessageWriter* writer, const paramType& param) { + WriteParam(writer, param.top); + WriteParam(writer, param.right); + WriteParam(writer, param.bottom); + WriteParam(writer, param.left); + } + + static bool Read(MessageReader* reader, paramType* result) { + return ( + ReadParam(reader, &result->top) && ReadParam(reader, &result->right) && + ReadParam(reader, &result->bottom) && ReadParam(reader, &result->left)); + } +}; + +template <class T> +struct ParamTraits<mozilla::gfx::IntMarginTyped<T>> { + typedef mozilla::gfx::IntMarginTyped<T> paramType; + + static void Write(MessageWriter* writer, const paramType& param) { + WriteParam(writer, param.top); + WriteParam(writer, param.right); + WriteParam(writer, param.bottom); + WriteParam(writer, param.left); + } + + static bool Read(MessageReader* reader, paramType* result) { + return ( + ReadParam(reader, &result->top) && ReadParam(reader, &result->right) && + ReadParam(reader, &result->bottom) && ReadParam(reader, &result->left)); + } +}; + +template <> +struct ParamTraits<nsRect> { + typedef nsRect paramType; + + static void Write(MessageWriter* writer, const paramType& param) { + WriteParam(writer, param.X()); + WriteParam(writer, param.Y()); + WriteParam(writer, param.Width()); + WriteParam(writer, param.Height()); + } + + static bool Read(MessageReader* reader, paramType* result) { + auto x = result->X(); + auto y = result->Y(); + auto w = result->Width(); + auto h = result->Height(); + bool retVal = (ReadParam(reader, &x) && ReadParam(reader, &y) && + ReadParam(reader, &w) && ReadParam(reader, &h)); + result->SetRect(x, y, w, h); + return retVal; + } +}; + +template <> +struct ParamTraits<nsRegion> + : RegionParamTraits<nsRegion, nsRect, nsRegion::RectIterator> {}; + +template <> +struct ParamTraits<GeckoProcessType> + : public ContiguousEnumSerializer< + GeckoProcessType, GeckoProcessType_Default, GeckoProcessType_End> {}; + +template <> +struct ParamTraits<mozilla::gfx::SurfaceFormat> + : public ContiguousEnumSerializer<mozilla::gfx::SurfaceFormat, + mozilla::gfx::SurfaceFormat::B8G8R8A8, + mozilla::gfx::SurfaceFormat::UNKNOWN> {}; + +template <> +struct ParamTraits<mozilla::gfx::ColorDepth> + : public ContiguousEnumSerializerInclusive< + mozilla::gfx::ColorDepth, mozilla::gfx::ColorDepth::_First, + mozilla::gfx::ColorDepth::_Last> {}; + +template <> +struct ParamTraits<mozilla::gfx::TransferFunction> + : public ContiguousEnumSerializerInclusive< + mozilla::gfx::TransferFunction, + mozilla::gfx::TransferFunction::_First, + mozilla::gfx::TransferFunction::_Last> {}; + +template <> +struct ParamTraits<mozilla::gfx::ColorRange> + : public ContiguousEnumSerializerInclusive< + mozilla::gfx::ColorRange, mozilla::gfx::ColorRange::_First, + mozilla::gfx::ColorRange::_Last> {}; + +template <> +struct ParamTraits<mozilla::gfx::YUVColorSpace> + : public ContiguousEnumSerializerInclusive< + mozilla::gfx::YUVColorSpace, mozilla::gfx::YUVColorSpace::_First, + mozilla::gfx::YUVColorSpace::_Last> {}; + +template <> +struct ParamTraits<mozilla::gfx::YUVRangedColorSpace> + : public ContiguousEnumSerializerInclusive< + mozilla::gfx::YUVRangedColorSpace, + mozilla::gfx::YUVRangedColorSpace::_First, + mozilla::gfx::YUVRangedColorSpace::_Last> {}; + +template <> +struct ParamTraits<mozilla::gfx::ColorSpace2> + : public ContiguousEnumSerializerInclusive< + mozilla::gfx::ColorSpace2, mozilla::gfx::ColorSpace2::_First, + mozilla::gfx::ColorSpace2::_Last> {}; + +template <> +struct ParamTraits<mozilla::StereoMode> + : public ContiguousEnumSerializer<mozilla::StereoMode, + mozilla::StereoMode::MONO, + mozilla::StereoMode::MAX> {}; + +template <> +struct ParamTraits<mozilla::gfx::ChromaSubsampling> + : public ContiguousEnumSerializerInclusive< + mozilla::gfx::ChromaSubsampling, + mozilla::gfx::ChromaSubsampling::_First, + mozilla::gfx::ChromaSubsampling::_Last> {}; + +template <> +struct ParamTraits<mozilla::gfx::ImplicitlyCopyableFloatArray> + : public ParamTraits<nsTArray<float>> { + typedef mozilla::gfx::ImplicitlyCopyableFloatArray paramType; +}; + +template <> +struct ParamTraits<mozilla::gfx::EmptyAttributes> { + typedef mozilla::gfx::EmptyAttributes paramType; + + static void Write(MessageWriter* aWriter, const paramType& aParam) {} + + static bool Read(MessageReader* aReader, paramType* aResult) { return true; } +}; + +template <> +struct ParamTraits<mozilla::gfx::MergeAttributes> { + typedef mozilla::gfx::MergeAttributes paramType; + + static void Write(MessageWriter* aWriter, const paramType& aParam) {} + + static bool Read(MessageReader* aReader, paramType* aResult) { return true; } +}; + +template <> +struct ParamTraits<mozilla::gfx::ToAlphaAttributes> { + typedef mozilla::gfx::ToAlphaAttributes paramType; + + static void Write(MessageWriter* aWriter, const paramType& aParam) {} + + static bool Read(MessageReader* aReader, paramType* aResult) { return true; } +}; + +template <> +struct ParamTraits<mozilla::gfx::TileAttributes> { + typedef mozilla::gfx::TileAttributes paramType; + + static void Write(MessageWriter* aWriter, const paramType& aParam) {} + + static bool Read(MessageReader* aReader, paramType* aResult) { return true; } +}; + +template <> +struct ParamTraits<mozilla::gfx::BlendAttributes> { + typedef mozilla::gfx::BlendAttributes paramType; + + static void Write(MessageWriter* aWriter, const paramType& aParam) { + WriteParam(aWriter, aParam.mBlendMode); + } + + static bool Read(MessageReader* aReader, paramType* aResult) { + return ReadParam(aReader, &aResult->mBlendMode); + } +}; + +template <> +struct ParamTraits<mozilla::gfx::MorphologyAttributes> { + typedef mozilla::gfx::MorphologyAttributes paramType; + + static void Write(MessageWriter* aWriter, const paramType& aParam) { + WriteParam(aWriter, aParam.mOperator); + WriteParam(aWriter, aParam.mRadii); + } + + static bool Read(MessageReader* aReader, paramType* aResult) { + if (!ReadParam(aReader, &aResult->mOperator) || + !ReadParam(aReader, &aResult->mRadii)) { + return false; + } + return true; + } +}; + +template <> +struct ParamTraits<mozilla::gfx::FloodAttributes> { + typedef mozilla::gfx::FloodAttributes paramType; + + static void Write(MessageWriter* aWriter, const paramType& aParam) { + WriteParam(aWriter, aParam.mColor); + } + + static bool Read(MessageReader* aReader, paramType* aResult) { + if (!ReadParam(aReader, &aResult->mColor)) { + return false; + } + return true; + } +}; + +template <> +struct ParamTraits<mozilla::gfx::OpacityAttributes> { + typedef mozilla::gfx::OpacityAttributes paramType; + + static void Write(MessageWriter* aWriter, const paramType& aParam) { + WriteParam(aWriter, aParam.mOpacity); + } + + static bool Read(MessageReader* aReader, paramType* aResult) { + if (!ReadParam(aReader, &aResult->mOpacity)) { + return false; + } + return true; + } +}; + +template <> +struct ParamTraits<mozilla::gfx::OffsetAttributes> { + typedef mozilla::gfx::OffsetAttributes paramType; + + static void Write(MessageWriter* aWriter, const paramType& aParam) { + WriteParam(aWriter, aParam.mValue); + } + + static bool Read(MessageReader* aReader, paramType* aResult) { + if (!ReadParam(aReader, &aResult->mValue)) { + return false; + } + return true; + } +}; + +template <> +struct ParamTraits<mozilla::gfx::DisplacementMapAttributes> { + typedef mozilla::gfx::DisplacementMapAttributes paramType; + + static void Write(MessageWriter* aWriter, const paramType& aParam) { + WriteParam(aWriter, aParam.mScale); + WriteParam(aWriter, aParam.mXChannel); + WriteParam(aWriter, aParam.mYChannel); + } + + static bool Read(MessageReader* aReader, paramType* aResult) { + if (!ReadParam(aReader, &aResult->mScale) || + !ReadParam(aReader, &aResult->mXChannel) || + !ReadParam(aReader, &aResult->mYChannel)) { + return false; + } + return true; + } +}; + +template <> +struct ParamTraits<mozilla::gfx::TurbulenceAttributes> { + typedef mozilla::gfx::TurbulenceAttributes paramType; + + static void Write(MessageWriter* aWriter, const paramType& aParam) { + WriteParam(aWriter, aParam.mOffset); + WriteParam(aWriter, aParam.mBaseFrequency); + WriteParam(aWriter, aParam.mSeed); + WriteParam(aWriter, aParam.mOctaves); + WriteParam(aWriter, aParam.mStitchable); + WriteParam(aWriter, aParam.mType); + } + + static bool Read(MessageReader* aReader, paramType* aResult) { + if (!ReadParam(aReader, &aResult->mOffset) || + !ReadParam(aReader, &aResult->mBaseFrequency) || + !ReadParam(aReader, &aResult->mSeed) || + !ReadParam(aReader, &aResult->mOctaves) || + !ReadParam(aReader, &aResult->mStitchable) || + !ReadParam(aReader, &aResult->mType)) { + return false; + } + return true; + } +}; + +template <> +struct ParamTraits<mozilla::gfx::ImageAttributes> { + typedef mozilla::gfx::ImageAttributes paramType; + + static void Write(MessageWriter* aWriter, const paramType& aParam) { + WriteParam(aWriter, aParam.mFilter); + WriteParam(aWriter, aParam.mInputIndex); + WriteParam(aWriter, aParam.mTransform); + } + + static bool Read(MessageReader* aReader, paramType* aResult) { + if (!ReadParam(aReader, &aResult->mFilter) || + !ReadParam(aReader, &aResult->mInputIndex) || + !ReadParam(aReader, &aResult->mTransform)) { + return false; + } + return true; + } +}; + +template <> +struct ParamTraits<mozilla::gfx::GaussianBlurAttributes> { + typedef mozilla::gfx::GaussianBlurAttributes paramType; + + static void Write(MessageWriter* aWriter, const paramType& aParam) { + WriteParam(aWriter, aParam.mStdDeviation); + } + + static bool Read(MessageReader* aReader, paramType* aResult) { + if (!ReadParam(aReader, &aResult->mStdDeviation)) { + return false; + } + return true; + } +}; + +template <> +struct ParamTraits<mozilla::gfx::DropShadowAttributes> { + typedef mozilla::gfx::DropShadowAttributes paramType; + + static void Write(MessageWriter* aWriter, const paramType& aParam) { + WriteParam(aWriter, aParam.mStdDeviation); + WriteParam(aWriter, aParam.mOffset); + WriteParam(aWriter, aParam.mColor); + } + + static bool Read(MessageReader* aReader, paramType* aResult) { + if (!ReadParam(aReader, &aResult->mStdDeviation) || + !ReadParam(aReader, &aResult->mOffset) || + !ReadParam(aReader, &aResult->mColor)) { + return false; + } + return true; + } +}; + +template <> +struct ParamTraits<mozilla::gfx::ColorMatrixAttributes> { + typedef mozilla::gfx::ColorMatrixAttributes paramType; + + static void Write(MessageWriter* aWriter, const paramType& aParam) { + WriteParam(aWriter, aParam.mType); + WriteParam(aWriter, aParam.mValues); + } + + static bool Read(MessageReader* aReader, paramType* aResult) { + if (!ReadParam(aReader, &aResult->mType) || + !ReadParam(aReader, &aResult->mValues)) { + return false; + } + return true; + } +}; + +template <> +struct ParamTraits<mozilla::gfx::ComponentTransferAttributes> { + typedef mozilla::gfx::ComponentTransferAttributes paramType; + + static void Write(MessageWriter* aWriter, const paramType& aParam) { + for (int i = 0; i < 4; ++i) { + WriteParam(aWriter, aParam.mTypes[i]); + } + for (int i = 0; i < 4; ++i) { + WriteParam(aWriter, aParam.mValues[i]); + } + } + + static bool Read(MessageReader* aReader, paramType* aResult) { + for (int i = 0; i < 4; ++i) { + if (!ReadParam(aReader, &aResult->mTypes[i])) { + return false; + } + } + for (int i = 0; i < 4; ++i) { + if (!ReadParam(aReader, &aResult->mValues[i])) { + return false; + } + } + return true; + } +}; + +template <> +struct ParamTraits<mozilla::gfx::ConvolveMatrixAttributes> { + typedef mozilla::gfx::ConvolveMatrixAttributes paramType; + + static void Write(MessageWriter* aWriter, const paramType& aParam) { + WriteParam(aWriter, aParam.mKernelSize); + WriteParam(aWriter, aParam.mKernelMatrix); + WriteParam(aWriter, aParam.mDivisor); + WriteParam(aWriter, aParam.mBias); + WriteParam(aWriter, aParam.mTarget); + WriteParam(aWriter, aParam.mEdgeMode); + WriteParam(aWriter, aParam.mKernelUnitLength); + WriteParam(aWriter, aParam.mPreserveAlpha); + } + + static bool Read(MessageReader* aReader, paramType* aResult) { + if (!ReadParam(aReader, &aResult->mKernelSize) || + !ReadParam(aReader, &aResult->mKernelMatrix) || + !ReadParam(aReader, &aResult->mDivisor) || + !ReadParam(aReader, &aResult->mBias) || + !ReadParam(aReader, &aResult->mTarget) || + !ReadParam(aReader, &aResult->mEdgeMode) || + !ReadParam(aReader, &aResult->mKernelUnitLength) || + !ReadParam(aReader, &aResult->mPreserveAlpha)) { + return false; + } + return true; + } +}; + +template <> +struct ParamTraits<mozilla::gfx::DiffuseLightingAttributes> { + typedef mozilla::gfx::DiffuseLightingAttributes paramType; + + static void Write(MessageWriter* aWriter, const paramType& aParam) { + WriteParam(aWriter, aParam.mLightType); + WriteParam(aWriter, aParam.mLightValues); + WriteParam(aWriter, aParam.mSurfaceScale); + WriteParam(aWriter, aParam.mKernelUnitLength); + WriteParam(aWriter, aParam.mColor); + WriteParam(aWriter, aParam.mLightingConstant); + WriteParam(aWriter, aParam.mSpecularExponent); + } + + static bool Read(MessageReader* aReader, paramType* aResult) { + if (!ReadParam(aReader, &aResult->mLightType) || + !ReadParam(aReader, &aResult->mLightValues) || + !ReadParam(aReader, &aResult->mSurfaceScale) || + !ReadParam(aReader, &aResult->mKernelUnitLength) || + !ReadParam(aReader, &aResult->mColor) || + !ReadParam(aReader, &aResult->mLightingConstant) || + !ReadParam(aReader, &aResult->mSpecularExponent)) { + return false; + } + return true; + } +}; + +template <> +struct ParamTraits<mozilla::gfx::SpecularLightingAttributes> { + typedef mozilla::gfx::SpecularLightingAttributes paramType; + + static void Write(MessageWriter* aWriter, const paramType& aParam) { + WriteParam(aWriter, aParam.mLightType); + WriteParam(aWriter, aParam.mLightValues); + WriteParam(aWriter, aParam.mSurfaceScale); + WriteParam(aWriter, aParam.mKernelUnitLength); + WriteParam(aWriter, aParam.mColor); + WriteParam(aWriter, aParam.mLightingConstant); + WriteParam(aWriter, aParam.mSpecularExponent); + } + + static bool Read(MessageReader* aReader, paramType* aResult) { + if (!ReadParam(aReader, &aResult->mLightType) || + !ReadParam(aReader, &aResult->mLightValues) || + !ReadParam(aReader, &aResult->mSurfaceScale) || + !ReadParam(aReader, &aResult->mKernelUnitLength) || + !ReadParam(aReader, &aResult->mColor) || + !ReadParam(aReader, &aResult->mLightingConstant) || + !ReadParam(aReader, &aResult->mSpecularExponent)) { + return false; + } + return true; + } +}; + +template <> +struct ParamTraits<mozilla::gfx::CompositeAttributes> { + typedef mozilla::gfx::CompositeAttributes paramType; + + static void Write(MessageWriter* aWriter, const paramType& aParam) { + WriteParam(aWriter, aParam.mOperator); + WriteParam(aWriter, aParam.mCoefficients); + } + + static bool Read(MessageReader* aReader, paramType* aResult) { + if (!ReadParam(aReader, &aResult->mOperator) || + !ReadParam(aReader, &aResult->mCoefficients)) { + return false; + } + return true; + } +}; + +template <> +struct ParamTraits<mozilla::gfx::Glyph> { + typedef mozilla::gfx::Glyph paramType; + static void Write(MessageWriter* aWriter, const paramType& aParam) { + WriteParam(aWriter, aParam.mIndex); + WriteParam(aWriter, aParam.mPosition); + } + + static bool Read(MessageReader* aReader, paramType* aResult) { + return (ReadParam(aReader, &aResult->mIndex) && + ReadParam(aReader, &aResult->mPosition)); + } +}; + +template <typename T, size_t Length> +struct ParamTraits<mozilla::Array<T, Length>> { + typedef mozilla::Array<T, Length> paramType; + static void Write(MessageWriter* aWriter, const paramType& aParam) { + for (size_t i = 0; i < Length; i++) { + WriteParam(aWriter, aParam[i]); + } + } + + static bool Read(MessageReader* aReader, paramType* aResult) { + for (size_t i = 0; i < Length; i++) { + if (!ReadParam<T>(aReader, &aResult->operator[](i))) { + return false; + } + } + return true; + } +}; + +template <> +struct ParamTraits<mozilla::SideBits> + : public BitFlagsEnumSerializer<mozilla::SideBits, + mozilla::SideBits::eAll> {}; + +template <> +struct ParamTraits<gfxSparseBitSet> { + typedef gfxSparseBitSet paramType; + static void Write(MessageWriter* aWriter, const paramType& aParam) { + WriteParam(aWriter, aParam.mBlockIndex); + WriteParam(aWriter, aParam.mBlocks); + } + static bool Read(MessageReader* aReader, paramType* aResult) { + return ReadParam(aReader, &aResult->mBlockIndex) && + ReadParam(aReader, &aResult->mBlocks); + } +}; + +template <> +struct ParamTraits<gfxSparseBitSet::Block> { + typedef gfxSparseBitSet::Block paramType; + static void Write(MessageWriter* aWriter, const paramType& aParam) { + aWriter->WriteBytes(&aParam, sizeof(aParam)); + } + static bool Read(MessageReader* aReader, paramType* aResult) { + return aReader->ReadBytesInto(aResult, sizeof(*aResult)); + } +}; + +// The actual FontVisibility enum is defined in gfxTypes.h +template <> +struct ParamTraits<FontVisibility> + : public ContiguousEnumSerializer<FontVisibility, FontVisibility::Unknown, + FontVisibility::Count> {}; + +template <> +struct ParamTraits<mozilla::fontlist::Pointer> { + typedef mozilla::fontlist::Pointer paramType; + static void Write(MessageWriter* aWriter, const paramType& aParam) { + uint32_t v = aParam.mBlockAndOffset; + WriteParam(aWriter, v); + } + static bool Read(MessageReader* aReader, paramType* aResult) { + uint32_t v; + if (ReadParam(aReader, &v)) { + aResult->mBlockAndOffset.store(v); + return true; + } + return false; + } +}; + +template <> +struct ParamTraits<mozilla::gfx::FenceInfo> { + typedef mozilla::gfx::FenceInfo paramType; + + static void Write(MessageWriter* aWriter, const paramType& aParam) { + WriteParam(aWriter, aParam.mFenceHandle); + WriteParam(aWriter, aParam.mFenceValue); + } + + static bool Read(MessageReader* aReader, paramType* aResult) { + if (!ReadParam(aReader, &aResult->mFenceHandle) || + !ReadParam(aReader, &aResult->mFenceValue)) { + return false; + } + return true; + } +}; + +} // namespace IPC + +namespace mozilla { +namespace ipc { + +template <> +struct IPDLParamTraits<gfx::PaintFragment> { + typedef mozilla::gfx::PaintFragment paramType; + static void Write(IPC::MessageWriter* aWriter, IProtocol* aActor, + paramType&& aParam) { + Shmem shmem; + if (aParam.mSize.IsEmpty() || + !aActor->AllocShmem(aParam.mRecording.mLen, &shmem)) { + WriteParam(aWriter, gfx::IntSize(0, 0)); + return; + } + + memcpy(shmem.get<uint8_t>(), aParam.mRecording.mData, + aParam.mRecording.mLen); + + WriteParam(aWriter, aParam.mSize); + WriteIPDLParam(aWriter, aActor, std::move(shmem)); + WriteParam(aWriter, aParam.mDependencies); + } + + static bool Read(IPC::MessageReader* aReader, IProtocol* aActor, + paramType* aResult) { + if (!ReadParam(aReader, &aResult->mSize)) { + return false; + } + if (aResult->mSize.IsEmpty()) { + return true; + } + Shmem shmem; + if (!ReadIPDLParam(aReader, aActor, &shmem) || + !ReadParam(aReader, &aResult->mDependencies)) { + aActor->DeallocShmem(shmem); + return false; + } + + if (!aResult->mRecording.Allocate(shmem.Size<uint8_t>())) { + aResult->mSize.SizeTo(0, 0); + aActor->DeallocShmem(shmem); + return true; + } + + memcpy(aResult->mRecording.mData, shmem.get<uint8_t>(), + shmem.Size<uint8_t>()); + aActor->DeallocShmem(shmem); + return true; + } +}; + +template <> +struct IPDLParamTraits<gfx::FileHandleWrapper*> { + static void Write(IPC::MessageWriter* aWriter, IProtocol* aActor, + gfx::FileHandleWrapper* aParam) { + if (!aParam) { + WriteIPDLParam(aWriter, aActor, false); + return; + } + WriteIPDLParam(aWriter, aActor, true); + + mozilla::ipc::FileDescriptor desc(aParam->GetHandle()); + WriteIPDLParam(aWriter, aActor, desc); + } + + static bool Read(IPC::MessageReader* aReader, IProtocol* aActor, + RefPtr<gfx::FileHandleWrapper>* aResult) { + *aResult = nullptr; + bool notnull = false; + if (!ReadIPDLParam(aReader, aActor, ¬null)) { + return false; + } + + if (!notnull) { + return true; + } + + mozilla::ipc::FileDescriptor desc; + if (!ReadIPDLParam(aReader, aActor, &desc)) { + return false; + } + auto wrapper = + MakeRefPtr<gfx::FileHandleWrapper>(desc.TakePlatformHandle()); + *aResult = std::move(wrapper); + return true; + } +}; + +} // namespace ipc +} // namespace mozilla + +#endif /* __GFXMESSAGEUTILS_H__ */ diff --git a/gfx/ipc/GraphicsMessages.ipdlh b/gfx/ipc/GraphicsMessages.ipdlh new file mode 100644 index 0000000000..6f201c9272 --- /dev/null +++ b/gfx/ipc/GraphicsMessages.ipdlh @@ -0,0 +1,92 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: sw=2 ts=8 et : + */ +/* 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 "mozilla/GfxMessageUtils.h"; + +using struct DxgiAdapterDesc from "mozilla/D3DMessageUtils.h"; +using struct mozilla::null_t from "mozilla/ipc/IPCCore.h"; +using mozilla::gfx::FeatureStatus from "gfxTelemetry.h"; +using mozilla::gfx::BackendType from "mozilla/gfx/Types.h"; +using mozilla::gfx::IntSize from "mozilla/gfx/Point.h"; +using gfxImageFormat from "mozilla/gfx/Types.h"; +using mozilla::gfx::D3D11Checks::VideoFormatOption from "mozilla/gfx/D3D11Checks.h"; +using mozilla::gfx::D3D11Checks::VideoFormatOptionSet from "mozilla/gfx/D3D11Checks.h"; + +namespace mozilla { +namespace gfx { + +struct D3D11DeviceStatus +{ + bool isWARP; + bool textureSharingWorks; + uint32_t featureLevel; + DxgiAdapterDesc adapter; + int32_t sequenceNumber; + VideoFormatOptionSet formatOptions; +}; + +struct DevicePrefs +{ + FeatureStatus hwCompositing; + FeatureStatus d3d11Compositing; + FeatureStatus oglCompositing; + FeatureStatus useD2D1; + FeatureStatus d3d11HwAngle; +}; + +struct ContentDeviceData +{ + DevicePrefs prefs; + D3D11DeviceStatus d3d11; + uint8_t[] cmsOutputProfileData; +}; + +// Represents the state of a feature that has failed to initialize. +struct FeatureFailure +{ + FeatureStatus status; + nsCString message; + nsCString failureId; +}; + +struct GPUDeviceData +{ + // If a feature state has changed from Enabled -> Failure, these will be non- + // null. + FeatureFailure? d3d11Compositing; + FeatureFailure? oglCompositing; + D3D11DeviceStatus? gpuDevice; +}; + +union GfxVarValue +{ + BackendType; + bool; + gfxImageFormat; + IntSize; + nsCString; + nsString; + int32_t; + float; + uint64_t[]; +}; + +struct GfxVarUpdate +{ + uint32_t index; + GfxVarValue value; +}; + +struct GfxInfoFeatureStatus +{ + int32_t feature; + int32_t status; + nsCString failureId; +}; + +} // namespace gfx +} // namespace mozilla diff --git a/gfx/ipc/InProcessCompositorSession.cpp b/gfx/ipc/InProcessCompositorSession.cpp new file mode 100644 index 0000000000..9efb83b57a --- /dev/null +++ b/gfx/ipc/InProcessCompositorSession.cpp @@ -0,0 +1,108 @@ +/* -*- 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 "InProcessCompositorSession.h" + +#include "mozilla/gfx/GPUProcessManager.h" +#include "mozilla/layers/CompositorBridgeChild.h" +#include "mozilla/layers/CompositorBridgeParent.h" +#include "mozilla/layers/CompositorManagerChild.h" +#include "mozilla/layers/CompositorManagerParent.h" +#include "mozilla/layers/IAPZCTreeManager.h" +#include "mozilla/widget/CompositorWidget.h" +#include "mozilla/widget/PlatformWidgetTypes.h" +#include "nsBaseWidget.h" + +namespace mozilla { +namespace layers { + +InProcessCompositorSession::InProcessCompositorSession( + nsBaseWidget* aWidget, widget::CompositorWidget* aCompositorWidget, + CompositorBridgeChild* aChild, CompositorBridgeParent* aParent) + : CompositorSession(aWidget, aCompositorWidget->AsDelegate(), aChild, + aParent->RootLayerTreeId()), + mCompositorBridgeParent(aParent), + mCompositorWidget(aCompositorWidget) { + gfx::GPUProcessManager::Get()->RegisterInProcessSession(this); +} + +/* static */ +RefPtr<InProcessCompositorSession> InProcessCompositorSession::Create( + nsBaseWidget* aWidget, WebRenderLayerManager* aLayerManager, + const LayersId& aRootLayerTreeId, CSSToLayoutDeviceScale aScale, + const CompositorOptions& aOptions, bool aUseExternalSurfaceSize, + const gfx::IntSize& aSurfaceSize, uint32_t aNamespace, + uint64_t aInnerWindowId) { + widget::CompositorWidgetInitData initData; + aWidget->GetCompositorWidgetInitData(&initData); + + RefPtr<CompositorWidget> widget = + CompositorWidget::CreateLocal(initData, aOptions, aWidget); + RefPtr<CompositorBridgeParent> parent = + CompositorManagerParent::CreateSameProcessWidgetCompositorBridge( + aScale, aOptions, aUseExternalSurfaceSize, aSurfaceSize, + aInnerWindowId); + MOZ_ASSERT(parent); + parent->InitSameProcess(widget, aRootLayerTreeId); + + RefPtr<CompositorBridgeChild> child = + CompositorManagerChild::CreateSameProcessWidgetCompositorBridge( + aLayerManager, aNamespace); + MOZ_ASSERT(child); + if (!child) { + gfxCriticalNote << "Failed to create CompositorBridgeChild"; + return nullptr; + } + + return new InProcessCompositorSession(aWidget, widget, child, parent); +} + +void InProcessCompositorSession::NotifySessionLost() { + // Hold a reference to mWidget since NotifyCompositorSessionLost may + // release the last reference mid-execution. + RefPtr<nsBaseWidget> widget(mWidget); + widget->NotifyCompositorSessionLost(this); +} + +CompositorBridgeParent* InProcessCompositorSession::GetInProcessBridge() const { + return mCompositorBridgeParent; +} + +void InProcessCompositorSession::SetContentController( + GeckoContentController* aController) { + mCompositorBridgeParent->SetControllerForLayerTree(mRootLayerTreeId, + aController); +} + +RefPtr<IAPZCTreeManager> InProcessCompositorSession::GetAPZCTreeManager() + const { + return mCompositorBridgeParent->GetAPZCTreeManager(mRootLayerTreeId); +} + +nsIWidget* InProcessCompositorSession::GetWidget() const { return mWidget; } + +void InProcessCompositorSession::Shutdown() { + // Destroy will synchronously wait for the parent to acknowledge shutdown, + // at which point CBP will defer a Release on the compositor thread. We + // can safely release our reference now, and let the destructor run on either + // thread. +#if defined(MOZ_WIDGET_ANDROID) + if (mUiCompositorControllerChild) { + mUiCompositorControllerChild->Destroy(); + mUiCompositorControllerChild = nullptr; + } +#endif // defined(MOZ_WIDGET_ANDROID) + if (mCompositorBridgeChild) { + mCompositorBridgeChild->Destroy(); + mCompositorBridgeChild = nullptr; + } + mCompositorBridgeParent = nullptr; + mCompositorWidget = nullptr; + gfx::GPUProcessManager::Get()->UnregisterInProcessSession(this); +} + +} // namespace layers +} // namespace mozilla diff --git a/gfx/ipc/InProcessCompositorSession.h b/gfx/ipc/InProcessCompositorSession.h new file mode 100644 index 0000000000..e9a346253a --- /dev/null +++ b/gfx/ipc/InProcessCompositorSession.h @@ -0,0 +1,54 @@ +/* -*- 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/. */ +#ifndef _include_mozilla_gfx_ipc_InProcessCompositorSession_h_ +#define _include_mozilla_gfx_ipc_InProcessCompositorSession_h_ + +#include "CompositorSession.h" +#include "mozilla/gfx/Point.h" +#include "Units.h" + +class nsIWidget; + +namespace mozilla { +namespace layers { + +class CompositorOptions; +class WebRenderLayerManager; + +// A CompositorSession where both the child and parent CompositorBridge reside +// in the same process. +class InProcessCompositorSession final : public CompositorSession { + public: + static RefPtr<InProcessCompositorSession> Create( + nsBaseWidget* baseWidget, WebRenderLayerManager* aLayerManager, + const LayersId& aRootLayerTreeId, CSSToLayoutDeviceScale aScale, + const CompositorOptions& aOptions, bool aUseExternalSurfaceSize, + const gfx::IntSize& aSurfaceSize, uint32_t aNamespace, + uint64_t aInnerWindowId); + + CompositorBridgeParent* GetInProcessBridge() const override; + void SetContentController(GeckoContentController* aController) override; + nsIWidget* GetWidget() const; + RefPtr<IAPZCTreeManager> GetAPZCTreeManager() const override; + void Shutdown() override; + + void NotifySessionLost(); + + private: + InProcessCompositorSession(nsBaseWidget* aWidget, + widget::CompositorWidget* aCompositorWidget, + CompositorBridgeChild* aChild, + CompositorBridgeParent* aParent); + + private: + RefPtr<CompositorBridgeParent> mCompositorBridgeParent; + RefPtr<CompositorWidget> mCompositorWidget; +}; + +} // namespace layers +} // namespace mozilla + +#endif // _include_mozilla_gfx_ipc_InProcessCompositorSession_h_ diff --git a/gfx/ipc/OverlayInfo.h b/gfx/ipc/OverlayInfo.h new file mode 100644 index 0000000000..3137e2ef98 --- /dev/null +++ b/gfx/ipc/OverlayInfo.h @@ -0,0 +1,53 @@ +/* -*- 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/. */ +#ifndef _include_mozilla_gfx_ipc_OverlayInfo_h_ +#define _include_mozilla_gfx_ipc_OverlayInfo_h_ + +namespace IPC { +template <typename> +struct ParamTraits; +} // namespace IPC + +namespace mozilla { +namespace layers { + +enum class OverlaySupportType : uint8_t { + None, + Software, + Direct, + Scaling, + MAX // sentinel for the count of all effect types +}; + +struct OverlayInfo { + // This constructor needed for IPDL purposes, don't use it anywhere else. + OverlayInfo() = default; + + bool mSupportsOverlays = false; + OverlaySupportType mNv12Overlay = OverlaySupportType::None; + OverlaySupportType mYuy2Overlay = OverlaySupportType::None; + OverlaySupportType mBgra8Overlay = OverlaySupportType::None; + OverlaySupportType mRgb10a2Overlay = OverlaySupportType::None; + + friend struct IPC::ParamTraits<OverlayInfo>; +}; + +struct SwapChainInfo { + // This constructor needed for IPDL purposes, don't use it anywhere else. + SwapChainInfo() = default; + + explicit SwapChainInfo(bool aTearingSupported) + : mTearingSupported(aTearingSupported) {} + + bool mTearingSupported = false; + + friend struct IPC::ParamTraits<SwapChainInfo>; +}; + +} // namespace layers +} // namespace mozilla + +#endif // _include_mozilla_gfx_ipc_OverlayInfo_h_ diff --git a/gfx/ipc/PCanvasManager.ipdl b/gfx/ipc/PCanvasManager.ipdl new file mode 100644 index 0000000000..1ba6a88a42 --- /dev/null +++ b/gfx/ipc/PCanvasManager.ipdl @@ -0,0 +1,52 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: sw=2 ts=8 et : + */ +/* 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 "mozilla/layers/LayersMessageUtils.h"; +include protocol PCanvas; +include protocol PWebGL; +include protocol PWebGPU; + +using mozilla::layers::RemoteTextureOwnerId from "mozilla/layers/LayersTypes.h"; +using mozilla::webgl::FrontBufferSnapshotIpc from "mozilla/dom/WebGLIpdl.h"; + +namespace mozilla { +namespace gfx { + +/** + * The PCanvasManager protocol is the top-level protocol between the main and + * worker threads in the content process, and the renderer thread in the + * compositor process. This protocol should be used to create accelerated + * canvas instances. + */ +[NeedsOtherPid, ParentProc=compositor, ChildProc=anydom] +sync protocol PCanvasManager +{ + manages PCanvas; + manages PWebGL; + manages PWebGPU; + +parent: + async PCanvas(); + + // Actor that represents one WebGL context. + async PWebGL(); + + // Actor that represents one WebGPU context. + async PWebGPU(); + + // Set the local manager ID for the canvas manager. + async Initialize(uint32_t aManagerId); + + // Get the front buffer pixels for the given manager/protocol. This is + // intended to be used by the main thread in the content process to block + // reading without having to block on the worker thread that owns the context + // instance. + sync GetSnapshot(uint32_t aManagerId, int32_t aProtocolId, RemoteTextureOwnerId? ownerId) returns (FrontBufferSnapshotIpc ret); +}; + +} // gfx +} // mozilla diff --git a/gfx/ipc/PGPU.ipdl b/gfx/ipc/PGPU.ipdl new file mode 100644 index 0000000000..b6e84bf142 --- /dev/null +++ b/gfx/ipc/PGPU.ipdl @@ -0,0 +1,191 @@ +/* -*- Mode: C++; tab-width: 8; 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 GraphicsMessages; +include MemoryReportTypes; +include HangTypes; +include PrefsTypes; +include protocol PAPZInputBridge; +include protocol PCompositorManager; +include protocol PImageBridge; +include protocol PProfiler; +include protocol PVRGPU; +include protocol PVRManager; +include protocol PVideoBridge; +include protocol PVsyncBridge; +include protocol PUiCompositorController; +include protocol PRemoteDecoderManager; + +#if defined(MOZ_SANDBOX) && defined(MOZ_DEBUG) && defined(ENABLE_TESTS) +include protocol PSandboxTesting; +#endif + +include "mozilla/ipc/ByteBufUtils.h"; +include "mozilla/layers/LayersMessageUtils.h"; + +using base::ProcessId from "base/process.h"; +using mozilla::dom::ContentParentId from "mozilla/dom/ipc/IdType.h"; +using mozilla::dom::NativeThreadId from "mozilla/dom/NativeThreadId.h"; +using mozilla::Telemetry::HistogramAccumulation from "mozilla/TelemetryComms.h"; +using mozilla::Telemetry::KeyedHistogramAccumulation from "mozilla/TelemetryComms.h"; +using mozilla::Telemetry::ScalarAction from "mozilla/TelemetryComms.h"; +using mozilla::Telemetry::KeyedScalarAction from "mozilla/TelemetryComms.h"; +using mozilla::Telemetry::ChildEventData from "mozilla/TelemetryComms.h"; +using mozilla::Telemetry::DiscardedData from "mozilla/TelemetryComms.h"; +using mozilla::gfx::Feature from "gfxFeature.h"; +using mozilla::gfx::Fallback from "gfxFallback.h"; +using mozilla::layers::LayersId from "mozilla/layers/LayersTypes.h"; +using mozilla::layers::OverlayInfo from "mozilla/layers/OverlayInfo.h"; +using mozilla::layers::SwapChainInfo from "mozilla/layers/OverlayInfo.h"; +using mozilla::media::MediaCodecsSupported from "MediaCodecsSupport.h"; +using mozilla::layers::VideoBridgeSource from "mozilla/layers/VideoBridgeUtils.h"; + +namespace mozilla { +namespace gfx { + +struct LayerTreeIdMapping { + LayersId layersId; + ProcessId ownerId; +}; + +// This protocol allows the UI process to talk to the GPU process. There is one +// instance of this protocol, with the GPUParent living on the main thread of +// the GPU process and the GPUChild living on the main thread of the UI process. +[NeedsOtherPid, ParentProc=GPU, ChildProc=Parent] +sync protocol PGPU +{ +parent: + // Sent by the UI process to initiate core settings. + async Init(GfxVarUpdate[] vars, + DevicePrefs devicePrefs, + LayerTreeIdMapping[] mapping, + GfxInfoFeatureStatus[] features, + uint32_t wrNamespace); + + async InitCompositorManager(Endpoint<PCompositorManagerParent> endpoint, uint32_t aNamespace); + async InitVsyncBridge(Endpoint<PVsyncBridgeParent> endpoint); + async InitImageBridge(Endpoint<PImageBridgeParent> endpoint); + async InitVideoBridge(Endpoint<PVideoBridgeParent> endpoint, VideoBridgeSource aSource); + async InitVRManager(Endpoint<PVRManagerParent> endpoint); + async InitUiCompositorController(LayersId rootLayerTreeId, Endpoint<PUiCompositorControllerParent> endpoint); + async InitAPZInputBridge(LayersId layersId, + Endpoint<PAPZInputBridgeParent> endpoint); + async InitProfiler(Endpoint<PProfilerChild> endpoint); + // Forward GPU process its endpoints to the VR process. + async InitVR(Endpoint<PVRGPUChild> endpoint); + // Called to update a gfx variable. + async UpdateVar(GfxVarUpdate var); + + async PreferenceUpdate(Pref pref); + + // Create a new content-process compositor bridge. + async NewContentCompositorManager(Endpoint<PCompositorManagerParent> endpoint, ContentParentId childId, uint32_t aNamespace); + async NewContentImageBridge(Endpoint<PImageBridgeParent> endpoint, ContentParentId childId); + async NewContentVRManager(Endpoint<PVRManagerParent> endpoint, ContentParentId childId); + async NewContentRemoteDecoderManager(Endpoint<PRemoteDecoderManagerParent> endpoint, ContentParentId childId); + + // Called to notify the GPU process of who owns a layersId. + sync AddLayerTreeIdMapping(LayerTreeIdMapping mapping); + async RemoveLayerTreeIdMapping(LayerTreeIdMapping mapping); + + // Request the current DeviceStatus from the GPU process. This blocks until + // one is available (i.e., Init has completed). + sync GetDeviceStatus() returns (GPUDeviceData status); + + // Request to simulate device reset and to get the updated DeviceStatus from + // the GPU process. This blocks until one is available (i.e., Init has completed). + async SimulateDeviceReset(); + + // Have a message be broadcasted to the GPU process by the GPU process + // observer service. + async NotifyGpuObservers(nsCString aTopic); + + async RequestMemoryReport(uint32_t generation, + bool anonymize, + bool minimizeMemoryUsage, + FileDescriptor? DMDFile) + returns (uint32_t aGeneration); + + async ShutdownVR(); + + // Functions supporting PerfStats data collection. + async UpdatePerfStatsCollectionMask(uint64_t aMask); + async CollectPerfStatsJSON() returns (nsCString aStats); + +#if defined(MOZ_SANDBOX) && defined(MOZ_DEBUG) && defined(ENABLE_TESTS) + async InitSandboxTesting(Endpoint<PSandboxTestingChild> aEndpoint); +#endif + + // Tells the gpu process to flush any pending telemetry. + // Used in tests and ping assembly. Buffer contains bincoded Rust structs. + // https://firefox-source-docs.mozilla.org/toolkit/components/glean/dev/ipc.html + async FlushFOGData() returns (ByteBuf buf); + + // Test-only method. + // Asks the gpu process to trigger test-only instrumentation. + // The unused returned value is to have a promise we can await. + async TestTriggerMetrics() returns (bool unused); + + // Causes the GPU process to crash. Used for tests and diagnostics. + async CrashProcess(); + +child: + // Sent when the GPU process has initialized devices. This occurs once, after + // Init(). + async InitComplete(GPUDeviceData data); + + // Sent when APZ detects checkerboarding and apz checkerboard reporting is enabled. + async ReportCheckerboard(uint32_t severity, nsCString log); + + // Graphics errors, analogous to PContent::GraphicsError + async GraphicsError(nsCString aError); + + async InitCrashReporter(NativeThreadId threadId); + + async CreateVRProcess(); + async ShutdownVRProcess(); + + // Have a message be broadcasted to the UI process by the UI process + // observer service. + async NotifyUiObservers(nsCString aTopic); + + // Messages for reporting telemetry to the UI process. + async AccumulateChildHistograms(HistogramAccumulation[] accumulations); + async AccumulateChildKeyedHistograms(KeyedHistogramAccumulation[] accumulations); + async UpdateChildScalars(ScalarAction[] actions); + async UpdateChildKeyedScalars(KeyedScalarAction[] actions); + async RecordChildEvents(ChildEventData[] events); + async RecordDiscardedData(DiscardedData data); + + async DeclareStable(); + async NotifyDeviceReset(GPUDeviceData status); + async NotifyOverlayInfo(OverlayInfo info); + async NotifySwapChainInfo(SwapChainInfo info); + async NotifyDisableRemoteCanvas(); + async FlushMemory(nsString reason); + + async AddMemoryReport(MemoryReport aReport); + + // Update the UI process after a feature's status has changed. This is used + // outside of the normal startup flow. + async UpdateFeature(Feature aFeature, FeatureFailure aChange); + + // Notify about:support/Telemetry that a fallback occurred. + async UsedFallback(Fallback aFallback, nsCString message); + + async BHRThreadHang(HangDetails aDetails); + + // Update the cached list of codec supported following a check in the + // GPU parent. + async UpdateMediaCodecsSupported(MediaCodecsSupported aSupported); + + // Sent from time-to-time to limit the amount of telemetry vulnerable to loss + // Buffer contains bincoded Rust structs. + // https://firefox-source-docs.mozilla.org/toolkit/components/glean/dev/ipc.html + async FOGData(ByteBuf buf); +}; + +} // namespace gfx +} // namespace mozilla diff --git a/gfx/ipc/PVsyncBridge.ipdl b/gfx/ipc/PVsyncBridge.ipdl new file mode 100644 index 0000000000..84fd7d8a17 --- /dev/null +++ b/gfx/ipc/PVsyncBridge.ipdl @@ -0,0 +1,27 @@ +/* -*- Mode: C++; tab-width: 8; 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 "mozilla/layers/LayersMessageUtils.h"; + +using class mozilla::TimeStamp from "mozilla/TimeStamp.h"; +using mozilla::layers::LayersId from "mozilla/layers/LayersTypes.h"; +using mozilla::VsyncEvent from "mozilla/VsyncDispatcher.h"; + +namespace mozilla { +namespace gfx { + +// This protocol only serves one purpose: deliver vsync notifications from a +// dedicated thread in the UI process to the compositor thread in the +// compositor process. The child side exists in the UI process, and the +// parent side in the GPU process. +[NeedsOtherPid, ParentProc=GPU, ChildProc=Parent] +sync protocol PVsyncBridge +{ +parent: + async NotifyVsync(VsyncEvent vsync, LayersId layersId); +}; + +} // namespace gfx +} // namespace mozilla diff --git a/gfx/ipc/RemoteCompositorSession.cpp b/gfx/ipc/RemoteCompositorSession.cpp new file mode 100644 index 0000000000..34b7cd4856 --- /dev/null +++ b/gfx/ipc/RemoteCompositorSession.cpp @@ -0,0 +1,101 @@ +/* -*- 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 "RemoteCompositorSession.h" +#include "gfxPlatform.h" +#include "mozilla/VsyncDispatcher.h" +#include "mozilla/gfx/GPUProcessManager.h" +#include "mozilla/layers/APZChild.h" +#include "mozilla/layers/APZCTreeManagerChild.h" +#include "mozilla/layers/CompositorBridgeChild.h" +#include "mozilla/layers/GeckoContentController.h" +#include "mozilla/Unused.h" +#include "nsBaseWidget.h" +#if defined(MOZ_WIDGET_ANDROID) +# include "mozilla/layers/UiCompositorControllerChild.h" +#endif // defined(MOZ_WIDGET_ANDROID) + +namespace mozilla { +namespace layers { + +using namespace gfx; +using namespace widget; + +RemoteCompositorSession::RemoteCompositorSession( + nsBaseWidget* aWidget, CompositorBridgeChild* aChild, + CompositorWidgetDelegate* aWidgetDelegate, APZCTreeManagerChild* aAPZ, + const LayersId& aRootLayerTreeId) + : CompositorSession(aWidget, aWidgetDelegate, aChild, aRootLayerTreeId), + mAPZ(aAPZ) { + MOZ_ASSERT(!gfxPlatform::IsHeadless()); + GPUProcessManager::Get()->RegisterRemoteProcessSession(this); + if (mAPZ) { + mAPZ->SetCompositorSession(this); + } +} + +RemoteCompositorSession::~RemoteCompositorSession() { + // This should have been shutdown first. + MOZ_ASSERT(!mCompositorBridgeChild); +#if defined(MOZ_WIDGET_ANDROID) + MOZ_ASSERT(!mUiCompositorControllerChild); +#endif // defined(MOZ_WIDGET_ANDROID) +} + +void RemoteCompositorSession::NotifySessionLost() { + // Hold a reference to mWidget since NotifyCompositorSessionLost may + // release the last reference mid-execution. + RefPtr<nsBaseWidget> widget(mWidget); + // Re-entrancy should be impossible: when we are being notified of a lost + // session, we have by definition not shut down yet. We will shutdown, but + // then will be removed from the notification list. + widget->NotifyCompositorSessionLost(this); +} + +CompositorBridgeParent* RemoteCompositorSession::GetInProcessBridge() const { + return nullptr; +} + +void RemoteCompositorSession::SetContentController( + GeckoContentController* aController) { + mContentController = aController; + mCompositorBridgeChild->SendPAPZConstructor(new APZChild(aController), + LayersId{0}); +} + +GeckoContentController* RemoteCompositorSession::GetContentController() { + return mContentController.get(); +} + +nsIWidget* RemoteCompositorSession::GetWidget() const { return mWidget; } + +RefPtr<IAPZCTreeManager> RemoteCompositorSession::GetAPZCTreeManager() const { + return mAPZ; +} + +void RemoteCompositorSession::Shutdown() { + mContentController = nullptr; + if (mAPZ) { + mAPZ->SetCompositorSession(nullptr); + mAPZ->Destroy(); + } + if (mCompositorBridgeChild) { + mCompositorBridgeChild->Destroy(); + mCompositorBridgeChild = nullptr; + } + mCompositorWidgetDelegate = nullptr; + mWidget = nullptr; +#if defined(MOZ_WIDGET_ANDROID) + if (mUiCompositorControllerChild) { + mUiCompositorControllerChild->Destroy(); + mUiCompositorControllerChild = nullptr; + } +#endif // defined(MOZ_WIDGET_ANDROID) + GPUProcessManager::Get()->UnregisterRemoteProcessSession(this); +} + +} // namespace layers +} // namespace mozilla diff --git a/gfx/ipc/RemoteCompositorSession.h b/gfx/ipc/RemoteCompositorSession.h new file mode 100644 index 0000000000..d55d56e07a --- /dev/null +++ b/gfx/ipc/RemoteCompositorSession.h @@ -0,0 +1,45 @@ +/* -*- 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/. */ +#ifndef include_mozilla_gfx_ipc_RemoteCompositorSession_h +#define include_mozilla_gfx_ipc_RemoteCompositorSession_h + +#include "CompositorSession.h" +#include "mozilla/gfx/Point.h" +#include "Units.h" + +class nsIWidget; + +namespace mozilla { +namespace layers { + +class APZCTreeManagerChild; + +class RemoteCompositorSession final : public CompositorSession { + public: + RemoteCompositorSession(nsBaseWidget* aWidget, CompositorBridgeChild* aChild, + CompositorWidgetDelegate* aWidgetDelegate, + APZCTreeManagerChild* aAPZ, + const LayersId& aRootLayerTreeId); + virtual ~RemoteCompositorSession(); + + CompositorBridgeParent* GetInProcessBridge() const override; + void SetContentController(GeckoContentController* aController) override; + GeckoContentController* GetContentController(); + nsIWidget* GetWidget() const; + RefPtr<IAPZCTreeManager> GetAPZCTreeManager() const override; + void Shutdown() override; + + void NotifySessionLost(); + + private: + RefPtr<APZCTreeManagerChild> mAPZ; + RefPtr<GeckoContentController> mContentController; +}; + +} // namespace layers +} // namespace mozilla + +#endif // include_mozilla_gfx_ipc_RemoteCompositorSession_h diff --git a/gfx/ipc/VsyncBridgeChild.cpp b/gfx/ipc/VsyncBridgeChild.cpp new file mode 100644 index 0000000000..044a24f320 --- /dev/null +++ b/gfx/ipc/VsyncBridgeChild.cpp @@ -0,0 +1,127 @@ +/* -*- 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 "VsyncBridgeChild.h" +#include "VsyncIOThreadHolder.h" +#include "mozilla/dom/ContentChild.h" +#include "mozilla/gfx/GPUProcessManager.h" +#include "mozilla/ipc/Endpoint.h" + +namespace mozilla { +namespace gfx { + +VsyncBridgeChild::VsyncBridgeChild(RefPtr<VsyncIOThreadHolder> aThread, + const uint64_t& aProcessToken) + : mThread(aThread), mProcessToken(aProcessToken) {} + +VsyncBridgeChild::~VsyncBridgeChild() = default; + +/* static */ +RefPtr<VsyncBridgeChild> VsyncBridgeChild::Create( + RefPtr<VsyncIOThreadHolder> aThread, const uint64_t& aProcessToken, + Endpoint<PVsyncBridgeChild>&& aEndpoint) { + RefPtr<VsyncBridgeChild> child = new VsyncBridgeChild(aThread, aProcessToken); + + RefPtr<nsIRunnable> task = NewRunnableMethod<Endpoint<PVsyncBridgeChild>&&>( + "gfx::VsyncBridgeChild::Open", child, &VsyncBridgeChild::Open, + std::move(aEndpoint)); + aThread->GetThread()->Dispatch(task.forget(), nsIThread::DISPATCH_NORMAL); + + return child; +} + +void VsyncBridgeChild::Open(Endpoint<PVsyncBridgeChild>&& aEndpoint) { + if (!aEndpoint.Bind(this)) { + // The GPU Process Manager might be gone if we receive ActorDestroy very + // late in shutdown. + if (GPUProcessManager* gpm = GPUProcessManager::Get()) + gpm->NotifyRemoteActorDestroyed(mProcessToken); + return; + } +} + +class NotifyVsyncTask : public Runnable { + public: + NotifyVsyncTask(RefPtr<VsyncBridgeChild> aVsyncBridge, + const VsyncEvent& aVsync, const layers::LayersId& aLayersId) + : Runnable("gfx::NotifyVsyncTask"), + mVsyncBridge(aVsyncBridge), + mVsync(aVsync), + mLayersId(aLayersId) {} + + NS_IMETHOD Run() override { + mVsyncBridge->NotifyVsyncImpl(mVsync, mLayersId); + return NS_OK; + } + + private: + RefPtr<VsyncBridgeChild> mVsyncBridge; + VsyncEvent mVsync; + layers::LayersId mLayersId; +}; + +bool VsyncBridgeChild::IsOnVsyncIOThread() const { + return mThread->IsOnCurrentThread(); +} + +void VsyncBridgeChild::NotifyVsync(const VsyncEvent& aVsync, + const layers::LayersId& aLayersId) { + // This should be on the Vsync thread (not the Vsync I/O thread). + MOZ_ASSERT(!IsOnVsyncIOThread()); + + RefPtr<NotifyVsyncTask> task = new NotifyVsyncTask(this, aVsync, aLayersId); + mThread->Dispatch(task.forget()); +} + +void VsyncBridgeChild::NotifyVsyncImpl(const VsyncEvent& aVsync, + const layers::LayersId& aLayersId) { + // This should be on the Vsync I/O thread. + MOZ_ASSERT(IsOnVsyncIOThread()); + + if (!mProcessToken) { + return; + } + SendNotifyVsync(aVsync, aLayersId); +} + +void VsyncBridgeChild::Close() { + if (!IsOnVsyncIOThread()) { + mThread->Dispatch(NewRunnableMethod("gfx::VsyncBridgeChild::Close", this, + &VsyncBridgeChild::Close)); + return; + } + + // We clear mProcessToken when the channel is closed. + if (!mProcessToken) { + return; + } + + // Clear the process token so we don't notify the GPUProcessManager. It + // already knows we're closed since it manually called Close, and in fact the + // GPM could have already been destroyed during shutdown. + mProcessToken = 0; + + // Close the underlying IPC channel. + PVsyncBridgeChild::Close(); +} + +void VsyncBridgeChild::ActorDestroy(ActorDestroyReason aWhy) { + if (mProcessToken) { + GPUProcessManager::Get()->NotifyRemoteActorDestroyed(mProcessToken); + mProcessToken = 0; + } +} + +void VsyncBridgeChild::ProcessingError(Result aCode, const char* aReason) { + MOZ_RELEASE_ASSERT(aCode == MsgDropped, + "Processing error in VsyncBridgeChild"); +} + +void VsyncBridgeChild::HandleFatalError(const char* aMsg) { + dom::ContentChild::FatalErrorIfNotUsingGPUProcess(aMsg, OtherPid()); +} + +} // namespace gfx +} // namespace mozilla diff --git a/gfx/ipc/VsyncBridgeChild.h b/gfx/ipc/VsyncBridgeChild.h new file mode 100644 index 0000000000..a0727ce5fe --- /dev/null +++ b/gfx/ipc/VsyncBridgeChild.h @@ -0,0 +1,55 @@ +/* -*- 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/. */ +#ifndef include_gfx_ipc_VsyncBridgeChild_h +#define include_gfx_ipc_VsyncBridgeChild_h + +#include "mozilla/RefPtr.h" +#include "mozilla/gfx/PVsyncBridgeChild.h" + +namespace mozilla { +namespace gfx { + +class VsyncIOThreadHolder; + +class VsyncBridgeChild final : public PVsyncBridgeChild { + friend class NotifyVsyncTask; + + public: + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(VsyncBridgeChild, final) + + static RefPtr<VsyncBridgeChild> Create( + RefPtr<VsyncIOThreadHolder> aThread, const uint64_t& aProcessToken, + Endpoint<PVsyncBridgeChild>&& aEndpoint); + + void Close(); + + void ActorDestroy(ActorDestroyReason aWhy) override; + void ProcessingError(Result aCode, const char* aReason) override; + + void NotifyVsync(const VsyncEvent& aVsync, const layers::LayersId& aLayersId); + + void HandleFatalError(const char* aMsg) override; + + private: + VsyncBridgeChild(RefPtr<VsyncIOThreadHolder>, const uint64_t& aProcessToken); + virtual ~VsyncBridgeChild(); + + void Open(Endpoint<PVsyncBridgeChild>&& aEndpoint); + + void NotifyVsyncImpl(const VsyncEvent& aVsync, + const layers::LayersId& aLayersId); + + bool IsOnVsyncIOThread() const; + + private: + RefPtr<VsyncIOThreadHolder> mThread; + uint64_t mProcessToken; +}; + +} // namespace gfx +} // namespace mozilla + +#endif // include_gfx_ipc_VsyncBridgeChild_h diff --git a/gfx/ipc/VsyncBridgeParent.cpp b/gfx/ipc/VsyncBridgeParent.cpp new file mode 100644 index 0000000000..c98b641ba2 --- /dev/null +++ b/gfx/ipc/VsyncBridgeParent.cpp @@ -0,0 +1,74 @@ +/* -*- 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 "VsyncBridgeParent.h" +#include "mozilla/ipc/Endpoint.h" +#include "mozilla/layers/CompositorBridgeParent.h" +#include "mozilla/layers/CompositorThread.h" + +using mozilla::layers::CompositorBridgeParent; +using mozilla::layers::CompositorThreadHolder; + +namespace mozilla { +namespace gfx { + +RefPtr<VsyncBridgeParent> VsyncBridgeParent::Start( + Endpoint<PVsyncBridgeParent>&& aEndpoint) { + RefPtr<VsyncBridgeParent> parent = new VsyncBridgeParent(); + + RefPtr<Runnable> task = NewRunnableMethod<Endpoint<PVsyncBridgeParent>&&>( + "gfx::VsyncBridgeParent::Open", parent, &VsyncBridgeParent::Open, + std::move(aEndpoint)); + layers::CompositorThread()->Dispatch(task.forget()); + + return parent; +} + +VsyncBridgeParent::VsyncBridgeParent() : mOpen(false) { + MOZ_COUNT_CTOR(VsyncBridgeParent); + mCompositorThreadRef = CompositorThreadHolder::GetSingleton(); +} + +VsyncBridgeParent::~VsyncBridgeParent() { MOZ_COUNT_DTOR(VsyncBridgeParent); } + +void VsyncBridgeParent::Open(Endpoint<PVsyncBridgeParent>&& aEndpoint) { + if (!aEndpoint.Bind(this)) { + // We can't recover from this. + MOZ_CRASH("Failed to bind VsyncBridgeParent to endpoint"); + } + mOpen = true; +} + +mozilla::ipc::IPCResult VsyncBridgeParent::RecvNotifyVsync( + const VsyncEvent& aVsync, const LayersId& aLayersId) { + CompositorBridgeParent::NotifyVsync(aVsync, aLayersId); + return IPC_OK(); +} + +void VsyncBridgeParent::Shutdown() { + if (!CompositorThreadHolder::IsInCompositorThread()) { + layers::CompositorThread()->Dispatch( + NewRunnableMethod("gfx::VsyncBridgeParent::ShutdownImpl", this, + &VsyncBridgeParent::ShutdownImpl)); + return; + } + + ShutdownImpl(); +} + +void VsyncBridgeParent::ShutdownImpl() { + if (mOpen) { + Close(); + mOpen = false; + } +} + +void VsyncBridgeParent::ActorDestroy(ActorDestroyReason aWhy) { + mOpen = false; + mCompositorThreadRef = nullptr; +} + +} // namespace gfx +} // namespace mozilla diff --git a/gfx/ipc/VsyncBridgeParent.h b/gfx/ipc/VsyncBridgeParent.h new file mode 100644 index 0000000000..1d1c5c7ef3 --- /dev/null +++ b/gfx/ipc/VsyncBridgeParent.h @@ -0,0 +1,47 @@ +/* -*- 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/. */ +#ifndef include_gfx_ipc_VsyncBridgeParent_h +#define include_gfx_ipc_VsyncBridgeParent_h + +#include "mozilla/RefPtr.h" +#include "mozilla/gfx/PVsyncBridgeParent.h" + +namespace mozilla { +namespace layers { +class CompositorThreadHolder; +} // namespace layers + +namespace gfx { + +class VsyncBridgeParent final : public PVsyncBridgeParent { + public: + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(VsyncBridgeParent, final) + + static RefPtr<VsyncBridgeParent> Start( + Endpoint<PVsyncBridgeParent>&& aEndpoint); + + mozilla::ipc::IPCResult RecvNotifyVsync(const VsyncEvent& aVsync, + const LayersId& aLayersId); + void ActorDestroy(ActorDestroyReason aWhy) override; + + void Shutdown(); + + private: + VsyncBridgeParent(); + ~VsyncBridgeParent(); + + void Open(Endpoint<PVsyncBridgeParent>&& aEndpoint); + void ShutdownImpl(); + + private: + bool mOpen; + RefPtr<layers::CompositorThreadHolder> mCompositorThreadRef; +}; + +} // namespace gfx +} // namespace mozilla + +#endif // include_gfx_ipc_VsyncBridgeParent_h diff --git a/gfx/ipc/VsyncIOThreadHolder.cpp b/gfx/ipc/VsyncIOThreadHolder.cpp new file mode 100644 index 0000000000..b5b04dcf18 --- /dev/null +++ b/gfx/ipc/VsyncIOThreadHolder.cpp @@ -0,0 +1,48 @@ +/* -*- 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 "VsyncIOThreadHolder.h" + +#include "mozilla/SchedulerGroup.h" + +namespace mozilla { +namespace gfx { + +VsyncIOThreadHolder::VsyncIOThreadHolder() { + MOZ_COUNT_CTOR(VsyncIOThreadHolder); +} + +VsyncIOThreadHolder::~VsyncIOThreadHolder() { + MOZ_COUNT_DTOR(VsyncIOThreadHolder); + + if (!mThread) { + return; + } + + if (NS_IsMainThread()) { + mThread->AsyncShutdown(); + } else { + SchedulerGroup::Dispatch(NewRunnableMethod( + "nsIThread::AsyncShutdown", mThread, &nsIThread::AsyncShutdown)); + } +} + +bool VsyncIOThreadHolder::Start() { + /* "VsyncIOThread" is used as the thread we send/recv IPC messages on. We + * don't use the "WindowsVsyncThread" directly because it isn't servicing an + * nsThread event loop which is needed for IPC to return results/notify us + * about shutdown etc. + * + * It would be better if we could notify the IPC IO thread directly to avoid + * the extra ping-ponging but that doesn't seem possible. */ + nsresult rv = NS_NewNamedThread("VsyncIOThread", getter_AddRefs(mThread)); + return NS_SUCCEEDED(rv); +} + +RefPtr<nsIThread> VsyncIOThreadHolder::GetThread() const { return mThread; } + +} // namespace gfx +} // namespace mozilla diff --git a/gfx/ipc/VsyncIOThreadHolder.h b/gfx/ipc/VsyncIOThreadHolder.h new file mode 100644 index 0000000000..8158834cab --- /dev/null +++ b/gfx/ipc/VsyncIOThreadHolder.h @@ -0,0 +1,43 @@ +/* -*- 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/. */ + +#ifndef mozilla_gfx_ipc_VsyncIOThreadHolder_h +#define mozilla_gfx_ipc_VsyncIOThreadHolder_h + +#include "mozilla/RefPtr.h" +#include "nsIThread.h" +#include "nsThreadUtils.h" + +namespace mozilla { +namespace gfx { + +class VsyncIOThreadHolder final { + public: + VsyncIOThreadHolder(); + + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(VsyncIOThreadHolder) + + bool Start(); + + RefPtr<nsIThread> GetThread() const; + + bool IsOnCurrentThread() const { return mThread->IsOnCurrentThread(); } + + void Dispatch(already_AddRefed<nsIRunnable> task) { + mThread->Dispatch(std::move(task), NS_DISPATCH_NORMAL); + } + + private: + ~VsyncIOThreadHolder(); + + private: + RefPtr<nsIThread> mThread; +}; + +} // namespace gfx +} // namespace mozilla + +#endif // mozilla_gfx_ipc_VsyncIOThreadHolder_h diff --git a/gfx/ipc/moz.build b/gfx/ipc/moz.build new file mode 100644 index 0000000000..b8a52079e4 --- /dev/null +++ b/gfx/ipc/moz.build @@ -0,0 +1,92 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +with Files("**"): + BUG_COMPONENT = ("Core", "Graphics") + +EXPORTS.mozilla += ["D3DMessageUtils.h", "GfxMessageUtils.h"] + +EXPORTS.mozilla.gfx += [ + "CanvasManagerChild.h", + "CanvasManagerParent.h", + "CanvasRenderThread.h", + "CrossProcessPaint.h", + "FileHandleWrapper.h", + "GPUChild.h", + "GPUParent.h", + "GPUProcessHost.h", + "GPUProcessImpl.h", + "GPUProcessListener.h", + "GPUProcessManager.h", + "VsyncBridgeChild.h", + "VsyncBridgeParent.h", + "VsyncIOThreadHolder.h", +] + +EXPORTS.mozilla.layers += [ + "CompositorOptions.h", + "CompositorSession.h", + "InProcessCompositorSession.h", + "OverlayInfo.h", + "RemoteCompositorSession.h", +] + +EXPORTS.mozilla.widget += [ + "CompositorWidgetVsyncObserver.h", +] + +UNIFIED_SOURCES += [ + "CanvasManagerChild.cpp", + "CanvasManagerParent.cpp", + "CanvasRenderThread.cpp", + "CompositorSession.cpp", + "CompositorWidgetVsyncObserver.cpp", + "CrossProcessPaint.cpp", + "D3DMessageUtils.cpp", + "FileHandleWrapper.cpp", + "GPUChild.cpp", + "GPUProcessHost.cpp", + "GPUProcessImpl.cpp", + "GPUProcessManager.cpp", + "InProcessCompositorSession.cpp", + "RemoteCompositorSession.cpp", + "VsyncBridgeChild.cpp", + "VsyncBridgeParent.cpp", + "VsyncIOThreadHolder.cpp", +] + +SOURCES += [ + "GPUParent.cpp", +] + +IPDL_SOURCES = [ + "GraphicsMessages.ipdlh", + "PCanvasManager.ipdl", + "PVsyncBridge.ipdl", +] + +PREPROCESSED_IPDL_SOURCES += [ + "PGPU.ipdl", +] + +LOCAL_INCLUDES += [ + "/dom/ipc", + "/gfx/cairo/cairo/src", + "/ipc/glue", + "/toolkit/crashreporter", + "/xpcom/threads", +] + +include("/ipc/chromium/chromium-config.mozbuild") + +FINAL_LIBRARY = "xul" + +if CONFIG["MOZ_WIDGET_TOOLKIT"] == "gtk": + CXXFLAGS += CONFIG["MOZ_GTK3_CFLAGS"] + +CXXFLAGS += ["-Werror=switch"] + +LOCAL_INCLUDES += CONFIG["SKIA_INCLUDES"] |