Bug 1407423 - Ensure that any time we have loaded the Oculus runtime libary, we are polling ShouldQuit,r=daoshengmu

- Ensure ovr_GetSessionStatus is polled even when a VR presentation
  is not active.
- When we fail to initialize an Oculus Session or detect VR hardware,
  immediately unload the Oculus Library as we can't poll for ShouldQuit
  without a valid Oculus session.
- When we poll ovr_GetSessionStatus, we are now updating the mounted state
  in VRDisplayInfo::mIsMounted.
- Added prefs to control enumeration throttling and timeout to release
  VR hardware when inactive.
- Some refactoring to make frame loop more understandable and less
  brittle.
- When throttling enumeration, we ensure that all other VR apis
  also throttle enumeration so that they don't pick up the same device
  during throttling.
- Some long functions in VRManager have been broken up and
  had their inner-workings documented in more detail.

MozReview-Commit-ID: CEYwwQ9mYd0

--HG--
extra : rebase_source : b82cd9f200e6311528d4bed469d1b8044e9fc7f2
extra : amend_source : e74f56f5ec95641fd478deb71d6c7ba5d2cba7b1
This commit is contained in:
Kearwood "Kip" Gilbert 2017-10-10 14:42:37 -07:00
Родитель 6e5bc11c14
Коммит 7e7cb086d3
15 изменённых файлов: 467 добавлений и 167 удалений

Просмотреть файл

