summaryrefslogtreecommitdiffstats
path: root/gfx/ipc
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--gfx/ipc/CanvasManagerChild.cpp338
-rw-r--r--gfx/ipc/CanvasManagerChild.h88
-rw-r--r--gfx/ipc/CanvasManagerParent.cpp236
-rw-r--r--gfx/ipc/CanvasManagerParent.h71
-rw-r--r--gfx/ipc/CanvasRenderThread.cpp279
-rw-r--r--gfx/ipc/CanvasRenderThread.h85
-rw-r--r--gfx/ipc/CompositorOptions.h94
-rw-r--r--gfx/ipc/CompositorSession.cpp35
-rw-r--r--gfx/ipc/CompositorSession.h105
-rw-r--r--gfx/ipc/CompositorWidgetVsyncObserver.cpp32
-rw-r--r--gfx/ipc/CompositorWidgetVsyncObserver.h41
-rw-r--r--gfx/ipc/CrossProcessPaint.cpp529
-rw-r--r--gfx/ipc/CrossProcessPaint.h189
-rw-r--r--gfx/ipc/D3DMessageUtils.cpp71
-rw-r--r--gfx/ipc/D3DMessageUtils.h46
-rw-r--r--gfx/ipc/FileHandleWrapper.cpp27
-rw-r--r--gfx/ipc/FileHandleWrapper.h63
-rw-r--r--gfx/ipc/GPUChild.cpp371
-rw-r--r--gfx/ipc/GPUChild.h108
-rw-r--r--gfx/ipc/GPUParent.cpp831
-rw-r--r--gfx/ipc/GPUParent.h133
-rw-r--r--gfx/ipc/GPUProcessHost.cpp249
-rw-r--r--gfx/ipc/GPUProcessHost.h164
-rw-r--r--gfx/ipc/GPUProcessImpl.cpp48
-rw-r--r--gfx/ipc/GPUProcessImpl.h41
-rw-r--r--gfx/ipc/GPUProcessListener.h28
-rw-r--r--gfx/ipc/GPUProcessManager.cpp1646
-rw-r--r--gfx/ipc/GPUProcessManager.h366
-rw-r--r--gfx/ipc/GfxMessageUtils.h1303
-rw-r--r--gfx/ipc/GraphicsMessages.ipdlh92
-rw-r--r--gfx/ipc/InProcessCompositorSession.cpp108
-rw-r--r--gfx/ipc/InProcessCompositorSession.h54
-rw-r--r--gfx/ipc/OverlayInfo.h53
-rw-r--r--gfx/ipc/PCanvasManager.ipdl52
-rw-r--r--gfx/ipc/PGPU.ipdl191
-rw-r--r--gfx/ipc/PVsyncBridge.ipdl27
-rw-r--r--gfx/ipc/RemoteCompositorSession.cpp101
-rw-r--r--gfx/ipc/RemoteCompositorSession.h45
-rw-r--r--gfx/ipc/VsyncBridgeChild.cpp127
-rw-r--r--gfx/ipc/VsyncBridgeChild.h55
-rw-r--r--gfx/ipc/VsyncBridgeParent.cpp74
-rw-r--r--gfx/ipc/VsyncBridgeParent.h47
-rw-r--r--gfx/ipc/VsyncIOThreadHolder.cpp48
-rw-r--r--gfx/ipc/VsyncIOThreadHolder.h43
-rw-r--r--gfx/ipc/moz.build92
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, &notnull)) {
+ 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"]