Replace gfxWindowsPlatform::mD3D11Status with gfxConfig. (bug 1254899 part 9, r=jrmuizel)
authorDavid Anderson <danderson@mozilla.com>
Thu, 28 Apr 2016 21:52:55 -0700
changeset 334410 deeca272bf66110678ce6876939811f9261eacb5
parent 334409 bc29b45d95c24187369306028a61f6be88d97557
child 334411 f4b8910028b3e5a33ead7e204c981616e67f132c
push id6249
push userjlund@mozilla.com
push dateMon, 01 Aug 2016 13:59:36 +0000
treeherdermozilla-beta@bad9d4f5bf7e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjrmuizel
bugs1254899
milestone49.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
Replace gfxWindowsPlatform::mD3D11Status with gfxConfig. (bug 1254899 part 9, r=jrmuizel)
gfx/config/gfxConfig.cpp
gfx/config/gfxConfig.h
gfx/config/gfxFallback.h
gfx/config/gfxFeature.cpp
gfx/config/gfxFeature.h
gfx/layers/d3d11/CompositorD3D11.cpp
gfx/src/gfxTelemetry.cpp
gfx/src/gfxTelemetry.h
gfx/thebes/gfxPlatform.cpp
gfx/thebes/gfxPlatform.h
gfx/thebes/gfxWindowsPlatform.cpp
gfx/thebes/gfxWindowsPlatform.h
widget/windows/GfxInfo.cpp
--- a/gfx/config/gfxConfig.cpp
+++ b/gfx/config/gfxConfig.cpp
@@ -110,16 +110,27 @@ gfxConfig::UserForceEnable(Feature aFeat
 
 /* static */ void
 gfxConfig::UserDisable(Feature aFeature, const char* aMessage)
 {
   FeatureState& state = sConfig.GetState(aFeature);
   state.UserDisable(aMessage);
 }
 
+/* static */ void
+gfxConfig::Reenable(Feature aFeature, Fallback aFallback)
+{
+  FeatureState& state = sConfig.GetState(aFeature);
+  MOZ_ASSERT(IsFeatureStatusFailure(state.GetValue()));
+
+  const char* message = state.GetRuntimeMessage();
+  EnableFallback(aFallback, message);
+  state.SetRuntime(FeatureStatus::Available, nullptr);
+}
+
 /* static */ bool
 gfxConfig::UseFallback(Fallback aFallback)
 {
   return sConfig.UseFallbackImpl(aFallback);
 }
 
 /* static */ void
 gfxConfig::EnableFallback(Fallback aFallback, const char* aMessage)
--- a/gfx/config/gfxConfig.h
+++ b/gfx/config/gfxConfig.h
@@ -111,16 +111,22 @@ public:
     return MaybeSetFailed(
       aFeature,
       (aStatus != FeatureStatus::Available &&
        aStatus != FeatureStatus::ForceEnabled),
       aStatus,
       aDisableMessage);
   }
 
+  // Re-enables a feature that was previously disabled, by attaching it to a
+  // fallback. The fallback inherits the message that was used for disabling
+  // the feature. This can be used, for example, when D3D11 fails at runtime
+  // but we acquire a second, successful device with WARP.
+  static void Reenable(Feature aFeature, Fallback aFallback);
+
   // Same as SetDefault, except if the feature already has a default value
   // set, the new value will be set as a runtime value. This is useful for
   // when the base value can change (for example, via an update from the
   // parent process).
   static bool InitOrUpdate(Feature aFeature,
                            bool aEnable,
                            FeatureStatus aDisableStatus,
                            const char* aDisableMessage);
