Bug 1381085 - Submit VR frames with a separate ID3DDeviceContextState. r=dmu, a=sledru
authorKearwood "Kip" Gilbert <kgilbert@mozilla.com>
Mon, 18 Sep 2017 16:19:49 -0700
changeset 431962 c7091b22d32efde7e224cb7b7671c121905a52bf
parent 431961 94e44c1f0875288e2612bcdff3f93f2b663b7909
child 431963 06cffe71eae8e440915e5b62c80629ed5c539ba7
push id7846
push userryanvm@gmail.com
push dateThu, 28 Sep 2017 16:35:23 +0000
treeherdermozilla-beta@2429ebbd68a9 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdmu, sledru
bugs1381085
milestone57.0
Bug 1381085 - Submit VR frames with a separate ID3DDeviceContextState. r=dmu, a=sledru - Using a separate ID3DDeviceContextState ensures that the WebVR context does not stomp over the mirrored state used by the MLGPU "Advanced" Layers rendering. MozReview-Commit-ID: 99mfdsjFrMI
dom/vr/test/reftest/reftest.list
gfx/vr/VRDisplayHost.cpp
gfx/vr/VRDisplayHost.h
gfx/vr/gfxVROculus.cpp
gfx/vr/gfxVROculus.h
gfx/vr/gfxVRPuppet.cpp
gfx/vr/gfxVRPuppet.h
--- a/dom/vr/test/reftest/reftest.list
+++ b/dom/vr/test/reftest/reftest.list
@@ -1,10 +1,11 @@
 # WebVR Reftests
 default-preferences pref(dom.vr.puppet.enabled,true) pref(dom.vr.test.enabled,true) pref(dom.vr.require-gesture,false) pref(dom.vr.puppet.submitframe,1)
 
-# VR SubmitFrame is only implemented for D3D11 and MacOSX now.
+# VR SubmitFrame is only implemented for D3D11.1 and MacOSX now.
+# Our Windows 7 test machines don't support D3D11.1, so we run these tests on Windows 8+ only.
 # We need to continue to investigate why these reftests can be run well in local,
 # but will be suspended until terminating on reftest D3D11 debug build.
-skip-if((!winWidget&&release_or_beta)||Android||gtkWidget||(winWidget&&isDebugBuild)||!layersGPUAccelerated) == draw_rect.html wrapper.html?draw_rect.png
+skip-if((!winWidget&&release_or_beta)||Android||gtkWidget||(winWidget&&isDebugBuild)||!layersGPUAccelerated||/^Windows\x20NT\x206\.1/.test(http.oscpu)) == draw_rect.html wrapper.html?draw_rect.png
 # On MacOSX platform, getting different color interpolation result.
 # For lower resolution Mac hardware, we need to adjust it to fuzzy-if(cocoaWidget,1,1200).
-fuzzy-if(cocoaWidget,1,600) skip-if((!winWidget&&release_or_beta)||Android||gtkWidget||(winWidget&&isDebugBuild)||!layersGPUAccelerated) == change_size.html wrapper.html?change_size.png
+fuzzy-if(cocoaWidget,1,600) skip-if((!winWidget&&release_or_beta)||Android||gtkWidget||(winWidget&&isDebugBuild)||!layersGPUAccelerated||/^Windows\x20NT\x206\.1/.test(http.oscpu)) == change_size.html wrapper.html?change_size.png
--- a/gfx/vr/VRDisplayHost.cpp
+++ b/gfx/vr/VRDisplayHost.cpp
@@ -9,44 +9,138 @@
 #include "mozilla/layers/TextureHost.h"
 #include "mozilla/dom/GamepadBinding.h" // For GamepadMappingType
 
 #if defined(XP_WIN)
 
 #include <d3d11.h>
 #include "gfxWindowsPlatform.h"
 #include "../layers/d3d11/CompositorD3D11.h"
+#include "mozilla/gfx/DeviceManagerDx.h"
 #include "mozilla/layers/TextureD3D11.h"
 
 #elif defined(XP_MACOSX)
 
 #include "mozilla/gfx/MacIOSurface.h"
 
 #endif
 
 using namespace mozilla;
 using namespace mozilla::gfx;
 using namespace mozilla::layers;
 
