Bug 1633725 - Improve WebXR Garbage Collection. Respect XRRigidTransform.inverse and XRView.transform [SameObject] requirements. r=kip,daoshengmu,jgilbert
authorImanol Fernandez <mortimergoro@gmail.com>
Fri, 01 May 2020 01:42:39 +0000
changeset 527414 9d60aed667c2f5e8f2567a0f99b73fa0abb444fa
parent 527413 06f8636f26bc3de90293b7b4a33847ae2801de14
child 527415 d2a8c353d4a5d30d718b4815dae5314349e05574
push id114708
push userigorostizaga@mozilla.com
push dateFri, 01 May 2020 10:00:31 +0000
treeherderautoland@9d60aed667c2 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskip, daoshengmu, jgilbert
bugs1633725
milestone77.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1633725 - Improve WebXR Garbage Collection. Respect XRRigidTransform.inverse and XRView.transform [SameObject] requirements. r=kip,daoshengmu,jgilbert - Lazily create XRRigidTransform DOMPoints - Reuse XRRigidTransform inverse (SameObject in the spec) - Reuse XRView transform (SameObject in the spec) - Reuse XRWebGLLayer viewport instances - Pool XRViewerPose instances - Pool XRFrame instances Differential Revision: https://phabricator.services.mozilla.com/D73071
dom/canvas/XRWebGLLayer.cpp
dom/canvas/XRWebGLLayer.h
dom/vr/XRFrame.cpp
dom/vr/XRNativeOriginLocalFloor.cpp
dom/vr/XRPose.cpp
dom/vr/XRPose.h
dom/vr/XRRigidTransform.cpp
dom/vr/XRRigidTransform.h
dom/vr/XRSession.cpp
dom/vr/XRSession.h
dom/vr/XRView.cpp
dom/vr/XRView.h
dom/vr/XRViewerPose.cpp
dom/vr/XRViewerPose.h
dom/vr/XRViewport.h
--- a/dom/canvas/XRWebGLLayer.cpp
+++ b/dom/canvas/XRWebGLLayer.cpp
@@ -20,17 +20,18 @@
 #include "nsIScriptError.h"
 
 using namespace mozilla::gl;
 
 namespace mozilla {
 namespace dom {
 
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(XRWebGLLayer, mParent, mSession, mWebGL,
-                                      mFramebuffer)
+                                      mFramebuffer, mLeftViewport,
+                                      mRightViewport)
 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(XRWebGLLayer, AddRef)
 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(XRWebGLLayer, Release)
 
 XRWebGLLayer::XRWebGLLayer(
     nsISupports* aParent, XRSession& aSession, bool aIgnoreDepthValues,
     double aFramebufferScaleFactor,
     RefPtr<mozilla::ClientWebGLContext> aWebGLContext,
     RefPtr<WebGLFramebufferJS> aFramebuffer,
@@ -212,22 +213,29 @@ uint32_t XRWebGLLayer::FramebufferHeight
     return mFramebufferOptions->height;
   }
   return mWebGL->GetHeight();
 }
 
 already_AddRefed<XRViewport> XRWebGLLayer::GetViewport(const XRView& aView) {
   const int32_t width = (aView.Eye() == XREye::None) ? FramebufferWidth()
                                                      : (FramebufferWidth() / 2);
-  gfx::IntRect viewportRect(0, 0, width, FramebufferHeight());
+  gfx::IntRect rect(0, 0, width, FramebufferHeight());
   if (aView.Eye() == XREye::Right) {
-    viewportRect.x = width;
+    rect.x = width;
   }
-  RefPtr<XRViewport> viewport = new XRViewport(mParent, viewportRect);
-  return viewport.forget();
+  RefPtr<XRViewport>& viewport =
+      aView.Eye() == XREye::Right ? mRightViewport : mLeftViewport;
+  if (!viewport) {
+    viewport = new XRViewport(mParent, rect);
+  } else {
+    viewport->mRect = rect;
+  }
+  RefPtr<XRViewport> result = viewport;
+  return result.forget();
 }
 
 /* static */ double XRWebGLLayer::GetNativeFramebufferScaleFactor(
     const GlobalObject& aGlobal, const XRSession& aSession) {
   if (aSession.IsEnded()) {
     return 0.0f;
   }
   // TODO: Get the maximum framebuffer size from each display.
--- a/dom/canvas/XRWebGLLayer.h
+++ b/dom/canvas/XRWebGLLayer.h
@@ -73,15 +73,17 @@ class XRWebGLLayer final : public nsWrap
   RefPtr<XRSession> mSession;
   RefPtr<mozilla::ClientWebGLContext> mWebGL;
   double mFramebufferScaleFactor;
   bool mCompositionDisabled;
 
  private:
   bool mIgnoreDepthValues;
   RefPtr<WebGLFramebufferJS> mFramebuffer;
+  RefPtr<XRViewport> mLeftViewport;
+  RefPtr<XRViewport> mRightViewport;
   Maybe<const webgl::OpaqueFramebufferOptions> mFramebufferOptions;
 };
 
 }  // namespace dom
 }  // namespace mozilla
 
 #endif  // mozilla_dom_XRWebGLLayer_h_