--- a/gfx/config/gfxFallback.h
+++ b/gfx/config/gfxFallback.h
@@ -9,16 +9,17 @@
 #include <stdint.h>
 #include "gfxTelemetry.h"
 
 namespace mozilla {
 namespace gfx {
 
 #define GFX_FALLBACK_MAP(_)                                                       \
   /* Name */                                                                      \
+  _(USE_D3D11_WARP_COMPOSITOR)                                                    \
   /* Add new entries above this comment */
 
 enum class Fallback : uint32_t {
 #define MAKE_ENUM(name) name,
   GFX_FALLBACK_MAP(MAKE_ENUM)
 #undef MAKE_ENUM
   NumValues
 };
--- a/gfx/config/gfxFeature.cpp
+++ b/gfx/config/gfxFeature.cpp
@@ -179,16 +179,23 @@ void
 FeatureState::SetRuntime(FeatureStatus aStatus, const char* aMessage)
 {
   // Default decision must have been made.
   MOZ_ASSERT(mDefault.mStatus != FeatureStatus::Unused);
 
   mRuntime.Set(aStatus, aMessage);
 }
 
+const char*
+FeatureState::GetRuntimeMessage() const
+{
+  MOZ_ASSERT(IsFeatureStatusFailure(mRuntime.mStatus));
+  return mRuntime.mMessage;
+}
+
 void
 FeatureState::Instance::Set(FeatureStatus aStatus, const char* aMessage /* = nullptr */)
 {
   mStatus = aStatus;
   if (aMessage) {
     PR_snprintf(mMessage, sizeof(mMessage), "%s", aMessage);
   }
 }
--- a/gfx/config/gfxFeature.h
+++ b/gfx/config/gfxFeature.h
@@ -11,16 +11,17 @@
 #include "mozilla/Assertions.h"
 
 namespace mozilla {
 namespace gfx {
 
 #define GFX_FEATURE_MAP(_)                                                        \
   /* Name,                        Type,         Description */                    \
   _(HW_COMPOSITING,               Feature,      "Compositing")                    \
+  _(D3D11_COMPOSITING,            Feature,      "Direct3D11 Compositing")         \
   /* Add new entries above this comment */
 
 enum class Feature : uint32_t {
 #define MAKE_ENUM(name, type, desc) name,
   GFX_FEATURE_MAP(MAKE_ENUM)
 #undef MAKE_ENUM
   NumValues
 };
@@ -52,16 +53,17 @@ class FeatureState
   bool MaybeSetFailed(FeatureStatus aStatus, const char* aMessage);
 
  private:
   void SetUser(FeatureStatus aStatus, const char* aMessage);
   void SetEnvironment(FeatureStatus aStatus, const char* aMessage);
   void SetRuntime(FeatureStatus aStatus, const char* aMessage);
   bool IsForcedOnByUser() const;
   bool DisabledByDefault() const;
+  const char* GetRuntimeMessage() const;
   bool IsInitialized() const {
     return mDefault.mStatus != FeatureStatus::Unused;
   }
 
   void AssertInitialized() const {
     MOZ_ASSERT(IsInitialized());
   }
 
--- a/gfx/layers/d3d11/CompositorD3D11.cpp
+++ b/gfx/layers/d3d11/CompositorD3D11.cpp
@@ -12,16 +12,17 @@
 #include "gfxWindowsPlatform.h"
 #include "nsIWidget.h"
 #include "nsIGfxInfo.h"
 #include "mozilla/layers/ImageHost.h"
 #include "mozilla/layers/ContentHost.h"
 #include "mozilla/layers/Effects.h"
 #include "nsWindowsHelpers.h"
 #include "gfxPrefs.h"
+#include "gfxConfig.h"
 #include "gfxCrashReporterUtils.h"
 #include "gfxVR.h"
 #include "mozilla/gfx/StackArray.h"
 #include "mozilla/Services.h"
 
 #include "mozilla/EnumeratedArray.h"
 #include "mozilla/Telemetry.h"
 #include "BlendShaderConstants.h"
@@ -193,17 +194,17 @@ CompositorD3D11::~CompositorD3D11()
   }
 }
 
 bool
 CompositorD3D11::Initialize()
 {
   ScopedGfxFeatureReporter reporter("D3D11 Layers");
 
-  MOZ_ASSERT(gfxPlatform::CanUseDirect3D11());
+  MOZ_ASSERT(gfxConfig::IsEnabled(Feature::D3D11_COMPOSITING));
 
   HRESULT hr;
 
   if (!gfxWindowsPlatform::GetPlatform()->GetD3D11Device(&mDevice)) {
     return false;
   }
 
   mDevice->GetImmediateContext(getter_AddRefs(mContext));
--- a/gfx/src/gfxTelemetry.cpp
+++ b/gfx/src/gfxTelemetry.cpp
@@ -27,16 +27,18 @@ FeatureStatusToString(FeatureStatus aSta
     case FeatureStatus::Disabled:
       return "disabled";
     case FeatureStatus::Available:
       return "available";
     case FeatureStatus::ForceEnabled:
       return "force_enabled";
     case FeatureStatus::CrashedOnStartup:
       return "crashed_on_startup";
+    case FeatureStatus::Broken:
+      return "broken";
     default:
       MOZ_ASSERT_UNREACHABLE("missing status case");
       return "unknown";
   }
 }
 
 bool
 IsFeatureStatusFailure(FeatureStatus aStatus)
--- a/gfx/src/gfxTelemetry.h
+++ b/gfx/src/gfxTelemetry.h
@@ -40,17 +40,20 @@ enum class FeatureStatus
 
   // This feature is available for use.
   Available,
 
   // This feature was explicitly force-enabled by the user.
   ForceEnabled,
 
   // This feature was disabled due to the startup crash guard.
-  CrashedOnStartup
+  CrashedOnStartup,
+
+  // This feature was attempted but later determined to be broken.
+  Broken
 };
 
 const char* FeatureStatusToString(FeatureStatus aStatus);
 bool IsFeatureStatusFailure(FeatureStatus aStatus);
 bool IsFeatureStatusSuccess(FeatureStatus aStatus);
 
 } // namespace gfx
 } // namespace mozilla
--- a/gfx/thebes/gfxPlatform.cpp
+++ b/gfx/thebes/gfxPlatform.cpp
@@ -2051,17 +2051,16 @@ gfxPlatform::OptimalFormatForContent(gfx
 /**
  * There are a number of layers acceleration (or layers in general) preferences
  * that should be consistent for the lifetime of the application (bug 840967).
  * As such, we will evaluate them all as soon as one of them is evaluated
  * and remember the values.  Changing these preferences during the run will
  * not have any effect until we restart.
  */
 static bool sLayersSupportsD3D9 = false;
-static bool sLayersSupportsD3D11 = false;
 bool gANGLESupportsD3D11 = false;
 static bool sLayersSupportsHardwareVideoDecoding = false;
 static bool sLayersHardwareVideoDecodingFailed = false;
 static bool sBufferRotationCheckPref = true;
 static bool sPrefBrowserTabsRemoteAutostart = false;
 
 static bool sLayersAccelerationPrefsInitialized = false;
 
@@ -2084,33 +2083,23 @@ gfxPlatform::InitAcceleration()
   sPrefBrowserTabsRemoteAutostart = BrowserTabsRemoteAutostart();
 
   nsCOMPtr<nsIGfxInfo> gfxInfo = services::GetGfxInfo();
   nsCString discardFailureId;
   int32_t status;
 #ifdef XP_WIN
   if (gfxConfig::IsForcedOnByUser(Feature::HW_COMPOSITING)) {
     sLayersSupportsD3D9 = true;
-    sLayersSupportsD3D11 = true;
   } else if (!gfxPrefs::LayersAccelerationDisabledDoNotUseDirectly() && gfxInfo) {
     if (NS_SUCCEEDED(gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_DIRECT3D_9_LAYERS, discardFailureId, &status))) {
       if (status == nsIGfxInfo::FEATURE_STATUS_OK) {
         MOZ_ASSERT(!sPrefBrowserTabsRemoteAutostart || IsVistaOrLater());
         sLayersSupportsD3D9 = true;
       }
     }
-    if (NS_SUCCEEDED(gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_DIRECT3D_11_LAYERS, discardFailureId, &status))) {
-      if (status == nsIGfxInfo::FEATURE_STATUS_OK) {
-        sLayersSupportsD3D11 = true;
-      }
-    }
-    if (!gfxPrefs::LayersD3D11DisableWARP()) {
-      // Always support D3D11 when WARP is allowed.
-      sLayersSupportsD3D11 = true;
-    }
     if (NS_SUCCEEDED(gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_DIRECT3D_11_ANGLE, discardFailureId, &status))) {
       if (status == nsIGfxInfo::FEATURE_STATUS_OK) {
         gANGLESupportsD3D11 = true;
       }
     }
   }
 #endif
 
