/* -*- 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&& aEndpoint, layers::SharedSurfacesHolder* aSharedSurfacesHolder, const dom::ContentParentId& aContentId) { MOZ_ASSERT(layers::CompositorThreadHolder::IsInCompositorThread()); auto manager = MakeRefPtr(aSharedSurfacesHolder, aContentId); nsCOMPtr owningThread = gfx::CanvasRenderThread::GetCanvasRenderThread(); MOZ_ASSERT(owningThread); owningThread->Dispatch(NewRunnableMethod&&>( "CanvasManagerParent::Bind", manager, &CanvasManagerParent::Bind, std::move(aEndpoint))); } /* static */ void CanvasManagerParent::Shutdown() { MOZ_ASSERT(NS_IsMainThread()); nsCOMPtr 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> 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, 16> actors; for (const auto& manager : sManagers) { for (const auto& canvas : manager->ManagedPCanvasParent()) { actors.AppendElement(static_cast(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&& 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 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(mContentId); } 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(); } already_AddRefed 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(mSharedSurfacesHolder, mContentId, mId); } 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->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 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