--- a/dom/vr/XRFrame.cpp
+++ b/dom/vr/XRFrame.cpp
@@ -49,22 +49,23 @@ already_AddRefed<XRViewerPose> XRFrame::
   // https://immersive-web.github.io/webxr/#poses-may-be-reported
 
   // TODO (Bug 1616393) - Check if poses must be limited:
   // https://immersive-web.github.io/webxr/#poses-must-be-limited
 
   gfx::PointDouble3D viewerPosition;
   gfx::QuaternionDouble viewerOrientation;
   bool emulatedPosition = aReferenceSpace.IsPositionEmulated();
-  nsTArray<RefPtr<XRView>> views;
 
   XRRenderState* renderState = mSession->GetActiveRenderState();
   float depthNear = (float)renderState->DepthNear();
   float depthFar = (float)renderState->DepthFar();
 
+  RefPtr<XRViewerPose> viewerPose;
+
   gfx::VRDisplayClient* display = mSession->GetDisplayClient();
   if (display) {
     // Have a VRDisplayClient
     const VRDisplayInfo& displayInfo =
         mSession->GetDisplayClient()->GetDisplayInfo();
     const gfx::VRHMDSensorState& sensorState = display->GetSensorState();
 
     viewerPosition = gfx::PointDouble3D(sensorState.pose.position[0],
@@ -73,70 +74,68 @@ already_AddRefed<XRViewerPose> XRFrame::
     viewerOrientation = gfx::QuaternionDouble(
         sensorState.pose.orientation[0], sensorState.pose.orientation[1],
         sensorState.pose.orientation[2], sensorState.pose.orientation[3]);
 
     // Quaternion was inverted for WebVR. We need to invert it here again.
     // TODO: Remove those extra inverts when WebVR support is disabled.
     viewerOrientation.Invert();
 
+    viewerPose = mSession->PooledViewerPose(viewerPosition, viewerOrientation,
+                                            emulatedPosition);
+
     gfx::Matrix4x4Double headTransform;
     headTransform.SetRotationFromQuaternion(viewerOrientation);
     headTransform.PostTranslate(viewerPosition);
 
     gfx::Matrix4x4Double originTransform;
     originTransform.SetRotationFromQuaternion(
         aReferenceSpace.GetEffectiveOriginOrientation());
     originTransform.PreTranslate(-aReferenceSpace.GetEffectiveOriginPosition());
 
     headTransform *= originTransform;
 
-    auto addEye = [&](XREye xrEye, VRDisplayState::Eye eye) {
+    auto updateEye = [&](int32_t viewIndex, VRDisplayState::Eye eye) {
       auto offset = displayInfo.GetEyeTranslation(eye);
       auto eyeFromHead = gfx::Matrix4x4Double::Translation(
           gfx::PointDouble3D(offset.x, offset.y, offset.z));
       auto eyeTransform = eyeFromHead * headTransform;
       gfx::PointDouble3D eyePosition;
       gfx::QuaternionDouble eyeRotation;
       gfx::PointDouble3D eyeScale;
       eyeTransform.Decompose(eyePosition, eyeRotation, eyeScale);
 
       const gfx::VRFieldOfView fov = displayInfo.mDisplayState.eyeFOV[eye];
       Matrix4x4 projection =
           fov.ConstructProjectionMatrix(depthNear, depthFar, true);
-      RefPtr<XRView> view =
-          new XRView(mParent, xrEye, eyePosition, eyeRotation, projection);
-      views.AppendElement(view);
+      viewerPose->GetEye(viewIndex)->Update(eyePosition, eyeRotation,
+                                            projection);
     };
 
-    addEye(XREye::Left, gfx::VRDisplayState::Eye_Left);
-    addEye(XREye::Right, gfx::VRDisplayState::Eye_Right);
+    updateEye(0, gfx::VRDisplayState::Eye_Left);
+    updateEye(1, gfx::VRDisplayState::Eye_Right);
   } else {
     auto inlineVerticalFov = renderState->GetInlineVerticalFieldOfView();
     const double fov =
         inlineVerticalFov.IsNull() ? M_PI * 0.5f : inlineVerticalFov.Value();
     HTMLCanvasElement* canvas = renderState->GetOutputCanvas();
     float aspect = 1.0f;
     if (canvas) {
       aspect = (float)canvas->Width() / (float)canvas->Height();
     }
     Matrix4x4 projection =
         ConstructInlineProjection((float)fov, aspect, depthNear, depthFar);
-    RefPtr<XRView> view = new XRView(mParent, XREye::None, gfx::PointDouble3D(),
-                                     gfx::QuaternionDouble(), projection);
-    views.AppendElement(view);
+
+    viewerPose = mSession->PooledViewerPose(viewerPosition, viewerOrientation,
+                                            emulatedPosition);
+    viewerPose->GetEye(0)->Update(gfx::PointDouble3D(), gfx::QuaternionDouble(),
+                                  projection);
   }
 
-  RefPtr<XRRigidTransform> transform =
-      new XRRigidTransform(mParent, viewerPosition, viewerOrientation);
-
-  RefPtr<XRViewerPose> pose =
-      new XRViewerPose(mParent, transform, emulatedPosition, views);
-
-  return pose.forget();
+  return viewerPose.forget();
 }
 
 XRPose* XRFrame::GetPose(const XRSpace& aSpace, const XRSpace& aBaseSpace,
                          ErrorResult& aRv) {
   if (!mActive) {
     aRv.ThrowInvalidStateError(
         "GetPose can not be called on an XRFrame that is not active.");
     return nullptr;
--- a/dom/vr/XRNativeOriginLocalFloor.cpp
+++ b/dom/vr/XRNativeOriginLocalFloor.cpp
@@ -24,17 +24,16 @@ XRNativeOriginLocalFloor::XRNativeOrigin
 }
 
 gfx::PointDouble3D XRNativeOriginLocalFloor::GetPosition() {
   // Keep returning {0,-fuzz,0} until a position can be found
   const auto standing =
       mDisplay->GetDisplayInfo().GetSittingToStandingTransform();
   if (!mInitialPositionValid || standing != mStandingTransform) {
     const gfx::VRHMDSensorState& sensorState = mDisplay->GetSensorState();
-    gfx::PointDouble3D origin;
     mInitialPosition.x = sensorState.pose.position[0];
     mInitialPosition.y = -mFloorRandom - standing._42;
     mInitialPosition.z = sensorState.pose.position[2];
     mInitialPositionValid = true;
     mStandingTransform = standing;
   }
   return mInitialPosition;
 }
--- a/dom/vr/XRPose.cpp
+++ b/dom/vr/XRPose.cpp
@@ -24,14 +24,18 @@ XRPose::XRPose(nsISupports* aParent, XRR
       mTransform(aTransform),
       mEmulatedPosition(aEmulatedPosition) {}
 
 JSObject* XRPose::WrapObject(JSContext* aCx,
                              JS::Handle<JSObject*> aGivenProto) {
   return XRPose_Binding::Wrap(aCx, this, aGivenProto);
 }
 
+void XRPose::SetEmulatedPosition(bool aEmulated) {
+  mEmulatedPosition = aEmulated;
+}
+
 XRRigidTransform* XRPose::Transform() { return mTransform; }
 
 bool XRPose::EmulatedPosition() const { return mEmulatedPosition; }
 
 }  // namespace dom
 }  // namespace mozilla
--- a/dom/vr/XRPose.h
+++ b/dom/vr/XRPose.h
@@ -20,16 +20,17 @@ class XRView;
 
 class XRPose : public nsISupports, public nsWrapperCache {
  public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(XRPose)
 
   explicit XRPose(nsISupports* aParent, XRRigidTransform* aTransform,
                   bool aEmulatedPosition);
+  void SetEmulatedPosition(bool aEmulated);
 
   // WebIDL Boilerplate
   nsISupports* GetParentObject() const { return mParent; }
   JSObject* WrapObject(JSContext* aCx,
                        JS::Handle<JSObject*> aGivenProto) override;
 
   // WebIDL Members
   XRRigidTransform* Transform();
--- a/dom/vr/XRRigidTransform.cpp
+++ b/dom/vr/XRRigidTransform.cpp
@@ -1,65 +1,67 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/XRRigidTransform.h"
 #include "mozilla/dom/DOMPoint.h"
+#include "mozilla/dom/Pose.h"
 #include "mozilla/dom/DOMPointBinding.h"
 
 namespace mozilla {
 namespace dom {
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(XRRigidTransform)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(XRRigidTransform)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mParent, mPosition, mOrientation)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mParent, mPosition, mOrientation, mInverse)
   NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
   tmp->mMatrixArray = nullptr;
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(XRRigidTransform)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent, mPosition, mOrientation)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent, mPosition, mOrientation, mInverse)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(XRRigidTransform)
   NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
   NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mMatrixArray)
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(XRRigidTransform, AddRef)
 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(XRRigidTransform, Release)
 
 XRRigidTransform::XRRigidTransform(nsISupports* aParent,
                                    const gfx::PointDouble3D& aPosition,
                                    const gfx::QuaternionDouble& aOrientation)
