gecko-dev/gfx/vr/VRServiceHost.cpp
Jens Stutte 1e19379853 Bug 1689953: Harmonize shutdown phase definitions across nsTerminator and AppShutdown r=dthayer,chutten
This patch wants to solve several quirks around the shutdown terminator.

 - Use the same shutdown phase definitions in AppShutdown and nsTerminator. This touches quite a few files.
 - Ensure that the terminator phase shift is handled before any shutdown observer notifications are sent and reduce its heartbeat duration.
 - Add missing phases to the shutdown telemetry.

Please note that this changes the unit of "tick" to 100ms rather than 1s.
As a side effect, we also remove the obsolete "shutdown-persist" context.

While the existing test coverage continues to prove the most important functions, we acknowledge the wish for better test coverage with [[ https://bugzilla.mozilla.org/show_bug.cgi?id=1693966 | bug 1693966 ]].

Differential Revision: https://phabricator.services.mozilla.com/D103626
2021-02-27 14:38:14 +00:00

329 lines
9.3 KiB
C++

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "VRServiceHost.h"
#include "VRGPUChild.h"
#include "VRPuppetCommandBuffer.h"
#include "mozilla/ClearOnShutdown.h"
#include "mozilla/gfx/GPUParent.h"
#include "service/VRService.h"
#include "VRManager.h"
namespace mozilla {
namespace gfx {
static StaticRefPtr<VRServiceHost> sVRServiceHostSingleton;
/* static */
void VRServiceHost::Init(bool aEnableVRProcess) {
MOZ_ASSERT(NS_IsMainThread());
if (sVRServiceHostSingleton == nullptr) {
sVRServiceHostSingleton = new VRServiceHost(aEnableVRProcess);
ClearOnShutdown(&sVRServiceHostSingleton);
}
}
/* static */
VRServiceHost* VRServiceHost::Get() {
MOZ_ASSERT(sVRServiceHostSingleton != nullptr);
return sVRServiceHostSingleton;
}
VRServiceHost::VRServiceHost(bool aEnableVRProcess)
: mVRService(nullptr),
mVRProcessEnabled(aEnableVRProcess),
mVRProcessStarted(false),
mVRServiceReadyInVRProcess(false),
mVRServiceRequested(false)
{
MOZ_COUNT_CTOR(VRServiceHost);
}
VRServiceHost::~VRServiceHost() {
MOZ_ASSERT(NS_IsMainThread());
MOZ_COUNT_DTOR(VRServiceHost);
}
void VRServiceHost::StartService() {
mVRServiceRequested = true;
if (mVRProcessEnabled) {
// VRService in the VR process
RefreshVRProcess();
} else if (mVRService) {
// VRService in the GPU process if enabled, or
// the parent process if the GPU process is not enabled.
mVRService->Start();
}
}
void VRServiceHost::StopService() {
mVRServiceRequested = false;
if (mVRProcessEnabled) {
// VRService in the VR process
RefreshVRProcess();
} else if (mVRService) {
// VRService in the GPU process if enabled, or
// the parent process if the GPU process is not enabled.
mVRService->Stop();
}
}
void VRServiceHost::Shutdown() {
PuppetReset();
StopService();
mVRService = nullptr;
}
void VRServiceHost::Refresh() {
if (mVRService) {
mVRService->Refresh();
}
}
void VRServiceHost::CreateService(volatile VRExternalShmem* aShmem) {
MOZ_ASSERT(!mVRProcessEnabled);
mVRService = VRService::Create(aShmem);
}
bool VRServiceHost::NeedVRProcess() {
if (!mVRProcessEnabled) {
return false;
}
return mVRServiceRequested;
}
void VRServiceHost::RefreshVRProcess() {
// Start or stop the VR process if needed
if (NeedVRProcess()) {
if (!mVRProcessStarted) {
CreateVRProcess();
}
} else {
if (mVRProcessStarted) {
ShutdownVRProcess();
}
}
}
void VRServiceHost::CreateVRProcess() {
// This is only allowed to run in the main thread of the GPU process
if (!XRE_IsGPUProcess()) {
return;
}
// Forward this to the main thread if not already there
if (!NS_IsMainThread()) {
RefPtr<Runnable> task = NS_NewRunnableFunction(
"VRServiceHost::CreateVRProcess",
[]() -> void { VRServiceHost::Get()->CreateVRProcess(); });
NS_DispatchToMainThread(task.forget());
return;
}
if (mVRProcessStarted) {
return;
}
mVRProcessStarted = true;
// Using PGPU channel to tell the main process
// to create the VR process.
gfx::GPUParent* gpu = GPUParent::GetSingleton();
MOZ_ASSERT(gpu);
Unused << gpu->SendCreateVRProcess();
}
void VRServiceHost::NotifyVRProcessStarted() {
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(mVRProcessEnabled);
if (!mVRProcessStarted) {
// We have received this after the VR process
// has been stopped; the VR service is no
// longer running in the VR process.
return;
}
if (!VRGPUChild::IsCreated()) {
return;
}
VRGPUChild* vrGPUChild = VRGPUChild::Get();
// The VR service has started in the VR process
// If there were pending puppet commands, we
// can send them now.
// This must occur before the VRService
// is started so the buffer can be seen
// by VRPuppetSession::Initialize().
if (!mPuppetPendingCommands.IsEmpty()) {
vrGPUChild->SendPuppetSubmit(mPuppetPendingCommands);
mPuppetPendingCommands.Clear();
}
vrGPUChild->SendStartVRService();
mVRServiceReadyInVRProcess = true;
}
void VRServiceHost::ShutdownVRProcess() {
// This is only allowed to run in the main thread of the GPU process
if (!XRE_IsGPUProcess()) {
return;
}
// Forward this to the main thread if not already there
if (!NS_IsMainThread()) {
RefPtr<Runnable> task = NS_NewRunnableFunction(
"VRServiceHost::ShutdownVRProcess",
[]() -> void { VRServiceHost::Get()->ShutdownVRProcess(); });
NS_DispatchToMainThread(task.forget());
return;
}
if (VRGPUChild::IsCreated()) {
VRGPUChild* vrGPUChild = VRGPUChild::Get();
vrGPUChild->SendStopVRService();
if (!vrGPUChild->IsClosed()) {
vrGPUChild->Close();
}
VRGPUChild::Shutdown();
}
if (!mVRProcessStarted) {
return;
}
// Using PGPU channel to tell the main process
// to shutdown VR process.
gfx::GPUParent* gpu = GPUParent::GetSingleton();
MOZ_ASSERT(gpu);
Unused << gpu->SendShutdownVRProcess();
mVRProcessStarted = false;
mVRServiceReadyInVRProcess = false;
}
void VRServiceHost::PuppetSubmit(const nsTArray<uint64_t>& aBuffer) {
if (!mVRProcessEnabled) {
// Puppet is running in this process, submit commands directly
VRPuppetCommandBuffer::Get().Submit(aBuffer);
return;
}
// We need to send the buffer to the VR process
SendPuppetSubmitToVRProcess(aBuffer);
}
void VRServiceHost::SendPuppetSubmitToVRProcess(
const nsTArray<uint64_t>& aBuffer) {
// This is only allowed to run in the main thread of the GPU process
if (!XRE_IsGPUProcess()) {
return;
}
// Forward this to the main thread if not already there
if (!NS_IsMainThread()) {
RefPtr<Runnable> task = NS_NewRunnableFunction(
"VRServiceHost::SendPuppetSubmitToVRProcess",
[buffer{aBuffer.Clone()}]() -> void {
VRServiceHost::Get()->SendPuppetSubmitToVRProcess(buffer);
});
NS_DispatchToMainThread(task.forget());
return;
}
if (!mVRServiceReadyInVRProcess) {
// Queue the commands to be sent to the VR process once it is started
mPuppetPendingCommands.AppendElements(aBuffer);
return;
}
if (VRGPUChild::IsCreated()) {
VRGPUChild* vrGPUChild = VRGPUChild::Get();
vrGPUChild->SendPuppetSubmit(aBuffer);
}
}
void VRServiceHost::PuppetReset() {
// If we're already into ShutdownFinal, the VRPuppetCommandBuffer instance
// will have been cleared, so don't try to access it after that point.
if (!mVRProcessEnabled &&
!(NS_IsMainThread() &&
PastShutdownPhase(ShutdownPhase::XPCOMShutdownFinal))) {
// Puppet is running in this process, tell it to reset directly.
VRPuppetCommandBuffer::Get().Reset();
}
mPuppetPendingCommands.Clear();
if (!mVRProcessStarted) {
// Process is stopped, so puppet state is already clear
return;
}
// We need to tell the VR process to reset the puppet
SendPuppetResetToVRProcess();
}
void VRServiceHost::SendPuppetResetToVRProcess() {
// This is only allowed to run in the main thread of the GPU process
if (!XRE_IsGPUProcess()) {
return;
}
// Forward this to the main thread if not already there
if (!NS_IsMainThread()) {
RefPtr<Runnable> task = NS_NewRunnableFunction(
"VRServiceHost::SendPuppetResetToVRProcess",
[]() -> void { VRServiceHost::Get()->SendPuppetResetToVRProcess(); });
NS_DispatchToMainThread(task.forget());
return;
}
if (VRGPUChild::IsCreated()) {
VRGPUChild* vrGPUChild = VRGPUChild::Get();
vrGPUChild->SendPuppetReset();
}
}
void VRServiceHost::CheckForPuppetCompletion() {
if (!mVRProcessEnabled) {
// Puppet is running in this process, ask it directly
if (VRPuppetCommandBuffer::Get().HasEnded()) {
VRManager::Get()->NotifyPuppetComplete();
}
}
if (!mPuppetPendingCommands.IsEmpty()) {
// There are puppet commands pending to be sent to the
// VR process once its started, thus it has not ended.
return;
}
if (!mVRProcessStarted) {
// The VR process will be kept alive as long
// as there is a queue in the puppet command
// buffer. If the process is stopped, we can
// infer that the queue has been cleared and
// puppet state is reset.
VRManager::Get()->NotifyPuppetComplete();
}
// We need to ask the VR process if the puppet has ended
SendPuppetCheckForCompletionToVRProcess();
// VRGPUChild::RecvNotifyPuppetComplete will call
// VRManager::NotifyPuppetComplete if the puppet has completed
// in the VR Process.
}
void VRServiceHost::SendPuppetCheckForCompletionToVRProcess() {
// This is only allowed to run in the main thread of the GPU process
if (!XRE_IsGPUProcess()) {
return;
}
// Forward this to the main thread if not already there
if (!NS_IsMainThread()) {
RefPtr<Runnable> task = NS_NewRunnableFunction(
"VRServiceHost::SendPuppetCheckForCompletionToVRProcess", []() -> void {
VRServiceHost::Get()->SendPuppetCheckForCompletionToVRProcess();
});
NS_DispatchToMainThread(task.forget());
return;
}
if (VRGPUChild::IsCreated()) {
VRGPUChild* vrGPUChild = VRGPUChild::Get();
vrGPUChild->SendPuppetCheckForCompletion();
}
}
} // namespace gfx
} // namespace mozilla