+VRDisplayHost::AutoRestoreRenderState::AutoRestoreRenderState(VRDisplayHost* aDisplay)
+  : mDisplay(aDisplay)
+  , mSuccess(true)
+{
+#if defined(XP_WIN)
+  ID3D11DeviceContext1* context = mDisplay->GetD3DDeviceContext();
+  ID3DDeviceContextState* state = mDisplay->GetD3DDeviceContextState();
+  if (!context || !state) {
+    mSuccess = false;
+    return;
+  }
+  context->SwapDeviceContextState(state, getter_AddRefs(mPrevDeviceContextState));
+#endif
+}
+
+VRDisplayHost::AutoRestoreRenderState::~AutoRestoreRenderState()
+{
+#if defined(XP_WIN)
+  ID3D11DeviceContext1* context = mDisplay->GetD3DDeviceContext();
+  if (context && mSuccess) {
+    context->SwapDeviceContextState(mPrevDeviceContextState, nullptr);
+  }
+#endif
+}
+
+bool
+VRDisplayHost::AutoRestoreRenderState::IsSuccess()
+{
+  return mSuccess;
+}
+
 VRDisplayHost::VRDisplayHost(VRDeviceType aType)
  : mFrameStarted(false)
 {
   MOZ_COUNT_CTOR(VRDisplayHost);
   mDisplayInfo.mType = aType;
   mDisplayInfo.mDisplayID = VRSystemManager::AllocateDisplayID();
   mDisplayInfo.mPresentingGroups = 0;
   mDisplayInfo.mGroupMask = kVRGroupContent;
   mDisplayInfo.mFrameId = 0;
 }
 
 VRDisplayHost::~VRDisplayHost()
 {
   MOZ_COUNT_DTOR(VRDisplayHost);
 }
 
+#if defined(XP_WIN)
+bool
+VRDisplayHost::CreateD3DObjects()
+{
+  if (!mDevice) {
+    RefPtr<ID3D11Device> device = gfx::DeviceManagerDx::Get()->GetCompositorDevice();
+    if (!device) {
+      NS_WARNING("VRDisplayHost::CreateD3DObjects failed to get a D3D11Device");
+      return false;
+    }
+    if (FAILED(device->QueryInterface(__uuidof(ID3D11Device1), getter_AddRefs(mDevice)))) {
+      NS_WARNING("VRDisplayHost::CreateD3DObjects failed to get a D3D11Device1");
+      return false;
+    }
+  }
+  if (!mContext) {
+    mDevice->GetImmediateContext1(getter_AddRefs(mContext));
+    if (!mContext) {
+      NS_WARNING("VRDisplayHost::CreateD3DObjects failed to get an immediate context");
+      return false;
+    }
+  }
+  if (!mDeviceContextState) {
+    D3D_FEATURE_LEVEL featureLevels[] {
+      D3D_FEATURE_LEVEL_11_1,
+      D3D_FEATURE_LEVEL_11_0
+    };
+    mDevice->CreateDeviceContextState(0,
+                                      featureLevels,
+                                      2,
+                                      D3D11_SDK_VERSION,
+                                      __uuidof(ID3D11Device1),
+                                      nullptr,
+                                      getter_AddRefs(mDeviceContextState));
+  }
+  if (!mDeviceContextState) {
+    NS_WARNING("VRDisplayHost::CreateD3DObjects failed to get a D3D11DeviceContextState");
+    return false;
+  }
+  return true;
+}
+
+ID3D11Device1*
+VRDisplayHost::GetD3DDevice()
+{
+  return mDevice;
+}
+
+ID3D11DeviceContext1*
+VRDisplayHost::GetD3DDeviceContext()
+{
+  return mContext;
+}
+
+ID3DDeviceContextState*
+VRDisplayHost::GetD3DDeviceContextState()
+{
+  return mDeviceContextState;
+}
+
+#endif // defined(XP_WIN)
+
 void
 VRDisplayHost::SetGroupMask(uint32_t aGroupMask)
 {
   mDisplayInfo.mGroupMask = aGroupMask;
 }
 
 bool
 VRDisplayHost::GetIsConnected()
--- a/gfx/vr/VRDisplayHost.h
+++ b/gfx/vr/VRDisplayHost.h
@@ -12,16 +12,19 @@
 #include "nsCOMPtr.h"
 #include "mozilla/RefPtr.h"
 #include "mozilla/gfx/2D.h"
 #include "mozilla/Atomics.h"
 #include "mozilla/EnumeratedArray.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/TypedEnumBits.h"
 #include "mozilla/dom/GamepadPoseState.h"