-    : mParent(aParent), mMatrixArray(nullptr) {
+    : mParent(aParent),
+      mMatrixArray(nullptr),
+      mPosition(nullptr),
+      mOrientation(nullptr),
+      mInverse(nullptr),
+      mRawPosition(aPosition),
+      mRawOrientation(aOrientation),
+      mNeedsUpdate(true) {
   mozilla::HoldJSObjects(this);
-  mPosition =
-      new DOMPoint(aParent, aPosition.x, aPosition.y, aPosition.z, 1.0f);
-  mOrientation = new DOMPoint(aParent, aOrientation.x, aOrientation.y,
-                              aOrientation.z, aOrientation.w);
 }
 
 XRRigidTransform::XRRigidTransform(nsISupports* aParent,
                                    const gfx::Matrix4x4Double& aTransform)
-    : mParent(aParent), mMatrixArray(nullptr) {
+    : mParent(aParent),
+      mMatrixArray(nullptr),
+      mPosition(nullptr),
+      mOrientation(nullptr),
+      mInverse(nullptr),
+      mNeedsUpdate(true) {
   mozilla::HoldJSObjects(this);
-  gfx::PointDouble3D position;
   gfx::PointDouble3D scale;
-  gfx::QuaternionDouble orientation;
-
-  aTransform.Decompose(position, orientation, scale);
-
-  mPosition = new DOMPoint(aParent, position.x, position.y, position.z, 1.0f);
-  mOrientation = new DOMPoint(aParent, orientation.x, orientation.y,
-                              orientation.z, orientation.w);
+  aTransform.Decompose(mRawPosition, mRawOrientation, scale);
 }
 
 XRRigidTransform::~XRRigidTransform() { mozilla::DropJSObjects(this); }
 
 /* static */ already_AddRefed<XRRigidTransform> XRRigidTransform::Constructor(
     const GlobalObject& aGlobal, const DOMPointInit& aOrigin,
     const DOMPointInit& aDirection, ErrorResult& aRv) {
   gfx::PointDouble3D position(aOrigin.mX, aOrigin.mY, aOrigin.mZ);
@@ -71,67 +73,101 @@ XRRigidTransform::~XRRigidTransform() { 
   return obj.forget();
 }
 
 JSObject* XRRigidTransform::WrapObject(JSContext* aCx,
                                        JS::Handle<JSObject*> aGivenProto) {
   return XRRigidTransform_Binding::Wrap(aCx, this, aGivenProto);
 }
 
-DOMPoint* XRRigidTransform::Position() { return mPosition; }
+DOMPoint* XRRigidTransform::Position() {
+  if (!mPosition) {
+    mPosition = new DOMPoint(mParent, mRawPosition.x, mRawPosition.y,
+                             mRawPosition.z, 1.0f);
+  }
 
-DOMPoint* XRRigidTransform::Orientation() { return mOrientation; }
+  return mPosition;
+}
+
+DOMPoint* XRRigidTransform::Orientation() {
+  if (!mOrientation) {
+    mOrientation = new DOMPoint(mParent, mRawOrientation.x, mRawOrientation.y,
+                                mRawOrientation.z, mRawOrientation.w);
+  }
+  return mOrientation;
+}
 
 XRRigidTransform& XRRigidTransform::operator=(const XRRigidTransform& aOther) {
-  mPosition->SetX(aOther.mPosition->X());
-  mPosition->SetY(aOther.mPosition->Y());
-  mPosition->SetZ(aOther.mPosition->Z());
-  mOrientation->SetX(aOther.mOrientation->X());
-  mOrientation->SetY(aOther.mOrientation->Y());
-  mOrientation->SetZ(aOther.mOrientation->Z());
-  mOrientation->SetW(aOther.mOrientation->W());
+  Update(aOther.mRawPosition, aOther.mRawOrientation);
   return *this;
 }
 
 gfx::QuaternionDouble XRRigidTransform::RawOrientation() const {
-  return gfx::QuaternionDouble(mOrientation->X(), mOrientation->Y(),
-                               mOrientation->Z(), mOrientation->W());
+  return mRawOrientation;
 }
 gfx::PointDouble3D XRRigidTransform::RawPosition() const {
-  return gfx::PointDouble3D(mPosition->X(), mPosition->Y(), mPosition->Z());
+  return mRawPosition;
+}
+
+void XRRigidTransform::Update(const gfx::PointDouble3D& aPosition,
+                              const gfx::QuaternionDouble& aOrientation) {
+  mNeedsUpdate = true;
+  mRawPosition = aPosition;
+  mRawOrientation = aOrientation;
+  if (mPosition) {
+    mPosition->SetX(aPosition.x);
+    mPosition->SetY(aPosition.y);
+    mPosition->SetZ(aPosition.z);
+  }
+  if (mOrientation) {
+    mOrientation->SetX(aOrientation.x);
+    mOrientation->SetY(aOrientation.y);
+    mOrientation->SetZ(aOrientation.z);
+    mOrientation->SetW(aOrientation.w);
+  }
+  if (mInverse) {
+    gfx::QuaternionDouble q(mRawOrientation);
+    gfx::PointDouble3D p = -mRawPosition;
+    p = q.RotatePoint(p);
+    q.Invert();
+    mInverse->Update(p, q);
+  }
 }
 
 void XRRigidTransform::GetMatrix(JSContext* aCx,
                                  JS::MutableHandle<JSObject*> aRetval,
                                  ErrorResult& aRv) {
-  if (!mMatrixArray) {
+  if (!mMatrixArray || mNeedsUpdate) {
+    mNeedsUpdate = false;
     gfx::Matrix4x4 mat;
     mat.SetRotationFromQuaternion(
-        gfx::Quaternion(mOrientation->X(), mOrientation->Y(), mOrientation->Z(),
-                        mOrientation->W()));
-    mat.PostTranslate((float)mPosition->X(), (float)mPosition->Y(),
-                      (float)mPosition->Z());
-    // Lazily create the Float32Array
-    mMatrixArray = dom::Float32Array::Create(aCx, this, 16, mat.components);
+        gfx::Quaternion((float)mRawOrientation.x, (float)mRawOrientation.y,
+                        (float)mRawOrientation.z, (float)mRawOrientation.w));
+    mat.PostTranslate((float)mRawPosition.x, (float)mRawPosition.y,
+                      (float)mRawPosition.z);
+
+    Pose::SetFloat32Array(aCx, this, aRetval, mMatrixArray, mat.components, 16,
+                          aRv);
     if (!mMatrixArray) {
-      aRv.NoteJSContextException(aCx);
       return;
     }
   }
   if (mMatrixArray) {
     JS::ExposeObjectToActiveJS(mMatrixArray);
   }
   aRetval.set(mMatrixArray);
 }
 
 already_AddRefed<XRRigidTransform> XRRigidTransform::Inverse() {
-  gfx::QuaternionDouble q(mOrientation->X(), mOrientation->Y(),
-                          mOrientation->Z(), mOrientation->W());
-  gfx::PointDouble3D p(-mPosition->X(), -mPosition->Y(), -mPosition->Z());
-  p = q.RotatePoint(p);
-  q.Invert();
-  RefPtr<XRRigidTransform> inv = new XRRigidTransform(mParent, p, q);
+  if (!mInverse) {
+    gfx::QuaternionDouble q(mRawOrientation);
+    gfx::PointDouble3D p = -mRawPosition;
+    p = q.RotatePoint(p);
+    q.Invert();
+    mInverse = new XRRigidTransform(mParent, p, q);
+  }
 
-  return inv.forget();
+  RefPtr<XRRigidTransform> inverse = mInverse;
+  return inverse.forget();
 }
 
 }  // namespace dom
 }  // namespace mozilla
--- a/dom/vr/XRRigidTransform.h
+++ b/dom/vr/XRRigidTransform.h
@@ -28,34 +28,39 @@ class XRRigidTransform final : public ns
   explicit XRRigidTransform(nsISupports* aParent,
                             const gfx::Matrix4x4Double& aTransform);
   static already_AddRefed<XRRigidTransform> Constructor(
       const GlobalObject& aGlobal, const DOMPointInit& aOrigin,
       const DOMPointInit& aDirection, ErrorResult& aRv);
   XRRigidTransform& operator=(const XRRigidTransform& aOther);
   gfx::QuaternionDouble RawOrientation() const;
   gfx::PointDouble3D RawPosition() const;
+  void Update(const gfx::PointDouble3D& aPosition,
+              const gfx::QuaternionDouble& aOrientation);
   // WebIDL Boilerplate
   nsISupports* GetParentObject() const { return mParent; }
   JSObject* WrapObject(JSContext* aCx,
                        JS::Handle<JSObject*> aGivenProto) override;
 
   // WebIDL Members
   DOMPoint* Position();
   DOMPoint* Orientation();
   void GetMatrix(JSContext* aCx, JS::MutableHandle<JSObject*> aRetval,
                  ErrorResult& aRv);
   already_AddRefed<XRRigidTransform> Inverse();
 
  protected:
   virtual ~XRRigidTransform();
 
+  nsCOMPtr<nsISupports> mParent;
+  JS::Heap<JSObject*> mMatrixArray;
   RefPtr<DOMPoint> mPosition;
   RefPtr<DOMPoint> mOrientation;
-
-  nsCOMPtr<nsISupports> mParent;
-  JS::Heap<JSObject*> mMatrixArray;
+  RefPtr<XRRigidTransform> mInverse;
+  gfx::PointDouble3D mRawPosition;
+  gfx::QuaternionDouble mRawOrientation;
+  bool mNeedsUpdate;
 };
 
 }  // namespace dom
 }  // namespace mozilla
 
 #endif  // mozilla_dom_XRRigidTransform_h_