@@ -2171,25 +2160,16 @@ gfxPlatform::CanUseDirect3D9()
 {
   // this function is called from the compositor thread, so it is not
   // safe to init the prefs etc. from here.
   MOZ_ASSERT(sLayersAccelerationPrefsInitialized);
   return sLayersSupportsD3D9;
 }
 
 bool
-gfxPlatform::CanUseDirect3D11()
-{
-  // this function is called from the compositor thread, so it is not
-  // safe to init the prefs etc. from here.
-  MOZ_ASSERT(sLayersAccelerationPrefsInitialized);
-  return sLayersSupportsD3D11;
-}
-
-bool
 gfxPlatform::CanUseHardwareVideoDecoding()
 {
   // this function is called from the compositor thread, so it is not
   // safe to init the prefs etc. from here.
   MOZ_ASSERT(sLayersAccelerationPrefsInitialized);
   return sLayersSupportsHardwareVideoDecoding && !sLayersHardwareVideoDecodingFailed;
 }
 
@@ -2450,35 +2430,36 @@ gfxPlatform::NotifyCompositorCreated(Lay
 
 void
 gfxPlatform::GetDeviceInitData(mozilla::gfx::DeviceInitData* aOut)
 {
   MOZ_ASSERT(XRE_IsParentProcess());
   aOut->useHwCompositing() = gfxConfig::IsEnabled(Feature::HW_COMPOSITING);
 }
 
-void
+bool
 gfxPlatform::UpdateDeviceInitData()
 {
   if (XRE_IsParentProcess()) {
     // The parent process figures out device initialization on its own.
-    return;
+    return false;
   }
 
   mozilla::gfx::DeviceInitData data;
   mozilla::dom::ContentChild::GetSingleton()->SendGetGraphicsDeviceInitData(&data);
 
   sDeviceInitDataDoNotUseDirectly = data;
 
   // Ensure that child processes have inherited the HW_COMPOSITING pref.
   gfxConfig::InitOrUpdate(
     Feature::HW_COMPOSITING,
     GetParentDevicePrefs().useHwCompositing(),
     FeatureStatus::Blocked,
     "Hardware-accelerated compositing disabled in parent process");
+  return true;
 }
 
 bool
 gfxPlatform::SupportsApzDragInput() const
 {
   return gfxPrefs::APZDragEnabled();
 }
 
--- a/gfx/thebes/gfxPlatform.h
+++ b/gfx/thebes/gfxPlatform.h
@@ -445,17 +445,16 @@ public:
     }
 
     // Are we in safe mode?
     static bool InSafeMode();
 
     static bool OffMainThreadCompositingEnabled();
 
     static bool CanUseDirect3D9();
-    static bool CanUseDirect3D11();
     virtual bool CanUseHardwareVideoDecoding();
     static bool CanUseDirect3D11ANGLE();
 
     // Returns a prioritized list of all available compositor backends.
     void GetCompositorBackends(bool useAcceleration, nsTArray<mozilla::layers::LayersBackend>& aBackends);
 
     /**
      * Is it possible to use buffer rotation.  Note that these
@@ -679,19 +678,20 @@ protected:
      * aBackendBitmask specifies the backends which are acceptable to the caller.
      * The backend used is determined by aBackendBitmask and the order specified
      * by the gfx.canvas.azure.backends pref.
      */
     void InitBackendPrefs(uint32_t aCanvasBitmask, mozilla::gfx::BackendType aCanvasDefault,
                           uint32_t aContentBitmask, mozilla::gfx::BackendType aContentDefault);
 
     /**
-     * If in a child process, triggers a refresh of device preferences.
+     * If in a child process, triggers a refresh of device preferences, then returns true.
+     * In a parent process, nothing happens and false is returned.
      */
