VRDisplay.cpp (27745B)
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/. */ 6 7 #include "mozilla/dom/VRDisplay.h" 8 9 #include "Navigator.h" 10 #include "VRDisplayClient.h" 11 #include "VRDisplayPresentation.h" 12 #include "VRManagerChild.h" 13 #include "gfxUtils.h" 14 #include "gfxVR.h" 15 #include "mozilla/Base64.h" 16 #include "mozilla/HoldDropJSObjects.h" 17 #include "mozilla/ProfilerMarkers.h" 18 #include "mozilla/Services.h" 19 #include "mozilla/StaticPrefs_dom.h" 20 #include "mozilla/dom/Element.h" 21 #include "mozilla/dom/ElementBinding.h" 22 #include "mozilla/dom/Promise.h" 23 #include "mozilla/dom/UserActivation.h" 24 #include "mozilla/dom/VRDisplayBinding.h" 25 #include "mozilla/gfx/DataSurfaceHelpers.h" 26 #include "nsGlobalWindowInner.h" 27 #include "nsIObserverService.h" 28 #include "nsISupportsPrimitives.h" 29 #include "nsWrapperCache.h" 30 31 using namespace mozilla::gfx; 32 33 namespace mozilla::dom { 34 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) {} 43 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) {} 51 52 bool VRDisplayCapabilities::HasPosition() const { 53 return bool(mFlags & gfx::VRDisplayCapabilityFlags::Cap_Position) || 54 bool(mFlags & gfx::VRDisplayCapabilityFlags::Cap_PositionEmulated); 55 } 56 57 bool VRDisplayCapabilities::HasOrientation() const { 58 return bool(mFlags & gfx::VRDisplayCapabilityFlags::Cap_Orientation); 59 } 60 61 bool VRDisplayCapabilities::HasExternalDisplay() const { 62 return bool(mFlags & gfx::VRDisplayCapabilityFlags::Cap_External); 63 } 64 65 bool VRDisplayCapabilities::CanPresent() const { 66 return bool(mFlags & gfx::VRDisplayCapabilityFlags::Cap_Present); 67 } 68 69 uint32_t VRDisplayCapabilities::MaxLayers() const { 70 return CanPresent() ? 1 : 0; 71 } 72 73 void VRDisplay::UpdateDisplayClient( 74 already_AddRefed<gfx::VRDisplayClient> aClient) { 75 mClient = std::move(aClient); 76 } 77 78 /*static*/ 79 bool VRDisplay::RefreshVRDisplays(uint64_t aWindowId) { 80 gfx::VRManagerChild* vm = gfx::VRManagerChild::Get(); 81 return vm && vm->RefreshVRDisplaysWithCallback(aWindowId); 82 } 83 84 /*static*/ 85 void VRDisplay::UpdateVRDisplays(nsTArray<RefPtr<VRDisplay>>& aDisplays, 86 nsPIDOMWindowInner* aWindow) { 87 nsTArray<RefPtr<VRDisplay>> displays; 88 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; 106 } 107 } 108 109 if (isNewDisplay) { 110 displays.AppendElement(new VRDisplay(aWindow, display)); 111 } 112 } 113 } 114 115 aDisplays = std::move(displays); 116 } 117 118 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(VRFieldOfView, mParent) 119 120 JSObject* VRFieldOfView::WrapObject(JSContext* aCx, 121 JS::Handle<JSObject*> aGivenProto) { 122 return VRFieldOfView_Binding::Wrap(aCx, this, aGivenProto); 123 } 124 125 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_WITH_JS_MEMBERS(VREyeParameters, 126 (mParent, mFOV), 127 (mOffset)) 128 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); 138 } 139 140 VREyeParameters::~VREyeParameters() { mozilla::DropJSObjects(this); } 141 142 VRFieldOfView* VREyeParameters::FieldOfView() { return mFOV; } 143 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; 153 } 154 } 155 aRetval.set(mOffset); 156 } 157 158 JSObject* VREyeParameters::WrapObject(JSContext* aCx, 159 JS::Handle<JSObject*> aGivenProto) { 160 return VREyeParameters_Binding::Wrap(aCx, this, aGivenProto); 161 } 162 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); 171 } 172 173 VRStageParameters::~VRStageParameters() { mozilla::DropJSObjects(this); } 174 175 JSObject* VRStageParameters::WrapObject(JSContext* aCx, 176 JS::Handle<JSObject*> aGivenProto) { 177 return VRStageParameters_Binding::Wrap(aCx, this, aGivenProto); 178 } 179 180 NS_IMPL_CYCLE_COLLECTION_CLASS(VRStageParameters) 181 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 187 188 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(VRStageParameters) 189 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent) 190 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END 191 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 197 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; 206 } 207 } 208 aRetval.set(mSittingToStandingTransformArray); 209 } 210 211 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(VRDisplayCapabilities, mParent) 212 213 JSObject* VRDisplayCapabilities::WrapObject(JSContext* aCx, 214 JS::Handle<JSObject*> aGivenProto) { 215 return VRDisplayCapabilities_Binding::Wrap(aCx, this, aGivenProto); 216 } 217 218 VRPose::VRPose(nsISupports* aParent, const gfx::VRHMDSensorState& aState) 219 : Pose(aParent), mVRState(aState) { 220 mozilla::HoldJSObjects(this); 221 } 222 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); 228 } 229 230 VRPose::~VRPose() { mozilla::DropJSObjects(this); } 231 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); 240 } 241 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); 252 } 253 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); 262 } 263 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); 271 } 272 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); 281 } 282 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); 291 } 292 293 void VRPose::Update(const gfx::VRHMDSensorState& aState) { mVRState = aState; } 294 295 JSObject* VRPose::WrapObject(JSContext* aCx, 296 JS::Handle<JSObject*> aGivenProto) { 297 return VRPose_Binding::Wrap(aCx, this, aGivenProto); 298 } 299 300 /* virtual */ 301 JSObject* VRDisplay::WrapObject(JSContext* aCx, 302 JS::Handle<JSObject*> aGivenProto) { 303 return VRDisplay_Binding::Wrap(aCx, this, aGivenProto); 304 } 305 306 VRDisplay::VRDisplay(nsPIDOMWindowInner* aWindow, gfx::VRDisplayClient* aClient) 307 : DOMEventTargetHelper(aWindow), 308 mClient(aClient), 309 mDepthNear(0.01f) // Default value from WebVR Spec 310 , 311 mDepthFar(10000.0f) // Default value from WebVR Spec 312 , 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()); 321 } 322 mozilla::HoldJSObjects(this); 323 nsCOMPtr<nsIObserverService> obs = services::GetObserverService(); 324 if (MOZ_LIKELY(obs)) { 325 obs->AddObserver(this, "inner-window-destroyed", false); 326 } 327 } 328 329 VRDisplay::~VRDisplay() { 330 MOZ_ASSERT(mShutdown); 331 mozilla::DropJSObjects(this); 332 } 333 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(); 338 } 339 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(); 349 } 350 351 VRDisplayCapabilities* VRDisplay::Capabilities() { return mCapabilities; } 352 353 VRStageParameters* VRDisplay::GetStageParameters() { return mStageParameters; } 354 355 uint32_t VRDisplay::DisplayId() const { 356 const gfx::VRDisplayInfo& info = mClient->GetDisplayInfo(); 357 return info.GetDisplayID(); 358 } 359 360 void VRDisplay::GetDisplayName(nsAString& aDisplayName) const { 361 const gfx::VRDisplayInfo& info = mClient->GetDisplayInfo(); 362 CopyUTF8toUTF16(MakeStringSpan(info.GetDisplayName()), aDisplayName); 363 } 364 365 void VRDisplay::UpdateFrameInfo() { 366 /** 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. 370 * 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. 375 * 376 * If we are not presenting WebVR content, the frame will never end and we 377 * should return the latest frame data always. 378 */ 379 mFrameInfo.Clear(); 380 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); 386 } 387 } 388 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; 395 } 396 aFrameData.Update(mFrameInfo); 397 return true; 398 } 399 400 already_AddRefed<VRPose> VRDisplay::GetPose() { 401 UpdateFrameInfo(); 402 RefPtr<VRPose> obj = new VRPose(GetParentObject(), mFrameInfo.mVRState); 403 404 return obj.forget(); 405 } 406 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. 412 } 413 414 void VRDisplay::StartVRNavigation() { mClient->StartVRNavigation(); } 415 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); 426 } 427 } 428 429 void VRDisplay::StopHandlingVRNavigationEvent() { 430 MOZ_ASSERT(mVRNavigationEventDepth > 0); 431 --mVRNavigationEventDepth; 432 if (mVRNavigationEventDepth == 0) { 433 mClient->StopVRNavigation(TimeDuration::FromMilliseconds(0)); 434 } 435 } 436 437 bool VRDisplay::IsHandlingVRNavigationEvent() { 438 if (mVRNavigationEventDepth == 0) { 439 return false; 440 } 441 if (mHandlingVRNavigationEventStart.IsNull()) { 442 return false; 443 } 444 TimeDuration timeout = 445 TimeDuration::FromMilliseconds(StaticPrefs::dom_vr_navigation_timeout()); 446 return timeout.ToMilliseconds() <= 0 || 447 (TimeStamp::Now() - mHandlingVRNavigationEventStart) <= timeout; 448 } 449 450 void VRDisplay::OnPresentationGenerationChanged() { ExitPresentInternal(); } 451 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; 459 } 460 461 RefPtr<Promise> promise = Promise::Create(global, aRv); 462 NS_ENSURE_TRUE(!aRv.Failed(), nullptr); 463 464 bool isChromePresentation = aCallerType == CallerType::System; 465 uint32_t presentationGroup = 466 isChromePresentation ? gfx::kVRGroupChrome : gfx::kVRGroupContent; 467 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); 494 } 495 mFrameInfo.Clear(); 496 promise->MaybeResolve(JS::UndefinedHandleValue); 497 } 498 return promise.forget(); 499 } 500 501 NS_IMETHODIMP 502 VRDisplay::Observe(nsISupports* aSubject, const char* aTopic, 503 const char16_t* aData) { 504 MOZ_ASSERT(NS_IsMainThread()); 505 506 if (strcmp(aTopic, "inner-window-destroyed") == 0) { 507 nsCOMPtr<nsISupportsPRUint64> wrapper = do_QueryInterface(aSubject); 508 NS_ENSURE_TRUE(wrapper, NS_ERROR_FAILURE); 509 510 uint64_t innerID; 511 nsresult rv = wrapper->GetData(&innerID); 512 NS_ENSURE_SUCCESS(rv, rv); 513 514 if (!GetOwnerWindow() || GetOwnerWindow()->WindowID() == innerID) { 515 Shutdown(); 516 } 517 518 return NS_OK; 519 } 520 521 // This should not happen. 522 return NS_ERROR_FAILURE; 523 } 524 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; 530 } 531 532 RefPtr<Promise> promise = Promise::Create(global, aRv); 533 NS_ENSURE_TRUE(!aRv.Failed(), nullptr); 534 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(); 542 } 543 544 return promise.forget(); 545 } 546 547 void VRDisplay::ExitPresentInternal() { mPresentation = nullptr; } 548 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"); 555 } 556 } 557 558 void VRDisplay::GetLayers(nsTArray<VRLayer>& result) { 559 if (mPresentation) { 560 mPresentation->GetDOMLayers(result); 561 } else { 562 result = nsTArray<VRLayer>(); 563 } 564 } 565 566 void VRDisplay::SubmitFrame() { 567 AUTO_PROFILER_MARKER("SubmitFrameAtVRDisplay", OTHER); 568 569 if (mClient && !mClient->IsPresentationGenerationCurrent()) { 570 mPresentation = nullptr; 571 mClient->MakePresentationGenerationCurrent(); 572 } 573 574 if (mPresentation) { 575 mPresentation->SubmitFrame(); 576 } 577 mFrameInfo.Clear(); 578 } 579 580 int32_t VRDisplay::RequestAnimationFrame(FrameRequestCallback& aCallback, 581 ErrorResult& aError) { 582 if (mShutdown) { 583 return 0; 584 } 585 586 gfx::VRManagerChild* vm = gfx::VRManagerChild::Get(); 587 588 int32_t handle; 589 aError = vm->ScheduleFrameRequestCallback(aCallback, &handle); 590 return handle; 591 } 592 593 void VRDisplay::CancelAnimationFrame(int32_t aHandle, ErrorResult& aError) { 594 gfx::VRManagerChild* vm = gfx::VRManagerChild::Get(); 595 vm->CancelFrameRequestCallback(aHandle); 596 } 597 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; 602 } 603 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; 610 } 611 if (mClient->GetDisplayInfo().GetPresentingGroups() & aGroupMask) { 612 return true; 613 } 614 return false; 615 } 616 617 bool VRDisplay::IsConnected() const { return mClient->GetIsConnected(); } 618 619 uint32_t VRDisplay::PresentingGroups() const { 620 return mClient->GetDisplayInfo().GetPresentingGroups(); 621 } 622 623 uint32_t VRDisplay::GroupMask() const { 624 return mClient->GetDisplayInfo().GetGroupMask(); 625 } 626 627 void VRDisplay::SetGroupMask(const uint32_t& aGroupMask) { 628 mClient->SetGroupMask(aGroupMask); 629 } 630 631 NS_IMPL_CYCLE_COLLECTION_INHERITED(VRDisplay, DOMEventTargetHelper, 632 mCapabilities, mStageParameters) 633 634 NS_IMPL_ADDREF_INHERITED(VRDisplay, DOMEventTargetHelper) 635 NS_IMPL_RELEASE_INHERITED(VRDisplay, DOMEventTargetHelper) 636 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) 641 642 NS_IMPL_CYCLE_COLLECTION_CLASS(VRFrameData) 643 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 652 653 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(VRFrameData) 654 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent, mPose) 655 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END 656 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 664 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); 673 } 674 675 VRFrameData::~VRFrameData() { mozilla::DropJSObjects(this); } 676 677 /* static */ 678 already_AddRefed<VRFrameData> VRFrameData::Constructor( 679 const GlobalObject& aGlobal) { 680 RefPtr<VRFrameData> obj = new VRFrameData(aGlobal.GetAsSupports()); 681 return obj.forget(); 682 } 683 684 JSObject* VRFrameData::WrapObject(JSContext* aCx, 685 JS::Handle<JSObject*> aGivenProto) { 686 return VRFrameData_Binding::Wrap(aCx, this, aGivenProto); 687 } 688 689 VRPose* VRFrameData::Pose() { return mPose; } 690 691 double VRFrameData::Timestamp() const { 692 // Converting from seconds to milliseconds 693 return mFrameInfo.mVRState.timestamp * 1000.0f; 694 } 695 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); 701 } 702 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); 708 } 709 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); 715 } 716 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); 722 } 723 724 void VRFrameData::Update(const VRFrameInfo& aFrameInfo) { 725 mFrameInfo = aFrameInfo; 726 mPose->Update(mFrameInfo.mVRState); 727 } 728 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) { 734 /** 735 * A mTimeStampOffset value of 0.0f indicates that this is the first 736 * iteration and an offset has not yet been set. 737 * 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. 742 * 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. 746 */ 747 mTimeStampOffset = 748 float(rand()) / float(RAND_MAX) * 10000.0f + 1000.0f - aState.timestamp; 749 } 750 mVRState.timestamp = aState.timestamp + mTimeStampOffset; 751 752 // Avoid division by zero within ConstructProjectionMatrix 753 const float kEpsilon = 0.00001f; 754 if (fabs(aDepthFar - aDepthNear) < kEpsilon) { 755 aDepthFar = aDepthNear + kEpsilon; 756 } 757 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)); 770 } 771 772 VRFrameInfo::VRFrameInfo() : mTimeStampOffset(0.0f) { 773 mVRState.inputFrameID = 0; 774 mVRState.timestamp = 0.0; 775 mVRState.flags = gfx::VRDisplayCapabilityFlags::Cap_None; 776 } 777 778 bool VRFrameInfo::IsDirty() { return mVRState.timestamp == 0; } 779 780 void VRFrameInfo::Clear() { mVRState.Clear(); } 781 782 } // namespace mozilla::dom