--- a/dom/vr/XRSession.cpp
+++ b/dom/vr/XRSession.cpp
@@ -14,48 +14,60 @@
 #include "XRRenderState.h"
 #include "XRBoundedReferenceSpace.h"
 #include "XRFrame.h"
 #include "XRNativeOrigin.h"
 #include "XRNativeOriginFixed.h"
 #include "XRNativeOriginViewer.h"
 #include "XRNativeOriginLocal.h"
 #include "XRNativeOriginLocalFloor.h"
+#include "XRView.h"
+#include "XRViewerPose.h"
 #include "VRLayerChild.h"
 #include "XRInputSourceArray.h"
 #include "nsGlobalWindow.h"
 #include "nsIObserverService.h"
 #include "nsISupportsPrimitives.h"
 #include "VRDisplayClient.h"
 #include "VRDisplayPresentation.h"
 
+/**
+ * Maximum instances of XRFrame and XRViewerPose objects
+ * created in the pool.
+ */
+const uint32_t kMaxPoolSize = 16;
+
 namespace mozilla {
 namespace dom {
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(XRSession)
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(XRSession,
                                                   DOMEventTargetHelper)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mXRSystem)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mActiveRenderState)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPendingRenderState)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mInputSources)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mViewerPosePool)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFramePool)
 
   for (uint32_t i = 0; i < tmp->mFrameRequestCallbacks.Length(); ++i) {
     NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mFrameRequestCallbacks[i]");
     cb.NoteXPCOMChild(tmp->mFrameRequestCallbacks[i].mCallback);
   }
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(XRSession, DOMEventTargetHelper)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mXRSystem)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mActiveRenderState)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mPendingRenderState)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mInputSources)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mViewerPosePool)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mFramePool)
 
   tmp->mFrameRequestCallbacks.Clear();
 
   // Don't need NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER because
   // DOMEventTargetHelper does it for us.
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED_0(XRSession, DOMEventTargetHelper)
@@ -104,17 +116,19 @@ XRSession::XRSession(
     const nsTArray<XRReferenceSpaceType>& aEnabledReferenceSpaceTypes)
     : DOMEventTargetHelper(aWindow),
       mXRSystem(aXRSystem),
       mShutdown(false),
       mEnded(false),
       mRefreshDriver(aRefreshDriver),
       mDisplayClient(aClient),
       mFrameRequestCallbackCounter(0),
