--- a/gfx/thebes/gfxPrefs.h
+++ b/gfx/thebes/gfxPrefs.h
@@ -385,16 +385,17 @@ private:
DECL_GFX_PREF(Live, "dom.vr.controller.enumerate.interval", VRControllerEnumerateInterval, int32_t, 1000);
DECL_GFX_PREF(Live, "dom.vr.display.enumerate.interval", VRDisplayEnumerateInterval, int32_t, 5000);
DECL_GFX_PREF(Live, "dom.vr.inactive.timeout", VRInactiveTimeout, int32_t, 5000);
DECL_GFX_PREF(Live, "dom.vr.poseprediction.enabled", VRPosePredictionEnabled, bool, true);
DECL_GFX_PREF(Live, "dom.vr.require-gesture", VRRequireGesture, bool, true);
DECL_GFX_PREF(Live, "dom.vr.puppet.enabled", VRPuppetEnabled, bool, false);
DECL_GFX_PREF(Live, "dom.vr.puppet.submitframe", VRPuppetSubmitFrame, uint32_t, 0);
DECL_GFX_PREF(Live, "dom.vr.display.rafMaxDuration", VRDisplayRafMaxDuration, uint32_t, 50);
+ DECL_GFX_PREF(Once, "dom.vr.service.enabled", VRServiceEnabled, bool, false);
DECL_GFX_PREF(Live, "dom.w3c_pointer_events.enabled", PointerEventsEnabled, bool, false);
DECL_GFX_PREF(Live, "general.smoothScroll", SmoothScrollEnabled, bool, true);
DECL_GFX_PREF(Live, "general.smoothScroll.currentVelocityWeighting",
SmoothScrollCurrentVelocityWeighting, float, 0.25);
DECL_GFX_PREF(Live, "general.smoothScroll.durationToIntervalRatio",
SmoothScrollDurationToIntervalRatio, int32_t, 200);
DECL_GFX_PREF(Live, "general.smoothScroll.lines.durationMaxMS",
--- a/gfx/vr/VRManager.cpp
+++ b/gfx/vr/VRManager.cpp
@@ -24,16 +24,19 @@
#endif
#if defined(XP_WIN) || defined(XP_MACOSX) || (defined(XP_LINUX) && !defined(MOZ_WIDGET_ANDROID))
#include "gfxVROpenVR.h"
#include "gfxVROSVR.h"
#endif
#include "gfxVRPuppet.h"
#include "ipc/VRLayerParent.h"
+#if !defined(MOZ_WIDGET_ANDROID)
+#include "service/VRService.h"
+#endif
using namespace mozilla;
using namespace mozilla::gfx;
using namespace mozilla::layers;
using namespace mozilla::gl;
namespace mozilla {
namespace gfx {
@@ -69,41 +72,60 @@ VRManager::VRManager()
* 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.
*/
- mExternalManager = VRSystemManagerExternal::Create();
+
+#if !defined(MOZ_WIDGET_ANDROID)
+ // The VR Service accesses all hardware from a separate process
+ // and replaces the other VRSystemManager when enabled.
+ mVRService = VRService::Create();
+ if (mVRService) {
+ mExternalManager = VRSystemManagerExternal::Create(mVRService->GetAPIShmem());
+ }
if (mExternalManager) {
+ mManagers.AppendElement(mExternalManager);
+ }
+#endif
+
+ if (!mExternalManager) {
+ mExternalManager = VRSystemManagerExternal::Create();
+ if (mExternalManager) {
mManagers.AppendElement(mExternalManager);
+ }
}
#if defined(XP_WIN)
- // The Oculus runtime is supported only on Windows
- mgr = VRSystemManagerOculus::Create();
- if (mgr) {
- mManagers.AppendElement(mgr);
+ if (!mVRService) {
+ // The Oculus runtime is supported only on Windows
+ mgr = VRSystemManagerOculus::Create();
+ if (mgr) {
+ mManagers.AppendElement(mgr);
+ }
}
#endif
#if defined(XP_WIN) || defined(XP_MACOSX) || (defined(XP_LINUX) && !defined(MOZ_WIDGET_ANDROID))
- // OpenVR is cross platform compatible
- mgr = VRSystemManagerOpenVR::Create();
- if (mgr) {
- mManagers.AppendElement(mgr);
- }
+ if (!mVRService) {
+ // OpenVR is cross platform compatible
+ mgr = VRSystemManagerOpenVR::Create();
+ if (mgr) {
+ mManagers.AppendElement(mgr);
+ }
- // OSVR is cross platform compatible
- mgr = VRSystemManagerOSVR::Create();
- if (mgr) {
- mManagers.AppendElement(mgr);
- }
+ // OSVR is cross platform compatible
+ mgr = VRSystemManagerOSVR::Create();
+ if (mgr) {
+ mManagers.AppendElement(mgr);
+ }
+ } // !mVRService
#endif
// Enable gamepad extensions while VR is enabled.
// Preference only can be set at the Parent process.
if (XRE_IsParentProcess() && gfxPrefs::VREnabled()) {
Preferences::SetBool("dom.gamepad.extensions.enabled", true);
}
}
@@ -130,16 +152,21 @@ VRManager::Destroy()
void
VRManager::Shutdown()
{
mVRDisplays.Clear();
mVRControllers.Clear();
for (uint32_t i = 0; i < mManagers.Length(); ++i) {
mManagers[i]->Shutdown();
}
+#if !defined(MOZ_WIDGET_ANDROID)
+ if (mVRService) {
+ mVRService->Stop();
+ }
+#endif
}
void
VRManager::Init()
{
mInitialized = true;
}
@@ -316,16 +343,21 @@ void
VRManager::RefreshVRDisplays(bool aMustDispatch)
{
/**
* If we aren't viewing WebVR content, don't enumerate
* new hardware, as it will cause some devices to power on
* or interrupt other VR activities.
*/
if (mVRDisplaysRequested || aMustDispatch) {
+#if !defined(MOZ_WIDGET_ANDROID)
+ if (mVRService) {
+ mVRService->Start();
+ }
+#endif
EnumerateVRDisplays();
}
/**
* VRSystemManager::GetHMDs will not activate new hardware
* or result in interruption of other VR activities.
* We can call it even when suppressing enumeration to get
* the already-enumerated displays.
--- a/gfx/vr/VRManager.h
+++ b/gfx/vr/VRManager.h
@@ -18,16 +18,19 @@ namespace mozilla {
namespace layers {
class TextureHost;
}
namespace gfx {
class VRLayerParent;
class VRManagerParent;
class VRDisplayHost;
+#if !defined(MOZ_WIDGET_ANDROID)
+class VRService;
+#endif
class VRSystemManagerPuppet;
class VRSystemManagerExternal;
class VRManager
{
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(mozilla::gfx::VRManager)
public:
@@ -87,16 +90,19 @@ private:
Atomic<bool> mInitialized;
TimeStamp mLastControllerEnumerationTime;
TimeStamp mLastDisplayEnumerationTime;
TimeStamp mLastActiveTime;
RefPtr<VRSystemManagerPuppet> mPuppetManager;
RefPtr<VRSystemManagerExternal> mExternalManager;
+#if !defined(MOZ_WIDGET_ANDROID)
+ RefPtr<VRService> mVRService;
+#endif
bool mVRDisplaysRequested;
bool mVRControllersRequested;
};
} // namespace gfx
} // namespace mozilla
#endif // GFX_VR_MANAGER_H
--- a/gfx/vr/gfxVRExternal.cpp
+++ b/gfx/vr/gfxVRExternal.cpp
@@ -86,27 +86,22 @@ VRDisplayExternal::ZeroSensor()
}
void
VRDisplayExternal::Refresh()
{
VRManager *vm = VRManager::Get();
VRSystemManagerExternal* manager = vm->GetExternalManager();
- manager->PullState(&mDisplayInfo.mDisplayState);
+ manager->PullState(&mDisplayInfo.mDisplayState, &mLastSensorState);
}
VRHMDSensorState
VRDisplayExternal::GetSensorState()
{
- VRManager *vm = VRManager::Get();
- VRSystemManagerExternal* manager = vm->GetExternalManager();
-
- manager->PullState(&mDisplayInfo.mDisplayState, &mLastSensorState);
-
return mLastSensorState;
}
void
VRDisplayExternal::StartPresentation()
{
if (mIsPresenting) {
return;
@@ -221,17 +216,17 @@ VRDisplayExternal::SubmitFrame(const lay
VRManager *vm = VRManager::Get();
VRSystemManagerExternal* manager = vm->GetExternalManager();
manager->PushState(&state);
VRDisplayState displayState;
memset(&displayState, 0, sizeof(VRDisplayState));
while (displayState.mLastSubmittedFrameId < aFrameId) {
- if (manager->PullState(&displayState)) {
+ if (manager->PullState(&displayState, &mLastSensorState)) {
if (!displayState.mIsConnected) {
// Service has shut down or hardware has been disconnected
return false;
}
}
#ifdef XP_WIN
Sleep(0);
#else
--- a/gfx/vr/moz.build
+++ b/gfx/vr/moz.build
@@ -49,16 +49,17 @@ SOURCES += [
'VRDisplayHost.cpp',
'VRDisplayLocal.cpp',
]
# Build OpenVR on Windows, Linux, and macOS desktop targets
if CONFIG['OS_TARGET'] in ('WINNT', 'Linux', 'Darwin'):
DIRS += [
'openvr',
+ 'service',
]
SOURCES += [
'gfxVROpenVR.cpp',
]
if CONFIG['OS_TARGET'] == 'WINNT':
SOURCES += [
'gfxVROculus.cpp',
new file mode 100644
--- /dev/null
+++ b/gfx/vr/service/OpenVRSession.cpp
@@ -0,0 +1,487 @@
+#include "OpenVRSession.h"
+
+#if defined(XP_WIN)
+#include <d3d11.h>
+#include "mozilla/gfx/DeviceManagerDx.h"
+#endif // defined(XP_WIN)
+
+#if defined(MOZILLA_INTERNAL_API)
+#include "mozilla/dom/GamepadEventTypes.h"
+#include "mozilla/dom/GamepadBinding.h"
+#endif
+
+#if !defined(M_PI)
+#define M_PI 3.14159265358979323846264338327950288
+#endif
+
+#define BTN_MASK_FROM_ID(_id) \
+ ::vr::ButtonMaskFromId(vr::EVRButtonId::_id)
+
+using namespace mozilla::gfx;
+
+namespace mozilla {
+namespace gfx {
+
+OpenVRSession::OpenVRSession()
+ : VRSession()
+ , mVRSystem(nullptr)
+ , mVRChaperone(nullptr)
+ , mVRCompositor(nullptr)
+ , mShouldQuit(false)
+{
+}
+
+OpenVRSession::~OpenVRSession()
+{
+ Shutdown();
+}
+
+bool
+OpenVRSession::Initialize(mozilla::gfx::VRSystemState& aSystemState)
+{
+ if (mVRSystem != nullptr) {
+ // Already initialized
+ return true;
+ }
+ if (!::vr::VR_IsHmdPresent()) {
+ fprintf(stderr, "No HMD detected, VR_IsHmdPresent returned false.\n");
+ return false;
+ }
+
+ ::vr::HmdError err;
+
+ ::vr::VR_Init(&err, ::vr::EVRApplicationType::VRApplication_Scene);
+ if (err) {
+ return false;
+ }
+
+ mVRSystem = (::vr::IVRSystem *)::vr::VR_GetGenericInterface(::vr::IVRSystem_Version, &err);
+ if (err || !mVRSystem) {
+ Shutdown();
+ return false;
+ }
+ mVRChaperone = (::vr::IVRChaperone *)::vr::VR_GetGenericInterface(::vr::IVRChaperone_Version, &err);
+ if (err || !mVRChaperone) {
+ Shutdown();
+ return false;
+ }
+ mVRCompositor = (::vr::IVRCompositor*)::vr::VR_GetGenericInterface(::vr::IVRCompositor_Version, &err);
+ if (err || !mVRCompositor) {
+ Shutdown();
+ return false;
+ }
+
+#if defined(XP_WIN)
+ if (!CreateD3DObjects()) {
+ Shutdown();
+ return false;
+ }
+
+#endif
+
+ // Configure coordinate system
+ mVRCompositor->SetTrackingSpace(::vr::TrackingUniverseSeated);
+
+ if (!InitState(aSystemState)) {
+ Shutdown();
+ return false;
+ }
+
+ // Succeeded
+ return true;
+}
+
+#if defined(XP_WIN)
+bool
+OpenVRSession::CreateD3DObjects()
+{
+ RefPtr<ID3D11Device> device = gfx::DeviceManagerDx::Get()->GetVRDevice();
+ if (!device) {
+ return false;
+ }
+ if (!CreateD3DContext(device)) {
+ return false;
+ }
+ return true;
+}
+#endif
+
+void
+OpenVRSession::Shutdown()
+{
+ if (mVRSystem || mVRCompositor || mVRSystem) {
+ ::vr::VR_Shutdown();
+ mVRCompositor = nullptr;
+ mVRChaperone = nullptr;
+ mVRSystem = nullptr;
+ }
+}
+
+bool
+OpenVRSession::InitState(VRSystemState& aSystemState)
+{
+ VRDisplayState& state = aSystemState.displayState;
+ strncpy(state.mDisplayName, "OpenVR HMD", kVRDisplayNameMaxLen);
+ state.mIsConnected = mVRSystem->IsTrackedDeviceConnected(::vr::k_unTrackedDeviceIndex_Hmd);
+ state.mIsMounted = false;
+ state.mCapabilityFlags = (VRDisplayCapabilityFlags)((int)VRDisplayCapabilityFlags::Cap_None |
+ (int)VRDisplayCapabilityFlags::Cap_Orientation |
+ (int)VRDisplayCapabilityFlags::Cap_Position |
+ (int)VRDisplayCapabilityFlags::Cap_External |
+ (int)VRDisplayCapabilityFlags::Cap_Present |
+ (int)VRDisplayCapabilityFlags::Cap_StageParameters);
+
+ ::vr::ETrackedPropertyError err;
+ bool bHasProximitySensor = mVRSystem->GetBoolTrackedDeviceProperty(::vr::k_unTrackedDeviceIndex_Hmd, ::vr::Prop_ContainsProximitySensor_Bool, &err);
+ if (err == ::vr::TrackedProp_Success && bHasProximitySensor) {
+ state.mCapabilityFlags = (VRDisplayCapabilityFlags)((int)state.mCapabilityFlags | (int)VRDisplayCapabilityFlags::Cap_MountDetection);
+ }
+
+ uint32_t w, h;
+ mVRSystem->GetRecommendedRenderTargetSize(&w, &h);
+ state.mEyeResolution.width = w;
+ state.mEyeResolution.height = h;
+
+ // default to an identity quaternion
+ aSystemState.sensorState.orientation[3] = 1.0f;
+
+ UpdateStageParameters(state);
+ UpdateEyeParameters(state);
+
+ VRHMDSensorState& sensorState = aSystemState.sensorState;
+ sensorState.flags = (VRDisplayCapabilityFlags)(
+ (int)VRDisplayCapabilityFlags::Cap_Orientation |
+ (int)VRDisplayCapabilityFlags::Cap_Position);
+ sensorState.orientation[3] = 1.0f; // Default to an identity quaternion
+
+ return true;
+}
+
+void
+OpenVRSession::UpdateStageParameters(VRDisplayState& state)
+{
+ float sizeX = 0.0f;
+ float sizeZ = 0.0f;
+ if (mVRChaperone->GetPlayAreaSize(&sizeX, &sizeZ)) {
+ ::vr::HmdMatrix34_t t = mVRSystem->GetSeatedZeroPoseToStandingAbsoluteTrackingPose();
+ state.mStageSize.width = sizeX;
+ state.mStageSize.height = sizeZ;
+
+ state.mSittingToStandingTransform[0] = t.m[0][0];
+ state.mSittingToStandingTransform[1] = t.m[1][0];
+ state.mSittingToStandingTransform[2] = t.m[2][0];
+ state.mSittingToStandingTransform[3] = 0.0f;
+
+ state.mSittingToStandingTransform[4] = t.m[0][1];
+ state.mSittingToStandingTransform[5] = t.m[1][1];
+ state.mSittingToStandingTransform[6] = t.m[2][1];
+ state.mSittingToStandingTransform[7] = 0.0f;
+
+ state.mSittingToStandingTransform[8] = t.m[0][2];
+ state.mSittingToStandingTransform[9] = t.m[1][2];
+ state.mSittingToStandingTransform[10] = t.m[2][2];
+ state.mSittingToStandingTransform[11] = 0.0f;
+
+ state.mSittingToStandingTransform[12] = t.m[0][3];
+ state.mSittingToStandingTransform[13] = t.m[1][3];
+ state.mSittingToStandingTransform[14] = t.m[2][3];
+ state.mSittingToStandingTransform[15] = 1.0f;
+ } else {
+ // If we fail, fall back to reasonable defaults.
+ // 1m x 1m space, 0.75m high in seated position
+
+ state.mStageSize.width = 1.0f;
+ state.mStageSize.height = 1.0f;
+
+ state.mSittingToStandingTransform[0] = 1.0f;
+ state.mSittingToStandingTransform[1] = 0.0f;
+ state.mSittingToStandingTransform[2] = 0.0f;
+ state.mSittingToStandingTransform[3] = 0.0f;
+
+ state.mSittingToStandingTransform[4] = 0.0f;
+ state.mSittingToStandingTransform[5] = 1.0f;
+ state.mSittingToStandingTransform[6] = 0.0f;
+ state.mSittingToStandingTransform[7] = 0.0f;
+
+ state.mSittingToStandingTransform[8] = 0.0f;
+ state.mSittingToStandingTransform[9] = 0.0f;
+ state.mSittingToStandingTransform[10] = 1.0f;
+ state.mSittingToStandingTransform[11] = 0.0f;
+
+ state.mSittingToStandingTransform[12] = 0.0f;
+ state.mSittingToStandingTransform[13] = 0.75f;
+ state.mSittingToStandingTransform[14] = 0.0f;
+ state.mSittingToStandingTransform[15] = 1.0f;
+ }
+}
+
+void
+OpenVRSession::UpdateEyeParameters(VRDisplayState& state, gfx::Matrix4x4* headToEyeTransforms /* = nullptr */)
+{
+ for (uint32_t eye = 0; eye < 2; ++eye) {
+ ::vr::HmdMatrix34_t eyeToHead = mVRSystem->GetEyeToHeadTransform(static_cast<::vr::Hmd_Eye>(eye));
+ state.mEyeTranslation[eye].x = eyeToHead.m[0][3];
+ state.mEyeTranslation[eye].y = eyeToHead.m[1][3];
+ state.mEyeTranslation[eye].z = eyeToHead.m[2][3];
+
+ float left, right, up, down;
+ mVRSystem->GetProjectionRaw(static_cast<::vr::Hmd_Eye>(eye), &left, &right, &up, &down);
+ state.mEyeFOV[eye].upDegrees = atan(-up) * 180.0 / M_PI;
+ state.mEyeFOV[eye].rightDegrees = atan(right) * 180.0 / M_PI;
+ state.mEyeFOV[eye].downDegrees = atan(down) * 180.0 / M_PI;
+ state.mEyeFOV[eye].leftDegrees = atan(-left) * 180.0 / M_PI;
+
+ if (headToEyeTransforms) {
+ Matrix4x4 pose;
+ // NOTE! eyeToHead.m is a 3x4 matrix, not 4x4. But
+ // because of its arrangement, we can copy the 12 elements in and
+ // then transpose them to the right place.
+ memcpy(&pose._11, &eyeToHead.m, sizeof(eyeToHead.m));
+ pose.Transpose();
+ pose.Invert();
+ headToEyeTransforms[eye] = pose;
+ }
+ }
+}
+
+void
+OpenVRSession::GetSensorState(VRSystemState& state)
+{
+ const uint32_t posesSize = ::vr::k_unTrackedDeviceIndex_Hmd + 1;
+ ::vr::TrackedDevicePose_t poses[posesSize];
+ // Note: We *must* call WaitGetPoses in order for any rendering to happen at all.
+ mVRCompositor->WaitGetPoses(nullptr, 0, poses, posesSize);
+ gfx::Matrix4x4 headToEyeTransforms[2];
+ UpdateEyeParameters(state.displayState, headToEyeTransforms);
+
+ ::vr::Compositor_FrameTiming timing;
+ timing.m_nSize = sizeof(::vr::Compositor_FrameTiming);
+ if (mVRCompositor->GetFrameTiming(&timing)) {
+ state.sensorState.timestamp = timing.m_flSystemTimeInSeconds;
+ } else {
+ // This should not happen, but log it just in case
+ fprintf(stderr, "OpenVR - IVRCompositor::GetFrameTiming failed");
+ }
+
+ if (poses[::vr::k_unTrackedDeviceIndex_Hmd].bDeviceIsConnected &&
+ poses[::vr::k_unTrackedDeviceIndex_Hmd].bPoseIsValid &&
+ poses[::vr::k_unTrackedDeviceIndex_Hmd].eTrackingResult == ::vr::TrackingResult_Running_OK)
+ {
+ const ::vr::TrackedDevicePose_t& pose = poses[::vr::k_unTrackedDeviceIndex_Hmd];
+
+ gfx::Matrix4x4 m;
+ // NOTE! mDeviceToAbsoluteTracking is a 3x4 matrix, not 4x4. But
+ // because of its arrangement, we can copy the 12 elements in and
+ // then transpose them to the right place. We do this so we can
+ // pull out a Quaternion.
+ memcpy(&m._11, &pose.mDeviceToAbsoluteTracking, sizeof(pose.mDeviceToAbsoluteTracking));
+ m.Transpose();
+
+ gfx::Quaternion rot;
+ rot.SetFromRotationMatrix(m);
+ rot.Invert();
+
+ state.sensorState.flags = (VRDisplayCapabilityFlags)((int)state.sensorState.flags | (int)VRDisplayCapabilityFlags::Cap_Orientation);
+ state.sensorState.orientation[0] = rot.x;
+ state.sensorState.orientation[1] = rot.y;
+ state.sensorState.orientation[2] = rot.z;
+ state.sensorState.orientation[3] = rot.w;
+ state.sensorState.angularVelocity[0] = pose.vAngularVelocity.v[0];
+ state.sensorState.angularVelocity[1] = pose.vAngularVelocity.v[1];
+ state.sensorState.angularVelocity[2] = pose.vAngularVelocity.v[2];
+
+ state.sensorState.flags =(VRDisplayCapabilityFlags)((int)state.sensorState.flags | (int)VRDisplayCapabilityFlags::Cap_Position);
+ state.sensorState.position[0] = m._41;
+ state.sensorState.position[1] = m._42;
+ state.sensorState.position[2] = m._43;
+ state.sensorState.linearVelocity[0] = pose.vVelocity.v[0];
+ state.sensorState.linearVelocity[1] = pose.vVelocity.v[1];
+ state.sensorState.linearVelocity[2] = pose.vVelocity.v[2];
+ }
+
+ state.sensorState.CalcViewMatrices(headToEyeTransforms);
+ state.sensorState.inputFrameID++;
+}
+
+void
+OpenVRSession::GetControllerState(VRSystemState &state)
+{
+ // TODO - Implement
+}
+
+void
+OpenVRSession::StartFrame(mozilla::gfx::VRSystemState& aSystemState)
+{
+ GetSensorState(aSystemState);
+ GetControllerState(aSystemState);
+}
+
+bool
+OpenVRSession::ShouldQuit() const
+{
+ return mShouldQuit;
+}
+
+void
+OpenVRSession::ProcessEvents(mozilla::gfx::VRSystemState& aSystemState)
+{
+ bool isHmdPresent = ::vr::VR_IsHmdPresent();
+ if (!isHmdPresent) {
+ mShouldQuit = true;
+ }
+
+ ::vr::VREvent_t event;
+ while (mVRSystem && mVRSystem->PollNextEvent(&event, sizeof(event))) {
+ switch (event.eventType) {
+ case ::vr::VREvent_TrackedDeviceUserInteractionStarted:
+ if (event.trackedDeviceIndex == ::vr::k_unTrackedDeviceIndex_Hmd) {
+ aSystemState.displayState.mIsMounted = true;
+ }
+ break;
+ case ::vr::VREvent_TrackedDeviceUserInteractionEnded:
+ if (event.trackedDeviceIndex == ::vr::k_unTrackedDeviceIndex_Hmd) {
+ aSystemState.displayState.mIsMounted = false;
+ }
+ break;
+ case ::vr::EVREventType::VREvent_TrackedDeviceActivated:
+ if (event.trackedDeviceIndex == ::vr::k_unTrackedDeviceIndex_Hmd) {
+ aSystemState.displayState.mIsConnected = true;
+ }
+ break;
+ case ::vr::EVREventType::VREvent_TrackedDeviceDeactivated:
+ if (event.trackedDeviceIndex == ::vr::k_unTrackedDeviceIndex_Hmd) {
+ aSystemState.displayState.mIsConnected = false;
+ }
+ break;
+ case ::vr::EVREventType::VREvent_DriverRequestedQuit:
+ case ::vr::EVREventType::VREvent_Quit:
+ case ::vr::EVREventType::VREvent_ProcessQuit:
+ case ::vr::EVREventType::VREvent_QuitAcknowledged:
+ case ::vr::EVREventType::VREvent_QuitAborted_UserPrompt:
+ mShouldQuit = true;
+ break;
+ default:
+ // ignore
+ break;
+ }
+ }
+}
+
+bool
+OpenVRSession::SubmitFrame(const mozilla::gfx::VRLayer_Stereo_Immersive& aLayer)
+{
+#if defined(XP_WIN)
+
+ if (aLayer.mTextureType == VRLayerTextureType::LayerTextureType_D3D10SurfaceDescriptor) {
+ RefPtr<ID3D11Texture2D> dxTexture;
+ HRESULT hr = mDevice->OpenSharedResource((HANDLE)aLayer.mTextureHandle,
+ __uuidof(ID3D11Texture2D),
+ (void**)(ID3D11Texture2D**)getter_AddRefs(dxTexture));
+ if (FAILED(hr) || !dxTexture) {
+ NS_WARNING("Failed to open shared texture");
+ return false;
+ }
+
+ // Similar to LockD3DTexture in TextureD3D11.cpp
+ RefPtr<IDXGIKeyedMutex> mutex;
+ dxTexture->QueryInterface((IDXGIKeyedMutex**)getter_AddRefs(mutex));
+ if (mutex) {
+ HRESULT hr = mutex->AcquireSync(0, 1000);
+ if (hr == WAIT_TIMEOUT) {
+ gfxDevCrash(LogReason::D3DLockTimeout) << "D3D lock mutex timeout";
+ }
+ else if (hr == WAIT_ABANDONED) {
+ gfxCriticalNote << "GFX: D3D11 lock mutex abandoned";
+ }
+ if (FAILED(hr)) {
+ NS_WARNING("Failed to lock the texture");
+ return false;
+ }
+ }
+ bool success = SubmitFrame((void *)dxTexture,
+ ::vr::ETextureType::TextureType_DirectX,
+ aLayer.mLeftEyeRect, aLayer.mRightEyeRect);
+ if (mutex) {
+ HRESULT hr = mutex->ReleaseSync(0);
+ if (FAILED(hr)) {
+ NS_WARNING("Failed to unlock the texture");
+ }
+ }
+ if (!success) {
+ return false;
+ }
+ return true;
+ }
+
+#elif defined(XP_MACOSX)
+
+ if (aLayer.mTextureType == VRLayerTextureType::LayerTextureType_MacIOSurface) {
+ return SubmitFrame(aLayer.mTextureHandle,
+ ::vr::ETextureType::TextureType_IOSurface,
+ aLayer.mLeftEyeRect, aLayer.mRightEyeRect);
+ }
+
+#endif
+
+ return false;
+}
+
+bool
+OpenVRSession::SubmitFrame(void* aTextureHandle,
+ ::vr::ETextureType aTextureType,
+ const VRLayerEyeRect& aLeftEyeRect,
+ const VRLayerEyeRect& aRightEyeRect)
+{
+ ::vr::Texture_t tex;
+ tex.handle = aTextureHandle;
+ tex.eType = aTextureType;
+ tex.eColorSpace = ::vr::EColorSpace::ColorSpace_Auto;
+
+ ::vr::VRTextureBounds_t bounds;
+ bounds.uMin = aLeftEyeRect.x;
+ bounds.vMin = 1.0 - aLeftEyeRect.y;
+ bounds.uMax = aLeftEyeRect.x + aLeftEyeRect.width;
+ bounds.vMax = 1.0 - (aLeftEyeRect.y + aLeftEyeRect.height);
+
+ ::vr::EVRCompositorError err;
+ err = mVRCompositor->Submit(::vr::EVREye::Eye_Left, &tex, &bounds);
+ if (err != ::vr::EVRCompositorError::VRCompositorError_None) {
+ printf_stderr("OpenVR Compositor Submit() failed.\n");
+ }
+
+ bounds.uMin = aRightEyeRect.x;
+ bounds.vMin = 1.0 - aRightEyeRect.y;
+ bounds.uMax = aRightEyeRect.x + aRightEyeRect.width;
+ bounds.vMax = 1.0 - (aRightEyeRect.y + aRightEyeRect.height);
+
+ err = mVRCompositor->Submit(::vr::EVREye::Eye_Right, &tex, &bounds);
+ if (err != ::vr::EVRCompositorError::VRCompositorError_None) {
+ printf_stderr("OpenVR Compositor Submit() failed.\n");
+ }
+
+ mVRCompositor->PostPresentHandoff();
+ return true;
+}
+
+void
+OpenVRSession::StopPresentation()
+{
+ mVRCompositor->ClearLastSubmittedFrame();
+
+ ::vr::Compositor_CumulativeStats stats;
+ mVRCompositor->GetCumulativeStats(&stats, sizeof(::vr::Compositor_CumulativeStats));
+ // TODO - Need to send telemetry back to browser.
+ // Bug 1473398 will refactor this original gfxVROpenVR code:
+ // const uint32_t droppedFramesPerSec = (stats.m_nNumReprojectedFrames -
+ // mTelemetry.mLastDroppedFrameCount) / duration.ToSeconds();
+ // Telemetry::Accumulate(Telemetry::WEBVR_DROPPED_FRAMES_IN_OPENVR, droppedFramesPerSec);
+}
+
+bool
+OpenVRSession::StartPresentation()
+{
+ return true;
+}
+
+} // namespace mozilla
+} // namespace gfx
new file mode 100644
--- /dev/null
+++ b/gfx/vr/service/OpenVRSession.h
@@ -0,0 +1,65 @@
+/* -*- 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/. */
+
+#ifndef GFX_VR_SERVICE_OPENVRSESSION_H
+#define GFX_VR_SERVICE_OPENVRSESSION_H
+
+#include "VRSession.h"
+
+#include "openvr.h"
+#include "mozilla/gfx/2D.h"
+#include "moz_external_vr.h"
+
+#if defined(XP_WIN)
+#include <d3d11_1.h>
+#elif defined(XP_MACOSX)
+class MacIOSurface;
+#endif
+
+namespace mozilla {
+namespace gfx {
+
+class OpenVRSession : public VRSession
+{
+public:
+ OpenVRSession();
+ virtual ~OpenVRSession();
+
+ bool Initialize(mozilla::gfx::VRSystemState& aSystemState) override;
+ void Shutdown() override;
+ void ProcessEvents(mozilla::gfx::VRSystemState& aSystemState) override;
+ void StartFrame(mozilla::gfx::VRSystemState& aSystemState) override;
+ bool ShouldQuit() const override;
+ bool StartPresentation() override;
+ void StopPresentation() override;
+ bool SubmitFrame(const mozilla::gfx::VRLayer_Stereo_Immersive& aLayer) override;
+
+private:
+ // OpenVR State
+ ::vr::IVRSystem* mVRSystem = nullptr;
+ ::vr::IVRChaperone* mVRChaperone = nullptr;
+ ::vr::IVRCompositor* mVRCompositor = nullptr;
+ bool mShouldQuit;
+
+ bool InitState(mozilla::gfx::VRSystemState& aSystemState);
+ void UpdateStageParameters(mozilla::gfx::VRDisplayState& state);
+ void UpdateEyeParameters(mozilla::gfx::VRDisplayState& state, gfx::Matrix4x4* headToEyeTransforms = nullptr);
+ void GetSensorState(mozilla::gfx::VRSystemState& state);
+ void GetControllerState(VRSystemState &state);
+
+ bool SubmitFrame(void* aTextureHandle,
+ ::vr::ETextureType aTextureType,
+ const VRLayerEyeRect& aLeftEyeRect,
+ const VRLayerEyeRect& aRightEyeRect);
+#if defined(XP_WIN)
+ bool CreateD3DObjects();
+#endif
+};
+
+} // namespace mozilla
+} // namespace gfx
+
+#endif // GFX_VR_SERVICE_OPENVRSESSION_H
new file mode 100644
--- /dev/null
+++ b/gfx/vr/service/VRService.cpp
@@ -0,0 +1,332 @@
+/* -*- 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 "VRService.h"
+#include "OpenVRSession.h"
+#include "gfxPrefs.h"
+#include "base/thread.h" // for Thread
+
+using namespace mozilla;
+using namespace mozilla::gfx;
+using namespace std;
+
+namespace {
+
+int64_t
+FrameIDFromBrowserState(const mozilla::gfx::VRBrowserState& aState)
+{
+ for (int iLayer=0; iLayer < kVRLayerMaxCount; iLayer++) {
+ const VRLayerState& layer = aState.layerState[iLayer];
+ if (layer.type == VRLayerType::LayerType_Stereo_Immersive) {
+ return layer.layer_stereo_immersive.mFrameId;
+ }
+ }
+ return 0;
+}
+
+bool
+IsImmersiveContentActive(const mozilla::gfx::VRBrowserState& aState)
+{
+ for (int iLayer=0; iLayer < kVRLayerMaxCount; iLayer++) {
+ const VRLayerState& layer = aState.layerState[iLayer];
+ if (layer.type == VRLayerType::LayerType_Stereo_Immersive) {
+ return true;
+ }
+ }
+ return false;
+}
+
+} // anonymous namespace
+
+/*static*/ already_AddRefed<VRService>
+VRService::Create()
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ if (!gfxPrefs::VRServiceEnabled()) {
+ return nullptr;
+ }
+
+ RefPtr<VRService> service = new VRService();
+ return service.forget();
+}
+
+VRService::VRService()
+ : mSystemState{}
+ , mBrowserState{}
+ , mServiceThread(nullptr)
+ , mShutdownRequested(false)
+{
+ memset(&mAPIShmem, 0, sizeof(mAPIShmem));
+}
+
+VRService::~VRService()
+{
+ Stop();
+}
+
+void
+VRService::Start()
+{
+ if (!mServiceThread) {
+ /**
+ * We must ensure that any time the service is re-started, that
+ * the VRSystemState is reset, including mSystemState.enumerationCompleted
+ * This must happen before VRService::Start returns to the caller, in order
+ * to prevent the WebVR/WebXR promises from being resolved before the
+ * enumeration has been completed.
+ */
+ memset(&mSystemState, 0, sizeof(mSystemState));
+ PushState(mSystemState);
+
+ mServiceThread = new base::Thread("VRService");
+ base::Thread::Options options;
+ /* Timeout values are powers-of-two to enable us get better data.
+ 128ms is chosen for transient hangs because 8Hz should be the minimally
+ acceptable goal for Compositor responsiveness (normal goal is 60Hz). */
+ options.transient_hang_timeout = 128; // milliseconds
+ /* 2048ms is chosen for permanent hangs because it's longer than most
+ * Compositor hangs seen in the wild, but is short enough to not miss getting
+ * native hang stacks. */
+ options.permanent_hang_timeout = 2048; // milliseconds
+
+ if (!mServiceThread->StartWithOptions(options)) {
+ delete mServiceThread;
+ mServiceThread = nullptr;
+ return;
+ }
+
+ mServiceThread->message_loop()->PostTask(NewRunnableMethod(
+ "gfx::VRService::ServiceInitialize",
+ this, &VRService::ServiceInitialize
+ ));
+ }
+}
+
+void
+VRService::Stop()
+{
+ if (mServiceThread) {
+ mServiceThread->message_loop()->PostTask(NewRunnableMethod(
+ "gfx::VRService::RequestShutdown",
+ this, &VRService::RequestShutdown
+ ));
+ delete mServiceThread;
+ mServiceThread = nullptr;
+ }
+}
+
+bool
+VRService::IsInServiceThread()
+{
+ return mServiceThread && mServiceThread->thread_id() == PlatformThread::CurrentId();
+}
+
+void
+VRService::RequestShutdown()
+{
+ MOZ_ASSERT(IsInServiceThread());
+ mShutdownRequested = true;
+}
+
+void
+VRService::ServiceInitialize()
+{
+ MOZ_ASSERT(IsInServiceThread());
+
+ mShutdownRequested = false;
+ memset(&mBrowserState, 0, sizeof(mBrowserState));
+
+ // Try to start a VRSession
+ unique_ptr<VRSession> session;
+
+ // Try OpenVR
+ session = make_unique<OpenVRSession>();
+ if (!session->Initialize(mSystemState)) {
+ session = nullptr;
+ }
+ if (session) {
+ mSession = std::move(session);
+ // Setting enumerationCompleted to true indicates to the browser
+ // that it should resolve any promises in the WebVR/WebXR API
+ // waiting for hardware detection.
+ mSystemState.enumerationCompleted = true;
+ PushState(mSystemState);
+
+ MessageLoop::current()->PostTask(NewRunnableMethod(
+ "gfx::VRService::ServiceWaitForImmersive",
+ this, &VRService::ServiceWaitForImmersive
+ ));
+ } else {
+ // VR hardware was not detected.
+ // We must inform the browser of the failure so it may try again
+ // later and resolve WebVR promises. A failure or shutdown is
+ // indicated by enumerationCompleted being set to true, with all
+ // other fields remaining zeroed out.
+ memset(&mSystemState, 0, sizeof(mSystemState));
+ mSystemState.enumerationCompleted = true;
+ PushState(mSystemState);
+ }
+}
+
+void
+VRService::ServiceShutdown()
+{
+ MOZ_ASSERT(IsInServiceThread());
+
+ mSession = nullptr;
+
+ // Notify the browser that we have shut down.
+ // This is indicated by enumerationCompleted being set
+ // to true, with all other fields remaining zeroed out.
+ memset(&mSystemState, 0, sizeof(mSystemState));
+ mSystemState.enumerationCompleted = true;
+ PushState(mSystemState);
+}
+
+void
+VRService::ServiceWaitForImmersive()
+{
+ MOZ_ASSERT(IsInServiceThread());
+ MOZ_ASSERT(mSession);
+
+ mSession->ProcessEvents(mSystemState);
+ PushState(mSystemState);
+ PullState(mBrowserState);
+
+ if (mSession->ShouldQuit() || mShutdownRequested) {
+ // Shut down
+ MessageLoop::current()->PostTask(NewRunnableMethod(
+ "gfx::VRService::ServiceShutdown",
+ this, &VRService::ServiceShutdown
+ ));
+ } else if (IsImmersiveContentActive(mBrowserState)) {
+ // Enter Immersive Mode
+ mSession->StartPresentation();
+ mSession->StartFrame(mSystemState);
+ PushState(mSystemState);
+
+ MessageLoop::current()->PostTask(NewRunnableMethod(
+ "gfx::VRService::ServiceImmersiveMode",
+ this, &VRService::ServiceImmersiveMode
+ ));
+ } else {
+ // Continue waiting for immersive mode
+ MessageLoop::current()->PostTask(NewRunnableMethod(
+ "gfx::VRService::ServiceWaitForImmersive",
+ this, &VRService::ServiceWaitForImmersive
+ ));
+ }
+}
+
+void
+VRService::ServiceImmersiveMode()
+{
+ MOZ_ASSERT(IsInServiceThread());
+ MOZ_ASSERT(mSession);
+
+ mSession->ProcessEvents(mSystemState);
+ PushState(mSystemState);
+ PullState(mBrowserState);
+
+ if (mSession->ShouldQuit() || mShutdownRequested) {
+ // Shut down
+ MessageLoop::current()->PostTask(NewRunnableMethod(
+ "gfx::VRService::ServiceShutdown",
+ this, &VRService::ServiceShutdown
+ ));
+ return;
+ } else if (!IsImmersiveContentActive(mBrowserState)) {
+ // Exit immersive mode
+ mSession->StopPresentation();
+ MessageLoop::current()->PostTask(NewRunnableMethod(
+ "gfx::VRService::ServiceWaitForImmersive",
+ this, &VRService::ServiceWaitForImmersive
+ ));
+ return;
+ }
+
+ uint64_t newFrameId = FrameIDFromBrowserState(mBrowserState);
+ if (newFrameId != mSystemState.displayState.mLastSubmittedFrameId) {
+ // A new immersive frame has been received.
+ // Submit the textures to the VR system compositor.
+ bool success = false;
+ for (int iLayer=0; iLayer < kVRLayerMaxCount; iLayer++) {
+ const VRLayerState& layer = mBrowserState.layerState[iLayer];
+ if (layer.type == VRLayerType::LayerType_Stereo_Immersive) {
+ success = mSession->SubmitFrame(layer.layer_stereo_immersive);
+ break;
+ }
+ }
+
+ // Changing mLastSubmittedFrameId triggers a new frame to start
+ // rendering. Changes to mLastSubmittedFrameId and the values
+ // used for rendering, such as headset pose, must be pushed
+ // atomically to the browser.
+ mSystemState.displayState.mLastSubmittedFrameId = newFrameId;
+ mSystemState.displayState.mLastSubmittedFrameSuccessful = success;
+ mSession->StartFrame(mSystemState);
+ PushState(mSystemState);
+ }
+
+ // Continue immersive mode
+ MessageLoop::current()->PostTask(NewRunnableMethod(
+ "gfx::VRService::ServiceImmersiveMode",
+ this, &VRService::ServiceImmersiveMode
+ ));
+}
+
+void
+VRService::PushState(const mozilla::gfx::VRSystemState& aState)
+{
+ // Copying the VR service state to the shmem is atomic, infallable,
+ // and non-blocking on x86/x64 architectures. Arm requires a mutex
+ // that is locked for the duration of the memcpy to and from shmem on
+ // both sides.
+
+#if defined(MOZ_WIDGET_ANDROID)
+ if (pthread_mutex_lock((pthread_mutex_t*)&(mExternalShmem->systemMutex)) == 0) {
+ memcpy((void *)&mAPIShmem.state, &aState, sizeof(VRSystemState));
+ pthread_mutex_unlock((pthread_mutex_t*)&(mExternalShmem->systemMutex));
+ }
+#else
+ mAPIShmem.generationA++;
+ memcpy((void *)&mAPIShmem.state, &aState, sizeof(VRSystemState));
+ mAPIShmem.generationB++;
+#endif
+}
+
+void
+VRService::PullState(mozilla::gfx::VRBrowserState& aState)
+{
+ // Copying the browser state from the shmem is non-blocking
+ // on x86/x64 architectures. Arm requires a mutex that is
+ // locked for the duration of the memcpy to and from shmem on
+ // both sides.
+ // On x86/x64 It is fallable -- If a dirty copy is detected by
+ // a mismatch of browserGenerationA and browserGenerationB,
+ // the copy is discarded and will not replace the last known
+ // browser state.
+
+#if defined(MOZ_WIDGET_ANDROID)
+ if (pthread_mutex_lock((pthread_mutex_t*)&(mExternalShmem->browserMutex)) == 0) {
+ memcpy(&aState, &tmp.browserState, sizeof(VRBrowserState));
+ pthread_mutex_unlock((pthread_mutex_t*)&(mExternalShmem->browserMutex));
+ }
+#else
+ VRExternalShmem tmp;
+ memcpy(&tmp, &mAPIShmem, sizeof(VRExternalShmem));
+ if (tmp.browserGenerationA == tmp.browserGenerationB && tmp.browserGenerationA != 0 && tmp.browserGenerationA != -1) {
+ memcpy(&aState, &tmp.browserState, sizeof(VRBrowserState));
+ }
+#endif
+}
+
+VRExternalShmem*
+VRService::GetAPIShmem()
+{
+ return &mAPIShmem;
+}
new file mode 100644
--- /dev/null
+++ b/gfx/vr/service/VRService.h
@@ -0,0 +1,79 @@
+/* -*- 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/. */
+
+#ifndef GFX_VR_SERVICE_VRSERVICE_H
+#define GFX_VR_SERVICE_VRSERVICE_H
+
+#include "mozilla/Atomics.h"
+
+#include "moz_external_vr.h"
+
+#include <thread>
+namespace base {
+class Thread;
+} // namespace base
+namespace mozilla {
+namespace gfx {
+
+class VRSession;
+
+class VRService
+{
+public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(VRService)
+ static already_AddRefed<VRService> Create();
+
+ void Start();
+ void Stop();
+ VRExternalShmem* GetAPIShmem();
+
+private:
+ VRService();
+ ~VRService();
+ void PushState(const mozilla::gfx::VRSystemState& aState);
+ void PullState(mozilla::gfx::VRBrowserState& aState);
+
+ /**
+ * VRSystemState contains the most recent state of the VR
+ * system, to be shared with the browser by Shmem.
+ * mSystemState is the VR Service copy of this data, which
+ * is memcpy'ed atomically to the Shmem.
+ * VRSystemState is written by the VR Service, but read-only
+ * by the browser.
+ */
+ VRSystemState mSystemState;
+ /**
+ * VRBrowserState contains the most recent state of the browser.
+ * mBrowserState is memcpy'ed from the Shmem atomically
+ */
+ VRBrowserState mBrowserState;
+
+ std::unique_ptr<VRSession> mSession;
+ base::Thread* mServiceThread;
+ bool mShutdownRequested;
+
+ VRExternalShmem mAPIShmem;
+
+ bool IsInServiceThread();
+ void RequestShutdown();
+
+ /**
+ * The VR Service thread is a state machine that always has one
+ * task queued depending on the state.
+ *
+ * VR Service thread state task functions:
+ */
+ void ServiceInitialize();
+ void ServiceShutdown();
+ void ServiceWaitForImmersive();
+ void ServiceImmersiveMode();
+
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif // GFX_VR_SERVICE_VRSERVICE_H
new file mode 100644
--- /dev/null
+++ b/gfx/vr/service/VRSession.cpp
@@ -0,0 +1,80 @@
+#include "VRSession.h"
+
+#include "moz_external_vr.h"
+
+#if defined(XP_WIN)
+#include <d3d11.h>
+#endif // defined(XP_WIN)
+
+using namespace mozilla::gfx;
+
+VRSession::VRSession()
+{
+
+}
+
+VRSession::~VRSession()
+{
+
+}
+
+#if defined(XP_WIN)
+bool
+VRSession::CreateD3DContext(RefPtr<ID3D11Device> aDevice)
+{
+ if (!mDevice) {
+ if (!aDevice) {
+ NS_WARNING("OpenVRSession::CreateD3DObjects failed to get a D3D11Device");
+ return false;
+ }
+ if (FAILED(aDevice->QueryInterface(__uuidof(ID3D11Device1), getter_AddRefs(mDevice)))) {
+ NS_WARNING("OpenVRSession::CreateD3DObjects failed to get a D3D11Device1");
+ return false;
+ }
+ }
+ if (!mContext) {
+ mDevice->GetImmediateContext1(getter_AddRefs(mContext));
+ if (!mContext) {
+ NS_WARNING("OpenVRSession::CreateD3DObjects failed to get an immediate context");
+ return false;
+ }
+ }
+ if (!mDeviceContextState) {
+ D3D_FEATURE_LEVEL featureLevels[] {
+ D3D_FEATURE_LEVEL_11_1,
+ D3D_FEATURE_LEVEL_11_0
+ };
+ mDevice->CreateDeviceContextState(0,
+ featureLevels,
+ 2,
+ D3D11_SDK_VERSION,
+ __uuidof(ID3D11Device1),
+ nullptr,
+ getter_AddRefs(mDeviceContextState));
+ }
+ if (!mDeviceContextState) {
+ NS_WARNING("VRDisplayHost::CreateD3DObjects failed to get a D3D11DeviceContextState");
+ return false;
+ }
+ return true;
+}
+
+ID3D11Device1*
+VRSession::GetD3DDevice()
+{
+ return mDevice;
+}
+
+ID3D11DeviceContext1*
+VRSession::GetD3DDeviceContext()
+{
+ return mContext;
+}
+
+ID3DDeviceContextState*
+VRSession::GetD3DDeviceContextState()
+{
+ return mDeviceContextState;
+}
+
+#endif // defined(XP_WIN)
new file mode 100644
--- /dev/null
+++ b/gfx/vr/service/VRSession.h
@@ -0,0 +1,53 @@
+/* -*- 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/. */
+
+#ifndef GFX_VR_SERVICE_VRSESSION_H
+#define GFX_VR_SERVICE_VRSESSION_H
+
+#include "VRSession.h"
+
+#include "moz_external_vr.h"
+
+#if defined(XP_WIN)
+#include <d3d11_1.h>
+#elif defined(XP_MACOSX)
+class MacIOSurface;
+#endif
+
+namespace mozilla {
+namespace gfx {
+
+class VRSession
+{
+public:
+ VRSession();
+ virtual ~VRSession();
+
+ virtual bool Initialize(mozilla::gfx::VRSystemState& aSystemState) = 0;
+ virtual void Shutdown() = 0;
+ virtual void ProcessEvents(mozilla::gfx::VRSystemState& aSystemState) = 0;
+ virtual void StartFrame(mozilla::gfx::VRSystemState& aSystemState) = 0;
+ virtual bool ShouldQuit() const = 0;
+ virtual bool StartPresentation() = 0;
+ virtual void StopPresentation() = 0;
+ virtual bool SubmitFrame(const mozilla::gfx::VRLayer_Stereo_Immersive& aLayer) = 0;
+
+#if defined(XP_WIN)
+protected:
+ bool CreateD3DContext(RefPtr<ID3D11Device> aDevice);
+ RefPtr<ID3D11Device1> mDevice;
+ RefPtr<ID3D11DeviceContext1> mContext;
+ ID3D11Device1* GetD3DDevice();
+ ID3D11DeviceContext1* GetD3DDeviceContext();
+ ID3DDeviceContextState* GetD3DDeviceContextState();
+ RefPtr<ID3DDeviceContextState> mDeviceContextState;
+#endif
+};
+
+} // namespace mozilla
+} // namespace gfx
+
+#endif // GFX_VR_SERVICE_VRSESSION_H
new file mode 100644
--- /dev/null
+++ b/gfx/vr/service/moz.build
@@ -0,0 +1,21 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+# Build OpenVR on Windows, Linux, and macOS desktop targets
+if CONFIG['OS_TARGET'] in ('WINNT', 'Linux', 'Darwin'):
+ UNIFIED_SOURCES += [
+ 'OpenVRSession.cpp',
+ 'VRService.cpp',
+ 'VRSession.cpp',
+ ]
+ LOCAL_INCLUDES += [
+ '/dom/base',
+ '/gfx/layers/d3d11',
+ '/gfx/thebes',
+ ]
+ include('/ipc/chromium/chromium-config.mozbuild')
+
+FINAL_LIBRARY = 'xul'
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -5278,16 +5278,18 @@ pref("dom.vr.puppet.enabled", false);
// result as a base64 image, 2: show it on the screen).
pref("dom.vr.puppet.submitframe", 0);
// The number of milliseconds since last frame start before triggering a new frame.
// When content is failing to submit frames on time or the lower level VR platform API's
// are rejecting frames, it determines the rate at which RAF callbacks will be called.
pref("dom.vr.display.rafMaxDuration", 50);
// VR test system.
pref("dom.vr.test.enabled", false);
+// Enable the VR Service, which interfaces with VR hardware in a separate thread
+pref("dom.vr.service.enabled", false);
// If the user puts a finger down on an element and we think the user
// might be executing a pan gesture, how long do we wait before
// tentatively deciding the gesture is actually a tap and activating
// the target element?
pref("ui.touch_activation.delay_ms", 100);
// If the user has clicked an element, how long do we keep the