Backed out changeset 9d8b4c0b99ed (bug 1945683) for causing btime failures. CLOSED...
[gecko.git] / dom / vr / VRDisplay.cpp
blobcb20ddee2de090c6d0926fad1bd1c580cfc9af44
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "nsWrapperCache.h"
9 #include "mozilla/dom/Element.h"
10 #include "mozilla/dom/ElementBinding.h"
11 #include "mozilla/dom/Promise.h"
12 #include "mozilla/dom/UserActivation.h"
13 #include "mozilla/dom/VRDisplay.h"
14 #include "mozilla/dom/VRDisplayBinding.h"
15 #include "mozilla/HoldDropJSObjects.h"
16 #include "mozilla/Base64.h"
17 #include "mozilla/ProfilerMarkers.h"
18 #include "mozilla/Services.h"
19 #include "mozilla/StaticPrefs_dom.h"
20 #include "mozilla/gfx/DataSurfaceHelpers.h"
21 #include "Navigator.h"
22 #include "gfxUtils.h"
23 #include "gfxVR.h"
24 #include "VRDisplayClient.h"
25 #include "VRManagerChild.h"
26 #include "VRDisplayPresentation.h"
27 #include "nsIObserverService.h"
28 #include "nsISupportsPrimitives.h"
29 #include "nsGlobalWindowInner.h"
31 using namespace mozilla::gfx;
33 namespace mozilla::dom {
35 VRFieldOfView::VRFieldOfView(nsISupports* aParent, double aUpDegrees,
36 double aRightDegrees, double aDownDegrees,
37 double aLeftDegrees)
38 : mParent(aParent),
39 mUpDegrees(aUpDegrees),
40 mRightDegrees(aRightDegrees),
41 mDownDegrees(aDownDegrees),
42 mLeftDegrees(aLeftDegrees) {}
44 VRFieldOfView::VRFieldOfView(nsISupports* aParent,
45 const gfx::VRFieldOfView& aSrc)
46 : mParent(aParent),
47 mUpDegrees(aSrc.upDegrees),
48 mRightDegrees(aSrc.rightDegrees),
49 mDownDegrees(aSrc.downDegrees),
50 mLeftDegrees(aSrc.leftDegrees) {}
52 bool VRDisplayCapabilities::HasPosition() const {
53 return bool(mFlags & gfx::VRDisplayCapabilityFlags::Cap_Position) ||
54 bool(mFlags & gfx::VRDisplayCapabilityFlags::Cap_PositionEmulated);
57 bool VRDisplayCapabilities::HasOrientation() const {
58 return bool(mFlags & gfx::VRDisplayCapabilityFlags::Cap_Orientation);
61 bool VRDisplayCapabilities::HasExternalDisplay() const {
62 return bool(mFlags & gfx::VRDisplayCapabilityFlags::Cap_External);
65 bool VRDisplayCapabilities::CanPresent() const {
66 return bool(mFlags & gfx::VRDisplayCapabilityFlags::Cap_Present);
69 uint32_t VRDisplayCapabilities::MaxLayers() const {
70 return CanPresent() ? 1 : 0;
73 void VRDisplay::UpdateDisplayClient(
74 already_AddRefed<gfx::VRDisplayClient> aClient) {
75 mClient = std::move(aClient);
78 /*static*/
79 bool VRDisplay::RefreshVRDisplays(uint64_t aWindowId) {
80 gfx::VRManagerChild* vm = gfx::VRManagerChild::Get();
81 return vm && vm->RefreshVRDisplaysWithCallback(aWindowId);
84 /*static*/
85 void VRDisplay::UpdateVRDisplays(nsTArray<RefPtr<VRDisplay>>& aDisplays,
86 nsPIDOMWindowInner* aWindow) {
87 nsTArray<RefPtr<VRDisplay>> displays;
89 gfx::VRManagerChild* vm = gfx::VRManagerChild::Get();
90 nsTArray<RefPtr<gfx::VRDisplayClient>> updatedDisplays;
91 if (vm) {
92 vm->GetVRDisplays(updatedDisplays);
93 for (size_t i = 0; i < updatedDisplays.Length(); i++) {
94 RefPtr<gfx::VRDisplayClient> display = updatedDisplays[i];
95 bool isNewDisplay = true;
96 for (size_t j = 0; j < aDisplays.Length(); j++) {
97 if (aDisplays[j]->GetClient()->GetDisplayInfo().GetDisplayID() ==
98 display->GetDisplayInfo().GetDisplayID()) {
99 displays.AppendElement(aDisplays[j]);
100 isNewDisplay = false;
101 } else {
102 RefPtr<gfx::VRDisplayClient> ref = display;
103 aDisplays[j]->UpdateDisplayClient(do_AddRef(display));
104 displays.AppendElement(aDisplays[j]);
105 isNewDisplay = false;
109 if (isNewDisplay) {
110 displays.AppendElement(new VRDisplay(aWindow, display));
115 aDisplays = std::move(displays);
118 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(VRFieldOfView, mParent)
120 JSObject* VRFieldOfView::WrapObject(JSContext* aCx,
121 JS::Handle<JSObject*> aGivenProto) {
122 return VRFieldOfView_Binding::Wrap(aCx, this, aGivenProto);
125 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_WITH_JS_MEMBERS(VREyeParameters,
126 (mParent, mFOV),
127 (mOffset))
129 VREyeParameters::VREyeParameters(nsISupports* aParent,
130 const gfx::Point3D& aEyeTranslation,
131 const gfx::VRFieldOfView& aFOV,
132 const gfx::IntSize& aRenderSize)
133 : mParent(aParent),
134 mEyeTranslation(aEyeTranslation),
135 mRenderSize(aRenderSize) {
136 mFOV = new VRFieldOfView(aParent, aFOV);
137 mozilla::HoldJSObjects(this);
140 VREyeParameters::~VREyeParameters() { mozilla::DropJSObjects(this); }
142 VRFieldOfView* VREyeParameters::FieldOfView() { return mFOV; }
144 void VREyeParameters::GetOffset(JSContext* aCx,
145 JS::MutableHandle<JSObject*> aRetval,
146 ErrorResult& aRv) {
147 if (!mOffset) {
148 // Lazily create the Float32Array
149 mOffset =
150 dom::Float32Array::Create(aCx, this, mEyeTranslation.components, aRv);
151 if (aRv.Failed()) {
152 return;
155 aRetval.set(mOffset);
158 JSObject* VREyeParameters::WrapObject(JSContext* aCx,
159 JS::Handle<JSObject*> aGivenProto) {
160 return VREyeParameters_Binding::Wrap(aCx, this, aGivenProto);
163 VRStageParameters::VRStageParameters(
164 nsISupports* aParent, const gfx::Matrix4x4& aSittingToStandingTransform,
165 const gfx::Size& aSize)
166 : mParent(aParent),
167 mSittingToStandingTransform(aSittingToStandingTransform),
168 mSittingToStandingTransformArray(nullptr),
169 mSize(aSize) {
170 mozilla::HoldJSObjects(this);
173 VRStageParameters::~VRStageParameters() { mozilla::DropJSObjects(this); }
175 JSObject* VRStageParameters::WrapObject(JSContext* aCx,
176 JS::Handle<JSObject*> aGivenProto) {
177 return VRStageParameters_Binding::Wrap(aCx, this, aGivenProto);
180 NS_IMPL_CYCLE_COLLECTION_CLASS(VRStageParameters)
182 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(VRStageParameters)
183 NS_IMPL_CYCLE_COLLECTION_UNLINK(mParent)
184 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
185 tmp->mSittingToStandingTransformArray = nullptr;
186 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
188 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(VRStageParameters)
189 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent)
190 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
192 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(VRStageParameters)
193 NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
194 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(
195 mSittingToStandingTransformArray)
196 NS_IMPL_CYCLE_COLLECTION_TRACE_END
198 void VRStageParameters::GetSittingToStandingTransform(
199 JSContext* aCx, JS::MutableHandle<JSObject*> aRetval, ErrorResult& aRv) {
200 if (!mSittingToStandingTransformArray) {
201 // Lazily create the Float32Array
202 mSittingToStandingTransformArray = dom::Float32Array::Create(
203 aCx, this, mSittingToStandingTransform.components, aRv);
204 if (aRv.Failed()) {
205 return;
208 aRetval.set(mSittingToStandingTransformArray);
211 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(VRDisplayCapabilities, mParent)
213 JSObject* VRDisplayCapabilities::WrapObject(JSContext* aCx,
214 JS::Handle<JSObject*> aGivenProto) {
215 return VRDisplayCapabilities_Binding::Wrap(aCx, this, aGivenProto);
218 VRPose::VRPose(nsISupports* aParent, const gfx::VRHMDSensorState& aState)
219 : Pose(aParent), mVRState(aState) {
220 mozilla::HoldJSObjects(this);
223 VRPose::VRPose(nsISupports* aParent) : Pose(aParent) {
224 mVRState.inputFrameID = 0;
225 mVRState.timestamp = 0.0;
226 mVRState.flags = gfx::VRDisplayCapabilityFlags::Cap_None;
227 mozilla::HoldJSObjects(this);
230 VRPose::~VRPose() { mozilla::DropJSObjects(this); }
232 void VRPose::GetPosition(JSContext* aCx, JS::MutableHandle<JSObject*> aRetval,
233 ErrorResult& aRv) {
234 const bool valid =
235 bool(mVRState.flags & gfx::VRDisplayCapabilityFlags::Cap_Position) ||
236 bool(mVRState.flags &
237 gfx::VRDisplayCapabilityFlags::Cap_PositionEmulated);
238 SetFloat32Array(aCx, this, aRetval, mPosition,
239 valid ? mVRState.pose.position.data() : nullptr, 3, aRv);
242 void VRPose::GetLinearVelocity(JSContext* aCx,
243 JS::MutableHandle<JSObject*> aRetval,
244 ErrorResult& aRv) {
245 const bool valid =
246 bool(mVRState.flags & gfx::VRDisplayCapabilityFlags::Cap_Position) ||
247 bool(mVRState.flags &
248 gfx::VRDisplayCapabilityFlags::Cap_PositionEmulated);
249 SetFloat32Array(aCx, this, aRetval, mLinearVelocity,
250 valid ? mVRState.pose.linearVelocity.data() : nullptr, 3,
251 aRv);
254 void VRPose::GetLinearAcceleration(JSContext* aCx,
255 JS::MutableHandle<JSObject*> aRetval,
256 ErrorResult& aRv) {
257 const bool valid = bool(
258 mVRState.flags & gfx::VRDisplayCapabilityFlags::Cap_LinearAcceleration);
259 SetFloat32Array(aCx, this, aRetval, mLinearAcceleration,
260 valid ? mVRState.pose.linearAcceleration.data() : nullptr, 3,
261 aRv);
264 void VRPose::GetOrientation(JSContext* aCx,
265 JS::MutableHandle<JSObject*> aRetval,
266 ErrorResult& aRv) {
267 const bool valid =
268 bool(mVRState.flags & gfx::VRDisplayCapabilityFlags::Cap_Orientation);
269 SetFloat32Array(aCx, this, aRetval, mOrientation,
270 valid ? mVRState.pose.orientation.data() : nullptr, 4, aRv);
273 void VRPose::GetAngularVelocity(JSContext* aCx,
274 JS::MutableHandle<JSObject*> aRetval,
275 ErrorResult& aRv) {
276 const bool valid =
277 bool(mVRState.flags & gfx::VRDisplayCapabilityFlags::Cap_Orientation);
278 SetFloat32Array(aCx, this, aRetval, mAngularVelocity,
279 valid ? mVRState.pose.angularVelocity.data() : nullptr, 3,
280 aRv);
283 void VRPose::GetAngularAcceleration(JSContext* aCx,
284 JS::MutableHandle<JSObject*> aRetval,
285 ErrorResult& aRv) {
286 const bool valid = bool(
287 mVRState.flags & gfx::VRDisplayCapabilityFlags::Cap_AngularAcceleration);
288 SetFloat32Array(aCx, this, aRetval, mAngularAcceleration,
289 valid ? mVRState.pose.angularAcceleration.data() : nullptr, 3,
290 aRv);
293 void VRPose::Update(const gfx::VRHMDSensorState& aState) { mVRState = aState; }
295 JSObject* VRPose::WrapObject(JSContext* aCx,
296 JS::Handle<JSObject*> aGivenProto) {
297 return VRPose_Binding::Wrap(aCx, this, aGivenProto);
300 /* virtual */
301 JSObject* VRDisplay::WrapObject(JSContext* aCx,
302 JS::Handle<JSObject*> aGivenProto) {
303 return VRDisplay_Binding::Wrap(aCx, this, aGivenProto);
306 VRDisplay::VRDisplay(nsPIDOMWindowInner* aWindow, gfx::VRDisplayClient* aClient)
307 : DOMEventTargetHelper(aWindow),
308 mClient(aClient),
309 mDepthNear(0.01f) // Default value from WebVR Spec
311 mDepthFar(10000.0f) // Default value from WebVR Spec
313 mVRNavigationEventDepth(0),
314 mShutdown(false) {
315 const gfx::VRDisplayInfo& info = aClient->GetDisplayInfo();
316 mCapabilities = new VRDisplayCapabilities(aWindow, info.GetCapabilities());
317 if (info.GetCapabilities() &
318 gfx::VRDisplayCapabilityFlags::Cap_StageParameters) {
319 mStageParameters = new VRStageParameters(
320 aWindow, info.GetSittingToStandingTransform(), info.GetStageSize());
322 mozilla::HoldJSObjects(this);
323 nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
324 if (MOZ_LIKELY(obs)) {
325 obs->AddObserver(this, "inner-window-destroyed", false);
329 VRDisplay::~VRDisplay() {
330 MOZ_ASSERT(mShutdown);
331 mozilla::DropJSObjects(this);
334 void VRDisplay::LastRelease() {
335 // We don't want to wait for the CC to free up the presentation
336 // for use in other documents, so we do this in LastRelease().
337 Shutdown();
340 already_AddRefed<VREyeParameters> VRDisplay::GetEyeParameters(VREye aEye) {
341 gfx::VRDisplayState::Eye eye = aEye == VREye::Left
342 ? gfx::VRDisplayState::Eye_Left
343 : gfx::VRDisplayState::Eye_Right;
344 RefPtr<VREyeParameters> params = new VREyeParameters(
345 GetParentObject(), mClient->GetDisplayInfo().GetEyeTranslation(eye),
346 mClient->GetDisplayInfo().GetEyeFOV(eye),
347 mClient->GetDisplayInfo().SuggestedEyeResolution());
348 return params.forget();
351 VRDisplayCapabilities* VRDisplay::Capabilities() { return mCapabilities; }
353 VRStageParameters* VRDisplay::GetStageParameters() { return mStageParameters; }
355 uint32_t VRDisplay::DisplayId() const {
356 const gfx::VRDisplayInfo& info = mClient->GetDisplayInfo();
357 return info.GetDisplayID();
360 void VRDisplay::GetDisplayName(nsAString& aDisplayName) const {
361 const gfx::VRDisplayInfo& info = mClient->GetDisplayInfo();
362 CopyUTF8toUTF16(MakeStringSpan(info.GetDisplayName()), aDisplayName);
365 void VRDisplay::UpdateFrameInfo() {
367 * The WebVR 1.1 spec Requires that VRDisplay.getPose and
368 * VRDisplay.getFrameData must return the same values until the next
369 * VRDisplay.submitFrame.
371 * mFrameInfo is marked dirty at the end of the frame or start of a new
372 * composition and lazily created here in order to receive mid-frame
373 * pose-prediction updates while still ensuring conformance to the WebVR spec
374 * requirements.
376 * If we are not presenting WebVR content, the frame will never end and we
377 * should return the latest frame data always.
379 mFrameInfo.Clear();
381 if ((mFrameInfo.IsDirty() && IsPresenting()) ||
382 mClient->GetDisplayInfo().GetPresentingGroups() == 0) {
383 const gfx::VRHMDSensorState& state = mClient->GetSensorState();
384 const gfx::VRDisplayInfo& info = mClient->GetDisplayInfo();
385 mFrameInfo.Update(info, state, mDepthNear, mDepthFar);
389 bool VRDisplay::GetFrameData(VRFrameData& aFrameData) {
390 UpdateFrameInfo();
391 if (!(mFrameInfo.mVRState.flags &
392 gfx::VRDisplayCapabilityFlags::Cap_Orientation)) {
393 // We must have at minimum Cap_Orientation for a valid pose.
394 return false;
396 aFrameData.Update(mFrameInfo);
397 return true;
400 already_AddRefed<VRPose> VRDisplay::GetPose() {
401 UpdateFrameInfo();
402 RefPtr<VRPose> obj = new VRPose(GetParentObject(), mFrameInfo.mVRState);
404 return obj.forget();
407 void VRDisplay::ResetPose() {
408 // ResetPose is deprecated and unimplemented
409 // We must keep this stub function around as its referenced by
410 // VRDisplay.webidl. Not asserting here, as that could break existing web
411 // content.
414 void VRDisplay::StartVRNavigation() { mClient->StartVRNavigation(); }
416 void VRDisplay::StartHandlingVRNavigationEvent() {
417 mHandlingVRNavigationEventStart = TimeStamp::Now();
418 ++mVRNavigationEventDepth;
419 TimeDuration timeout =
420 TimeDuration::FromMilliseconds(StaticPrefs::dom_vr_navigation_timeout());
421 // A 0 or negative TimeDuration indicates that content may take
422 // as long as it wishes to respond to the event, as long as
423 // it happens before the event exits.
424 if (timeout.ToMilliseconds() > 0) {
425 mClient->StopVRNavigation(timeout);
429 void VRDisplay::StopHandlingVRNavigationEvent() {
430 MOZ_ASSERT(mVRNavigationEventDepth > 0);
431 --mVRNavigationEventDepth;
432 if (mVRNavigationEventDepth == 0) {
433 mClient->StopVRNavigation(TimeDuration::FromMilliseconds(0));
437 bool VRDisplay::IsHandlingVRNavigationEvent() {
438 if (mVRNavigationEventDepth == 0) {
439 return false;
441 if (mHandlingVRNavigationEventStart.IsNull()) {
442 return false;
444 TimeDuration timeout =
445 TimeDuration::FromMilliseconds(StaticPrefs::dom_vr_navigation_timeout());
446 return timeout.ToMilliseconds() <= 0 ||
447 (TimeStamp::Now() - mHandlingVRNavigationEventStart) <= timeout;
450 void VRDisplay::OnPresentationGenerationChanged() { ExitPresentInternal(); }
452 already_AddRefed<Promise> VRDisplay::RequestPresent(
453 const nsTArray<VRLayer>& aLayers, CallerType aCallerType,
454 ErrorResult& aRv) {
455 nsCOMPtr<nsIGlobalObject> global = GetParentObject();
456 if (!global) {
457 aRv.Throw(NS_ERROR_FAILURE);
458 return nullptr;
461 RefPtr<Promise> promise = Promise::Create(global, aRv);
462 NS_ENSURE_TRUE(!aRv.Failed(), nullptr);
464 bool isChromePresentation = aCallerType == CallerType::System;
465 uint32_t presentationGroup =
466 isChromePresentation ? gfx::kVRGroupChrome : gfx::kVRGroupContent;
468 mClient->SetXRAPIMode(gfx::VRAPIMode::WebVR);
469 if (!UserActivation::IsHandlingUserInput() && !isChromePresentation &&
470 !IsHandlingVRNavigationEvent() && StaticPrefs::dom_vr_require_gesture() &&
471 !IsPresenting()) {
472 // The WebVR API states that if called outside of a user gesture, the
473 // promise must be rejected. We allow VR presentations to start within
474 // trusted events such as vrdisplayactivate, which triggers in response to
475 // HMD proximity sensors and when navigating within a VR presentation.
476 // This user gesture requirement is not enforced for chrome/system code.
477 promise->MaybeRejectWithUndefined();
478 } else if (!IsPresenting() && IsAnyPresenting(presentationGroup)) {
479 // Only one presentation allowed per VRDisplay on a
480 // first-come-first-serve basis.
481 // If this Javascript context is presenting, then we can replace our
482 // presentation with a new one containing new layers but we should never
483 // replace the presentation of another context.
484 // Simultaneous presentations in other groups are allowed in separate
485 // Javascript contexts to enable browser UI from chrome/system contexts.
486 // Eventually, this restriction will be loosened to enable multitasking
487 // use cases.
488 promise->MaybeRejectWithUndefined();
489 } else {
490 if (mPresentation) {
491 mPresentation->UpdateLayers(aLayers);
492 } else {
493 mPresentation = mClient->BeginPresentation(aLayers, presentationGroup);
495 mFrameInfo.Clear();
496 promise->MaybeResolve(JS::UndefinedHandleValue);
498 return promise.forget();
501 NS_IMETHODIMP
502 VRDisplay::Observe(nsISupports* aSubject, const char* aTopic,
503 const char16_t* aData) {
504 MOZ_ASSERT(NS_IsMainThread());
506 if (strcmp(aTopic, "inner-window-destroyed") == 0) {
507 nsCOMPtr<nsISupportsPRUint64> wrapper = do_QueryInterface(aSubject);
508 NS_ENSURE_TRUE(wrapper, NS_ERROR_FAILURE);
510 uint64_t innerID;
511 nsresult rv = wrapper->GetData(&innerID);
512 NS_ENSURE_SUCCESS(rv, rv);
514 if (!GetOwnerWindow() || GetOwnerWindow()->WindowID() == innerID) {
515 Shutdown();
518 return NS_OK;
521 // This should not happen.
522 return NS_ERROR_FAILURE;
525 already_AddRefed<Promise> VRDisplay::ExitPresent(ErrorResult& aRv) {
526 nsCOMPtr<nsIGlobalObject> global = GetParentObject();
527 if (!global) {
528 aRv.Throw(NS_ERROR_FAILURE);
529 return nullptr;
532 RefPtr<Promise> promise = Promise::Create(global, aRv);
533 NS_ENSURE_TRUE(!aRv.Failed(), nullptr);
535 if (!IsPresenting()) {
536 // We can not exit a presentation outside of the context that
537 // started the presentation.
538 promise->MaybeRejectWithUndefined();
539 } else {
540 promise->MaybeResolve(JS::UndefinedHandleValue);
541 ExitPresentInternal();
544 return promise.forget();
547 void VRDisplay::ExitPresentInternal() { mPresentation = nullptr; }
549 void VRDisplay::Shutdown() {
550 mShutdown = true;
551 ExitPresentInternal();
552 nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
553 if (MOZ_LIKELY(obs)) {
554 obs->RemoveObserver(this, "inner-window-destroyed");
558 void VRDisplay::GetLayers(nsTArray<VRLayer>& result) {
559 if (mPresentation) {
560 mPresentation->GetDOMLayers(result);
561 } else {
562 result = nsTArray<VRLayer>();
566 void VRDisplay::SubmitFrame() {
567 AUTO_PROFILER_TRACING_MARKER("VR", "SubmitFrameAtVRDisplay", OTHER);
569 if (mClient && !mClient->IsPresentationGenerationCurrent()) {
570 mPresentation = nullptr;
571 mClient->MakePresentationGenerationCurrent();
574 if (mPresentation) {
575 mPresentation->SubmitFrame();
577 mFrameInfo.Clear();
580 int32_t VRDisplay::RequestAnimationFrame(FrameRequestCallback& aCallback,
581 ErrorResult& aError) {
582 if (mShutdown) {
583 return 0;
586 gfx::VRManagerChild* vm = gfx::VRManagerChild::Get();
588 int32_t handle;
589 aError = vm->ScheduleFrameRequestCallback(aCallback, &handle);
590 return handle;
593 void VRDisplay::CancelAnimationFrame(int32_t aHandle, ErrorResult& aError) {
594 gfx::VRManagerChild* vm = gfx::VRManagerChild::Get();
595 vm->CancelFrameRequestCallback(aHandle);
598 bool VRDisplay::IsPresenting() const {
599 // IsPresenting returns true only if this Javascript context is presenting
600 // and will return false if another context is presenting.
601 return mPresentation != nullptr;
604 bool VRDisplay::IsAnyPresenting(uint32_t aGroupMask) const {
605 // IsAnyPresenting returns true if either this VRDisplay object or any other
606 // from anther Javascript context is presenting with a group matching
607 // aGroupMask.
608 if (mPresentation && (mPresentation->GetGroup() & aGroupMask)) {
609 return true;
611 if (mClient->GetDisplayInfo().GetPresentingGroups() & aGroupMask) {
612 return true;
614 return false;
617 bool VRDisplay::IsConnected() const { return mClient->GetIsConnected(); }
619 uint32_t VRDisplay::PresentingGroups() const {
620 return mClient->GetDisplayInfo().GetPresentingGroups();
623 uint32_t VRDisplay::GroupMask() const {
624 return mClient->GetDisplayInfo().GetGroupMask();
627 void VRDisplay::SetGroupMask(const uint32_t& aGroupMask) {
628 mClient->SetGroupMask(aGroupMask);
631 NS_IMPL_CYCLE_COLLECTION_INHERITED(VRDisplay, DOMEventTargetHelper,
632 mCapabilities, mStageParameters)
634 NS_IMPL_ADDREF_INHERITED(VRDisplay, DOMEventTargetHelper)
635 NS_IMPL_RELEASE_INHERITED(VRDisplay, DOMEventTargetHelper)
637 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(VRDisplay)
638 NS_INTERFACE_MAP_ENTRY(nsIObserver)
639 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, EventTarget)
640 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
642 NS_IMPL_CYCLE_COLLECTION_CLASS(VRFrameData)
644 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(VRFrameData)
645 NS_IMPL_CYCLE_COLLECTION_UNLINK(mParent, mPose)
646 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
647 tmp->mLeftProjectionMatrix = nullptr;
648 tmp->mLeftViewMatrix = nullptr;
649 tmp->mRightProjectionMatrix = nullptr;
650 tmp->mRightViewMatrix = nullptr;
651 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
653 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(VRFrameData)
654 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent, mPose)
655 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
657 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(VRFrameData)
658 NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
659 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mLeftProjectionMatrix)
660 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mLeftViewMatrix)
661 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mRightProjectionMatrix)
662 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mRightViewMatrix)
663 NS_IMPL_CYCLE_COLLECTION_TRACE_END
665 VRFrameData::VRFrameData(nsISupports* aParent)
666 : mParent(aParent),
667 mLeftProjectionMatrix(nullptr),
668 mLeftViewMatrix(nullptr),
669 mRightProjectionMatrix(nullptr),
670 mRightViewMatrix(nullptr) {
671 mozilla::HoldJSObjects(this);
672 mPose = new VRPose(aParent);
675 VRFrameData::~VRFrameData() { mozilla::DropJSObjects(this); }
677 /* static */
678 already_AddRefed<VRFrameData> VRFrameData::Constructor(
679 const GlobalObject& aGlobal) {
680 RefPtr<VRFrameData> obj = new VRFrameData(aGlobal.GetAsSupports());
681 return obj.forget();
684 JSObject* VRFrameData::WrapObject(JSContext* aCx,
685 JS::Handle<JSObject*> aGivenProto) {
686 return VRFrameData_Binding::Wrap(aCx, this, aGivenProto);
689 VRPose* VRFrameData::Pose() { return mPose; }
691 double VRFrameData::Timestamp() const {
692 // Converting from seconds to milliseconds
693 return mFrameInfo.mVRState.timestamp * 1000.0f;
696 void VRFrameData::GetLeftProjectionMatrix(JSContext* aCx,
697 JS::MutableHandle<JSObject*> aRetval,
698 ErrorResult& aRv) {
699 Pose::SetFloat32Array(aCx, this, aRetval, mLeftProjectionMatrix,
700 mFrameInfo.mLeftProjection.components, 16, aRv);
703 void VRFrameData::GetLeftViewMatrix(JSContext* aCx,
704 JS::MutableHandle<JSObject*> aRetval,
705 ErrorResult& aRv) {
706 Pose::SetFloat32Array(aCx, this, aRetval, mLeftViewMatrix,
707 mFrameInfo.mLeftView.components, 16, aRv);
710 void VRFrameData::GetRightProjectionMatrix(JSContext* aCx,
711 JS::MutableHandle<JSObject*> aRetval,
712 ErrorResult& aRv) {
713 Pose::SetFloat32Array(aCx, this, aRetval, mRightProjectionMatrix,
714 mFrameInfo.mRightProjection.components, 16, aRv);
717 void VRFrameData::GetRightViewMatrix(JSContext* aCx,
718 JS::MutableHandle<JSObject*> aRetval,
719 ErrorResult& aRv) {
720 Pose::SetFloat32Array(aCx, this, aRetval, mRightViewMatrix,
721 mFrameInfo.mRightView.components, 16, aRv);
724 void VRFrameData::Update(const VRFrameInfo& aFrameInfo) {
725 mFrameInfo = aFrameInfo;
726 mPose->Update(mFrameInfo.mVRState);
729 void VRFrameInfo::Update(const gfx::VRDisplayInfo& aInfo,
730 const gfx::VRHMDSensorState& aState, float aDepthNear,
731 float aDepthFar) {
732 mVRState = aState;
733 if (mTimeStampOffset == 0.0f) {
735 * A mTimeStampOffset value of 0.0f indicates that this is the first
736 * iteration and an offset has not yet been set.
738 * Generate a value for mTimeStampOffset such that if aState.timestamp is
739 * monotonically increasing, aState.timestamp + mTimeStampOffset will never
740 * be a negative number and will start at a pseudo-random offset
741 * between 1000.0f and 11000.0f seconds.
743 * We use a pseudo random offset rather than 0.0f just to discourage users
744 * from making the assumption that the timestamp returned in the WebVR API
745 * has a base of 0, which is not necessarily true in all UA's.
747 mTimeStampOffset =
748 float(rand()) / float(RAND_MAX) * 10000.0f + 1000.0f - aState.timestamp;
750 mVRState.timestamp = aState.timestamp + mTimeStampOffset;
752 // Avoid division by zero within ConstructProjectionMatrix
753 const float kEpsilon = 0.00001f;
754 if (fabs(aDepthFar - aDepthNear) < kEpsilon) {
755 aDepthFar = aDepthNear + kEpsilon;
758 const gfx::VRFieldOfView leftFOV =
759 aInfo.mDisplayState.eyeFOV[gfx::VRDisplayState::Eye_Left];
760 mLeftProjection =
761 leftFOV.ConstructProjectionMatrix(aDepthNear, aDepthFar, true);
762 const gfx::VRFieldOfView rightFOV =
763 aInfo.mDisplayState.eyeFOV[gfx::VRDisplayState::Eye_Right];
764 mRightProjection =
765 rightFOV.ConstructProjectionMatrix(aDepthNear, aDepthFar, true);
766 memcpy(mLeftView.components, aState.leftViewMatrix.data(),
767 sizeof(aState.leftViewMatrix));
768 memcpy(mRightView.components, aState.rightViewMatrix.data(),
769 sizeof(aState.rightViewMatrix));
772 VRFrameInfo::VRFrameInfo() : mTimeStampOffset(0.0f) {
773 mVRState.inputFrameID = 0;
774 mVRState.timestamp = 0.0;
775 mVRState.flags = gfx::VRDisplayCapabilityFlags::Cap_None;
778 bool VRFrameInfo::IsDirty() { return mVRState.timestamp == 0; }
780 void VRFrameInfo::Clear() { mVRState.Clear(); }
782 } // namespace mozilla::dom