-      mEnabledReferenceSpaceTypes(aEnabledReferenceSpaceTypes) {
+      mEnabledReferenceSpaceTypes(aEnabledReferenceSpaceTypes),
+      mViewerPosePoolIndex(0),
+      mFramePoolIndex(0) {
   if (aClient) {
     aClient->SessionStarted(this);
   }
   mActiveRenderState = new XRRenderState(aWindow, this);
   mStartTimeStamp = TimeStamp::Now();
   if (IsImmersive()) {
     mDisplayPresentation =
         mDisplayClient->BeginPresentation({}, aPresentationGroup);
@@ -289,18 +303,17 @@ void XRSession::StartFrame() {
   }
 
   // Determine timestamp for the callbacks
   TimeStamp nowTime = TimeStamp::Now();
   mozilla::TimeDuration duration = nowTime - mStartTimeStamp;
   DOMHighResTimeStamp timeStamp = duration.ToMilliseconds();
 
   // Create an XRFrame for the callbacks
-  RefPtr<XRFrame> frame = new XRFrame(GetParentObject(), this);
-
+  RefPtr<XRFrame> frame = PooledFrame();
   frame->StartAnimationFrame();
 
   mActiveRenderState->GetBaseLayer()->StartAnimationFrame();
   nsTArray<XRFrameRequest> callbacks;
   callbacks.AppendElements(mFrameRequestCallbacks);
   mFrameRequestCallbacks.Clear();
   for (auto& callback : callbacks) {
     callback.Call(timeStamp, *frame);
@@ -388,16 +401,20 @@ int32_t XRSession::RequestAnimationFrame
 
 void XRSession::CancelAnimationFrame(int32_t aHandle, ErrorResult& aError) {
   mFrameRequestCallbacks.RemoveElementSorted(aHandle);
 }
 
 void XRSession::Shutdown() {
   mShutdown = true;
   ExitPresentInternal();
+  mViewerPosePool.Clear();
+  mViewerPosePoolIndex = 0;
+  mFramePool.Clear();
+  mFramePoolIndex = 0;
 
   // Unregister from nsRefreshObserver
   if (mRefreshDriver) {
     mRefreshDriver->RemoveRefreshObserver(this, FlushType::Display);
     mRefreshDriver = nullptr;
   }
 }
 
@@ -444,10 +461,52 @@ void XRSession::DisconnectFromOwner() {
 }
 
 void XRSession::LastRelease() {
   // We don't want to wait for the GC to free up the presentation
   // for use in other documents, so we do this in LastRelease().
   Shutdown();
 }
 
+RefPtr<XRViewerPose> XRSession::PooledViewerPose(
+    const gfx::PointDouble3D& aPosition,
+    const gfx::QuaternionDouble& aOrientation, bool aEmulatedPosition) {
+  RefPtr<XRViewerPose> pose;
+  if (mViewerPosePool.Length() > mViewerPosePoolIndex) {
+    pose = mViewerPosePool.ElementAt(mViewerPosePoolIndex);
+    pose->Transform()->Update(aPosition, aOrientation);
+    pose->SetEmulatedPosition(aEmulatedPosition);
+  } else {
+    RefPtr<XRRigidTransform> transform =
+        new XRRigidTransform(this, aPosition, aOrientation);
+    nsTArray<RefPtr<XRView>> views;
+    if (IsImmersive()) {
+      views.AppendElement(new XRView(GetParentObject(), XREye::Left));
+      views.AppendElement(new XRView(GetParentObject(), XREye::Right));
+    } else {
+      views.AppendElement(new XRView(GetParentObject(), XREye::None));
+    }
+    pose = new XRViewerPose(this, transform, aEmulatedPosition, views);
+    mViewerPosePool.AppendElement(pose);
+  }
+
+  mViewerPosePoolIndex++;
+  if (mViewerPosePoolIndex >= kMaxPoolSize) {
+    mViewerPosePoolIndex = 0;
+  }
+
+  return pose;
+}
+
+RefPtr<XRFrame> XRSession::PooledFrame() {
+  RefPtr<XRFrame> frame;
+  if (mFramePool.Length() > mFramePoolIndex) {
+    frame = mFramePool.ElementAt(mFramePoolIndex);
+  } else {
+    frame = new XRFrame(GetParentObject(), this);
+    mFramePool.AppendElement(frame);
+  }
+
+  return frame;
+}
+
 }  // namespace dom
 }  // namespace mozilla
--- a/dom/vr/XRSession.h
+++ b/dom/vr/XRSession.h
@@ -16,28 +16,30 @@
 namespace mozilla {
 namespace gfx {
 class VRDisplayClient;
 class VRDisplayPresentation;
 }  // namespace gfx
 namespace dom {
 
 class XRSystem;
+enum class XREye : uint8_t;
 enum class XRReferenceSpaceType : uint8_t;
 enum class XRSessionMode : uint8_t;
 enum class XRVisibilityState : uint8_t;
 class XRFrame;
 class XRFrameRequestCallback;
 class XRInputSource;
 class XRInputSourceArray;
 class XRLayer;
 struct XRReferenceSpaceOptions;
 class XRRenderState;
 struct XRRenderStateInit;
 class XRSpace;
+class XRViewerPose;
 
 class XRSession final : public DOMEventTargetHelper, public nsARefreshObserver {
  public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(XRSession, DOMEventTargetHelper)
 
  private:
   explicit XRSession(
@@ -87,28 +89,32 @@ class XRSession final : public DOMEventT
   // Non WebIDL Members
   gfx::VRDisplayClient* GetDisplayClient();
   XRRenderState* GetActiveRenderState();
   bool IsEnded() const;
   bool IsImmersive() const;
   MOZ_CAN_RUN_SCRIPT
   void StartFrame();
   void ExitPresent();
+  RefPtr<XRViewerPose> PooledViewerPose(
+      const gfx::PointDouble3D& aPosition,
+      const gfx::QuaternionDouble& aOrientation, bool aEmulatedPosition);
 
   // nsARefreshObserver
   MOZ_CAN_RUN_SCRIPT
   void WillRefresh(mozilla::TimeStamp aTime) override;
 
  protected:
   virtual ~XRSession();
   void LastRelease() override;
   void DisconnectFromOwner() override;
   void Shutdown();
   void ExitPresentInternal();
   void ApplyPendingRenderState();
+  RefPtr<XRFrame> PooledFrame();
   RefPtr<XRSystem> mXRSystem;
   bool mShutdown;
   bool mEnded;
   RefPtr<nsRefreshDriver> mRefreshDriver;
   RefPtr<gfx::VRDisplayClient> mDisplayClient;
   RefPtr<gfx::VRDisplayPresentation> mDisplayPresentation;
   RefPtr<XRRenderState> mActiveRenderState;
   RefPtr<XRRenderState> mPendingRenderState;
@@ -129,14 +135,18 @@ class XRSession final : public DOMEventT
     RefPtr<mozilla::dom::XRFrameRequestCallback> mCallback;
     int32_t mHandle;
   };
 
   int32_t mFrameRequestCallbackCounter;
   nsTArray<XRFrameRequest> mFrameRequestCallbacks;
   mozilla::TimeStamp mStartTimeStamp;
   nsTArray<XRReferenceSpaceType> mEnabledReferenceSpaceTypes;
+  nsTArray<RefPtr<XRViewerPose>> mViewerPosePool;
+  uint32_t mViewerPosePoolIndex;
+  nsTArray<RefPtr<XRFrame>> mFramePool;
+  uint32_t mFramePoolIndex;
 };
 
 }  // namespace dom
 }  // namespace mozilla
 
 #endif  // mozilla_dom_XRSession_h_
--- a/dom/vr/XRView.cpp
+++ b/dom/vr/XRView.cpp
@@ -7,80 +7,87 @@
 #include "mozilla/dom/XRView.h"
 
 namespace mozilla {
 namespace dom {
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(XRView)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(XRView)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mParent)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mParent, mTransform)
   NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
   tmp->mJSProjectionMatrix = nullptr;
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(XRView)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent, mTransform)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(XRView)
   NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
   NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mJSProjectionMatrix)
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(XRView, AddRef)
 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(XRView, Release)
 
