Bug 1293793 - Ensure VRFrameData.timestamp is a monotonically increasing value
- Oculus and OSVR VRFrameData.timestamp values were already returning correct
timestamps using their respective API's timestamp functions.
- OpenVR is now using timestamp values returned by the OpenVR API.
- A pseudo-random base for VRFrameData.timestamp has been implemented
in order to avoid leaking details related to how long the user has
been using their VR headset before hitting a page.
- More details on timestamp base within code comments...
MozReview-Commit-ID: 7VdiRn7l8Rb
--- a/dom/vr/VRDisplay.cpp
+++ b/dom/vr/VRDisplay.cpp
@@ -748,16 +748,33 @@ VRFrameData::Update(const VRFrameInfo& a
void
VRFrameInfo::Update(const gfx::VRDisplayInfo& aInfo,
const gfx::VRHMDSensorState& aState,
float aDepthNear,
float aDepthFar)
{
mVRState = aState;
+ if (mTimeStampOffset == 0.0f) {
+ /**
+ * A mTimeStampOffset value of 0.0f indicates that this is the first iteration
+ * and an offset has not yet been set.
+ *
+ * Generate a value for mTimeStampOffset such that if aState.timestamp is
+ * monotonically increasing, aState.timestamp + mTimeStampOffset will never
+ * be a negative number and will start at a pseudo-random offset
+ * between 1000.0f and 11000.0f seconds.
+ *
+ * We use a pseudo random offset rather than 0.0f just to discourage users
+ * from making the assumption that the timestamp returned in the WebVR API
+ * has a base of 0, which is not necessarily true in all UA's.
+ */
+ mTimeStampOffset = float(rand()) / RAND_MAX * 10000.0f + 1000.0f - aState.timestamp;
+ }
+ mVRState.timestamp = aState.timestamp + mTimeStampOffset;
gfx::Quaternion qt;
if (mVRState.flags & gfx::VRDisplayCapabilityFlags::Cap_Orientation) {
qt.x = mVRState.orientation[0];
qt.y = mVRState.orientation[1];
qt.z = mVRState.orientation[2];
qt.w = mVRState.orientation[3];
}
@@ -785,16 +802,17 @@ VRFrameInfo::Update(const gfx::VRDisplay
const gfx::VRFieldOfView leftFOV = aInfo.mEyeFOV[gfx::VRDisplayInfo::Eye_Left];
mLeftProjection = leftFOV.ConstructProjectionMatrix(aDepthNear, aDepthFar, true);
const gfx::VRFieldOfView rightFOV = aInfo.mEyeFOV[gfx::VRDisplayInfo::Eye_Right];
mRightProjection = rightFOV.ConstructProjectionMatrix(aDepthNear, aDepthFar, true);
}
VRFrameInfo::VRFrameInfo()
+ : mTimeStampOffset(0.0f)
{
mVRState.Clear();
}
bool
VRFrameInfo::IsDirty()
{
return mVRState.timestamp == 0;
--- a/dom/vr/VRDisplay.h
+++ b/dom/vr/VRDisplay.h
@@ -145,16 +145,23 @@ struct VRFrameInfo
bool IsDirty();
gfx::VRHMDSensorState mVRState;
gfx::Matrix4x4 mLeftProjection;
gfx::Matrix4x4 mLeftView;
gfx::Matrix4x4 mRightProjection;
gfx::Matrix4x4 mRightView;
+ /**
+ * In order to avoid leaking information related to the duration of
+ * the user's VR session, we re-base timestamps.
+ * mTimeStampOffset is added to the actual timestamp returned by the
+ * underlying VR platform API when returned through WebVR API's.
+ */
+ double mTimeStampOffset;
};
class VRFrameData final : public nsWrapperCache
{
public:
NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(VRFrameData)
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(VRFrameData)
--- a/gfx/vr/gfxVROpenVR.cpp
+++ b/gfx/vr/gfxVROpenVR.cpp
@@ -292,17 +292,25 @@ VRDisplayOpenVR::GetSensorState(double t
PollEvents();
::vr::TrackedDevicePose_t poses[::vr::k_unMaxTrackedDeviceCount];
// Note: We *must* call WaitGetPoses in order for any rendering to happen at all
mVRCompositor->WaitGetPoses(poses, ::vr::k_unMaxTrackedDeviceCount, nullptr, 0);
VRHMDSensorState result;
result.Clear();
- result.timestamp = PR_Now();
+
+ ::vr::Compositor_FrameTiming timing;
+ timing.m_nSize = sizeof(::vr::Compositor_FrameTiming);
+ if (mVRCompositor->GetFrameTiming(&timing)) {
+ result.timestamp = timing.m_flSystemTimeInSeconds;
+ } else {
+ // This should not happen, but log it just in case
+ NS_WARNING("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;