-    void UpdateDeviceInitData();
+    virtual bool UpdateDeviceInitData();
 
     /**
      * Increase the global device counter after a device has been removed/reset.
      */
     void BumpDeviceCounter();
 
     /**
      * returns the first backend named in the pref gfx.canvas.azure.backends
--- a/gfx/thebes/gfxWindowsPlatform.cpp
+++ b/gfx/thebes/gfxWindowsPlatform.cpp
@@ -360,17 +360,16 @@ NS_IMPL_ISUPPORTS(D3D9SharedTextureRepor
 
 gfxWindowsPlatform::gfxWindowsPlatform()
   : mRenderMode(RENDER_GDI)
   , mDeviceLock("gfxWindowsPlatform.mDeviceLock")
   , mIsWARP(false)
   , mHasDeviceReset(false)
   , mHasFakeDeviceReset(false)
   , mCompositorD3D11TextureSharingWorks(false)
-  , mD3D11Status(FeatureStatus::Unused)
   , mD2D1Status(FeatureStatus::Unused)
   , mHasD3D9DeviceReset(false)
 {
   mUseClearTypeForDownloadableFonts = UNINITIALIZED_VALUE;
   mUseClearTypeAlways = UNINITIALIZED_VALUE;
 
   /* 
    * Initialize COM 
@@ -411,16 +410,17 @@ gfxWindowsPlatform::InitAcceleration()
   if (IsWin8OrLater()) {
     mFeatureLevels.AppendElement(D3D_FEATURE_LEVEL_11_1);
   }
   mFeatureLevels.AppendElement(D3D_FEATURE_LEVEL_11_0);
   mFeatureLevels.AppendElement(D3D_FEATURE_LEVEL_10_1);
   mFeatureLevels.AppendElement(D3D_FEATURE_LEVEL_10_0);
   mFeatureLevels.AppendElement(D3D_FEATURE_LEVEL_9_3);
 
+  InitializeConfig();
   InitializeDevices();
   UpdateRenderMode();
 }
 
 bool
 gfxWindowsPlatform::CanUseHardwareVideoDecoding()
 {
   if (!gfxPrefs::LayersPreferD3D9() && !mCompositorD3D11TextureSharingWorks) {
@@ -525,17 +525,17 @@ gfxWindowsPlatform::UpdateRenderMode()
   UpdateBackendPrefs();
 
   if (didReset) {
     mScreenReferenceDrawTarget =
       CreateOffscreenContentDrawTarget(IntSize(1, 1), SurfaceFormat::B8G8R8A8);
     if (!mScreenReferenceDrawTarget) {
       gfxCriticalNote << "Failed to update reference draw target after device reset"
                       << ", D3D11 device:" << hexa(Factory::GetDirect3D11Device())
-                      << ", D3D11 status:" << FeatureStatusToString(GetD3D11Status())
+                      << ", D3D11 status:" << FeatureStatusToString(gfxConfig::GetValue(Feature::D3D11_COMPOSITING))
                       << ", D2D1 device:" << hexa(Factory::GetD2D1Device())
                       << ", D2D1 status:" << FeatureStatusToString(GetD2D1Status())
                       << ", content:" << int(GetDefaultContentBackend())
                       << ", compositor:" << int(GetCompositorBackend());
       MOZ_CRASH("GFX: Failed to update reference draw target after device reset");
     }
   }
 }
@@ -1907,81 +1907,109 @@ bool DoesD3D11TextureSharingWork(ID3D11D
   return DoesD3D11TextureSharingWorkInternal(device, DXGI_FORMAT_B8G8R8A8_UNORM, D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE);
 }
 
 bool DoesD3D11AlphaTextureSharingWork(ID3D11Device *device)
 {
   return DoesD3D11TextureSharingWorkInternal(device, DXGI_FORMAT_R8_UNORM, D3D11_BIND_SHADER_RESOURCE);
 }
 
-bool
-gfxWindowsPlatform::CanUseWARP()
+static inline bool
+IsGfxInfoStatusOkay(int32_t aFeature)
 {
-  if (gfxPrefs::LayersD3D11ForceWARP()) {
+  nsCOMPtr<nsIGfxInfo> gfxInfo = services::GetGfxInfo();
+  if (!gfxInfo) {
     return true;
   }
 
-  // The child process can only use WARP if the parent process is also using
-  // WARP.
-  if (XRE_IsContentProcess()) {
-    return GetParentDevicePrefs().useD3D11WARP();
+  int32_t status;
+  nsCString discardFailureId;
+  if (FAILED(gfxInfo->GetFeatureStatus(aFeature, discardFailureId, &status))) {
+    return true;
   }
 
+  return status == nsIGfxInfo::FEATURE_STATUS_OK;
+}
+
+static inline bool
+IsWARPStable()
+{
   // It seems like nvdxgiwrap makes a mess of WARP. See bug 1154703.
-  if (!IsWin8OrLater() ||
-      gfxPrefs::LayersD3D11DisableWARP() ||
-      GetModuleHandleA("nvdxgiwrap.dll"))
-  {
+  if (!IsWin8OrLater() || GetModuleHandleA("nvdxgiwrap.dll")) {
     return false;
   }
   return true;
 }
 
-FeatureStatus
-gfxWindowsPlatform::CheckD3D11Support(bool* aCanUseHardware)
+void
+gfxWindowsPlatform::InitializeConfig()
 {
-  // Don't revive D3D11 support after a failure.
-  if (IsFeatureStatusFailure(mD3D11Status)) {
-    return mD3D11Status;
-  }
-
-  if (XRE_IsContentProcess()) {
-    if (!GetParentDevicePrefs().useD3D11()) {
-      return FeatureStatus::Blocked;
-    }
-    *aCanUseHardware = !GetParentDevicePrefs().useD3D11WARP();
-    return FeatureStatus::Available;
+  if (!XRE_IsParentProcess()) {
+    return;
   }
 
-  if (gfxPrefs::LayersD3D11ForceWARP()) {
-    *aCanUseHardware = false;
-    return FeatureStatus::Available;
-  }
-  if (gfxConfig::IsForcedOnByUser(Feature::HW_COMPOSITING)) {
-    *aCanUseHardware = true;
-    return FeatureStatus::Available;
+  InitializeD3D11Config();
+}
+
+void
+gfxWindowsPlatform::InitializeD3D11Config()
+{
+  MOZ_ASSERT(XRE_IsParentProcess());
+
+  FeatureState& d3d11 = gfxConfig::GetFeature(Feature::D3D11_COMPOSITING);
+
+  d3d11.EnableByDefault();
+
+  // If the user prefers D3D9, act as though they disabled D3D11.
+  if (gfxPrefs::LayersPreferD3D9()) {
+    d3d11.UserDisable("Disabled due to user preference for Direct3D 9");
+    return;
   }
 
-  if (nsCOMPtr<nsIGfxInfo> gfxInfo = services::GetGfxInfo()) {
-    int32_t status;
-    nsCString discardFailureId;
-    if (NS_SUCCEEDED(gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_DIRECT3D_11_LAYERS, discardFailureId, &status))) {
-      if (status != nsIGfxInfo::FEATURE_STATUS_OK) {
-        if (CanUseWARP()) {
-          *aCanUseHardware = false;
-          return FeatureStatus::Available;
-        }
-        return FeatureStatus::Blacklisted;
-      }
+  if (!IsGfxInfoStatusOkay(nsIGfxInfo::FEATURE_DIRECT3D_11_LAYERS)) {
+    if (IsWARPStable() && !gfxPrefs::LayersD3D11DisableWARP()) {
+      // We do not expect hardware D3D11 to work, so we'll try WARP.
+      gfxConfig::EnableFallback(
+        Fallback::USE_D3D11_WARP_COMPOSITOR,
+        "Hardware-accelerated Direct3D11 compositing is blocklisted");
+    } else {
+      // There is little to no chance of D3D11 working, so just disable it.
+      d3d11.Disable(FeatureStatus::Blacklisted, "Hardware-accelerated Direct3D11 compositing is blocklisted");
     }
   }
 
-  // If we've used WARP once, we continue to use it after device resets.
-  *aCanUseHardware = !mIsWARP;
-  return FeatureStatus::Available;
+  // Check if the user really, really wants WARP.
+  if (gfxPrefs::LayersD3D11ForceWARP()) {
+    // Force D3D11 on even if we disabled it.
+    d3d11.UserForceEnable("User force-enabled WARP on disabled hardware");
+
+    gfxConfig::EnableFallback(
+      Fallback::USE_D3D11_WARP_COMPOSITOR,
+      "Force-enabled by user preference");
+  }
+}
+
+bool
+gfxWindowsPlatform::UpdateDeviceInitData()
+{
+  if (!gfxPlatform::UpdateDeviceInitData()) {
+    return false;
+  }
+
+  if (gfxConfig::InitOrUpdate(Feature::D3D11_COMPOSITING,
+                              GetParentDevicePrefs().useD3D11(),
+                              FeatureStatus::Disabled,
+                              "Disabled by parent process"))
+  {
+    if (GetParentDevicePrefs().useD3D11WARP()) {
+      gfxConfig::EnableFallback(Fallback::USE_D3D11_WARP_COMPOSITOR, "Requested by parent process");
+    }
+  }
+
+  return true;
 }
 
 // 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;
 
 bool
@@ -1998,37 +2026,41 @@ gfxWindowsPlatform::AttemptD3D11DeviceCr
         mFeatureLevels.Elements(), mFeatureLevels.Length(),
         D3D11_SDK_VERSION, getter_AddRefs(aOutDevice), nullptr, nullptr);
   } MOZ_SEH_EXCEPT (EXCEPTION_EXECUTE_HANDLER) {
     return false;
   }
   return true;
 }
 
-FeatureStatus
-gfxWindowsPlatform::AttemptD3D11DeviceCreation()
+void
+gfxWindowsPlatform::AttemptD3D11DeviceCreation(FeatureState& d3d11)
 {
   RefPtr<IDXGIAdapter1> adapter = GetDXGIAdapter();
   if (!adapter) {
-    return FeatureStatus::Unavailable;
+    d3d11.SetFailed(FeatureStatus::Unavailable, "Failed to acquire a DXGI adapter");
+    return;
   }
 
   HRESULT hr;
   RefPtr<ID3D11Device> device;
   if (!AttemptD3D11DeviceCreationHelper(adapter, device, hr)) {
     gfxCriticalError() << "Crash during D3D11 device creation";
-    return FeatureStatus::CrashedInHandler;
+    d3d11.SetFailed(FeatureStatus::CrashedInHandler, "Crashed trying to acquire a D3D11 device");
+    return;
   }
 
   if (FAILED(hr) || !device) {
     gfxCriticalError() << "D3D11 device creation failed: " << hexa(hr);
-    return FeatureStatus::Failed;
+    d3d11.SetFailed(FeatureStatus::Failed, "Failed to acquire a D3D11 device");
+    return;
   }
   if (!DoesD3D11DeviceWork()) {
-    return FeatureStatus::Blocked;
+    d3d11.SetFailed(FeatureStatus::Broken, "Direct3D11 device was determined to be broken");
+    return;
   }
 
   {
     MutexAutoLock lock(mDeviceLock);
     mD3D11Device = device;
   }
 
   // Only test this when not using WARP since it can fail and cause
@@ -2036,17 +2068,16 @@ gfxWindowsPlatform::AttemptD3D11DeviceCr
   mCompositorD3D11TextureSharingWorks = ::DoesD3D11TextureSharingWork(mD3D11Device);
 
   if (!mCompositorD3D11TextureSharingWorks || DoesRenderTargetViewNeedsRecreating(mD3D11Device)) {
     gANGLESupportsD3D11 = false;
   }
 
   mD3D11Device->SetExceptionMode(0);
   mIsWARP = false;
-  return FeatureStatus::Available;
 }
 
 bool
 gfxWindowsPlatform::AttemptWARPDeviceCreationHelper(
   ScopedGfxFeatureReporter& aReporterWARP,
   RefPtr<ID3D11Device>& aOutDevice,
   HRESULT& aResOut)
 {
@@ -2062,47 +2093,49 @@ gfxWindowsPlatform::AttemptWARPDeviceCre
 
     aReporterWARP.SetSuccessful();
   } MOZ_SEH_EXCEPT (EXCEPTION_EXECUTE_HANDLER) {
     return false;
   }
   return true;
 }
 
-FeatureStatus
+void
 gfxWindowsPlatform::AttemptWARPDeviceCreation()
 {
   ScopedGfxFeatureReporter reporterWARP("D3D11-WARP", gfxPrefs::LayersD3D11ForceWARP());
+  FeatureState& d3d11 = gfxConfig::GetFeature(Feature::D3D11_COMPOSITING);
 
   HRESULT hr;
   RefPtr<ID3D11Device> device;
   if (!AttemptWARPDeviceCreationHelper(reporterWARP, device, hr)) {
     gfxCriticalError() << "Exception occurred initializing WARP D3D11 device!";
-    return FeatureStatus::CrashedInHandler;
+    d3d11.SetFailed(FeatureStatus::CrashedInHandler, "Crashed creating a D3D11 WARP device");
+    return;
   }
 
   if (FAILED(hr) || !device) {
     // This should always succeed... in theory.
     gfxCriticalError() << "Failed to initialize WARP D3D11 device! " << hexa(hr);
-    return FeatureStatus::Failed;
+    d3d11.SetFailed(FeatureStatus::Failed, "Failed to create a D3D11 WARP device");
+    return;
   }
 
   {
     MutexAutoLock lock(mDeviceLock);
     mD3D11Device = device;
   }
 
   // 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";
@@ -2254,42 +2287,39 @@ void
 gfxWindowsPlatform::InitializeDevices()
 {
   // Ask the parent process for an updated list of which devices to create.
   UpdateDeviceInitData();
 
   // If acceleration is disabled, we refuse to initialize anything.
   FeatureStatus compositing = gfxConfig::GetValue(Feature::HW_COMPOSITING);
   if (IsFeatureStatusFailure(compositing)) {
+    gfxConfig::DisableByDefault(Feature::D3D11_COMPOSITING, compositing, "Hardware compositing is disabled");
     return;
   }
 
   MOZ_ASSERT(!InSafeMode());
 
   // If we previously crashed initializing devices, bail out now.
   D3D11LayersCrashGuard detectCrashes;
   if (detectCrashes.Crashed()) {
     gfxConfig::SetFailed(Feature::HW_COMPOSITING,
                          FeatureStatus::CrashedOnStartup,
                          "Crashed during startup in a previous session");
-    return;
-  }
-
-  // If we're going to prefer D3D9, stop here. The rest of this function
-  // attempts to use D3D11 features.
-  if (gfxPrefs::LayersPreferD3D9()) {
-    mD3D11Status = FeatureStatus::Disabled;
+    gfxConfig::SetFailed(Feature::D3D11_COMPOSITING,
+                         FeatureStatus::CrashedOnStartup,
+                         "Harware acceleration crashed during startup in a previous session");
     return;
   }
 
   // First, initialize D3D11. If this succeeds we attempt to use Direct2D.
   InitializeD3D11();
 
   // Initialize Direct2D.
-  if (mD3D11Status == FeatureStatus::Available) {
+  if (gfxConfig::IsEnabled(Feature::D3D11_COMPOSITING)) {
     InitializeD2D();
   }
 
   // Usually we want D2D in order to use DWrite, but if the users have it
   // forced, we'll let them have it, as unsupported configuration.
   if (gfxPrefs::DirectWriteFontRenderingForceEnabled() &&
       IsFeatureStatusFailure(mD2D1Status) &&
       !mDWriteFactory) {
@@ -2315,71 +2345,77 @@ gfxWindowsPlatform::InitializeD3D11()
   // This function attempts to initialize our D3D11 devices, if the hardware
   // is not blacklisted for D3D11 layers. This first attempt will try to create
   // a hardware accelerated device. If this creation fails or the hardware is
   // blacklisted, then this function will abort if WARP is disabled, causing us
   // to fallback to D3D9 or Basic layers. If WARP is not disabled it will use
   // a WARP device which should always be available on Windows 7 and higher.
 
   // Check if D3D11 is supported on this hardware.
-  bool canUseHardware = true;
-  mD3D11Status = CheckD3D11Support(&canUseHardware);
-  if (IsFeatureStatusFailure(mD3D11Status)) {
+  FeatureState& d3d11 = gfxConfig::GetFeature(Feature::D3D11_COMPOSITING);
+  if (!d3d11.IsEnabled()) {
     return;
   }
 
   // Check if D3D11 is available on this system.
   nsModuleHandle d3d11Module(LoadLibrarySystem32(L"d3d11.dll"));
   sD3D11CreateDeviceFn =
     (decltype(D3D11CreateDevice)*)GetProcAddress(d3d11Module, "D3D11CreateDevice");
   if (!sD3D11CreateDeviceFn) {
     // We should just be on Windows Vista or XP in this case.
-    mD3D11Status = FeatureStatus::Unavailable;
+    d3d11.SetFailed(FeatureStatus::Unavailable, "Direct3D11 not available on this computer");
     return;
   }
 
   // Check if a failure was injected for testing.
   if (gfxPrefs::DeviceFailForTesting()) {
-    mD3D11Status = FeatureStatus::Failed;
+    d3d11.SetFailed(FeatureStatus::Failed, "Direct3D11 device failure simulated by preference");
     return;
   }
 
   if (XRE_IsParentProcess()) {
-    // First try to create a hardware accelerated device.
-    if (canUseHardware) {
-      mD3D11Status = AttemptD3D11DeviceCreation();
-      if (mD3D11Status == FeatureStatus::CrashedInHandler) {
+    if (!gfxConfig::UseFallback(Fallback::USE_D3D11_WARP_COMPOSITOR)) {
+      AttemptD3D11DeviceCreation(d3d11);
+      if (d3d11.GetValue() == FeatureStatus::CrashedInHandler) {
         return;
       }
+
+      // If we failed to get a device, but WARP is allowed and might work,
+      // re-enable D3D11 and switch to WARP.
+      if (!mD3D11Device && IsWARPStable() && !gfxPrefs::LayersD3D11DisableWARP()) {
+        gfxConfig::Reenable(Feature::D3D11_COMPOSITING, Fallback::USE_D3D11_WARP_COMPOSITOR);
+      }
     }
 
     // If that failed, see if we can use WARP.
-    if (!mD3D11Device) {
-      if (!CanUseWARP()) {
-        mD3D11Status = FeatureStatus::Blocked;
+    if (gfxConfig::UseFallback(Fallback::USE_D3D11_WARP_COMPOSITOR)) {
+      MOZ_ASSERT(d3d11.IsEnabled());
+      MOZ_ASSERT(!mD3D11Device);
+      MOZ_ASSERT(IsWARPStable() || gfxPrefs::LayersD3D11ForceWARP());
+
+      AttemptWARPDeviceCreation();
+      if (d3d11.GetValue() == FeatureStatus::CrashedInHandler) {
         return;
       }
-      mD3D11Status = AttemptWARPDeviceCreation();
     }
 
     // If we still have no device by now, exit.
     if (!mD3D11Device) {
-      MOZ_ASSERT(IsFeatureStatusFailure(mD3D11Status));
+      MOZ_ASSERT(!gfxConfig::IsEnabled(Feature::D3D11_COMPOSITING));
       return;
     }
 
     // Either device creation function should have returned Available.
-    MOZ_ASSERT(mD3D11Status == FeatureStatus::Available);
+    MOZ_ASSERT(d3d11.IsEnabled());
   } 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;
+    mIsWARP = gfxConfig::UseFallback(Fallback::USE_D3D11_WARP_COMPOSITOR);
     mCompositorD3D11TextureSharingWorks = GetParentDevicePrefs().d3d11TextureSharingWorks();
-    mD3D11Status = FeatureStatus::Available;
   }
 
   if (CanUseD3D11ImageBridge()) {
     if (AttemptD3D11ImageBridgeDeviceCreation() == FeatureStatus::CrashedInHandler) {
       DisableD3D11AfterCrash();
       return;
     }
   }
@@ -2393,17 +2429,19 @@ gfxWindowsPlatform::InitializeD3D11()
   // 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::CrashedInHandler;
+  gfxConfig::Disable(Feature::D3D11_COMPOSITING,
+    FeatureStatus::CrashedInHandler,
+    "Crashed while acquiring a Direct3D11 device");
   ResetD3D11Devices();
 }
 
 void
 gfxWindowsPlatform::ResetD3D11Devices()
 {
   MutexAutoLock lock(mDeviceLock);
 
@@ -2855,17 +2893,17 @@ void
 gfxWindowsPlatform::GetAcceleratedCompositorBackends(nsTArray<LayersBackend>& aBackends)
 {
   if (gfxPrefs::LayersPreferOpenGL()) {
     aBackends.AppendElement(LayersBackend::LAYERS_OPENGL);
   }
 
   bool allowTryingD3D9 = false;
   if (!gfxPrefs::LayersPreferD3D9()) {
-    if (gfxPlatform::CanUseDirect3D11() && mD3D11Device) {
+    if (mD3D11Device) {
       aBackends.AppendElement(LayersBackend::LAYERS_D3D11);
     } else {
       allowTryingD3D9 = gfxPrefs::LayersAllowD3D9Fallback();
       NS_WARNING("Direct3D 11-accelerated layers are not supported on this system.");
     }
   }
 
   if (gfxPrefs::LayersPreferD3D9() || !IsVistaOrLater() || allowTryingD3D9) {
@@ -2876,28 +2914,19 @@ gfxWindowsPlatform::GetAcceleratedCompos
       NS_WARNING("Direct3D 9-accelerated layers are not supported on this system.");
     }
   }
 }
 
 // Some features are dependent on other features. If this is the case, we
 // try to propagate the status of the parent feature if it wasn't available.
 FeatureStatus
-gfxWindowsPlatform::GetD3D11Status() const
-{
-  if (!gfxConfig::IsEnabled(Feature::HW_COMPOSITING)) {
-    return gfxConfig::GetValue(Feature::HW_COMPOSITING);
-  }
-  return mD3D11Status;
-}
-
-FeatureStatus
 gfxWindowsPlatform::GetD2D1Status() const
 {
-  if (GetD3D11Status() != FeatureStatus::Available) {
+  if (!gfxConfig::IsEnabled(Feature::D3D11_COMPOSITING)) {
     return FeatureStatus::Unavailable;
   }
   return mD2D1Status;
 }
 
 unsigned
 gfxWindowsPlatform::GetD3D11Version()
 {
@@ -2912,17 +2941,17 @@ void
 gfxWindowsPlatform::GetDeviceInitData(DeviceInitData* aOut)
 {
   // Check for device resets before giving back new graphics information.
   UpdateRenderMode();
 
   gfxPlatform::GetDeviceInitData(aOut);
 
   // IPDL initializes each field to false for us so we can early return.
-  if (GetD3D11Status() != FeatureStatus::Available) {
+  if (!gfxConfig::IsEnabled(Feature::D3D11_COMPOSITING)) {
     return;
   }
 
   aOut->useD3D11() = true;
   aOut->useD3D11ImageBridge() = !!mD3D11ImageBridgeDevice;
   aOut->d3d11TextureSharingWorks() = mCompositorD3D11TextureSharingWorks;
   aOut->useD3D11WARP() = mIsWARP;
   aOut->useD2D1() = (GetD2D1Status() == FeatureStatus::Available);
--- a/gfx/thebes/gfxWindowsPlatform.h
+++ b/gfx/thebes/gfxWindowsPlatform.h
@@ -41,16 +41,17 @@
 #define D3D_FEATURE_LEVEL_11_1 static_cast<D3D_FEATURE_LEVEL>(0xb100)
 #define D3D_FL9_1_REQ_TEXTURE2D_U_OR_V_DIMENSION 2048
 #define D3D_FL9_3_REQ_TEXTURE2D_U_OR_V_DIMENSION 4096
 #endif
 
 namespace mozilla {
 namespace gfx {
 class DrawTarget;
+class FeatureState;
 }
 namespace layers {
 class DeviceManagerD3D9;
 class ReadbackManagerD3D11;
 }
 }
 struct IDirect3DDevice9;
 struct ID3D11Device;
@@ -241,17 +242,16 @@ public:
     // Recreate devices as needed for a device reset. Returns true if a device
     // reset occurred.
     bool HandleDeviceReset();
     void UpdateBackendPrefs();
 
     // Return the diagnostic status of DirectX initialization. If
     // initialization has not been attempted, this returns
     // FeatureStatus::Unused.
-    mozilla::gfx::FeatureStatus GetD3D11Status() const;
     mozilla::gfx::FeatureStatus GetD2D1Status() const;
     unsigned GetD3D11Version();
 
     void TestDeviceReset(DeviceResetReason aReason);
 
     virtual already_AddRefed<mozilla::gfx::VsyncSource> CreateHardwareVsyncSource() override;
     static mozilla::Atomic<size_t> sD3D11MemoryUsed;
     static mozilla::Atomic<size_t> sD3D9MemoryUsed;
@@ -265,16 +265,17 @@ public:
     bool SupportsPluginDirectDXGIDrawing();
 
 protected:
     bool AccelerateLayersByDefault() override {
       return true;
     }
     void GetAcceleratedCompositorBackends(nsTArray<mozilla::layers::LayersBackend>& aBackends) override;
     virtual void GetPlatformCMSOutputProfile(void* &mem, size_t &size) override;
+    bool UpdateDeviceInitData() override;
 
 protected:
     RenderMode mRenderMode;
 
     int8_t mUseClearTypeForDownloadableFonts;
     int8_t mUseClearTypeAlways;
 
 private:
@@ -283,26 +284,27 @@ private:
 
     void InitializeDevices();
     void InitializeD3D11();
     void InitializeD2D();
     bool InitDWriteSupport();
 
     void DisableD2D();
 
-    mozilla::gfx::FeatureStatus CheckD3D11Support(bool* aCanUseHardware);
+    void InitializeConfig();
+    void InitializeD3D11Config();
     mozilla::gfx::FeatureStatus CheckD2D1Support();
 
-    mozilla::gfx::FeatureStatus AttemptD3D11DeviceCreation();
+    void AttemptD3D11DeviceCreation(mozilla::gfx::FeatureState& d3d11);
     bool AttemptD3D11DeviceCreationHelper(
         IDXGIAdapter1* aAdapter,
         RefPtr<ID3D11Device>& aOutDevice,
         HRESULT& aResOut);
 
-    mozilla::gfx::FeatureStatus AttemptWARPDeviceCreation();
+    void AttemptWARPDeviceCreation();
     bool AttemptWARPDeviceCreationHelper(
         mozilla::ScopedGfxFeatureReporter& aReporterWARP,
         RefPtr<ID3D11Device>& aOutDevice,
         HRESULT& aResOut);
 
     bool AttemptD3D11ImageBridgeDeviceCreationHelper(
         IDXGIAdapter1* aAdapter, HRESULT& aResOut);
     mozilla::gfx::FeatureStatus AttemptD3D11ImageBridgeDeviceCreation();
@@ -337,15 +339,14 @@ private:
     bool mCompositorD3D11TextureSharingWorks;
     mozilla::Atomic<bool> mHasD3D9DeviceReset;
     DeviceResetReason mDeviceResetReason;
 
     RefPtr<mozilla::layers::ReadbackManagerD3D11> mD3D11ReadbackManager;
 
     // These should not be accessed directly. Use the Get[Feature]Status
     // accessors instead.
-    mozilla::gfx::FeatureStatus mD3D11Status;
     mozilla::gfx::FeatureStatus mD2D1Status;
 
     nsTArray<D3D_FEATURE_LEVEL> mFeatureLevels;
 };
 
 #endif /* GFX_WINDOWS_PLATFORM_H */
