summaryrefslogtreecommitdiffstats
path: root/gfx/vr/VRManager.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'gfx/vr/VRManager.cpp')
-rw-r--r--gfx/vr/VRManager.cpp393
1 files changed, 393 insertions, 0 deletions
diff --git a/gfx/vr/VRManager.cpp b/gfx/vr/VRManager.cpp
new file mode 100644
index 000000000..672e9e3a1
--- /dev/null
+++ b/gfx/vr/VRManager.cpp
@@ -0,0 +1,393 @@
+/* -*- Mode: C++; tab-width: 20; 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 "VRManager.h"
+#include "VRManagerParent.h"
+#include "gfxVR.h"
+#include "gfxVROpenVR.h"
+#include "mozilla/ClearOnShutdown.h"
+#include "mozilla/dom/VRDisplay.h"
+#include "mozilla/dom/GamepadEventTypes.h"
+#include "mozilla/layers/TextureHost.h"
+#include "mozilla/Unused.h"
+
+#include "gfxPrefs.h"
+#include "gfxVR.h"
+#if defined(XP_WIN)
+#include "gfxVROculus.h"
+#endif
+#if defined(XP_WIN) || defined(XP_MACOSX) || defined(XP_LINUX)
+#include "gfxVROSVR.h"
+#endif
+#include "ipc/VRLayerParent.h"
+
+using namespace mozilla;
+using namespace mozilla::gfx;
+using namespace mozilla::layers;
+using namespace mozilla::gl;
+
+namespace mozilla {
+namespace gfx {
+
+static StaticRefPtr<VRManager> sVRManagerSingleton;
+
+/*static*/ void
+VRManager::ManagerInit()
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ if (sVRManagerSingleton == nullptr) {
+ sVRManagerSingleton = new VRManager();
+ ClearOnShutdown(&sVRManagerSingleton);
+ }
+}
+
+VRManager::VRManager()
+ : mInitialized(false)
+{
+ MOZ_COUNT_CTOR(VRManager);
+ MOZ_ASSERT(sVRManagerSingleton == nullptr);
+
+ RefPtr<VRDisplayManager> mgr;
+ RefPtr<VRControllerManager> controllerMgr;
+
+ /**
+ * We must add the VRDisplayManager's to mManagers in a careful order to
+ * ensure that we don't detect the same VRDisplay from multiple API's.
+ *
+ * Oculus comes first, as it will only enumerate Oculus HMD's and is the
+ * native interface for Oculus HMD's.
+ *
+ * OpenvR comes second, as it is the native interface for HTC Vive
+ * which is the most common HMD at this time.
+ *
+ * OSVR will be used if Oculus SDK and OpenVR don't detect any HMDS,
+ * to support everyone else.
+ */
+
+#if defined(XP_WIN)
+ // The Oculus runtime is supported only on Windows
+ mgr = VRDisplayManagerOculus::Create();
+ if (mgr) {
+ mManagers.AppendElement(mgr);
+ }
+#endif
+
+#if defined(XP_WIN) || defined(XP_MACOSX) || defined(XP_LINUX)
+ // OpenVR is cross platform compatible
+ mgr = VRDisplayManagerOpenVR::Create();
+ if (mgr) {
+ mManagers.AppendElement(mgr);
+ }
+
+ controllerMgr = VRControllerManagerOpenVR::Create();
+ if (mgr) {
+ mControllerManagers.AppendElement(controllerMgr);
+ }
+
+ // OSVR is cross platform compatible
+ mgr = VRDisplayManagerOSVR::Create();
+ if (mgr) {
+ mManagers.AppendElement(mgr);
+ }
+#endif
+ // Enable gamepad extensions while VR is enabled.
+ if (gfxPrefs::VREnabled()) {
+ Preferences::SetBool("dom.gamepad.extensions.enabled", true);
+ }
+}
+
+VRManager::~VRManager()
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(!mInitialized);
+ MOZ_COUNT_DTOR(VRManager);
+}
+
+void
+VRManager::Destroy()
+{
+ mVRDisplays.Clear();
+ for (uint32_t i = 0; i < mManagers.Length(); ++i) {
+ mManagers[i]->Destroy();
+ }
+
+ mVRControllers.Clear();
+ for (uint32_t i = 0; i < mControllerManagers.Length(); ++i) {
+ mControllerManagers[i]->Destroy();
+ }
+ mInitialized = false;
+}
+
+void
+VRManager::Init()
+{
+ for (uint32_t i = 0; i < mManagers.Length(); ++i) {
+ mManagers[i]->Init();
+ }
+
+ for (uint32_t i = 0; i < mControllerManagers.Length(); ++i) {
+ mControllerManagers[i]->Init();
+ }
+ mInitialized = true;
+}
+
+/* static */VRManager*
+VRManager::Get()
+{
+ MOZ_ASSERT(sVRManagerSingleton != nullptr);
+
+ return sVRManagerSingleton;
+}
+
+void
+VRManager::AddVRManagerParent(VRManagerParent* aVRManagerParent)
+{
+ if (mVRManagerParents.IsEmpty()) {
+ Init();
+ }
+ mVRManagerParents.PutEntry(aVRManagerParent);
+}
+
+void
+VRManager::RemoveVRManagerParent(VRManagerParent* aVRManagerParent)
+{
+ mVRManagerParents.RemoveEntry(aVRManagerParent);
+ if (mVRManagerParents.IsEmpty()) {
+ Destroy();
+ }
+}
+
+void
+VRManager::NotifyVsync(const TimeStamp& aVsyncTimestamp)
+{
+ const double kVRDisplayRefreshMaxDuration = 5000; // milliseconds
+
+ bool bHaveEventListener = false;
+
+ for (auto iter = mVRManagerParents.Iter(); !iter.Done(); iter.Next()) {
+ VRManagerParent *vmp = iter.Get()->GetKey();
+ if (mVRDisplays.Count()) {
+ Unused << vmp->SendNotifyVSync();
+ }
+ bHaveEventListener |= vmp->HaveEventListener();
+ }
+
+ for (auto iter = mVRDisplays.Iter(); !iter.Done(); iter.Next()) {
+ gfx::VRDisplayHost* display = iter.UserData();
+ display->NotifyVSync();
+ }
+
+ if (bHaveEventListener) {
+ for (uint32_t i = 0; i < mControllerManagers.Length(); ++i) {
+ mControllerManagers[i]->HandleInput();
+ }
+ // If content has set an EventHandler to be notified of VR display events
+ // we must continually refresh the VR display enumeration to check
+ // for events that we must fire such as Window.onvrdisplayconnect
+ // Note that enumeration itself may activate display hardware, such
+ // as Oculus, so we only do this when we know we are displaying content
+ // that is looking for VR displays.
+ if (mLastRefreshTime.IsNull()) {
+ // This is the first vsync, must refresh VR displays
+ RefreshVRDisplays();
+ RefreshVRControllers();
+ mLastRefreshTime = TimeStamp::Now();
+ } else {
+ // We don't have to do this every frame, so check if we
+ // have refreshed recently.
+ TimeDuration duration = TimeStamp::Now() - mLastRefreshTime;
+ if (duration.ToMilliseconds() > kVRDisplayRefreshMaxDuration) {
+ RefreshVRDisplays();
+ RefreshVRControllers();
+ mLastRefreshTime = TimeStamp::Now();
+ }
+ }
+ }
+}
+
+void
+VRManager::NotifyVRVsync(const uint32_t& aDisplayID)
+{
+ for (auto iter = mVRManagerParents.Iter(); !iter.Done(); iter.Next()) {
+ Unused << iter.Get()->GetKey()->SendNotifyVRVSync(aDisplayID);
+ }
+}
+
+void
+VRManager::RefreshVRDisplays(bool aMustDispatch)
+{
+ nsTArray<RefPtr<gfx::VRDisplayHost> > displays;
+
+ /** We don't wish to enumerate the same display from multiple managers,
+ * so stop as soon as we get a display.
+ * It is still possible to get multiple displays from a single manager,
+ * but do not wish to mix-and-match for risk of reporting a duplicate.
+ *
+ * XXX - Perhaps there will be a better way to detect duplicate displays
+ * in the future.
+ */
+ for (uint32_t i = 0; i < mManagers.Length() && displays.Length() == 0; ++i) {
+ mManagers[i]->GetHMDs(displays);
+ }
+
+ bool displayInfoChanged = false;
+
+ if (displays.Length() != mVRDisplays.Count()) {
+ // Catch cases where a VR display has been removed
+ displayInfoChanged = true;
+ }
+
+ for (const auto& display: displays) {
+ if (!GetDisplay(display->GetDisplayInfo().GetDisplayID())) {
+ // This is a new display
+ displayInfoChanged = true;
+ break;
+ }
+
+ if (display->CheckClearDisplayInfoDirty()) {
+ // This display's info has changed
+ displayInfoChanged = true;
+ break;
+ }
+ }
+
+ if (displayInfoChanged) {
+ mVRDisplays.Clear();
+ for (const auto& display: displays) {
+ mVRDisplays.Put(display->GetDisplayInfo().GetDisplayID(), display);
+ }
+ }
+
+ if (displayInfoChanged || aMustDispatch) {
+ DispatchVRDisplayInfoUpdate();
+ }
+}
+
+void
+VRManager::DispatchVRDisplayInfoUpdate()
+{
+ nsTArray<VRDisplayInfo> update;
+ GetVRDisplayInfo(update);
+
+ for (auto iter = mVRManagerParents.Iter(); !iter.Done(); iter.Next()) {
+ Unused << iter.Get()->GetKey()->SendUpdateDisplayInfo(update);
+ }
+}
+
+
+/**
+ * Get any VR displays that have already been enumerated without
+ * activating any new devices.
+ */
+void
+VRManager::GetVRDisplayInfo(nsTArray<VRDisplayInfo>& aDisplayInfo)
+{
+ aDisplayInfo.Clear();
+ for (auto iter = mVRDisplays.Iter(); !iter.Done(); iter.Next()) {
+ gfx::VRDisplayHost* display = iter.UserData();
+ aDisplayInfo.AppendElement(VRDisplayInfo(display->GetDisplayInfo()));
+ }
+}
+
+RefPtr<gfx::VRDisplayHost>
+VRManager::GetDisplay(const uint32_t& aDisplayID)
+{
+ RefPtr<gfx::VRDisplayHost> display;
+ if (mVRDisplays.Get(aDisplayID, getter_AddRefs(display))) {
+ return display;
+ }
+ return nullptr;
+}
+
+void
+VRManager::SubmitFrame(VRLayerParent* aLayer, layers::PTextureParent* aTexture,
+ const gfx::Rect& aLeftEyeRect,
+ const gfx::Rect& aRightEyeRect)
+{
+ TextureHost* th = TextureHost::AsTextureHost(aTexture);
+ mLastFrame = th;
+ RefPtr<VRDisplayHost> display = GetDisplay(aLayer->GetDisplayID());
+ if (display) {
+ display->SubmitFrame(aLayer, 0, aTexture, aLeftEyeRect, aRightEyeRect);
+ }
+}
+
+RefPtr<gfx::VRControllerHost>
+VRManager::GetController(const uint32_t& aControllerID)
+{
+ RefPtr<gfx::VRControllerHost> controller;
+ if (mVRControllers.Get(aControllerID, getter_AddRefs(controller))) {
+ return controller;
+ }
+ return nullptr;
+}
+
+void
+VRManager::GetVRControllerInfo(nsTArray<VRControllerInfo>& aControllerInfo)
+{
+ aControllerInfo.Clear();
+ for (auto iter = mVRControllers.Iter(); !iter.Done(); iter.Next()) {
+ gfx::VRControllerHost* controller = iter.UserData();
+ aControllerInfo.AppendElement(VRControllerInfo(controller->GetControllerInfo()));
+ }
+}
+
+void
+VRManager::RefreshVRControllers()
+{
+ nsTArray<RefPtr<gfx::VRControllerHost>> controllers;
+
+ for (uint32_t i = 0; i < mControllerManagers.Length()
+ && controllers.Length() == 0; ++i) {
+ mControllerManagers[i]->GetControllers(controllers);
+ }
+
+ bool controllerInfoChanged = false;
+
+ if (controllers.Length() != mVRControllers.Count()) {
+ // Catch cases where VR controllers has been removed
+ controllerInfoChanged = true;
+ }
+
+ for (const auto& controller : controllers) {
+ if (!GetController(controller->GetControllerInfo().GetControllerID())) {
+ // This is a new controller
+ controllerInfoChanged = true;
+ break;
+ }
+ }
+
+ if (controllerInfoChanged) {
+ mVRControllers.Clear();
+ for (const auto& controller : controllers) {
+ mVRControllers.Put(controller->GetControllerInfo().GetControllerID(),
+ controller);
+ }
+ }
+}
+
+void
+VRManager::ScanForDevices()
+{
+ for (uint32_t i = 0; i < mControllerManagers.Length(); ++i) {
+ mControllerManagers[i]->ScanForDevices();
+ }
+}
+
+template<class T>
+void
+VRManager::NotifyGamepadChange(const T& aInfo)
+{
+ dom::GamepadChangeEvent e(aInfo);
+
+ for (auto iter = mVRManagerParents.Iter(); !iter.Done(); iter.Next()) {
+ Unused << iter.Get()->GetKey()->SendGamepadUpdate(e);
+ }
+}
+
+} // namespace gfx
+} // namespace mozilla