--- a/dom/vr/VRDisplay.cpp
+++ b/dom/vr/VRDisplay.cpp
@@ -499,30 +499,51 @@ VRDisplay::Capabilities()
}
VRStageParameters*
VRDisplay::GetStageParameters()
{
return mStageParameters;
}
+void
+VRDisplay::UpdateFrameInfo()
+{
+ /**
+ * The WebVR 1.1 spec Requires that VRDisplay.getPose and VRDisplay.getFrameData
+ * must return the same values until the next VRDisplay.submitFrame.
+ *
+ * mFrameInfo is marked dirty at the end of the frame or start of a new
+ * composition and lazily created here in order to receive mid-frame
+ * pose-prediction updates while still ensuring conformance to the WebVR spec
+ * requirements.
+ *
+ * If we are not presenting WebVR content, the frame will never end and we should
+ * return the latest frame data always.
+ */
+ if (mFrameInfo.IsDirty() || !mPresentation) {
+ gfx::VRHMDSensorState state = mClient->GetSensorState();
+ const gfx::VRDisplayInfo& info = mClient->GetDisplayInfo();
+ mFrameInfo.Update(info, state, mDepthNear, mDepthFar);
+ }
+}
+
bool
VRDisplay::GetFrameData(VRFrameData& aFrameData)
{
- gfx::VRHMDSensorState state = mClient->GetSensorState();
- const gfx::VRDisplayInfo& info = mClient->GetDisplayInfo();
- aFrameData.Update(info, state, mDepthNear, mDepthFar);
+ UpdateFrameInfo();
+ aFrameData.Update(mFrameInfo);
return true;
}
already_AddRefed<VRPose>
VRDisplay::GetPose()
{
- gfx::VRHMDSensorState state = mClient->GetSensorState();
- RefPtr<VRPose> obj = new VRPose(GetParentObject(), state);
+ UpdateFrameInfo();
+ RefPtr<VRPose> obj = new VRPose(GetParentObject(), mFrameInfo.mVRState);
return obj.forget();
}
void
VRDisplay::ResetPose()
{
mClient->ZeroSensor();
@@ -544,16 +565,17 @@ VRDisplay::RequestPresent(const nsTArray
NS_ENSURE_TRUE(obs, nullptr);
if (mClient->GetIsPresenting()) {
// Only one presentation allowed per VRDisplay
// on a first-come-first-serve basis.
promise->MaybeRejectWithUndefined();
} else {
mPresentation = mClient->BeginPresentation(aLayers);
+ mFrameInfo.Clear();
nsresult rv = obs->AddObserver(this, "inner-window-destroyed", false);
if (NS_WARN_IF(NS_FAILED(rv))) {
mPresentation = nullptr;
promise->MaybeRejectWithUndefined();
} else {
promise->MaybeResolve(JS::UndefinedHandleValue);
}
@@ -632,16 +654,17 @@ VRDisplay::SubmitFrame(const Optional<No
{
if (mPresentation) {
if (aPose.WasPassed()) {
mPresentation->SubmitFrame(aPose.Value().FrameID());
} else {
mPresentation->SubmitFrame(0);
}
}
+ mFrameInfo.Clear();
}
int32_t
VRDisplay::RequestAnimationFrame(FrameRequestCallback& aCallback,
ErrorResult& aError)
{
gfx::VRManagerChild* vm = gfx::VRManagerChild::Get();
@@ -760,65 +783,74 @@ VRFrameData::LazyCreateMatrix(JS::Heap<J
JS::ExposeObjectToActiveJS(aArray);
}
aRetval.set(aArray);
}
double
VRFrameData::Timestamp() const
{
- return mVRState.timestamp * 1000.0f; // Converting from seconds to milliseconds
+ // Converting from seconds to milliseconds
+ return mFrameInfo.mVRState.timestamp * 1000.0f;
}
void
VRFrameData::GetLeftProjectionMatrix(JSContext* aCx,
JS::MutableHandle<JSObject*> aRetval,
ErrorResult& aRv)
{
- LazyCreateMatrix(mLeftProjectionMatrix, mLeftProjection, aCx, aRetval, aRv);
+ LazyCreateMatrix(mLeftProjectionMatrix, mFrameInfo.mLeftProjection, aCx,
+ aRetval, aRv);
}
void
VRFrameData::GetLeftViewMatrix(JSContext* aCx,
JS::MutableHandle<JSObject*> aRetval,
ErrorResult& aRv)
{
- LazyCreateMatrix(mLeftViewMatrix, mLeftView, aCx, aRetval, aRv);
+ LazyCreateMatrix(mLeftViewMatrix, mFrameInfo.mLeftView, aCx, aRetval, aRv);
}
void
VRFrameData::GetRightProjectionMatrix(JSContext* aCx,
JS::MutableHandle<JSObject*> aRetval,
ErrorResult& aRv)
{
- LazyCreateMatrix(mRightProjectionMatrix, mRightProjection, aCx, aRetval, aRv);
+ LazyCreateMatrix(mRightProjectionMatrix, mFrameInfo.mRightProjection, aCx,
+ aRetval, aRv);
}
void
VRFrameData::GetRightViewMatrix(JSContext* aCx,
JS::MutableHandle<JSObject*> aRetval,
ErrorResult& aRv)
{
- LazyCreateMatrix(mRightViewMatrix, mRightView, aCx, aRetval, aRv);
+ LazyCreateMatrix(mRightViewMatrix, mFrameInfo.mRightView, aCx, aRetval, aRv);
}
void
-VRFrameData::Update(const gfx::VRDisplayInfo& aInfo,
- const gfx::VRHMDSensorState& aState,
- float aDepthNear,
- float aDepthFar)
+VRFrameData::Update(const VRFrameInfo& aFrameInfo)
{
- mVRState = aState;
+ mFrameInfo = aFrameInfo;
mLeftProjectionMatrix = nullptr;
mLeftViewMatrix = nullptr;
mRightProjectionMatrix = nullptr;
mRightViewMatrix = nullptr;
- mPose = new VRPose(GetParentObject(), aState);
+ mPose = new VRPose(GetParentObject(), mFrameInfo.mVRState);
+}
+
+void
+VRFrameInfo::Update(const gfx::VRDisplayInfo& aInfo,
+ const gfx::VRHMDSensorState& aState,
+ float aDepthNear,
+ float aDepthFar)
+{
+ mVRState = aState;
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];
}
@@ -843,13 +875,29 @@ VRFrameData::Update(const gfx::VRDisplay
if (fabs(aDepthFar - aDepthNear) < kEpsilon) {
aDepthFar = aDepthNear + kEpsilon;
}
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()
+{
+ mVRState.Clear();
+}
+
+bool
+VRFrameInfo::IsDirty()
+{
+ return mVRState.timestamp == 0;
+}
+
+void
+VRFrameInfo::Clear()
+{
+ mVRState.Clear();
}
} // namespace dom
} // namespace mozilla
--- a/dom/vr/VRDisplay.h
+++ b/dom/vr/VRDisplay.h
@@ -140,30 +140,46 @@ protected:
JS::Heap<JSObject*> mLinearVelocity;
JS::Heap<JSObject*> mLinearAcceleration;
JS::Heap<JSObject*> mOrientation;
JS::Heap<JSObject*> mAngularVelocity;
JS::Heap<JSObject*> mAngularAcceleration;
};
+struct VRFrameInfo
+{
+ VRFrameInfo();
+
+ void Update(const gfx::VRDisplayInfo& aInfo,
+ const gfx::VRHMDSensorState& aState,
+ float aDepthNear,
+ float aDepthFar);
+
+ void Clear();
+ bool IsDirty();
+
+ gfx::VRHMDSensorState mVRState;
+ gfx::Matrix4x4 mLeftProjection;
+ gfx::Matrix4x4 mLeftView;
+ gfx::Matrix4x4 mRightProjection;
+ gfx::Matrix4x4 mRightView;
+};
+
class VRFrameData final : public nsWrapperCache
{
public:
NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(VRFrameData)
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(VRFrameData)
explicit VRFrameData(nsISupports* aParent);
static already_AddRefed<VRFrameData> Constructor(const GlobalObject& aGlobal,
ErrorResult& aRv);
- void Update(const gfx::VRDisplayInfo& aInfo,
- const gfx::VRHMDSensorState& aState,
- float aDepthNear,
- float aDepthFar);
+ void Update(const VRFrameInfo& aFrameInfo);
// WebIDL Members
double Timestamp() const;
void GetLeftProjectionMatrix(JSContext* aCx,
JS::MutableHandle<JSObject*> aRetval,
ErrorResult& aRv);
void GetLeftViewMatrix(JSContext* aCx,
JS::MutableHandle<JSObject*> aRetval,
@@ -180,28 +196,23 @@ public:
// WebIDL Boilerplate
nsISupports* GetParentObject() const { return mParent; }
virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
protected:
~VRFrameData();
nsCOMPtr<nsISupports> mParent;
- gfx::VRHMDSensorState mVRState;
+ VRFrameInfo mFrameInfo;
RefPtr<VRPose> mPose;
JS::Heap<JSObject*> mLeftProjectionMatrix;
JS::Heap<JSObject*> mLeftViewMatrix;
JS::Heap<JSObject*> mRightProjectionMatrix;
JS::Heap<JSObject*> mRightViewMatrix;
- gfx::Matrix4x4 mLeftProjection;
- gfx::Matrix4x4 mLeftView;
- gfx::Matrix4x4 mRightProjection;
- gfx::Matrix4x4 mRightView;
-
void LazyCreateMatrix(JS::Heap<JSObject*>& aArray, gfx::Matrix4x4& aMat,
JSContext* aCx, JS::MutableHandle<JSObject*> aRetval,
ErrorResult& aRv);
};
class VRStageParameters final : public nsWrapperCache
{
public:
@@ -327,27 +338,37 @@ public:
void CancelAnimationFrame(int32_t aHandle, mozilla::ErrorResult& aError);
protected:
VRDisplay(nsPIDOMWindowInner* aWindow, gfx::VRDisplayClient* aClient);
virtual ~VRDisplay();
virtual void LastRelease() override;
void ExitPresentInternal();
+ void UpdateFrameInfo();
RefPtr<gfx::VRDisplayClient> mClient;
uint32_t mDisplayId;
nsString mDisplayName;
RefPtr<VRDisplayCapabilities> mCapabilities;
RefPtr<VRStageParameters> mStageParameters;
double mDepthNear;
double mDepthFar;
RefPtr<gfx::VRDisplayPresentation> mPresentation;
+
+ /**
+ * The WebVR 1.1 spec Requires that VRDisplay.getPose and VRDisplay.getFrameData
+ * must return the same values until the next VRDisplay.submitFrame.
+ * mFrameInfo is updated only on the first call to either function within one
+ * frame. Subsequent calls before the next SubmitFrame or ExitPresent call
+ * will use these cached values.
+ */
+ VRFrameInfo mFrameInfo;
};
} // namespace dom
} // namespace mozilla
#endif