-XRView::XRView(nsISupports* aParent, const XREye& aEye,
-               const gfx::PointDouble3D& aPosition,
-               const gfx::QuaternionDouble& aOrientation,
-               const gfx::Matrix4x4& aProjectionMatrix)
+XRView::XRView(nsISupports* aParent, const XREye& aEye)
     : mParent(aParent),
       mEye(aEye),
-      mPosition(aPosition),
-      mOrientation(aOrientation),
-      mProjectionMatrix(aProjectionMatrix),
+      mPosition(gfx::PointDouble3D()),
+      mOrientation(gfx::QuaternionDouble()),
       mJSProjectionMatrix(nullptr) {
   mozilla::HoldJSObjects(this);
 }
 
 XRView::~XRView() { mozilla::DropJSObjects(this); }
 
 JSObject* XRView::WrapObject(JSContext* aCx,
                              JS::Handle<JSObject*> aGivenProto) {
   return XRView_Binding::Wrap(aCx, this, aGivenProto);
 }
 
+void XRView::Update(const gfx::PointDouble3D& aPosition,
+                    const gfx::QuaternionDouble& aOrientation,
+                    const gfx::Matrix4x4& aProjectionMatrix) {
+  mPosition = aPosition;
+  mOrientation = aOrientation;
+  mProjectionMatrix = aProjectionMatrix;
+  if (mTransform) {
+    mTransform->Update(aPosition, aOrientation);
+  }
+  if (aProjectionMatrix != mProjectionMatrix) {
+    mProjectionNeedsUpdate = true;
+    mProjectionMatrix = aProjectionMatrix;
+  }
+}
+
 XREye XRView::Eye() const { return mEye; }
 
 void XRView::GetProjectionMatrix(JSContext* aCx,
                                  JS::MutableHandle<JSObject*> aRetval,
                                  ErrorResult& aRv) {
-  LazyCreateMatrix(mJSProjectionMatrix, mProjectionMatrix, aCx, aRetval, aRv);
+  if (!mJSProjectionMatrix || mProjectionNeedsUpdate) {
+    mProjectionNeedsUpdate = false;
+    gfx::Matrix4x4 mat;
+
+    Pose::SetFloat32Array(aCx, this, aRetval, mJSProjectionMatrix,
+                          mProjectionMatrix.components, 16, aRv);
+    if (!mJSProjectionMatrix) {
+      return;
+    }
+  }
+  if (mJSProjectionMatrix) {
+    JS::ExposeObjectToActiveJS(mJSProjectionMatrix);
+  }
+  aRetval.set(mJSProjectionMatrix);
 }
 
 already_AddRefed<XRRigidTransform> XRView::GetTransform(ErrorResult& aRv) {
-  RefPtr<XRRigidTransform> transform =
-      new XRRigidTransform(mParent, mPosition, mOrientation);
+  if (!mTransform) {
+    mTransform = new XRRigidTransform(mParent, mPosition, mOrientation);
+  }
+  RefPtr<XRRigidTransform> transform = mTransform;
   return transform.forget();
-  ;
-}
-
-void XRView::LazyCreateMatrix(JS::Heap<JSObject*>& aArray, gfx::Matrix4x4& aMat,
-                              JSContext* aCx,
-                              JS::MutableHandle<JSObject*> aRetval,
-                              ErrorResult& aRv) {
-  if (!aArray) {
-    // Lazily create the Float32Array
-    aArray = dom::Float32Array::Create(aCx, this, 16, aMat.components);
-    if (!aArray) {
-      aRv.NoteJSContextException(aCx);
-      return;
-    }
-  }
-  if (aArray) {
-    JS::ExposeObjectToActiveJS(aArray);
-  }
-  aRetval.set(aArray);
 }
 
 }  // namespace dom
 }  // namespace mozilla