--- a/widget/windows/GfxInfo.cpp
+++ b/widget/windows/GfxInfo.cpp
@@ -2,16 +2,17 @@
 /* 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/ArrayUtils.h"
 
 #include <windows.h>
 #include <setupapi.h>
+#include "gfxConfig.h"
 #include "gfxWindowsPlatform.h"
 #include "GfxInfo.h"
 #include "GfxInfoWebGL.h"
 #include "nsUnicharUtils.h"
 #include "prenv.h"
 #include "prprf.h"
 #include "GfxDriverInfo.h"
 #include "mozilla/Preferences.h"
@@ -21,16 +22,17 @@
 
 #if defined(MOZ_CRASHREPORTER)
 #include "nsExceptionHandler.h"
 #include "nsICrashReporter.h"
 #define NS_CRASHREPORTER_CONTRACTID "@mozilla.org/toolkit/crash-reporter;1"
 #endif
 
 using namespace mozilla;
+using namespace mozilla::gfx;
 using namespace mozilla::widget;
 
 #ifdef DEBUG
 NS_IMPL_ISUPPORTS_INHERITED(GfxInfo, GfxInfoBase, nsIGfxInfoDebug)
 #endif
 
 static const uint32_t allWindowsVersions = 0xffffffff;
 
@@ -1255,31 +1257,40 @@ GfxInfo::FindMonitors(JSContext* aCx, JS
 
 void
 GfxInfo::DescribeFeatures(JSContext* aCx, JS::Handle<JSObject*> aObj)
 {
   JS::Rooted<JSObject*> obj(aCx);
 
   gfxWindowsPlatform* platform = gfxWindowsPlatform::GetPlatform();
 
-  gfx::FeatureStatus d3d11 = platform->GetD3D11Status();
+  gfx::FeatureStatus d3d11 = gfxConfig::GetValue(Feature::D3D11_COMPOSITING);
   if (!InitFeatureObject(aCx, aObj, "d3d11", d3d11, &obj)) {
     return;
   }
   if (d3d11 == gfx::FeatureStatus::Available) {
     JS::Rooted<JS::Value> val(aCx, JS::Int32Value(platform->GetD3D11Version()));
     JS_SetProperty(aCx, obj, "version", val);
 
     val = JS::BooleanValue(platform->IsWARP());
     JS_SetProperty(aCx, obj, "warp", val);
 
     val = JS::BooleanValue(platform->CompositorD3D11TextureSharingWorks());
     JS_SetProperty(aCx, obj, "textureSharing", val);
 
-    val = JS::BooleanValue(!platform->CanUseDirect3D11());
+    bool blacklisted = false;
+    if (nsCOMPtr<nsIGfxInfo> gfxInfo = services::GetGfxInfo()) {
+      int32_t status;
+      nsCString discardFailureId;
+      if (SUCCEEDED(gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_DIRECT3D_11_LAYERS, discardFailureId, &status))) {
+        blacklisted = (status != nsIGfxInfo::FEATURE_STATUS_OK);
+      }
+    }
+
+    val = JS::BooleanValue(blacklisted);
     JS_SetProperty(aCx, obj, "blacklisted", val);
   }
 
   gfx::FeatureStatus d2d = platform->GetD2D1Status();
   if (!InitFeatureObject(aCx, aObj, "d2d", d2d, &obj)) {
     return;
   }
   {