Don't attempt to use WARP after D3D11CreateDevice crashes. (bug 1201202, r=bas)
authorDavid Anderson <dvander@alliedmods.net>
Sun, 13 Sep 2015 23:52:29 -0700
changeset 296588 e91af2be3c3e384a619839cc28a7e8f81a9d0d43
parent 296587 8d28818620984759e64d3e78dd0c841cf8bba493
child 296589 fb1f0c400ec04e49840b1bf68efa98c50b01c952
push id962
push userjlund@mozilla.com
push dateFri, 04 Dec 2015 23:28:54 +0000
treeherdermozilla-release@23a2d286e80f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbas
bugs1201202
milestone43.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
Don't attempt to use WARP after D3D11CreateDevice crashes. (bug 1201202, r=bas)
gfx/src/gfxTelemetry.cpp
gfx/src/gfxTelemetry.h
gfx/thebes/gfxWindowsPlatform.cpp
gfx/thebes/gfxWindowsPlatform.h
--- a/gfx/src/gfxTelemetry.cpp
+++ b/gfx/src/gfxTelemetry.cpp
@@ -11,16 +11,18 @@ namespace gfx {
 const char*
 FeatureStatusToString(FeatureStatus aStatus)
 {
   switch (aStatus) {
     case FeatureStatus::Unused:
       return "unused";
     case FeatureStatus::Unavailable:
       return "unavailable";
+    case FeatureStatus::Crashed:
+      return "crashed";
     case FeatureStatus::Blocked:
       return "blocked";
     case FeatureStatus::Blacklisted:
       return "blacklisted";
     case FeatureStatus::Failed:
       return "failed";
     case FeatureStatus::Disabled:
       return "disabled";
--- a/gfx/src/gfxTelemetry.h
+++ b/gfx/src/gfxTelemetry.h
@@ -16,16 +16,19 @@ enum class FeatureStatus
 {
   // This feature has not been requested.
   Unused,
 
   // This feature is unavailable due to Safe Mode or not being included with
   // the operating system.
   Unavailable,
 
+  // This feature crashed immediately when we tried to initialize it.
+  Crashed,
+
   // This feature was blocked for reasons outside the blacklist, such as a
   // runtime test failing.
   Blocked,
 
   // This feature has been blocked by the graphics blacklist.
   Blacklisted,
 
   // This feature was attempted but failed to activate.
--- a/gfx/thebes/gfxWindowsPlatform.cpp
+++ b/gfx/thebes/gfxWindowsPlatform.cpp
@@ -484,22 +484,19 @@ gfxWindowsPlatform::HandleDeviceReset()
         processes[i]->SendTestGraphicsDeviceReset(uint32_t(resetReason));
       }
     }
   } else {
     Telemetry::Accumulate(Telemetry::DEVICE_RESET_REASON, uint32_t(resetReason));
   }
 
   // Remove devices and adapters.
+  ResetD3D11Devices();
   mD3D10Device = nullptr;
-  mD3D11Device = nullptr;
-  mD3D11ContentDevice = nullptr;
-  mD3D11ImageBridgeDevice = nullptr;
   mAdapter = nullptr;
-  Factory::SetDirect3D11Device(nullptr);
   Factory::SetDirect3D10Device(nullptr);
 
   // Reset local state. Note: we leave feature status variables as-is. They
   // will be recomputed by InitializeDevices().
   mHasDeviceReset = false;
   mHasFakeDeviceReset = false;
   mCompositorD3D11TextureSharingWorks = false;
   mDeviceResetReason = DeviceResetReason::OK;
@@ -1992,61 +1989,66 @@ gfxWindowsPlatform::CheckD3D11Support(bo
   return FeatureStatus::Available;
 }
 
 // We don't have access to the D3D11CreateDevice type in gfxWindowsPlatform.h,
 // since it doesn't include d3d11.h, so we use a static here. It should only
 // be used within InitializeD3D11.
 decltype(D3D11CreateDevice)* sD3D11CreateDeviceFn = nullptr;
 
-void
+FeatureStatus
 gfxWindowsPlatform::AttemptD3D11DeviceCreation()
 {
   RefPtr<IDXGIAdapter1> adapter = GetDXGIAdapter();
   if (!adapter) {
-    return;
+    return FeatureStatus::Unavailable;
   }
 
   HRESULT hr = E_INVALIDARG;
   MOZ_SEH_TRY {
     hr =
       sD3D11CreateDeviceFn(adapter, D3D_DRIVER_TYPE_UNKNOWN, nullptr,
                            // Use
                            // D3D11_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS
                            // to prevent bug 1092260. IE 11 also uses this flag.
                            D3D11_CREATE_DEVICE_BGRA_SUPPORT | D3D11_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS,
                            mFeatureLevels.Elements(), mFeatureLevels.Length(),
                            D3D11_SDK_VERSION, byRef(mD3D11Device), nullptr, nullptr);
   } MOZ_SEH_EXCEPT (EXCEPTION_EXECUTE_HANDLER) {
     gfxCriticalError() << "Crash during D3D11 device creation";
-    return;
+    return FeatureStatus::Crashed;
   }
 
-  if (FAILED(hr) || !DoesD3D11DeviceWork()) {
-    gfxCriticalError() << "D3D11 device creation failed" << hexa(hr);
+  if (FAILED(hr) || !mD3D11Device) {
     mD3D11Device = nullptr;
-    return;
+    gfxCriticalError() << "D3D11 device creation failed: " << hexa(hr);
+    return FeatureStatus::Failed;
+  }
+  if (!DoesD3D11DeviceWork()) {
+    mD3D11Device = nullptr;
+    return FeatureStatus::Blocked;
   }
   if (!mD3D11Device) {
-    return;
+    return FeatureStatus::Failed;
   }
 
   // Only test this when not using WARP since it can fail and cause
   // GetDeviceRemovedReason to return weird values.
   mCompositorD3D11TextureSharingWorks = ::DoesD3D11TextureSharingWork(mD3D11Device);
 
   if (!mCompositorD3D11TextureSharingWorks || !DoesRenderTargetViewNeedsRecreating(mD3D11Device)) {
       gANGLESupportsD3D11 = false;
   }
 
   mD3D11Device->SetExceptionMode(0);
   mIsWARP = false;
+  return FeatureStatus::Available;
 }
 
-void
+FeatureStatus
 gfxWindowsPlatform::AttemptWARPDeviceCreation()
 {
   ScopedGfxFeatureReporter reporterWARP("D3D11-WARP", gfxPrefs::LayersD3D11ForceWARP());
 
   MOZ_SEH_TRY {
     HRESULT hr =
       sD3D11CreateDeviceFn(nullptr, D3D_DRIVER_TYPE_WARP, nullptr,
                            // Use
@@ -2054,33 +2056,33 @@ gfxWindowsPlatform::AttemptWARPDeviceCre
                            // to prevent bug 1092260. IE 11 also uses this flag.
                            D3D11_CREATE_DEVICE_BGRA_SUPPORT,
                            mFeatureLevels.Elements(), mFeatureLevels.Length(),
                            D3D11_SDK_VERSION, byRef(mD3D11Device), nullptr, nullptr);
 
     if (FAILED(hr)) {
       // This should always succeed... in theory.
       gfxCriticalError() << "Failed to initialize WARP D3D11 device! " << hexa(hr);
-      return;
+      return FeatureStatus::Failed;
     }
 
     reporterWARP.SetSuccessful();
   } MOZ_SEH_EXCEPT (EXCEPTION_EXECUTE_HANDLER) {
     gfxCriticalError() << "Exception occurred initializing WARP D3D11 device!";
-    return;
-
+    return FeatureStatus::Crashed;
   }
 
   // Only test for texture sharing on Windows 8 since it puts the device into
   // an unusable state if used on Windows 7
   if (IsWin8OrLater()) {
     mCompositorD3D11TextureSharingWorks = ::DoesD3D11TextureSharingWork(mD3D11Device);
   }
   mD3D11Device->SetExceptionMode(0);
   mIsWARP = true;
+  return FeatureStatus::Available;
 }
 
 bool
 gfxWindowsPlatform::ContentAdapterIsParentAdapter(ID3D11Device* device)
 {
   DXGI_ADAPTER_DESC desc;
   if (!GetDxgiDesc(device, &desc)) {
     gfxCriticalNote << "Could not query device DXGI adapter info";
@@ -2096,94 +2098,95 @@ gfxWindowsPlatform::ContentAdapterIsPare
   {
     gfxCriticalNote << "VendorIDMismatch " << hexa(parent.vendorID()) << " " << hexa(desc.VendorId);
     return false;
   }
 
   return true;
 }
 
-bool
+FeatureStatus
 gfxWindowsPlatform::AttemptD3D11ContentDeviceCreation()
 {
   RefPtr<IDXGIAdapter1> adapter;
   if (!mIsWARP) {
     adapter = GetDXGIAdapter();
     if (!adapter) {
-      return false;
+      return FeatureStatus::Unavailable;
     }
   }
 
   HRESULT hr = E_INVALIDARG;
   MOZ_SEH_TRY {
     hr =
       sD3D11CreateDeviceFn(adapter,
                            mIsWARP ? D3D_DRIVER_TYPE_WARP : D3D_DRIVER_TYPE_UNKNOWN,
                            nullptr,
                            D3D11_CREATE_DEVICE_BGRA_SUPPORT,
                            mFeatureLevels.Elements(), mFeatureLevels.Length(),
                            D3D11_SDK_VERSION, byRef(mD3D11ContentDevice), nullptr, nullptr);
   } MOZ_SEH_EXCEPT (EXCEPTION_EXECUTE_HANDLER) {
-    return false;
+    return FeatureStatus::Crashed;
   }
 
   if (FAILED(hr)) {
-    return false;
+    return FeatureStatus::Failed;
   }
 
   // InitializeD2D() will abort early if the compositor device did not support
   // texture sharing. If we're in the content process, we can't rely on the
   // parent device alone: some systems have dual GPUs that are capable of
   // binding the parent and child processes to different GPUs. As a safety net,
   // we re-check texture sharing against the newly created D3D11 content device.
   // If it fails, we won't use Direct2D.
   if (XRE_IsContentProcess()) {
     if (!DoesD3D11TextureSharingWork(mD3D11ContentDevice) ||
         !ContentAdapterIsParentAdapter(mD3D11ContentDevice))
     {
       mD3D11ContentDevice = nullptr;
-      return false;
+      return FeatureStatus::Failed;
     }
   }
 
   mD3D11ContentDevice->SetExceptionMode(0);
 
   nsRefPtr<ID3D10Multithread> multi;
   mD3D11ContentDevice->QueryInterface(__uuidof(ID3D10Multithread), getter_AddRefs(multi));
   multi->SetMultithreadProtected(TRUE);
 
   Factory::SetDirect3D11Device(mD3D11ContentDevice);
-  return true;
+  return FeatureStatus::Available;
 }
 
-void
+FeatureStatus
 gfxWindowsPlatform::AttemptD3D11ImageBridgeDeviceCreation()
 {
   HRESULT hr = E_INVALIDARG;
   MOZ_SEH_TRY{
     hr =
       sD3D11CreateDeviceFn(GetDXGIAdapter(), D3D_DRIVER_TYPE_UNKNOWN, nullptr,
                            D3D11_CREATE_DEVICE_BGRA_SUPPORT,
                            mFeatureLevels.Elements(), mFeatureLevels.Length(),
                            D3D11_SDK_VERSION, byRef(mD3D11ImageBridgeDevice), nullptr, nullptr);
   } MOZ_SEH_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
-    return;
+    return FeatureStatus::Crashed;
   }
 
   if (FAILED(hr)) {
-    return;
+    return FeatureStatus::Failed;
   }
 
   mD3D11ImageBridgeDevice->SetExceptionMode(0);
   if (!DoesD3D11AlphaTextureSharingWork(mD3D11ImageBridgeDevice) ||
       (XRE_IsContentProcess() && !ContentAdapterIsParentAdapter(mD3D11ImageBridgeDevice)))
   {
     mD3D11ImageBridgeDevice = nullptr;
-    return;
+    return FeatureStatus::Failed;
   }
+  return FeatureStatus::Available;
 }
 
 void
 gfxWindowsPlatform::SetDeviceInitData(mozilla::gfx::DeviceInitData& aData)
 {
   MOZ_ASSERT(XRE_IsContentProcess());
   sDeviceInitDataDoNotUseDirectly = aData;
 }
@@ -2283,49 +2286,77 @@ gfxWindowsPlatform::InitializeD3D11()
   if (gfxPrefs::DeviceFailForTesting()) {
     mD3D11Status = FeatureStatus::Failed;
     return;
   }
 
   if (XRE_IsParentProcess()) {
     // First try to create a hardware accelerated device.
     if (canUseHardware) {
-      AttemptD3D11DeviceCreation();
+      mD3D11Status = AttemptD3D11DeviceCreation();
+      if (mD3D11Status == FeatureStatus::Crashed) {
+        return;
+      }
     }
 
     // If that failed, see if we can use WARP.
-    if (!mD3D11Device && CanUseWARP()) {
-      AttemptWARPDeviceCreation();
+    if (!mD3D11Device) {
+      if (!CanUseWARP()) {
+        mD3D11Status = FeatureStatus::Blocked;
+        return;
+      }
+      mD3D11Status = AttemptWARPDeviceCreation();
     }
 
+    // If we still have no device by now, exit.
     if (!mD3D11Device) {
-      // Nothing more we can do.
-      mD3D11Status = FeatureStatus::Failed;
+      MOZ_ASSERT(IsFeatureStatusFailure(mD3D11Status));
       return;
     }
+
+    // Either device creation function should have returned Available.
+    MOZ_ASSERT(mD3D11Status == FeatureStatus::Available);
   } else {
     // Child processes do not need a compositor, but they do need to know
     // whether the parent process is using WARP and whether or not texture
     // sharing works.
     mIsWARP = !canUseHardware;
     mCompositorD3D11TextureSharingWorks = GetParentDevicePrefs().d3d11TextureSharingWorks();
+    mD3D11Status = FeatureStatus::Available;
   }
 
-  mD3D11Status = FeatureStatus::Available;
-
   if (CanUseD3D11ImageBridge()) {
-    AttemptD3D11ImageBridgeDeviceCreation();
+    if (AttemptD3D11ImageBridgeDeviceCreation() == FeatureStatus::Crashed) {
+      DisableD3D11AfterCrash();
+      return;
+    }
   }
 
   // We leak these everywhere and we need them our entire runtime anyway, let's
   // leak it here as well. We keep the pointer to sD3D11CreateDeviceFn around
   // as well for D2D1 and device resets.
   d3d11Module.disown();
 }
 
+void
+gfxWindowsPlatform::DisableD3D11AfterCrash()
+{
+  mD3D11Status = FeatureStatus::Crashed;
+  ResetD3D11Devices();
+}
+
+void
+gfxWindowsPlatform::ResetD3D11Devices()
+{
+  mD3D11Device = nullptr;
+  mD3D11ContentDevice = nullptr;
+  mD3D11ImageBridgeDevice = nullptr;
+  Factory::SetDirect3D11Device(nullptr);
+}
+
 static bool
 IsD2DBlacklisted()
 {
   nsCOMPtr<nsIGfxInfo> gfxInfo = services::GetGfxInfo();
   if (gfxInfo) {
     int32_t status;
     if (NS_SUCCEEDED(gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_DIRECT2D, &status))) {
       if (status != nsIGfxInfo::FEATURE_STATUS_OK) {
@@ -2434,22 +2465,25 @@ gfxWindowsPlatform::InitializeD2D1()
 {
   ScopedGfxFeatureReporter d2d1_1("D2D1.1");
 
   mD2D1Status = CheckD2D1Support();
   if (IsFeatureStatusFailure(mD2D1Status)) {
     return;
   }
 
-  if (!AttemptD3D11ContentDeviceCreation()) {
-    mD2D1Status = FeatureStatus::Failed;
+  mD2D1Status = AttemptD3D11ContentDeviceCreation();
+  if (IsFeatureStatusFailure(mD2D1Status)) {
+    if (mD2D1Status == FeatureStatus::Crashed) {
+      DisableD3D11AfterCrash();
+    }
     return;
   }
 
-  mD2D1Status = FeatureStatus::Available;
+  MOZ_ASSERT(mD2D1Status == FeatureStatus::Available);
   d2d1_1.SetSuccessful();
 }
 
 already_AddRefed<ID3D11Device>
 gfxWindowsPlatform::CreateD3D11DecoderDevice()
 {
   nsModuleHandle d3d11Module(LoadLibrarySystem32(L"d3d11.dll"));
   decltype(D3D11CreateDevice)* d3d11CreateDevice = (decltype(D3D11CreateDevice)*)
--- a/gfx/thebes/gfxWindowsPlatform.h
+++ b/gfx/thebes/gfxWindowsPlatform.h
@@ -314,23 +314,26 @@ private:
     bool InitDWriteSupport();
 
     void DisableD2D();
 
     mozilla::gfx::FeatureStatus CheckAccelerationSupport();
     mozilla::gfx::FeatureStatus CheckD3D11Support(bool* aCanUseHardware);
     mozilla::gfx::FeatureStatus CheckD2DSupport();
     mozilla::gfx::FeatureStatus CheckD2D1Support();
-    void AttemptD3D11DeviceCreation();
-    void AttemptWARPDeviceCreation();
-    void AttemptD3D11ImageBridgeDeviceCreation();
-    bool AttemptD3D11ContentDeviceCreation();
+    mozilla::gfx::FeatureStatus AttemptD3D11DeviceCreation();
+    mozilla::gfx::FeatureStatus AttemptWARPDeviceCreation();
+    mozilla::gfx::FeatureStatus AttemptD3D11ImageBridgeDeviceCreation();
+    mozilla::gfx::FeatureStatus AttemptD3D11ContentDeviceCreation();
     bool CanUseD3D11ImageBridge();
     bool ContentAdapterIsParentAdapter(ID3D11Device* device);
 
+    void DisableD3D11AfterCrash();
+    void ResetD3D11Devices();
+
     IDXGIAdapter1 *GetDXGIAdapter();
     bool IsDeviceReset(HRESULT hr, DeviceResetReason* aReason);
 
 #ifdef CAIRO_HAS_DWRITE_FONT
     nsRefPtr<IDWriteFactory> mDWriteFactory;
     nsRefPtr<IDWriteRenderingParams> mRenderingParams[TEXT_RENDERING_COUNT];
     DWRITE_MEASURING_MODE mMeasuringMode;
 #endif