@ -367,6 +367,9 @@ private:
DECL_GFX_PREF(Live, "dom.vr.oculus.quit.timeout", VROculusQuitTimeout, int32_t, 30000); DECL_GFX_PREF(Live, "dom.vr.oculus.quit.timeout", VROculusQuitTimeout, int32_t, 30000);
DECL_GFX_PREF(Once, "dom.vr.openvr.enabled", VROpenVREnabled, bool, false); DECL_GFX_PREF(Once, "dom.vr.openvr.enabled", VROpenVREnabled, bool, false);
DECL_GFX_PREF(Once, "dom.vr.osvr.enabled", VROSVREnabled, bool, false); DECL_GFX_PREF(Once, "dom.vr.osvr.enabled", VROSVREnabled, bool, false);
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.poseprediction.enabled", VRPosePredictionEnabled, bool, true);
DECL_GFX_PREF(Live, "dom.vr.require-gesture", VRRequireGesture, 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.enabled", VRPuppetEnabled, bool, false);

Просмотреть файл

@ -41,7 +41,7 @@ public:
virtual void ZeroSensor() = 0; virtual void ZeroSensor() = 0;
virtual void StartPresentation() = 0; virtual void StartPresentation() = 0;
virtual void StopPresentation() = 0; virtual void StopPresentation() = 0;
virtual void NotifyVSync(); void NotifyVSync();
void StartFrame(); void StartFrame();
void SubmitFrame(VRLayerParent* aLayer, void SubmitFrame(VRLayerParent* aLayer,

Просмотреть файл

@ -56,6 +56,8 @@ VRManager::ManagerInit()
VRManager::VRManager() VRManager::VRManager()
: mInitialized(false) : mInitialized(false)
, mVRTestSystemCreated(false) , mVRTestSystemCreated(false)
, mVRDisplaysRequested(false)
, mVRControllersRequested(false)
{ {
MOZ_COUNT_CTOR(VRManager); MOZ_COUNT_CTOR(VRManager);
MOZ_ASSERT(sVRManagerSingleton == nullptr); MOZ_ASSERT(sVRManagerSingleton == nullptr);
@ -174,12 +176,8 @@ VRManager::RemoveVRManagerParent(VRManagerParent* aVRManagerParent)
} }
void void
VRManager::NotifyVsync(const TimeStamp& aVsyncTimestamp) VRManager::UpdateRequestedDevices()
{ {
MOZ_ASSERT(VRListenerThreadHolder::IsInVRListenerThread());
const double kVRDisplayRefreshMaxDuration = 5000; // milliseconds
const double kVRDisplayInactiveMaxDuration = 30000; // milliseconds
bool bHaveEventListener = false; bool bHaveEventListener = false;
bool bHaveControllerListener = false; bool bHaveControllerListener = false;
@ -189,61 +187,55 @@ VRManager::NotifyVsync(const TimeStamp& aVsyncTimestamp)
bHaveControllerListener |= vmp->HaveControllerListener(); bHaveControllerListener |= vmp->HaveControllerListener();
} }
// VRDisplayHost::NotifyVSync may modify mVRDisplays, so we iterate mVRDisplaysRequested = bHaveEventListener;
// through a local copy here. // We only currently allow controllers to be used when
nsTArray<RefPtr<VRDisplayHost>> displays; // also activating a VR display
for (auto iter = mVRDisplays.Iter(); !iter.Done(); iter.Next()) { mVRControllersRequested = mVRDisplaysRequested && bHaveControllerListener;
displays.AppendElement(iter.UserData()); }
}
for (const auto& display: displays) { /**
display->NotifyVSync(); * VRManager::NotifyVsync must be called on every 2d vsync (usually at 60hz).
* This must be called even when no WebVR site is active.
* If we don't have a 2d display attached to the system, we can call this
* at the VR display's native refresh rate.
**/
void
VRManager::NotifyVsync(const TimeStamp& aVsyncTimestamp)
{
MOZ_ASSERT(VRListenerThreadHolder::IsInVRListenerThread());
UpdateRequestedDevices();
for (const auto& manager : mManagers) {
manager->NotifyVSync();
} }
if (bHaveEventListener) { // We must continually refresh the VR display enumeration to check
// If content has set an EventHandler to be notified of VR display events // for events that we must fire such as Window.onvrdisplayconnect
// we must continually refresh the VR display enumeration to check // Note that enumeration itself may activate display hardware, such
// for events that we must fire such as Window.onvrdisplayconnect // as Oculus, so we only do this when we know we are displaying content
// Note that enumeration itself may activate display hardware, such // that is looking for VR displays.
// as Oculus, so we only do this when we know we are displaying content RefreshVRDisplays();
// that is looking for VR displays.
if (mLastRefreshTime.IsNull()) {
// This is the first vsync, must refresh VR displays
RefreshVRDisplays();
if (bHaveControllerListener) {
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();
if (bHaveControllerListener) {
RefreshVRControllers();
}
mLastRefreshTime = TimeStamp::Now();
}
}
if (bHaveControllerListener) { // Update state and enumeration of VR controllers
for (const auto& manager: mManagers) { RefreshVRControllers();
if (!manager->GetIsPresenting()) {
manager->HandleInput();
}
}
}
}
CheckForInactiveTimeout();
}
void
VRManager::CheckForInactiveTimeout()
{
// Shut down the VR devices when not in use // Shut down the VR devices when not in use
if (bHaveEventListener || bHaveControllerListener) { if (mVRDisplaysRequested || mVRControllersRequested) {
// We are using a VR device, keep it alive // We are using a VR device, keep it alive
mLastActiveTime = TimeStamp::Now(); mLastActiveTime = TimeStamp::Now();
} else if (mLastActiveTime.IsNull()) { }
else if (mLastActiveTime.IsNull()) {
Shutdown(); Shutdown();
} else { }
else {
TimeDuration duration = TimeStamp::Now() - mLastActiveTime; TimeDuration duration = TimeStamp::Now() - mLastActiveTime;
if (duration.ToMilliseconds() > kVRDisplayInactiveMaxDuration) { if (duration.ToMilliseconds() > gfxPrefs::VRInactiveTimeout()) {
Shutdown(); Shutdown();
} }
} }
@ -267,26 +259,80 @@ VRManager::NotifyVRVsync(const uint32_t& aDisplayID)
RefreshVRDisplays(); RefreshVRDisplays();
} }
void
VRManager::EnumerateVRDisplays()
{
/**
* Throttle the rate of enumeration to the interval set in
* VRDisplayEnumerateInterval
*/
if (!mLastDisplayEnumerationTime.IsNull()) {
TimeDuration duration = TimeStamp::Now() - mLastDisplayEnumerationTime;
if (duration.ToMilliseconds() < gfxPrefs::VRDisplayEnumerateInterval()) {
return;
}
}
/**
* Any VRSystemManager instance may request that no enumeration
* should occur, including enumeration from other VRSystemManager
* instances.
*/
for (const auto& manager : mManagers) {
if (manager->ShouldInhibitEnumeration()) {
return;
}
}
/**
* If we get this far, don't try again until
* the VRDisplayEnumerateInterval elapses
*/
mLastDisplayEnumerationTime = TimeStamp::Now();
/**
* VRSystemManagers are inserted into mManagers in
* a strict order of priority. The managers for the
* most device-specialized API's will have a chance
* to enumerate devices before the more generic
* device-agnostic APIs.
*/
for (const auto& manager : mManagers) {
manager->Enumerate();
/**
* After a VRSystemManager::Enumerate is called, it may request
* that further enumeration should stop. This can be used to prevent
* erraneous redundant enumeration of the same HMD by multiple managers.
* XXX - Perhaps there will be a better way to detect duplicate displays
* in the future.
*/
if (manager->ShouldInhibitEnumeration()) {
return;
}
}
}
void void
VRManager::RefreshVRDisplays(bool aMustDispatch) VRManager::RefreshVRDisplays(bool aMustDispatch)
{ {
nsTArray<RefPtr<gfx::VRDisplayHost> > displays; /**
* 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) {
EnumerateVRDisplays();
}
/** We don't wish to enumerate the same display from multiple managers, /**
* so stop as soon as we get a display. * VRSystemManager::GetHMDs will not activate new hardware
* It is still possible to get multiple displays from a single manager, * or result in interruption of other VR activities.
* but do not wish to mix-and-match for risk of reporting a duplicate. * We can call it even when suppressing enumeration to get
* * the already-enumerated displays.
* 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) { nsTArray<RefPtr<gfx::VRDisplayHost> > displays;
if (mManagers[i]->GetHMDs(displays)) { for (const auto& manager: mManagers) {
// GetHMDs returns true to indicate that no further enumeration from manager->GetHMDs(displays);
// other managers should be performed. This prevents erraneous
// redundant enumeration of the same HMD by multiple managers.
break;
}
} }
bool displayInfoChanged = false; bool displayInfoChanged = false;
@ -383,10 +429,10 @@ VRManager::GetVRControllerInfo(nsTArray<VRControllerInfo>& aControllerInfo)
void void
VRManager::RefreshVRControllers() VRManager::RefreshVRControllers()
{ {
nsTArray<RefPtr<gfx::VRControllerHost>> controllers;
ScanForControllers(); ScanForControllers();
nsTArray<RefPtr<gfx::VRControllerHost>> controllers;
for (uint32_t i = 0; i < mManagers.Length() for (uint32_t i = 0; i < mManagers.Length()
&& controllers.Length() == 0; ++i) { && controllers.Length() == 0; ++i) {
mManagers[i]->GetControllers(controllers); mManagers[i]->GetControllers(controllers);
@ -419,9 +465,25 @@ VRManager::RefreshVRControllers()
void void
VRManager::ScanForControllers() VRManager::ScanForControllers()
{ {
// We don't have to do this every frame, so check if we
// have enumerated recently
if (!mLastControllerEnumerationTime.IsNull()) {
TimeDuration duration = TimeStamp::Now() - mLastControllerEnumerationTime;
if (duration.ToMilliseconds() < gfxPrefs::VRControllerEnumerateInterval()) {
return;
}
}
// Only enumerate controllers once we need them
if (!mVRControllersRequested) {
return;
}
for (uint32_t i = 0; i < mManagers.Length(); ++i) { for (uint32_t i = 0; i < mManagers.Length(); ++i) {
mManagers[i]->ScanForControllers(); mManagers[i]->ScanForControllers();
} }
mLastControllerEnumerationTime = TimeStamp::Now();
} }
void void

Просмотреть файл

@ -64,6 +64,9 @@ private:
void Shutdown(); void Shutdown();
void DispatchVRDisplayInfoUpdate(); void DispatchVRDisplayInfoUpdate();
void UpdateRequestedDevices();
void EnumerateVRDisplays();
void CheckForInactiveTimeout();
typedef nsTHashtable<nsRefPtrHashKey<VRManagerParent>> VRManagerParentSet; typedef nsTHashtable<nsRefPtrHashKey<VRManagerParent>> VRManagerParentSet;
VRManagerParentSet mVRManagerParents; VRManagerParentSet mVRManagerParents;
@ -79,9 +82,12 @@ private:
Atomic<bool> mInitialized; Atomic<bool> mInitialized;
TimeStamp mLastRefreshTime; TimeStamp mLastControllerEnumerationTime;
TimeStamp mLastDisplayEnumerationTime;
TimeStamp mLastActiveTime; TimeStamp mLastActiveTime;
bool mVRTestSystemCreated; bool mVRTestSystemCreated;
bool mVRDisplaysRequested;
bool mVRControllersRequested;
}; };
} // namespace gfx } // namespace gfx

Просмотреть файл

@ -9,6 +9,7 @@
#include "gfxVR.h" #include "gfxVR.h"
#include "mozilla/dom/GamepadEventTypes.h" #include "mozilla/dom/GamepadEventTypes.h"
#include "mozilla/dom/GamepadBinding.h" #include "mozilla/dom/GamepadBinding.h"
#include "VRDisplayHost.h"
#ifndef M_PI #ifndef M_PI
# define M_PI 3.14159265358979323846 # define M_PI 3.14159265358979323846
@ -32,6 +33,58 @@ VRSystemManager::AllocateControllerID()
return ++sControllerBase; return ++sControllerBase;
} }
/**
* VRSystemManager::NotifyVsync must be called even when a WebVR site is
* not active, in order to poll for respond to VR Platform API requests.
* This should be called very often, ideally once per frame.
* VRSystemManager::Refresh will not activate VR hardware or
* initialize VR runtimes that have not already been activated.
*/
void
VRSystemManager::NotifyVSync()
{
// VRDisplayHost::NotifyVSync may modify mVRDisplays, so we iterate
// through a local copy here.
nsTArray<RefPtr<VRDisplayHost>> displays;
GetHMDs(displays);
for (const auto& display : displays) {
display->NotifyVSync();
}
// Ensure that the controller state is updated at least
// on every 2d display VSync when not in a VR presentation.
if (!GetIsPresenting()) {
HandleInput();
}
}
/**
* VRSystemManager::GetHMDs must not be called unless
* VRSystemManager::ShouldInhibitEnumeration is called
* on all VRSystemManager instances and they all return
* false.
*
* This is used to ensure that VR devices that can be
* enumerated by multiple API's are only enumerated by
* one API.
*
* GetHMDs is called for the most specific API
* (ie. Oculus SDK) first before calling GetHMDs on
* more generic api's (ie. OpenVR) to ensure that a device
* is accessed using the API most optimized for it.
*
* ShouldInhibitEnumeration may also be used to prevent
* devices from jumping to other API's when they are
* intentionally ignored, such as when responding to
* requests by the VR platform to unload the libraries
* for runtime software updates.
*/
bool
VRSystemManager::ShouldInhibitEnumeration()
{
return false;
}
Matrix4x4 Matrix4x4
VRFieldOfView::ConstructProjectionMatrix(float zNear, float zFar, VRFieldOfView::ConstructProjectionMatrix(float zNear, float zFar,
bool rightHanded) const bool rightHanded) const

Просмотреть файл

@ -347,7 +347,10 @@ public:
virtual void Destroy() = 0; virtual void Destroy() = 0;
virtual void Shutdown() = 0; virtual void Shutdown() = 0;
virtual bool GetHMDs(nsTArray<RefPtr<VRDisplayHost>>& aHMDResult) = 0; virtual void Enumerate() = 0;
virtual void NotifyVSync();
virtual bool ShouldInhibitEnumeration();
virtual void GetHMDs(nsTArray<RefPtr<VRDisplayHost>>& aHMDResult) = 0;
virtual bool GetIsPresenting() = 0; virtual bool GetIsPresenting() = 0;
virtual void HandleInput() = 0; virtual void HandleInput() = 0;
virtual void GetControllers(nsTArray<RefPtr<VRControllerHost>>& aControllerResult) = 0; virtual void GetControllers(nsTArray<RefPtr<VRControllerHost>>& aControllerResult) = 0;

Просмотреть файл

@ -550,25 +550,51 @@ VRSystemManagerOSVR::Shutdown()
osvr_ClientShutdown(m_ctx); osvr_ClientShutdown(m_ctx);
} }
bool void
VRSystemManagerOSVR::GetHMDs(nsTArray<RefPtr<VRDisplayHost>>& aHMDResult) VRSystemManagerOSVR::NotifyVSync()
{
VRSystemManager::NotifyVSync();
// TODO - Check for device disconnection or other OSVR events
}
void
VRSystemManagerOSVR::Enumerate()
{ {
// make sure context, interface and display are initialized // make sure context, interface and display are initialized
CheckOSVRStatus(); CheckOSVRStatus();
if (!Init()) { if (!Init()) {
return false; return;
} }
mHMDInfo = new VRDisplayOSVR(&m_ctx, &m_iface, &m_display); mHMDInfo = new VRDisplayOSVR(&m_ctx, &m_iface, &m_display);
}
bool
VRSystemManagerOSVR::ShouldInhibitEnumeration()
{
if (VRSystemManager::ShouldInhibitEnumeration()) {
return true;
}
if (mHMDInfo) { if (mHMDInfo) {
aHMDResult.AppendElement(mHMDInfo); // When we find an a VR device, don't
// allow any further enumeration as it
// may get picked up redundantly by other
// API's.
return true; return true;
} }
return false; return false;
} }
void
VRSystemManagerOSVR::GetHMDs(nsTArray<RefPtr<VRDisplayHost>>& aHMDResult)
{
if (mHMDInfo) {
aHMDResult.AppendElement(mHMDInfo);
}
}
bool bool
VRSystemManagerOSVR::GetIsPresenting() VRSystemManagerOSVR::GetIsPresenting()
{ {

Просмотреть файл

@ -80,7 +80,10 @@ public:
static already_AddRefed<VRSystemManagerOSVR> Create(); static already_AddRefed<VRSystemManagerOSVR> Create();
virtual void Destroy() override; virtual void Destroy() override;
virtual void Shutdown() override; virtual void Shutdown() override;
virtual bool GetHMDs(nsTArray<RefPtr<VRDisplayHost>>& aHMDResult) override; virtual void NotifyVSync() override;
virtual void Enumerate() override;
virtual bool ShouldInhibitEnumeration() override;
virtual void GetHMDs(nsTArray<RefPtr<VRDisplayHost>>& aHMDResult) override;
virtual bool GetIsPresenting() override; virtual bool GetIsPresenting() override;
virtual void HandleInput() override; virtual void HandleInput() override;
virtual void GetControllers(nsTArray<RefPtr<VRControllerHost>>& virtual void GetControllers(nsTArray<RefPtr<VRControllerHost>>&

Просмотреть файл

@ -204,8 +204,11 @@ VROculusSession::VROculusSession()
, mSession(nullptr) , mSession(nullptr)
, mInitFlags((ovrInitFlags)0) , mInitFlags((ovrInitFlags)0)
, mTextureSet(nullptr) , mTextureSet(nullptr)
, mPresenting(false) , mRequestPresentation(false)
, mRequestTracking(false)
, mDrawBlack(false) , mDrawBlack(false)
, mIsConnected(false)
, mIsMounted(false)
{ {
} }
@ -219,26 +222,47 @@ VROculusSession::Get()
bool bool
VROculusSession::IsTrackingReady() const VROculusSession::IsTrackingReady() const
{ {
return mSession != nullptr; // We should return true only if the HMD is connected and we
// are ready for tracking
MOZ_ASSERT(!mIsConnected || mSession);
return mIsConnected;
} }
bool bool
VROculusSession::IsRenderReady() const VROculusSession::IsPresentationReady() const
{ {
return !mRenderTargets.IsEmpty(); return !mRenderTargets.IsEmpty();
} }
bool
VROculusSession::IsMounted() const
{
return mIsMounted;
}
void void
VROculusSession::StopTracking() VROculusSession::StopTracking()
{ {
Uninitialize(true); if (mRequestTracking) {
mRequestTracking = false;
Refresh();
}
}
void
VROculusSession::StartTracking()
{
if (!mRequestTracking) {
mRequestTracking = true;
Refresh();
}
} }
void void
VROculusSession::StartPresentation(const IntSize& aSize) VROculusSession::StartPresentation(const IntSize& aSize)
{ {
if (!mPresenting) { if (!mRequestPresentation) {
mPresenting = true; mRequestPresentation = true;
mTelemetry.Clear(); mTelemetry.Clear();
mTelemetry.mPresentationStart = TimeStamp::Now(); mTelemetry.mPresentationStart = TimeStamp::Now();
@ -258,9 +282,9 @@ VROculusSession::StartPresentation(const IntSize& aSize)
void void
VROculusSession::StopPresentation() VROculusSession::StopPresentation()
{ {
if (mPresenting) { if (mRequestPresentation) {
mLastPresentationEnd = TimeStamp::Now(); mLastPresentationEnd = TimeStamp::Now();
mPresenting = false; mRequestPresentation = false;
const TimeDuration duration = mLastPresentationEnd - mTelemetry.mPresentationStart; const TimeDuration duration = mLastPresentationEnd - mTelemetry.mPresentationStart;
Telemetry::Accumulate(Telemetry::WEBVR_USERS_VIEW_IN, 1); Telemetry::Accumulate(Telemetry::WEBVR_USERS_VIEW_IN, 1);
@ -315,6 +339,8 @@ VROculusSession::StopSession()
{ {
if (mSession) { if (mSession) {
ovr_Destroy(mSession); ovr_Destroy(mSession);
mIsConnected = false;
mIsMounted = false;
mSession = nullptr; mSession = nullptr;
} }
} }
@ -337,9 +363,14 @@ VROculusSession::Refresh(bool aForceRefresh)
return; return;
} }
if (!mRequestTracking) {
Uninitialize(true);
return;
}
ovrInitFlags flags = (ovrInitFlags)(ovrInit_RequestVersion | ovrInit_MixedRendering); ovrInitFlags flags = (ovrInitFlags)(ovrInit_RequestVersion | ovrInit_MixedRendering);
bool bInvisible = true; bool bInvisible = true;
if (mPresenting) { if (mRequestPresentation) {
bInvisible = false; bInvisible = false;
} else if (!mLastPresentationEnd.IsNull()) { } else if (!mLastPresentationEnd.IsNull()) {
TimeDuration duration = TimeStamp::Now() - mLastPresentationEnd; TimeDuration duration = TimeStamp::Now() - mLastPresentationEnd;
@ -384,15 +415,25 @@ VROculusSession::Refresh(bool aForceRefresh)
Uninitialize(false); Uninitialize(false);
} }
Initialize(flags); if(!Initialize(flags)) {
// If we fail to initialize, ensure the Oculus libraries
// are unloaded, as we can't poll for ovrSessionStatus::ShouldQuit
// without an active ovrSession.
Uninitialize(true);
}
if (mSession) { if (mSession) {
ovrSessionStatus status; ovrSessionStatus status;
if (OVR_SUCCESS(ovr_GetSessionStatus(mSession, &status))) { if (OVR_SUCCESS(ovr_GetSessionStatus(mSession, &status))) {
mIsConnected = status.HmdPresent;
mIsMounted = status.HmdMounted;
if (status.ShouldQuit) { if (status.ShouldQuit) {
mLastShouldQuit = TimeStamp::Now(); mLastShouldQuit = TimeStamp::Now();
Uninitialize(true); Uninitialize(true);
} }
} else {
mIsConnected = false;
mIsMounted = false;
} }
} }
} }
@ -437,7 +478,7 @@ VROculusSession::Initialize(ovrInitFlags aFlags)
bool bool
VROculusSession::StartRendering() VROculusSession::StartRendering()
{ {
if (!mPresenting) { if (!mRequestPresentation) {
// Nothing to do if we aren't presenting // Nothing to do if we aren't presenting
return true; return true;
} }
@ -973,7 +1014,7 @@ VRDisplayOculus::StartPresentation()
return; return;
} }
mSession->StartPresentation(IntSize(mDisplayInfo.mEyeResolution.width * 2, mDisplayInfo.mEyeResolution.height)); mSession->StartPresentation(IntSize(mDisplayInfo.mEyeResolution.width * 2, mDisplayInfo.mEyeResolution.height));
if (!mSession->IsRenderReady()) { if (!mSession->IsPresentationReady()) {
return; return;
} }
@ -1103,7 +1144,7 @@ VRDisplayOculus::SubmitFrame(ID3D11Texture2D* aSource,
return false; return false;
} }
if (!mSession->IsRenderReady()) { if (!mSession->IsPresentationReady()) {
return false; return false;
} }
/** /**
@ -1248,18 +1289,10 @@ VRDisplayOculus::SubmitFrame(ID3D11Texture2D* aSource,
} }
void void
VRDisplayOculus::NotifyVSync() VRDisplayOculus::Refresh()
{ {
mSession->Refresh(); mDisplayInfo.mIsConnected = mSession->IsTrackingReady();
if (mSession->IsTrackingReady()) { mDisplayInfo.mIsMounted = mSession->IsMounted();
ovrSessionStatus sessionStatus;
ovrResult ovr = ovr_GetSessionStatus(mSession->Get(), &sessionStatus);
mDisplayInfo.mIsConnected = (ovr == ovrSuccess && sessionStatus.HmdPresent);
} else {
mDisplayInfo.mIsConnected = false;
}
VRDisplayHost::NotifyVSync();
} }
VRControllerOculus::VRControllerOculus(dom::GamepadHand aHand, uint32_t aDisplayID) VRControllerOculus::VRControllerOculus(dom::GamepadHand aHand, uint32_t aDisplayID)
@ -1517,37 +1550,66 @@ VRSystemManagerOculus::Shutdown()
mDisplay = nullptr; mDisplay = nullptr;
} }
void
VRSystemManagerOculus::NotifyVSync()
{
VRSystemManager::NotifyVSync();
if (!mSession) {
return;
}
mSession->Refresh();
if (mDisplay) {
mDisplay->Refresh();
}
// Detect disconnection
if (!mSession->IsTrackingReady()) {
// No HMD connected
mDisplay = nullptr;
}
}
bool bool
VRSystemManagerOculus::GetHMDs(nsTArray<RefPtr<VRDisplayHost>>& aHMDResult) VRSystemManagerOculus::ShouldInhibitEnumeration()
{
if (VRSystemManager::ShouldInhibitEnumeration()) {
return true;
}
if (mDisplay) {
// When we find an Oculus VR device, don't
// allow any further enumeration as it
// may get picked up redundantly by other
// API's such as OpenVR.
return true;
}
if (mSession && mSession->IsQuitTimeoutActive()) {
// When we are responding to ShouldQuit, we return true here
// to prevent further enumeration by other VRSystemManager's such as
// VRSystemManagerOpenVR which would also enumerate the connected Oculus
// HMD, resulting in interference with the Oculus runtime software updates.
return true;
}
return false;
}
void
VRSystemManagerOculus::Enumerate()
{ {
if (!mSession) { if (!mSession) {
mSession = new VROculusSession(); mSession = new VROculusSession();
} }
mSession->Refresh(); mSession->StartTracking();
if (mSession->IsQuitTimeoutActive()) { if (mDisplay == nullptr && mSession->IsTrackingReady()) {
// We have responded to a ShouldQuit flag set by the Oculus runtime
// and are waiting for a timeout duration to elapse before allowing
// re-initialization of the Oculus OVR lib. We return true in this case
// to prevent further enumeration by other VRSystemManager's such as
// VRSystemManagerOpenVR which would also enumerate the connected Oculus
// HMD, resulting in interference with the Oculus runtime software updates.
mDisplay = nullptr;
return true;
}
if (!mSession->IsTrackingReady()) {
// No HMD connected.
mDisplay = nullptr;
} else if (mDisplay == nullptr) {
// HMD Detected // HMD Detected
mDisplay = new VRDisplayOculus(mSession); mDisplay = new VRDisplayOculus(mSession);
} }
}
void
VRSystemManagerOculus::GetHMDs(nsTArray<RefPtr<VRDisplayHost>>& aHMDResult)
{
if (mDisplay) { if (mDisplay) {
aHMDResult.AppendElement(mDisplay); aHMDResult.AppendElement(mDisplay);
return true;
} }
return false;
} }
bool bool

Просмотреть файл

@ -42,12 +42,14 @@ class VROculusSession
public: public:
VROculusSession(); VROculusSession();
void Refresh(bool aForceRefresh = false); void Refresh(bool aForceRefresh = false);
void StartTracking();
void StopTracking();
bool IsTrackingReady() const; bool IsTrackingReady() const;
bool IsRenderReady() const;
ovrSession Get();
void StartPresentation(const IntSize& aSize); void StartPresentation(const IntSize& aSize);
void StopPresentation(); void StopPresentation();
void StopTracking(); bool IsPresentationReady() const;
bool IsMounted() const;
ovrSession Get();
bool IsQuitTimeoutActive(); bool IsQuitTimeoutActive();
already_AddRefed<layers::CompositingRenderTargetD3D11> GetNextRenderTarget(); already_AddRefed<layers::CompositingRenderTargetD3D11> GetNextRenderTarget();
ovrTextureSwapChain GetSwapChain(); ovrTextureSwapChain GetSwapChain();
@ -65,8 +67,12 @@ private:
// The timestamp of the last ending presentation // The timestamp of the last ending presentation
TimeStamp mLastPresentationEnd; TimeStamp mLastPresentationEnd;
VRTelemetry mTelemetry; VRTelemetry mTelemetry;
bool mPresenting; bool mRequestPresentation;
bool mRequestTracking;
bool mTracking;
bool mDrawBlack; bool mDrawBlack;
bool mIsConnected;
bool mIsMounted;
~VROculusSession(); ~VROculusSession();
void Uninitialize(bool aUnloadLib); void Uninitialize(bool aUnloadLib);
@ -84,7 +90,6 @@ private:
class VRDisplayOculus : public VRDisplayHost class VRDisplayOculus : public VRDisplayHost
{ {
public: public:
virtual void NotifyVSync() override;
void ZeroSensor() override; void ZeroSensor() override;
protected: protected:
@ -100,6 +105,7 @@ protected:
public: public:
explicit VRDisplayOculus(VROculusSession* aSession); explicit VRDisplayOculus(VROculusSession* aSession);
void Destroy(); void Destroy();
void Refresh();
protected: protected:
virtual ~VRDisplayOculus(); virtual ~VRDisplayOculus();
@ -176,7 +182,10 @@ public:
static already_AddRefed<VRSystemManagerOculus> Create(); static already_AddRefed<VRSystemManagerOculus> Create();
virtual void Destroy() override; virtual void Destroy() override;
virtual void Shutdown() override; virtual void Shutdown() override;
virtual bool GetHMDs(nsTArray<RefPtr<VRDisplayHost> >& aHMDResult) override; virtual void Enumerate() override;
virtual void NotifyVSync() override;
virtual bool ShouldInhibitEnumeration() override;
virtual void GetHMDs(nsTArray<RefPtr<VRDisplayHost> >& aHMDResult) override;
virtual bool GetIsPresenting() override; virtual bool GetIsPresenting() override;
virtual void HandleInput() override; virtual void HandleInput() override;
virtual void GetControllers(nsTArray<RefPtr<VRControllerHost>>& virtual void GetControllers(nsTArray<RefPtr<VRControllerHost>>&

Просмотреть файл

@ -202,8 +202,10 @@ VRDisplayOpenVR::GetIsHmdPresent()
} }
void void
VRDisplayOpenVR::PollEvents() VRDisplayOpenVR::Refresh()
{ {
mIsHmdPresent = ::vr::VR_IsHmdPresent();
::vr::VREvent_t event; ::vr::VREvent_t event;
while (mVRSystem && mVRSystem->PollNextEvent(&event, sizeof(event))) { while (mVRSystem && mVRSystem->PollNextEvent(&event, sizeof(event))) {
switch (event.eventType) { switch (event.eventType) {
@ -244,8 +246,6 @@ VRDisplayOpenVR::PollEvents()
VRHMDSensorState VRHMDSensorState
VRDisplayOpenVR::GetSensorState() VRDisplayOpenVR::GetSensorState()
{ {
PollEvents();
const uint32_t posesSize = ::vr::k_unTrackedDeviceIndex_Hmd + 1; const uint32_t posesSize = ::vr::k_unTrackedDeviceIndex_Hmd + 1;
::vr::TrackedDevicePose_t poses[posesSize]; ::vr::TrackedDevicePose_t poses[posesSize];
// Note: We *must* call WaitGetPoses in order for any rendering to happen at all. // Note: We *must* call WaitGetPoses in order for any rendering to happen at all.
@ -422,17 +422,6 @@ VRDisplayOpenVR::SubmitFrame(MacIOSurface* aMacIOSurface,
#endif #endif
void
VRDisplayOpenVR::NotifyVSync()
{
// We check if HMD is available once per frame.
mIsHmdPresent = ::vr::VR_IsHmdPresent();
// Make sure we respond to OpenVR events even when not presenting
PollEvents();
VRDisplayHost::NotifyVSync();
}
VRControllerOpenVR::VRControllerOpenVR(dom::GamepadHand aHand, uint32_t aDisplayID, VRControllerOpenVR::VRControllerOpenVR(dom::GamepadHand aHand, uint32_t aDisplayID,
uint32_t aNumButtons, uint32_t aNumTriggers, uint32_t aNumButtons, uint32_t aNumTriggers,
uint32_t aNumAxes, const nsCString& aId) uint32_t aNumAxes, const nsCString& aId)
@ -633,50 +622,87 @@ VRSystemManagerOpenVR::Shutdown()
mVRSystem = nullptr; mVRSystem = nullptr;
} }
bool void
VRSystemManagerOpenVR::GetHMDs(nsTArray<RefPtr<VRDisplayHost>>& aHMDResult) VRSystemManagerOpenVR::NotifyVSync()
{ {
if (!::vr::VR_IsHmdPresent() || VRSystemManager::NotifyVSync();
(mOpenVRHMD && !mOpenVRHMD->GetIsHmdPresent())) {
// OpenVR runtime could be quit accidentally, // Avoid doing anything unless we have already
// and we make it re-initialize. // successfully enumerated and loaded the OpenVR
mOpenVRHMD = nullptr; // runtime.
mVRSystem = nullptr; if (mVRSystem == nullptr) {
} else if (mOpenVRHMD == nullptr) { return;
}
if (mOpenVRHMD) {
mOpenVRHMD->Refresh();
if (!mOpenVRHMD->GetIsHmdPresent()) {
// OpenVR runtime could be quit accidentally
// or a device could be disconnected.
// We free up resources and must re-initialize
// if a device is detected again later.
mOpenVRHMD = nullptr;
mVRSystem = nullptr;
}
}
}
void
VRSystemManagerOpenVR::Enumerate()
{
if (mOpenVRHMD == nullptr && ::vr::VR_IsHmdPresent()) {
::vr::HmdError err; ::vr::HmdError err;
::vr::VR_Init(&err, ::vr::EVRApplicationType::VRApplication_Scene); ::vr::VR_Init(&err, ::vr::EVRApplicationType::VRApplication_Scene);
if (err) { if (err) {
return false; return;
} }
::vr::IVRSystem *system = (::vr::IVRSystem *)::vr::VR_GetGenericInterface(::vr::IVRSystem_Version, &err); ::vr::IVRSystem *system = (::vr::IVRSystem *)::vr::VR_GetGenericInterface(::vr::IVRSystem_Version, &err);
if (err || !system) { if (err || !system) {
::vr::VR_Shutdown(); ::vr::VR_Shutdown();
return false; return;
} }
::vr::IVRChaperone *chaperone = (::vr::IVRChaperone *)::vr::VR_GetGenericInterface(::vr::IVRChaperone_Version, &err); ::vr::IVRChaperone *chaperone = (::vr::IVRChaperone *)::vr::VR_GetGenericInterface(::vr::IVRChaperone_Version, &err);
if (err || !chaperone) { if (err || !chaperone) {
::vr::VR_Shutdown(); ::vr::VR_Shutdown();
return false; return;
} }
::vr::IVRCompositor *compositor = (::vr::IVRCompositor*)::vr::VR_GetGenericInterface(::vr::IVRCompositor_Version, &err); ::vr::IVRCompositor *compositor = (::vr::IVRCompositor*)::vr::VR_GetGenericInterface(::vr::IVRCompositor_Version, &err);
if (err || !compositor) { if (err || !compositor) {
::vr::VR_Shutdown(); ::vr::VR_Shutdown();
return false; return;
} }
mVRSystem = system; mVRSystem = system;
mOpenVRHMD = new VRDisplayOpenVR(system, chaperone, compositor); mOpenVRHMD = new VRDisplayOpenVR(system, chaperone, compositor);
} }
}
bool
VRSystemManagerOpenVR::ShouldInhibitEnumeration()
{
if (VRSystemManager::ShouldInhibitEnumeration()) {
return true;
}
if (mOpenVRHMD) { if (mOpenVRHMD) {
aHMDResult.AppendElement(mOpenVRHMD); // When we find an a VR device, don't
// allow any further enumeration as it
// may get picked up redundantly by other
// API's.
return true; return true;
} }
return false; return false;
} }
void
VRSystemManagerOpenVR::GetHMDs(nsTArray<RefPtr<VRDisplayHost>>& aHMDResult)
{
if (mOpenVRHMD) {
aHMDResult.AppendElement(mOpenVRHMD);
}
}
bool bool
VRSystemManagerOpenVR::GetIsPresenting() VRSystemManagerOpenVR::GetIsPresenting()
{ {

Просмотреть файл

@ -30,7 +30,6 @@ namespace impl {
class VRDisplayOpenVR : public VRDisplayHost class VRDisplayOpenVR : public VRDisplayHost
{ {
public: public:
virtual void NotifyVSync() override;
void ZeroSensor() override; void ZeroSensor() override;
bool GetIsHmdPresent(); bool GetIsHmdPresent();
@ -54,7 +53,7 @@ public:
explicit VRDisplayOpenVR(::vr::IVRSystem *aVRSystem, explicit VRDisplayOpenVR(::vr::IVRSystem *aVRSystem,
::vr::IVRChaperone *aVRChaperone, ::vr::IVRChaperone *aVRChaperone,
::vr::IVRCompositor *aVRCompositor); ::vr::IVRCompositor *aVRCompositor);
void Refresh();
protected: protected:
virtual ~VRDisplayOpenVR(); virtual ~VRDisplayOpenVR();
void Destroy(); void Destroy();
@ -70,7 +69,6 @@ protected:
void UpdateStageParameters(); void UpdateStageParameters();
void UpdateEyeParameters(gfx::Matrix4x4* aHeadToEyeTransforms = nullptr); void UpdateEyeParameters(gfx::Matrix4x4* aHeadToEyeTransforms = nullptr);
void PollEvents();
bool SubmitFrame(void* aTextureHandle, bool SubmitFrame(void* aTextureHandle,
::vr::ETextureType aTextureType, ::vr::ETextureType aTextureType,
const IntSize& aSize, const IntSize& aSize,
@ -127,7 +125,10 @@ public:
virtual void Destroy() override; virtual void Destroy() override;
virtual void Shutdown() override; virtual void Shutdown() override;
virtual bool GetHMDs(nsTArray<RefPtr<VRDisplayHost> >& aHMDResult) override; virtual void NotifyVSync() override;
virtual void Enumerate() override;
virtual bool ShouldInhibitEnumeration() override;
virtual void GetHMDs(nsTArray<RefPtr<VRDisplayHost>>& aHMDResult) override;
virtual bool GetIsPresenting() override; virtual bool GetIsPresenting() override;
virtual void HandleInput() override; virtual void HandleInput() override;
virtual void GetControllers(nsTArray<RefPtr<VRControllerHost>>& virtual void GetControllers(nsTArray<RefPtr<VRControllerHost>>&

Просмотреть файл

@ -566,12 +566,10 @@ VRDisplayPuppet::SubmitFrame(const mozilla::layers::EGLImageDescriptor* aDescrip
#endif #endif
void void
VRDisplayPuppet::NotifyVSync() VRDisplayPuppet::Refresh()
{ {
// We update mIsConneced once per frame. // We update mIsConneced once per refresh.
mDisplayInfo.mIsConnected = true; mDisplayInfo.mIsConnected = true;
VRDisplayHost::NotifyVSync();
} }
VRControllerPuppet::VRControllerPuppet(dom::GamepadHand aHand, uint32_t aDisplayID) VRControllerPuppet::VRControllerPuppet(dom::GamepadHand aHand, uint32_t aDisplayID)
@ -703,14 +701,45 @@ VRSystemManagerPuppet::Shutdown()
mPuppetHMD = nullptr; mPuppetHMD = nullptr;
} }
bool void
VRSystemManagerPuppet::GetHMDs(nsTArray<RefPtr<VRDisplayHost>>& aHMDResult) VRSystemManagerPuppet::NotifyVSync()
{
VRSystemManager::NotifyVSync();
if (mPuppetHMD) {
mPuppetHMD->Refresh();
}
}
void
VRSystemManagerPuppet::Enumerate()
{ {
if (mPuppetHMD == nullptr) { if (mPuppetHMD == nullptr) {
mPuppetHMD = new VRDisplayPuppet(); mPuppetHMD = new VRDisplayPuppet();
} }
aHMDResult.AppendElement(mPuppetHMD); }
return true;
bool
VRSystemManagerPuppet::ShouldInhibitEnumeration()
{
if (VRSystemManager::ShouldInhibitEnumeration()) {
return true;
}
if (mPuppetHMD) {
// When we find an a VR device, don't
// allow any further enumeration as it
// may get picked up redundantly by other
// API's.
return true;
}
return false;
}
void
VRSystemManagerPuppet::GetHMDs(nsTArray<RefPtr<VRDisplayHost>>& aHMDResult)
{
if (mPuppetHMD) {
aHMDResult.AppendElement(mPuppetHMD);
}
} }
bool bool

Просмотреть файл

@ -24,7 +24,6 @@ class VRDisplayPuppet : public VRDisplayHost
{ {
public: public:
void SetDisplayInfo(const VRDisplayInfo& aDisplayInfo); void SetDisplayInfo(const VRDisplayInfo& aDisplayInfo);
virtual void NotifyVSync() override;
void SetSensorState(const VRHMDSensorState& aSensorState); void SetSensorState(const VRHMDSensorState& aSensorState);
void ZeroSensor() override; void ZeroSensor() override;
@ -50,6 +49,7 @@ protected:
public: public:
explicit VRDisplayPuppet(); explicit VRDisplayPuppet();
void Refresh();
protected: protected:
virtual ~VRDisplayPuppet(); virtual ~VRDisplayPuppet();
@ -110,7 +110,9 @@ public:
virtual void Destroy() override; virtual void Destroy() override;
virtual void Shutdown() override; virtual void Shutdown() override;
virtual bool GetHMDs(nsTArray<RefPtr<VRDisplayHost>>& aHMDResult) override; virtual void Enumerate() override;
virtual bool ShouldInhibitEnumeration() override;
virtual void GetHMDs(nsTArray<RefPtr<VRDisplayHost>>& aHMDResult) override;
virtual bool GetIsPresenting() override; virtual bool GetIsPresenting() override;
virtual void HandleInput() override; virtual void HandleInput() override;
virtual void GetControllers(nsTArray<RefPtr<VRControllerHost>>& virtual void GetControllers(nsTArray<RefPtr<VRControllerHost>>&
@ -123,6 +125,7 @@ public:
double aDuration, double aDuration,
uint32_t aPromiseID) override; uint32_t aPromiseID) override;
virtual void StopVibrateHaptic(uint32_t aControllerIdx) override; virtual void StopVibrateHaptic(uint32_t aControllerIdx) override;
virtual void NotifyVSync() override;
protected: protected:
VRSystemManagerPuppet(); VRSystemManagerPuppet();

Просмотреть файл

@ -5157,6 +5157,20 @@ pref("dom.vr.openvr.enabled", true);
// See Bug 1310663 (Linux) // See Bug 1310663 (Linux)
pref("dom.vr.openvr.enabled", false); pref("dom.vr.openvr.enabled", false);
#endif #endif
// Minimum number of milliseconds that the browser will wait before
// attempting to poll again for connected VR controllers. The browser
// will not attempt to poll for VR controllers until it needs to use them.
pref("dom.vr.controller.enumerate.interval", 1000);
// Minimum number of milliseconds that the browser will wait before
// attempting to poll again for connected VR displays. The browser
// will not attempt to poll for VR displays until it needs to use
// them, such as when detecting a WebVR site.
pref("dom.vr.display.enumerate.interval", 5000);
// Minimum number of milliseconds that the VR session will be kept
// alive after the browser and content no longer are using the
// hardware. If a VR multitasking environment, this should be set
// very low or set to 0.
pref("dom.vr.inactive.timeout", 5000);
// Pose prediction reduces latency effects by returning future predicted HMD // Pose prediction reduces latency effects by returning future predicted HMD
// poses to callers of the WebVR API. This currently only has an effect for // poses to callers of the WebVR API. This currently only has an effect for
// Oculus Rift on SDK 0.8 or greater. // Oculus Rift on SDK 0.8 or greater.