--- a/dom/vr/XRView.h
+++ b/dom/vr/XRView.h
@@ -18,42 +18,41 @@ namespace dom {
 enum class XREye : uint8_t;
 class XRRigidTransform;
 
 class XRView final : public nsWrapperCache {
  public:
   NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(XRView)
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(XRView)
 
-  explicit XRView(nsISupports* aParent, const XREye& aEye,
-                  const gfx::PointDouble3D& aPosition,
-                  const gfx::QuaternionDouble& aOrientation,
-                  const gfx::Matrix4x4& aProjectionMatrix);
+  explicit XRView(nsISupports* aParent, const XREye& aEye);
 
+  void Update(const gfx::PointDouble3D& aPosition,
+              const gfx::QuaternionDouble& aOrientation,
+              const gfx::Matrix4x4& aProjectionMatrix);
   // WebIDL Boilerplate
   nsISupports* GetParentObject() const { return mParent; }
   JSObject* WrapObject(JSContext* aCx,
                        JS::Handle<JSObject*> aGivenProto) override;
 
   // WebIDL Members
   XREye Eye() const;
   void GetProjectionMatrix(JSContext* aCx, JS::MutableHandle<JSObject*> aRetval,
                            ErrorResult& aRv);
   already_AddRefed<XRRigidTransform> GetTransform(ErrorResult& aRv);
 
  protected:
   virtual ~XRView();
-  void LazyCreateMatrix(JS::Heap<JSObject*>& aArray, gfx::Matrix4x4& aMat,
-                        JSContext* aCx, JS::MutableHandle<JSObject*> aRetval,
-                        ErrorResult& aRv);
 
   nsCOMPtr<nsISupports> mParent;
   XREye mEye;
   gfx::PointDouble3D mPosition;
   gfx::QuaternionDouble mOrientation;
   gfx::Matrix4x4 mProjectionMatrix;
   JS::Heap<JSObject*> mJSProjectionMatrix;
+  bool mProjectionNeedsUpdate = true;
+  RefPtr<XRRigidTransform> mTransform;
 };
 
 }  // namespace dom
 }  // namespace mozilla
 
 #endif  // mozilla_dom_XRView_h_
