summaryrefslogtreecommitdiffstats
path: root/gfx/vr/ipc/VRProcessManager.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'gfx/vr/ipc/VRProcessManager.cpp')
-rw-r--r--gfx/vr/ipc/VRProcessManager.cpp287
1 files changed, 287 insertions, 0 deletions
diff --git a/gfx/vr/ipc/VRProcessManager.cpp b/gfx/vr/ipc/VRProcessManager.cpp
new file mode 100644
index 0000000000..f3bc13bac0
--- /dev/null
+++ b/gfx/vr/ipc/VRProcessManager.cpp
@@ -0,0 +1,287 @@
+/* -*- 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 "VRProcessManager.h"
+
+#include "VRProcessParent.h"
+#include "VRChild.h"
+#include "VRGPUChild.h"
+#include "VRGPUParent.h"
+#include "mozilla/dom/ContentParent.h"
+#include "mozilla/gfx/Logging.h"
+#include "mozilla/ipc/Endpoint.h"
+#include "mozilla/MemoryReportingProcess.h"
+#include "mozilla/Preferences.h"
+
+namespace mozilla {
+namespace gfx {
+
+static StaticAutoPtr<VRProcessManager> sSingleton;
+
+/* static */
+VRProcessManager* VRProcessManager::Get() { return sSingleton; }
+
+/* static */
+void VRProcessManager::Initialize() {
+ MOZ_ASSERT(XRE_IsParentProcess());
+ if (sSingleton == nullptr) {
+ sSingleton = new VRProcessManager();
+ }
+}
+
+/* static */
+void VRProcessManager::Shutdown() { sSingleton = nullptr; }
+
+VRProcessManager::VRProcessManager() : mProcess(nullptr), mVRChild(nullptr) {
+ MOZ_COUNT_CTOR(VRProcessManager);
+
+ mObserver = new Observer(this);
+ nsContentUtils::RegisterShutdownObserver(mObserver);
+ Preferences::AddStrongObserver(mObserver, "");
+}
+
+VRProcessManager::~VRProcessManager() {
+ MOZ_COUNT_DTOR(VRProcessManager);
+
+ if (mObserver) {
+ nsContentUtils::UnregisterShutdownObserver(mObserver);
+ Preferences::RemoveObserver(mObserver, "");
+ mObserver = nullptr;
+ }
+
+ DestroyProcess();
+ // The VR process should have already been shut down.
+ MOZ_ASSERT(!mProcess);
+}
+
+void VRProcessManager::LaunchVRProcess() {
+ if (mProcess) {
+ return;
+ }
+
+ // The subprocess is launched asynchronously, so we wait for a callback to
+ // acquire the IPDL actor.
+ mProcess = new VRProcessParent(this);
+ if (!mProcess->Launch()) {
+ DisableVRProcess("Failed to launch VR process");
+ }
+}
+
+void VRProcessManager::DisableVRProcess(const char* aMessage) {
+ if (!StaticPrefs::dom_vr_process_enabled_AtStartup()) {
+ return;
+ }
+
+ DestroyProcess();
+}
+
+void VRProcessManager::DestroyProcess() {
+ if (!mProcess) {
+ return;
+ }
+
+ mProcess->Shutdown();
+ mProcess = nullptr;
+ mVRChild = nullptr;
+
+ CrashReporter::AnnotateCrashReport(CrashReporter::Annotation::VRProcessStatus,
+ "Destroyed"_ns);
+}
+
+bool VRProcessManager::EnsureVRReady() {
+ if (mProcess && !mProcess->IsConnected()) {
+ if (!mProcess->WaitForLaunch()) {
+ // If this fails, we should have fired OnProcessLaunchComplete and
+ // removed the process.
+ MOZ_ASSERT(!mProcess && !mVRChild);
+ return false;
+ }
+ }
+
+ if (mVRChild) {
+ if (mVRChild->EnsureVRReady()) {
+ return true;
+ }
+
+ // If the initialization above fails, we likely have a GPU process teardown
+ // waiting in our message queue (or will soon). We need to ensure we don't
+ // restart it later because if we fail here, our callers assume they should
+ // fall back to a combined UI/GPU process. This also ensures our internal
+ // state is consistent (e.g. process token is reset).
+ DisableVRProcess("Failed to initialize VR process");
+ }
+
+ return false;
+}
+
+void VRProcessManager::OnProcessLaunchComplete(VRProcessParent* aParent) {
+ MOZ_ASSERT(mProcess && mProcess == aParent);
+
+ mVRChild = mProcess->GetActor();
+
+ if (!mProcess->IsConnected()) {
+ DestroyProcess();
+ return;
+ }
+
+ // 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(!mVRChild->SendPreferenceUpdate(pref));
+ }
+ mQueuedPrefs.Clear();
+
+ CrashReporter::AnnotateCrashReport(CrashReporter::Annotation::VRProcessStatus,
+ "Running"_ns);
+}
+
+void VRProcessManager::OnProcessUnexpectedShutdown(VRProcessParent* aParent) {
+ MOZ_ASSERT(mProcess && mProcess == aParent);
+
+ DestroyProcess();
+}
+
+bool VRProcessManager::CreateGPUBridges(
+ base::ProcessId aOtherProcess,
+ mozilla::ipc::Endpoint<PVRGPUChild>* aOutVRBridge) {
+ if (!CreateGPUVRManager(aOtherProcess, aOutVRBridge)) {
+ return false;
+ }
+ return true;
+}
+
+bool VRProcessManager::CreateGPUVRManager(
+ base::ProcessId aOtherProcess,
+ mozilla::ipc::Endpoint<PVRGPUChild>* aOutEndpoint) {
+ if (mProcess && !mProcess->IsConnected()) {
+ NS_WARNING("VR process haven't connected with the parent process yet");
+ return false;
+ }
+
+ base::ProcessId vrparentPid = mProcess
+ ? mProcess->OtherPid() // VR process id.
+ : base::GetCurrentProcId();
+
+ ipc::Endpoint<PVRGPUParent> vrparentPipe;
+ ipc::Endpoint<PVRGPUChild> vrchildPipe;
+ nsresult rv = PVRGPU::CreateEndpoints(vrparentPid, // vr process id
+ aOtherProcess, // gpu process id
+ &vrparentPipe, &vrchildPipe);
+
+ if (NS_FAILED(rv)) {
+ gfxCriticalNote << "Could not create gpu-vr bridge: " << hexa(int(rv));
+ return false;
+ }
+
+ // Bind vr-gpu pipe to VRParent and make a PVRGPU connection.
+ VRChild* vrChild = mProcess->GetActor();
+ vrChild->SendNewGPUVRManager(std::move(vrparentPipe));
+
+ *aOutEndpoint = std::move(vrchildPipe);
+ return true;
+}
+
+NS_IMPL_ISUPPORTS(VRProcessManager::Observer, nsIObserver);
+
+VRProcessManager::Observer::Observer(VRProcessManager* aManager)
+ : mManager(aManager) {}
+
+NS_IMETHODIMP
+VRProcessManager::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);
+ }
+ return NS_OK;
+}
+
+void VRProcessManager::CleanShutdown() { DestroyProcess(); }
+
+void VRProcessManager::OnXPCOMShutdown() {
+ if (mObserver) {
+ nsContentUtils::UnregisterShutdownObserver(mObserver);
+ Preferences::RemoveObserver(mObserver, "");
+ mObserver = nullptr;
+ }
+
+ CleanShutdown();
+}
+
+void VRProcessManager::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_VR,
+ /* remoteType */ ""_ns);
+ if (!!mVRChild) {
+ MOZ_ASSERT(mQueuedPrefs.IsEmpty());
+ mVRChild->SendPreferenceUpdate(pref);
+ } else {
+ mQueuedPrefs.AppendElement(pref);
+ }
+}
+
+VRChild* VRProcessManager::GetVRChild() { return mProcess->GetActor(); }
+
+class VRMemoryReporter : public MemoryReportingProcess {
+ public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(VRMemoryReporter, override)
+
+ bool IsAlive() const override {
+ if (VRProcessManager* vpm = VRProcessManager::Get()) {
+ return !!vpm->GetVRChild();
+ }
+ return false;
+ }
+
+ bool SendRequestMemoryReport(
+ const uint32_t& aGeneration, const bool& aAnonymize,
+ const bool& aMinimizeMemoryUsage,
+ const Maybe<ipc::FileDescriptor>& aDMDFile) override {
+ VRChild* child = GetChild();
+ if (!child) {
+ return false;
+ }
+
+ return child->SendRequestMemoryReport(aGeneration, aAnonymize,
+ aMinimizeMemoryUsage, aDMDFile);
+ }
+
+ int32_t Pid() const override {
+ if (VRChild* child = GetChild()) {
+ return (int32_t)child->OtherPid();
+ }
+ return 0;
+ }
+
+ private:
+ VRChild* GetChild() const {
+ if (VRProcessManager* vpm = VRProcessManager::Get()) {
+ if (VRChild* child = vpm->GetVRChild()) {
+ return child;
+ }
+ }
+ return nullptr;
+ }
+
+ protected:
+ ~VRMemoryReporter() = default;
+};
+
+RefPtr<MemoryReportingProcess> VRProcessManager::GetProcessMemoryReporter() {
+ if (!EnsureVRReady()) {
+ return nullptr;
+ }
+ return new VRMemoryReporter();
+}
+
+} // namespace gfx
+} // namespace mozilla