+#if defined(XP_WIN)
+#include <d3d11_1.h>
+#endif
 
 #if defined(XP_MACOSX)
 class MacIOSurface;
 #endif
 namespace mozilla {
 namespace layers {
 class PTextureParent;
 #if defined(XP_WIN)
@@ -51,16 +54,29 @@ public:
                    uint64_t aFrameId,
                    const gfx::Rect& aLeftEyeRect,
                    const gfx::Rect& aRightEyeRect);
 
   bool CheckClearDisplayInfoDirty();
   void SetGroupMask(uint32_t aGroupMask);
   bool GetIsConnected();
 
+  class AutoRestoreRenderState {
+  public:
+    explicit AutoRestoreRenderState(VRDisplayHost* aDisplay);
+    ~AutoRestoreRenderState();
+    bool IsSuccess();
+  private:
+    RefPtr<VRDisplayHost> mDisplay;
+#if defined(XP_WIN)
+    RefPtr<ID3DDeviceContextState> mPrevDeviceContextState;
+#endif
+    bool mSuccess;
+  };
+
 protected:
   explicit VRDisplayHost(VRDeviceType aType);
   virtual ~VRDisplayHost();
 
 #if defined(XP_WIN)
   // Subclasses should override this SubmitFrame function.
   // Returns true if the SubmitFrame call will block as necessary
   // to control timing of the next frame and throttle the render loop
@@ -84,16 +100,29 @@ protected:
 
 protected:
   virtual VRHMDSensorState GetSensorState() = 0;
 
 private:
   VRDisplayInfo mLastUpdateDisplayInfo;
   TimeStamp mLastFrameStart;
   bool mFrameStarted;
+
+#if defined(XP_WIN)
+protected:
+  bool CreateD3DObjects();
+  RefPtr<ID3D11Device1> mDevice;
+  RefPtr<ID3D11DeviceContext1> mContext;
+  ID3D11Device1* GetD3DDevice();
+  ID3D11DeviceContext1* GetD3DDeviceContext();
+  ID3DDeviceContextState* GetD3DDeviceContextState();
+
+private:
+  RefPtr<ID3DDeviceContextState> mDeviceContextState;
+#endif
 };
 
 class VRControllerHost {
 public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(VRControllerHost)
 
   const VRControllerInfo& GetControllerInfo() const;
   void SetButtonPressed(uint64_t aBit);
--- a/gfx/vr/gfxVROculus.cpp
+++ b/gfx/vr/gfxVROculus.cpp
@@ -905,37 +905,24 @@ VRDisplayOculus::GetSensorState(double a
   result.flags |= VRDisplayCapabilityFlags::Cap_Present;
 
   return result;
 }
 
 void
 VRDisplayOculus::StartPresentation()
 {
+  if (!CreateD3DObjects()) {
+    return;
+  }
   mSession->StartPresentation(IntSize(mDisplayInfo.mEyeResolution.width * 2, mDisplayInfo.mEyeResolution.height));
   if (!mSession->IsRenderReady()) {
     return;
   }
 
-  if (!mDevice) {
-    mDevice = gfx::DeviceManagerDx::Get()->GetCompositorDevice();
-    if (!mDevice) {
-      NS_WARNING("Failed to get a D3D11Device for Oculus");
-      return;
-    }
-  }
-
-  if (!mContext) {
-    mDevice->GetImmediateContext(getter_AddRefs(mContext));
-    if (!mContext) {
-      NS_WARNING("Failed to get immediate context for Oculus");
-      return;
-    }
-  }
-
   if (!mQuadVS) {
     if (FAILED(mDevice->CreateVertexShader(sLayerQuadVS.mData, sLayerQuadVS.mLength, nullptr, &mQuadVS))) {
       NS_WARNING("Failed to create vertex shader for Oculus");
       return;
     }
   }
 
   if (!mQuadPS) {
@@ -1043,17 +1030,26 @@ VRDisplayOculus::UpdateConstantBuffers()
 }
 
 bool
 VRDisplayOculus::SubmitFrame(TextureSourceD3D11* aSource,
   const IntSize& aSize,
   const gfx::Rect& aLeftEyeRect,
   const gfx::Rect& aRightEyeRect)
 {
-  if (!mSession->IsRenderReady() || !mDevice || !mContext) {
+  if (!CreateD3DObjects()) {
+    return false;
+  }
+
+  AutoRestoreRenderState restoreState(this);
+  if (!restoreState.IsSuccess()) {
+    return false;
+  }
+
+  if (!mSession->IsRenderReady()) {
     return false;
   }
   /**
     * XXX - We should resolve fail the promise returned by
     *       VRDisplay.requestPresent() when the DX11 resources fail allocation
     *       in VRDisplayOculus::StartPresentation().
     *       Bailing out here prevents the crash but content should be aware
     *       that frames are not being presented.
--- a/gfx/vr/gfxVROculus.h
+++ b/gfx/vr/gfxVROculus.h
@@ -103,18 +103,16 @@ protected:
   virtual ~VRDisplayOculus();
 
   VRHMDSensorState GetSensorState(double absTime);
 
   ovrHmdDesc mDesc;
   RefPtr<VROculusSession> mSession;
   ovrFovPort mFOVPort[2];
 
-  RefPtr<ID3D11Device> mDevice;
-  RefPtr<ID3D11DeviceContext> mContext;
   ID3D11VertexShader* mQuadVS;
   ID3D11PixelShader* mQuadPS;
   RefPtr<ID3D11SamplerState> mLinearSamplerState;
   layers::VertexShaderConstants mVSConstants;
   layers::PixelShaderConstants mPSConstants;
   RefPtr<ID3D11Buffer> mVSConstantBuffer;
   RefPtr<ID3D11Buffer> mPSConstantBuffer;
   RefPtr<ID3D11Buffer> mVertexBuffer;
--- a/gfx/vr/gfxVRPuppet.cpp
+++ b/gfx/vr/gfxVRPuppet.cpp
@@ -161,27 +161,17 @@ void
 VRDisplayPuppet::StartPresentation()
 {
   if (mIsPresenting) {
     return;
   }
   mIsPresenting = true;
 
 #if defined(XP_WIN)
-  if (!mDevice) {
-    mDevice = gfx::DeviceManagerDx::Get()->GetCompositorDevice();
-    if (!mDevice) {
-      NS_WARNING("Failed to get a D3D11Device for Puppet");
-      return;
-    }
-  }
-
-  mDevice->GetImmediateContext(getter_AddRefs(mContext));
-  if (!mContext) {
-    NS_WARNING("Failed to get immediate context for Puppet");
+  if (!CreateD3DObjects()) {
     return;
   }
 
   if (FAILED(mDevice->CreateVertexShader(sLayerQuadVS.mData,
                       sLayerQuadVS.mLength, nullptr, &mQuadVS))) {
     NS_WARNING("Failed to create vertex shader for Puppet");
     return;
   }
@@ -288,16 +278,24 @@ VRDisplayPuppet::SubmitFrame(TextureSour
                              const IntSize& aSize,
                              const gfx::Rect& aLeftEyeRect,
                              const gfx::Rect& aRightEyeRect)
 {
   if (!mIsPresenting) {
     return false;
   }
 
+  if (!CreateD3DObjects()) {
+    return false;
+  }
+  AutoRestoreRenderState restoreState(this);
+  if (!restoreState.IsSuccess()) {
+    return false;
+  }
+
   VRManager *vm = VRManager::Get();
   MOZ_ASSERT(vm);
 
   switch (gfxPrefs::VRPuppetSubmitFrame()) {
     case 0:
       // The VR frame is not displayed.
       break;
     case 1:
--- a/gfx/vr/gfxVRPuppet.h
+++ b/gfx/vr/gfxVRPuppet.h
@@ -51,18 +51,16 @@ protected:
   void Destroy();
 
   bool mIsPresenting;
 
 private:
 #if defined(XP_WIN)
   bool UpdateConstantBuffers();
 
-  RefPtr<ID3D11Device> mDevice;
-  RefPtr<ID3D11DeviceContext> mContext;
   ID3D11VertexShader* mQuadVS;
   ID3D11PixelShader* mQuadPS;
   RefPtr<ID3D11SamplerState> mLinearSamplerState;
   layers::VertexShaderConstants mVSConstants;
   layers::PixelShaderConstants mPSConstants;
   RefPtr<ID3D11Buffer> mVSConstantBuffer;
   RefPtr<ID3D11Buffer> mPSConstantBuffer;
   RefPtr<ID3D11Buffer> mVertexBuffer;