--- a/dom/vr/XRViewerPose.cpp
+++ b/dom/vr/XRViewerPose.cpp
@@ -28,14 +28,18 @@ XRViewerPose::XRViewerPose(nsISupports* 
                            const nsTArray<RefPtr<XRView>>& aViews)
     : XRPose(aParent, aTransform, aEmulatedPosition), mViews(aViews) {}
 
 JSObject* XRViewerPose::WrapObject(JSContext* aCx,
                                    JS::Handle<JSObject*> aGivenProto) {
   return XRViewerPose_Binding::Wrap(aCx, this, aGivenProto);
 }
 
+RefPtr<XRView>& XRViewerPose::GetEye(int32_t aIndex) {
+  return mViews.ElementAt(aIndex);
+}
+
 void XRViewerPose::GetViews(nsTArray<RefPtr<XRView>>& aResult) {
   aResult = mViews;
 }
 
 }  // namespace dom
 }  // namespace mozilla
--- a/dom/vr/XRViewerPose.h
+++ b/dom/vr/XRViewerPose.h
@@ -22,16 +22,17 @@ class XRView;
 class XRViewerPose final : public XRPose {
  public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(XRViewerPose, XRPose)
 
   explicit XRViewerPose(nsISupports* aParent, XRRigidTransform* aTransform,
                         bool aEmulatedPosition,
                         const nsTArray<RefPtr<XRView>>& aViews);
+  RefPtr<XRView>& GetEye(int32_t aIndex);
 
   // WebIDL Boilerplate
   JSObject* WrapObject(JSContext* aCx,
                        JS::Handle<JSObject*> aGivenProto) override;
 
   // WebIDL Members
   void GetViews(nsTArray<RefPtr<XRView>>& aResult);
 
--- a/dom/vr/XRViewport.h
+++ b/dom/vr/XRViewport.h
@@ -33,15 +33,17 @@ class XRViewport final : public nsWrappe
   int32_t Y();
   int32_t Width();
   int32_t Height();
 
  protected:
   virtual ~XRViewport() = default;
 
   nsCOMPtr<nsISupports> mParent;
+
+ public:
   gfx::IntRect mRect;
 };
 
 }  // namespace dom
 }  // namespace mozilla
 
 #endif  // mozilla_dom_XRViewport_h_