/* -*- 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 "mozilla/dom/WebGLParent.h" #include "mozilla/gfx/CanvasRenderThread.h" #include "mozilla/gfx/gfxVars.h" #include "mozilla/ipc/Endpoint.h" #include "mozilla/layers/CompositorThread.h" #include "mozilla/StaticPrefs_webgl.h" #include "mozilla/webgpu/WebGPUParent.h" #include "mozilla/webrender/RenderThread.h" #include "nsIThread.h" #include "nsThreadUtils.h" namespace mozilla::gfx { CanvasManagerParent::ManagerSet CanvasManagerParent::sManagers; /* static */ void CanvasManagerParent::Init( Endpoint&& aEndpoint) { MOZ_ASSERT(layers::CompositorThreadHolder::IsInCompositorThread()); auto manager = MakeRefPtr(); if (!gfxVars::SupportsThreadsafeGL()) { nsCOMPtr owningThread; owningThread = wr::RenderThread::GetRenderThread(); MOZ_ASSERT(owningThread); owningThread->Dispatch(NewRunnableMethod&&>( "CanvasManagerParent::Bind", manager, &CanvasManagerParent::Bind, std::move(aEndpoint))); } else if (gfxVars::UseCanvasRenderThread()) { nsCOMPtr owningThread; owningThread = gfx::CanvasRenderThread::GetCanvasRenderThread(); MOZ_ASSERT(owningThread); owningThread->Dispatch(NewRunnableMethod&&>( "CanvasManagerParent::Bind", manager, &CanvasManagerParent::Bind, std::move(aEndpoint))); } else { manager->Bind(std::move(aEndpoint)); } } /* static */ void CanvasManagerParent::Shutdown() { MOZ_ASSERT(NS_IsMainThread()); nsCOMPtr owningThread; if (!gfxVars::SupportsThreadsafeGL()) { owningThread = wr::RenderThread::GetRenderThread(); } else if (gfxVars::UseCanvasRenderThread()) { owningThread = gfx::CanvasRenderThread::GetCanvasRenderThread(); } else { owningThread = layers::CompositorThread(); } if (!owningThread) { return; } NS_DispatchAndSpinEventLoopUntilComplete( "CanvasManagerParent::Shutdown"_ns, owningThread, NS_NewRunnableFunction("CanvasManagerParent::Shutdown", []() -> void { CanvasManagerParent::ShutdownInternal(); })); } /* static */ void CanvasManagerParent::ShutdownInternal() { nsTArray> 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(); } } CanvasManagerParent::CanvasManagerParent() = default; CanvasManagerParent::~CanvasManagerParent() = default; void CanvasManagerParent::Bind(Endpoint&& aEndpoint) { if (!aEndpoint.Bind(this)) { NS_WARNING("Failed to bind CanvasManagerParent!"); return; } sManagers.Insert(this); } void CanvasManagerParent::ActorDestroy(ActorDestroyReason aWhy) { sManagers.Remove(this); } already_AddRefed 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(); } already_AddRefed CanvasManagerParent::AllocPWebGPUParent() { if (NS_WARN_IF(!gfxVars::AllowWebGPU())) { MOZ_ASSERT_UNREACHABLE("AllocPWebGPUParent without WebGPU"); return nullptr; } return MakeAndAddRef(); } 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(); } mozilla::ipc::IPCResult CanvasManagerParent::RecvGetSnapshot( const uint32_t& aManagerId, const int32_t& aProtocolId, const Maybe& aOwnerId, webgl::FrontBufferSnapshotIpc* aResult) { if (!aManagerId) { return IPC_FAIL(this, "invalid id"); } IProtocol* actor = nullptr; for (CanvasManagerParent* i : sManagers) { if (i->OtherPidMaybeInvalid() == OtherPidMaybeInvalid() && 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 webgl = static_cast(actor); mozilla::ipc::IPCResult rv = webgl->GetFrontBufferSnapshot(&buffer, this); if (!rv) { return rv; } } break; case ProtocolId::PWebGPUMsgStart: { RefPtr webgpu = static_cast(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(size.width); buffer.surfSize.y = static_cast(size.height); } break; default: return IPC_FAIL(this, "unsupported protocol"); } *aResult = std::move(buffer); return IPC_OK(); } } // namespace mozilla::gfx