summaryrefslogtreecommitdiffstats
path: root/gfx/vr/gfxVROSVR.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'gfx/vr/gfxVROSVR.cpp')
-rw-r--r--gfx/vr/gfxVROSVR.cpp529
1 files changed, 529 insertions, 0 deletions
diff --git a/gfx/vr/gfxVROSVR.cpp b/gfx/vr/gfxVROSVR.cpp
new file mode 100644
index 000000000..8b275e923
--- /dev/null
+++ b/gfx/vr/gfxVROSVR.cpp
@@ -0,0 +1,529 @@
+/* -*- 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 <math.h>
+
+#include "prlink.h"
+#include "prmem.h"
+#include "prenv.h"
+#include "gfxPrefs.h"
+#include "nsString.h"
+#include "mozilla/Preferences.h"
+
+#include "mozilla/gfx/Quaternion.h"
+
+#ifdef XP_WIN
+#include "../layers/d3d11/CompositorD3D11.h"
+#include "../layers/d3d11/TextureD3D11.h"
+#endif
+
+#include "gfxVROSVR.h"
+
+#ifndef M_PI
+#define M_PI 3.14159265358979323846
+#endif
+
+using namespace mozilla::layers;
+using namespace mozilla::gfx;
+using namespace mozilla::gfx::impl;
+
+namespace {
+// need to typedef functions that will be used in the code below
+extern "C" {
+typedef OSVR_ClientContext (*pfn_osvrClientInit)(
+ const char applicationIdentifier[], uint32_t flags);
+typedef OSVR_ReturnCode (*pfn_osvrClientShutdown)(OSVR_ClientContext ctx);
+typedef OSVR_ReturnCode (*pfn_osvrClientUpdate)(OSVR_ClientContext ctx);
+typedef OSVR_ReturnCode (*pfn_osvrClientCheckStatus)(OSVR_ClientContext ctx);
+typedef OSVR_ReturnCode (*pfn_osvrClientGetInterface)(
+ OSVR_ClientContext ctx, const char path[], OSVR_ClientInterface* iface);
+typedef OSVR_ReturnCode (*pfn_osvrClientFreeInterface)(
+ OSVR_ClientContext ctx, OSVR_ClientInterface iface);
+typedef OSVR_ReturnCode (*pfn_osvrGetOrientationState)(
+ OSVR_ClientInterface iface, OSVR_TimeValue* timestamp,
+ OSVR_OrientationState* state);
+typedef OSVR_ReturnCode (*pfn_osvrGetPositionState)(OSVR_ClientInterface iface,
+ OSVR_TimeValue* timestamp,
+ OSVR_PositionState* state);
+typedef OSVR_ReturnCode (*pfn_osvrClientGetDisplay)(OSVR_ClientContext ctx,
+ OSVR_DisplayConfig* disp);
+typedef OSVR_ReturnCode (*pfn_osvrClientFreeDisplay)(OSVR_DisplayConfig disp);
+typedef OSVR_ReturnCode (*pfn_osvrClientGetNumEyesForViewer)(
+ OSVR_DisplayConfig disp, OSVR_ViewerCount viewer, OSVR_EyeCount* eyes);
+typedef OSVR_ReturnCode (*pfn_osvrClientGetViewerEyePose)(
+ OSVR_DisplayConfig disp, OSVR_ViewerCount viewer, OSVR_EyeCount eye,
+ OSVR_Pose3* pose);
+typedef OSVR_ReturnCode (*pfn_osvrClientGetDisplayDimensions)(
+ OSVR_DisplayConfig disp, OSVR_DisplayInputCount displayInputIndex,
+ OSVR_DisplayDimension* width, OSVR_DisplayDimension* height);
+typedef OSVR_ReturnCode (
+ *pfn_osvrClientGetViewerEyeSurfaceProjectionClippingPlanes)(
+ OSVR_DisplayConfig disp, OSVR_ViewerCount viewer, OSVR_EyeCount eye,
+ OSVR_SurfaceCount surface, double* left, double* right, double* bottom,
+ double* top);
+typedef OSVR_ReturnCode (*pfn_osvrClientGetRelativeViewportForViewerEyeSurface)(
+ OSVR_DisplayConfig disp, OSVR_ViewerCount viewer, OSVR_EyeCount eye,
+ OSVR_SurfaceCount surface, OSVR_ViewportDimension* left,
+ OSVR_ViewportDimension* bottom, OSVR_ViewportDimension* width,
+ OSVR_ViewportDimension* height);
+typedef OSVR_ReturnCode (*pfn_osvrClientGetViewerEyeSurfaceProjectionMatrixf)(
+ OSVR_DisplayConfig disp, OSVR_ViewerCount viewer, OSVR_EyeCount eye,
+ OSVR_SurfaceCount surface, float near, float far,
+ OSVR_MatrixConventions flags, float* matrix);
+typedef OSVR_ReturnCode (*pfn_osvrClientCheckDisplayStartup)(
+ OSVR_DisplayConfig disp);
+typedef OSVR_ReturnCode (*pfn_osvrClientSetRoomRotationUsingHead)(
+ OSVR_ClientContext ctx);
+}
+
+static pfn_osvrClientInit osvr_ClientInit = nullptr;
+static pfn_osvrClientShutdown osvr_ClientShutdown = nullptr;
+static pfn_osvrClientUpdate osvr_ClientUpdate = nullptr;
+static pfn_osvrClientCheckStatus osvr_ClientCheckStatus = nullptr;
+static pfn_osvrClientGetInterface osvr_ClientGetInterface = nullptr;
+static pfn_osvrClientFreeInterface osvr_ClientFreeInterface = nullptr;
+static pfn_osvrGetOrientationState osvr_GetOrientationState = nullptr;
+static pfn_osvrGetPositionState osvr_GetPositionState = nullptr;
+static pfn_osvrClientGetDisplay osvr_ClientGetDisplay = nullptr;
+static pfn_osvrClientFreeDisplay osvr_ClientFreeDisplay = nullptr;
+static pfn_osvrClientGetNumEyesForViewer osvr_ClientGetNumEyesForViewer =
+ nullptr;
+static pfn_osvrClientGetViewerEyePose osvr_ClientGetViewerEyePose = nullptr;
+static pfn_osvrClientGetDisplayDimensions osvr_ClientGetDisplayDimensions =
+ nullptr;
+static pfn_osvrClientGetViewerEyeSurfaceProjectionClippingPlanes
+ osvr_ClientGetViewerEyeSurfaceProjectionClippingPlanes = nullptr;
+static pfn_osvrClientGetRelativeViewportForViewerEyeSurface
+ osvr_ClientGetRelativeViewportForViewerEyeSurface = nullptr;
+static pfn_osvrClientGetViewerEyeSurfaceProjectionMatrixf
+ osvr_ClientGetViewerEyeSurfaceProjectionMatrixf = nullptr;
+static pfn_osvrClientCheckDisplayStartup osvr_ClientCheckDisplayStartup =
+ nullptr;
+static pfn_osvrClientSetRoomRotationUsingHead
+ osvr_ClientSetRoomRotationUsingHead = nullptr;
+
+bool
+LoadOSVRRuntime()
+{
+ static PRLibrary* osvrUtilLib = nullptr;
+ static PRLibrary* osvrCommonLib = nullptr;
+ static PRLibrary* osvrClientLib = nullptr;
+ static PRLibrary* osvrClientKitLib = nullptr;
+ //this looks up the path in the about:config setting, from greprefs.js or modules\libpref\init\all.js
+ nsAdoptingCString osvrUtilPath =
+ mozilla::Preferences::GetCString("gfx.vr.osvr.utilLibPath");
+ nsAdoptingCString osvrCommonPath =
+ mozilla::Preferences::GetCString("gfx.vr.osvr.commonLibPath");
+ nsAdoptingCString osvrClientPath =
+ mozilla::Preferences::GetCString("gfx.vr.osvr.clientLibPath");
+ nsAdoptingCString osvrClientKitPath =
+ mozilla::Preferences::GetCString("gfx.vr.osvr.clientKitLibPath");
+
+ //we need all the libs to be valid
+ if ((!osvrUtilPath) || (!osvrCommonPath) || (!osvrClientPath) ||
+ (!osvrClientKitPath)) {
+ return false;
+ }
+
+ osvrUtilLib = PR_LoadLibrary(osvrUtilPath.BeginReading());
+ osvrCommonLib = PR_LoadLibrary(osvrCommonPath.BeginReading());
+ osvrClientLib = PR_LoadLibrary(osvrClientPath.BeginReading());
+ osvrClientKitLib = PR_LoadLibrary(osvrClientKitPath.BeginReading());
+
+ if (!osvrUtilLib) {
+ printf_stderr("[OSVR] Failed to load OSVR Util library!\n");
+ return false;
+ }
+ if (!osvrCommonLib) {
+ printf_stderr("[OSVR] Failed to load OSVR Common library!\n");
+ return false;
+ }
+ if (!osvrClientLib) {
+ printf_stderr("[OSVR] Failed to load OSVR Client library!\n");
+ return false;
+ }
+ if (!osvrClientKitLib) {
+ printf_stderr("[OSVR] Failed to load OSVR ClientKit library!\n");
+ return false;
+ }
+
+// make sure all functions that we'll be using are available
+#define REQUIRE_FUNCTION(_x) \
+ do { \
+ *(void**) & osvr_##_x = \
+ (void*)PR_FindSymbol(osvrClientKitLib, "osvr" #_x); \
+ if (!osvr_##_x) { \
+ printf_stderr("osvr" #_x " symbol missing\n"); \
+ goto fail; \
+ } \
+ } while (0)
+
+ REQUIRE_FUNCTION(ClientInit);
+ REQUIRE_FUNCTION(ClientShutdown);
+ REQUIRE_FUNCTION(ClientUpdate);
+ REQUIRE_FUNCTION(ClientCheckStatus);
+ REQUIRE_FUNCTION(ClientGetInterface);
+ REQUIRE_FUNCTION(ClientFreeInterface);
+ REQUIRE_FUNCTION(GetOrientationState);
+ REQUIRE_FUNCTION(GetPositionState);
+ REQUIRE_FUNCTION(ClientGetDisplay);
+ REQUIRE_FUNCTION(ClientFreeDisplay);
+ REQUIRE_FUNCTION(ClientGetNumEyesForViewer);
+ REQUIRE_FUNCTION(ClientGetViewerEyePose);
+ REQUIRE_FUNCTION(ClientGetDisplayDimensions);
+ REQUIRE_FUNCTION(ClientGetViewerEyeSurfaceProjectionClippingPlanes);
+ REQUIRE_FUNCTION(ClientGetRelativeViewportForViewerEyeSurface);
+ REQUIRE_FUNCTION(ClientGetViewerEyeSurfaceProjectionMatrixf);
+ REQUIRE_FUNCTION(ClientCheckDisplayStartup);
+ REQUIRE_FUNCTION(ClientSetRoomRotationUsingHead);
+
+#undef REQUIRE_FUNCTION
+
+ return true;
+
+fail:
+ return false;
+}
+
+} // namespace
+
+mozilla::gfx::VRFieldOfView
+SetFromTanRadians(double left, double right, double bottom, double top)
+{
+ mozilla::gfx::VRFieldOfView fovInfo;
+ fovInfo.leftDegrees = atan(left) * 180.0 / M_PI;
+ fovInfo.rightDegrees = atan(right) * 180.0 / M_PI;
+ fovInfo.upDegrees = atan(top) * 180.0 / M_PI;
+ fovInfo.downDegrees = atan(bottom) * 180.0 / M_PI;
+ return fovInfo;
+}
+
+VRDisplayOSVR::VRDisplayOSVR(OSVR_ClientContext* context,
+ OSVR_ClientInterface* iface,
+ OSVR_DisplayConfig* display)
+ : VRDisplayHost(VRDeviceType::OSVR)
+ , m_ctx(context)
+ , m_iface(iface)
+ , m_display(display)
+{
+
+ MOZ_COUNT_CTOR_INHERITED(VRDisplayOSVR, VRDisplayHost);
+
+ mDisplayInfo.mIsConnected = true;
+ mDisplayInfo.mDisplayName.AssignLiteral("OSVR HMD");
+ mDisplayInfo.mCapabilityFlags = VRDisplayCapabilityFlags::Cap_None;
+ mDisplayInfo.mCapabilityFlags =
+ VRDisplayCapabilityFlags::Cap_Orientation | VRDisplayCapabilityFlags::Cap_Position;
+
+ mDisplayInfo.mCapabilityFlags |= VRDisplayCapabilityFlags::Cap_External;
+ mDisplayInfo.mCapabilityFlags |= VRDisplayCapabilityFlags::Cap_Present;
+
+ // XXX OSVR display topology allows for more than one viewer
+ // will assume only one viewer for now (most likely stay that way)
+
+ OSVR_EyeCount numEyes;
+ osvr_ClientGetNumEyesForViewer(*m_display, 0, &numEyes);
+
+ for (uint8_t eye = 0; eye < numEyes; eye++) {
+ double left, right, bottom, top;
+ // XXX for now there is only one surface per eye
+ osvr_ClientGetViewerEyeSurfaceProjectionClippingPlanes(
+ *m_display, 0, eye, 0, &left, &right, &bottom, &top);
+ mDisplayInfo.mEyeFOV[eye] =
+ SetFromTanRadians(-left, right, -bottom, top);
+ }
+
+ // XXX Assuming there is only one display input for now
+ // however, it's possible to have more than one (dSight with 2 HDMI inputs)
+ OSVR_DisplayDimension width, height;
+ osvr_ClientGetDisplayDimensions(*m_display, 0, &width, &height);
+
+
+ for (uint8_t eye = 0; eye < numEyes; eye++) {
+
+ OSVR_ViewportDimension l, b, w, h;
+ osvr_ClientGetRelativeViewportForViewerEyeSurface(*m_display, 0, eye, 0, &l,
+ &b, &w, &h);
+ mDisplayInfo.mEyeResolution.width = w;
+ mDisplayInfo.mEyeResolution.height = h;
+ OSVR_Pose3 eyePose;
+ // Viewer eye pose may not be immediately available, update client context until we get it
+ OSVR_ReturnCode ret =
+ osvr_ClientGetViewerEyePose(*m_display, 0, eye, &eyePose);
+ while (ret != OSVR_RETURN_SUCCESS) {
+ osvr_ClientUpdate(*m_ctx);
+ ret = osvr_ClientGetViewerEyePose(*m_display, 0, eye, &eyePose);
+ }
+ mDisplayInfo.mEyeTranslation[eye].x = eyePose.translation.data[0];
+ mDisplayInfo.mEyeTranslation[eye].y = eyePose.translation.data[1];
+ mDisplayInfo.mEyeTranslation[eye].z = eyePose.translation.data[2];
+ }
+}
+
+void
+VRDisplayOSVR::Destroy()
+{
+ // destroy non-owning pointers
+ m_ctx = nullptr;
+ m_iface = nullptr;
+ m_display = nullptr;
+}
+
+void
+VRDisplayOSVR::ZeroSensor()
+{
+ // recenter pose aka reset yaw
+ osvr_ClientSetRoomRotationUsingHead(*m_ctx);
+}
+
+VRHMDSensorState
+VRDisplayOSVR::GetSensorState()
+{
+
+ //update client context before anything
+ //this usually goes into app's mainloop
+ osvr_ClientUpdate(*m_ctx);
+
+ VRHMDSensorState result;
+ OSVR_TimeValue timestamp;
+ result.Clear();
+
+ OSVR_OrientationState orientation;
+
+ OSVR_ReturnCode ret =
+ osvr_GetOrientationState(*m_iface, &timestamp, &orientation);
+
+ result.timestamp = timestamp.seconds;
+
+ if (ret == OSVR_RETURN_SUCCESS) {
+ result.flags |= VRDisplayCapabilityFlags::Cap_Orientation;
+ result.orientation[0] = orientation.data[1];
+ result.orientation[1] = orientation.data[2];
+ result.orientation[2] = orientation.data[3];
+ result.orientation[3] = orientation.data[0];
+ }
+
+ OSVR_PositionState position;
+ ret = osvr_GetPositionState(*m_iface, &timestamp, &position);
+ if (ret == OSVR_RETURN_SUCCESS) {
+ result.flags |= VRDisplayCapabilityFlags::Cap_Position;
+ result.position[0] = position.data[0];
+ result.position[1] = position.data[1];
+ result.position[2] = position.data[2];
+ }
+
+ return result;
+}
+
+VRHMDSensorState
+VRDisplayOSVR::GetImmediateSensorState()
+{
+ return GetSensorState();
+}
+
+#if defined(XP_WIN)
+
+void
+VRDisplayOSVR::SubmitFrame(TextureSourceD3D11* aSource,
+ const IntSize& aSize,
+ const VRHMDSensorState& aSensorState,
+ const gfx::Rect& aLeftEyeRect,
+ const gfx::Rect& aRightEyeRect)
+{
+ // XXX Add code to submit frame
+}
+
+#endif
+
+void
+VRDisplayOSVR::StartPresentation()
+{
+ // XXX Add code to start VR Presentation
+}
+
+void
+VRDisplayOSVR::StopPresentation()
+{
+ // XXX Add code to end VR Presentation
+}
+
+already_AddRefed<VRDisplayManagerOSVR>
+VRDisplayManagerOSVR::Create()
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ if (!gfxPrefs::VREnabled() || !gfxPrefs::VROSVREnabled()) {
+ return nullptr;
+ }
+ if (!LoadOSVRRuntime()) {
+ return nullptr;
+ }
+ RefPtr<VRDisplayManagerOSVR> manager = new VRDisplayManagerOSVR();
+ return manager.forget();
+}
+
+void
+VRDisplayManagerOSVR::CheckOSVRStatus()
+{
+ if (mOSVRInitialized) {
+ return;
+ }
+
+ // client context must be initialized first
+ InitializeClientContext();
+
+ // update client context
+ osvr_ClientUpdate(m_ctx);
+
+ // initialize interface and display if they are not initialized yet
+ InitializeInterface();
+ InitializeDisplay();
+
+ // OSVR is fully initialized now
+ if (mClientContextInitialized && mDisplayConfigInitialized &&
+ mInterfaceInitialized) {
+ mOSVRInitialized = true;
+ }
+}
+
+void
+VRDisplayManagerOSVR::InitializeClientContext()
+{
+ // already initialized
+ if (mClientContextInitialized) {
+ return;
+ }
+
+ // first time creating
+ if (!m_ctx) {
+ // get client context
+ m_ctx = osvr_ClientInit("com.osvr.webvr", 0);
+ // update context
+ osvr_ClientUpdate(m_ctx);
+ // verify we are connected
+ if (OSVR_RETURN_SUCCESS == osvr_ClientCheckStatus(m_ctx)) {
+ mClientContextInitialized = true;
+ }
+ }
+ // client context exists but not up and running yet
+ else {
+ // update context
+ osvr_ClientUpdate(m_ctx);
+ if (OSVR_RETURN_SUCCESS == osvr_ClientCheckStatus(m_ctx)) {
+ mClientContextInitialized = true;
+ }
+ }
+}
+
+void
+VRDisplayManagerOSVR::InitializeInterface()
+{
+ // already initialized
+ if (mInterfaceInitialized) {
+ return;
+ }
+ //Client context must be initialized before getting interface
+ if (mClientContextInitialized) {
+ // m_iface will remain nullptr if no interface is returned
+ if (OSVR_RETURN_SUCCESS ==
+ osvr_ClientGetInterface(m_ctx, "/me/head", &m_iface)) {
+ mInterfaceInitialized = true;
+ }
+ }
+}
+
+void
+VRDisplayManagerOSVR::InitializeDisplay()
+{
+ // display is fully configured
+ if (mDisplayConfigInitialized) {
+ return;
+ }
+
+ //Client context must be initialized before getting interface
+ if (mClientContextInitialized) {
+ // first time creating display object
+ if (m_display == nullptr) {
+
+ OSVR_ReturnCode ret = osvr_ClientGetDisplay(m_ctx, &m_display);
+
+ if (ret == OSVR_RETURN_SUCCESS) {
+ osvr_ClientUpdate(m_ctx);
+ // display object may have been created but not fully startup
+ if (OSVR_RETURN_SUCCESS == osvr_ClientCheckDisplayStartup(m_display)) {
+ mDisplayConfigInitialized = true;
+ }
+ }
+
+ // Typically once we get Display object, pose data is available after
+ // clientUpdate but sometimes it takes ~ 200 ms to get
+ // a succesfull connection, so we might have to run a few update cycles
+ } else {
+
+ if (OSVR_RETURN_SUCCESS == osvr_ClientCheckDisplayStartup(m_display)) {
+ mDisplayConfigInitialized = true;
+ }
+ }
+ }
+}
+
+bool
+VRDisplayManagerOSVR::Init()
+{
+
+ // OSVR server should be running in the background
+ // It would load plugins and take care of detecting HMDs
+ if (!mOSVRInitialized) {
+ nsIThread* thread = nullptr;
+ NS_GetCurrentThread(&thread);
+ mOSVRThread = already_AddRefed<nsIThread>(thread);
+
+ // initialize client context
+ InitializeClientContext();
+ // try to initialize interface
+ InitializeInterface();
+ // try to initialize display object
+ InitializeDisplay();
+ // verify all components are initialized
+ CheckOSVRStatus();
+ }
+
+ return mOSVRInitialized;
+}
+
+void
+VRDisplayManagerOSVR::Destroy()
+{
+ if (mOSVRInitialized) {
+ MOZ_ASSERT(NS_GetCurrentThread() == mOSVRThread);
+ mOSVRThread = nullptr;
+ mHMDInfo = nullptr;
+ mOSVRInitialized = false;
+ }
+ // client context may not have been initialized
+ if (m_ctx) {
+ osvr_ClientFreeDisplay(m_display);
+ }
+ // osvr checks that m_ctx or m_iface are not null
+ osvr_ClientFreeInterface(m_ctx, m_iface);
+ osvr_ClientShutdown(m_ctx);
+}
+
+void
+VRDisplayManagerOSVR::GetHMDs(nsTArray<RefPtr<VRDisplayHost>>& aHMDResult)
+{
+ // make sure context, interface and display are initialized
+ CheckOSVRStatus();
+
+ if (!mOSVRInitialized) {
+ return;
+ }
+
+ mHMDInfo = new VRDisplayOSVR(&m_ctx, &m_iface, &m_display);
+
+ if (mHMDInfo) {
+ aHMDResult.AppendElement(mHMDInfo);
+ }
+}