Introduce gfxConfig, a manager for graphics feature settings. (bug 1254899 part 3, r=milan)
☠☠ backed out by df82a3088812 ☠ ☠
authorDavid Anderson <danderson@mozilla.com>
Wed, 27 Apr 2016 22:54:25 -0700
changeset 357313 2de214d5d1e098230aa00d80813615104a27f067
parent 357312 f2056259d227bb92c7712e84f61c90f65d502fba
child 357314 64e58f9625ebb834fb232f01311bf4e17252ca6c
push id16755
push useryura.zenevich@gmail.com
push dateThu, 28 Apr 2016 15:12:20 +0000
reviewersmilan
bugs1254899
milestone49.0a1
Introduce gfxConfig, a manager for graphics feature settings. (bug 1254899 part 3, r=milan)
gfx/config/gfxConfig.cpp
gfx/config/gfxConfig.h
gfx/config/gfxFallback.h
gfx/config/gfxFeature.cpp
gfx/config/gfxFeature.h
gfx/config/moz.build
gfx/layers/d3d11/CompositorD3D11.cpp
gfx/layers/d3d9/CompositorD3D9.cpp
gfx/layers/opengl/CompositorOGL.cpp
gfx/moz.build
gfx/src/gfxTelemetry.cpp
gfx/src/gfxTelemetry.h
gfx/thebes/gfxPlatform.cpp
gfx/thebes/gfxPlatform.h
gfx/thebes/gfxPrefs.h
gfx/thebes/gfxWindowsPlatform.cpp
widget/nsBaseWidget.cpp
widget/windows/nsWindow.cpp
new file mode 100644
--- /dev/null
+++ b/gfx/config/gfxConfig.cpp
@@ -0,0 +1,139 @@
+/* -*- Mode: C++; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set sts=2 ts=8 sw=2 tw=99 et: */
+/* 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 "gfxConfig.h"
+
+namespace mozilla {
+namespace gfx {
+
+static gfxConfig sConfig;
+
+// When querying the current stae of a feature, assert that we initialized some
+// default value for the feature already. This ensures that the config/init
+// order is correct.
+/* static */ void
+gfxConfig::AssertStatusInitialized(Feature aFeature)
+{
+  sConfig.GetState(aFeature).AssertInitialized();
+}
+
+/* static */ bool
+gfxConfig::IsEnabled(Feature aFeature)
+{
+  AssertStatusInitialized(aFeature);
+
+  FeatureStatus status = GetValue(aFeature);
+  return status == FeatureStatus::Available || status == FeatureStatus::ForceEnabled;
+}
+
+/* static */ bool
+gfxConfig::IsDisabledByDefault(Feature aFeature)
+{
+  AssertStatusInitialized(aFeature);
+
+  const FeatureState& state = sConfig.GetState(aFeature);
+  return state.DisabledByDefault();
+}
+
+/* static */ bool
+gfxConfig::IsForcedOnByUser(Feature aFeature)
+{
+  AssertStatusInitialized(aFeature);
+
+  const FeatureState& state = sConfig.GetState(aFeature);
+  return state.IsForcedOnByUser();
+}
+
+/* static */ FeatureStatus
+gfxConfig::GetValue(Feature aFeature)
+{
+  AssertStatusInitialized(aFeature);
+
+  const FeatureState& state = sConfig.GetState(aFeature);
+  return state.GetValue();
+}
+
+/* static */
+bool
+gfxConfig::SetDefault(Feature aFeature,
+                      bool aEnable,
+                      FeatureStatus aDisableStatus,
+                      const char* aDisableMessage)
+{
+  FeatureState& state = sConfig.GetState(aFeature);
+  if (!aEnable) {
+    state.DisableByDefault(aDisableStatus, aDisableMessage);
+    return false;
+  }
+  state.EnableByDefault();
+  return true;
+}
+
+/* static */ void
+gfxConfig::Disable(Feature aFeature, FeatureStatus aStatus, const char* aMessage)
+{
+  AssertStatusInitialized(aFeature);
+
+  // We should never bother setting a runtime status to "enabled," since it would
+  // override an explicit user decision to disable it.
+  MOZ_ASSERT(IsFeatureStatusFailure(aStatus));
+
+  FeatureState& state = sConfig.GetState(aFeature);
+  state.SetRuntime(aStatus, aMessage);
+}
+
+/* static */ void
+gfxConfig::UserEnable(Feature aFeature, const char* aMessage)
+{
+  AssertStatusInitialized(aFeature);
+
+  FeatureState& state = sConfig.GetState(aFeature);
+  state.SetUser(FeatureStatus::Available, aMessage);
+}
+/* static */ void
+gfxConfig::UserForceEnable(Feature aFeature, const char* aMessage)
+{
+  AssertStatusInitialized(aFeature);
+
+  FeatureState& state = sConfig.GetState(aFeature);
+  state.SetUser(FeatureStatus::ForceEnabled, aMessage);
+}
+
+/* static */ void
+gfxConfig::UserDisable(Feature aFeature, const char* aMessage)
+{
+  AssertStatusInitialized(aFeature);
+
+  FeatureState& state = sConfig.GetState(aFeature);
+  state.SetUser(FeatureStatus::Disabled, aMessage);
+}
+
+/* static */ bool
+gfxConfig::UseFallback(Fallback aFallback)
+{
+  return sConfig.UseFallbackImpl(aFallback);
+}
+
+/* static */ void
+gfxConfig::EnableFallback(Fallback aFallback, const char* aMessage)
+{
+  // Ignore aMessage for now.
+  sConfig.EnableFallbackImpl(aFallback);
+}
+
+bool
+gfxConfig::UseFallbackImpl(Fallback aFallback) const
+{
+  return !!(mFallbackBits & (uint64_t(1) << uint64_t(aFallback)));
+}
+
+void
+gfxConfig::EnableFallbackImpl(Fallback aFallback)
+{
+  mFallbackBits |= (uint64_t(1) << uint64_t(aFallback));
+}
+
+} // namespace gfx
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/gfx/config/gfxConfig.h
@@ -0,0 +1,100 @@
+/* -*- Mode: C++; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set sts=2 ts=8 sw=2 tw=99 et: */
+/* 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/. */
+#ifndef mozilla_gfx_config_gfxConfig_h
+#define mozilla_gfx_config_gfxConfig_h
+
+#include "gfxFeature.h"
+#include "gfxFallback.h"
+#include "mozilla/Assertions.h"
+
+namespace mozilla {
+namespace gfx {
+
+class gfxConfig
+{
+public:
+  // Query whether a parameter is enabled, taking into account any user or
+  // runtime overrides. The algorithm works as follow:
+  //
+  //  1. If a runtime decision disabled the parameter, return false.
+  //  2. If a user decision enabled or disabled the parameter, return true or
+  //     false accordingly.
+  //  3. Return whether or not the base value is enabled or disabled.
+  static bool IsEnabled(Feature aFeature);
+
+  // Query the history of a parameter. ForcedOnByUser returns whether or not
+  // the user specifically used a "force" preference to enable the parameter.
+  // IsDisabledByDefault returns whether or not the initial status of the
+  // feature, before adding user prefs and runtime decisions, was disabled.
+  static bool IsForcedOnByUser(Feature aFeature);
+  static bool IsDisabledByDefault(Feature aFeature);
+
+  // Query the raw computed status value of a parameter.
+  static FeatureStatus GetValue(Feature aFeature);
+
+  // Initialize the base value of a parameter. The return value is aEnable.
+  static bool SetDefault(Feature aFeature,
+                         bool aEnable,
+                         FeatureStatus aDisableStatus,
+                         const char* aDisableMessage);
+
+  // Disable a parameter based on a runtime decision.
+  static void Disable(Feature aFeature,
+                      FeatureStatus aStatus,
+                      const char* aMessage);
+
+  // Convenience helper for Disable().
+  static bool UpdateIfFailed(Feature aFeature,
+                             bool aEnable,
+                             FeatureStatus aDisableStatus,
+                             const char* aDisableMessage)
+  {
+    if (!aEnable) {
+      Disable(aFeature, aDisableStatus, aDisableMessage);
+      return false;
+    }
+    return true;
+  }
+
+  // Set a user status that overrides the base value (but not runtime value)
+  // of a parameter.
+  static void UserEnable(Feature aFeature, const char* aMessage);
+  static void UserForceEnable(Feature aFeature, const char* aMessage);
+  static void UserDisable(Feature aFeature, const char* aMessage);
+
+  // Query whether a fallback has been toggled.
+  static bool UseFallback(Fallback aFallback);
+
+  // Enable a fallback.
+  static void EnableFallback(Fallback aFallback, const char* aMessage);
+
+private:
+  FeatureState& GetState(Feature aFeature) {
+    MOZ_ASSERT(size_t(aFeature) < kNumFeatures);
+    return mFeatures[size_t(aFeature)];
+  }
+  const FeatureState& GetState(Feature aFeature) const {
+    MOZ_ASSERT(size_t(aFeature) < kNumFeatures);
+    return mFeatures[size_t(aFeature)];
+  }
+
+  bool UseFallbackImpl(Fallback aFallback) const;
+  void EnableFallbackImpl(Fallback aFallback);
+
+  static void AssertStatusInitialized(Feature aFeature);
+
+private:
+  static const size_t kNumFeatures = size_t(Feature::NumValues);
+
+private:
+  FeatureState mFeatures[kNumFeatures];
+  uint64_t mFallbackBits;
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif // mozilla_gfx_config_gfxConfig_h
new file mode 100644
--- /dev/null
+++ b/gfx/config/gfxFallback.h
@@ -0,0 +1,29 @@
+/* -*- Mode: C++; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set sts=2 ts=8 sw=2 tw=99 et: */
+/* 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/. */
+#ifndef mozilla_gfx_config_gfxFallback_h
+#define mozilla_gfx_config_gfxFallback_h
+
+#include <stdint.h>
+#include "gfxTelemetry.h"
+
+namespace mozilla {
+namespace gfx {
+
+#define GFX_FALLBACK_MAP(_)                                                       \
+  /* Name */                                                                      \
+  /* 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
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif // mozilla_gfx_config_gfxFallback_h
new file mode 100644
--- /dev/null
+++ b/gfx/config/gfxFeature.cpp
@@ -0,0 +1,85 @@
+/* -*- Mode: C++; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set sts=2 ts=8 sw=2 tw=99 et: */
+/* 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 "gfxFeature.h"
+#include "prprf.h"
+
+namespace mozilla {
+namespace gfx {
+
+FeatureStatus
+FeatureState::GetValue() const
+{
+  if (mRuntime.mStatus != FeatureStatus::Unused) {
+    return mRuntime.mStatus;
+  }
+  if (mUser.mStatus != FeatureStatus::Unused) {
+    return mUser.mStatus;
+  }
+  return mDefault.mStatus;
+}
+
+bool
+FeatureState::DisabledByDefault() const
+{
+  return mDefault.mStatus != FeatureStatus::Available;
+}
+
+bool
+FeatureState::IsForcedOnByUser() const
+{
+  return mUser.mStatus == FeatureStatus::ForceEnabled;
+}
+
+void
+FeatureState::EnableByDefault()
+{
+  // User/runtime decisions should not have been made yet.
+  MOZ_ASSERT(mUser.mStatus == FeatureStatus::Unused);
+  MOZ_ASSERT(mRuntime.mStatus == FeatureStatus::Unused);
+
+  mDefault.Set(FeatureStatus::Available);
+}
+
+void
+FeatureState::DisableByDefault(FeatureStatus aStatus, const char* aMessage)
+{
+  // User/runtime decisions should not have been made yet.
+  MOZ_ASSERT(mUser.mStatus == FeatureStatus::Unused);
+  MOZ_ASSERT(mRuntime.mStatus == FeatureStatus::Unused);
+
+  mDefault.Set(aStatus, aMessage);
+}
+
+void
+FeatureState::SetUser(FeatureStatus aStatus, const char* aMessage)
+{
+  // Default decision must have been made, but not runtime.
+  MOZ_ASSERT(mDefault.mStatus != FeatureStatus::Unused);
+  MOZ_ASSERT(mRuntime.mStatus == FeatureStatus::Unused);
+
+  mUser.Set(aStatus, aMessage);
+}
+
+void
+FeatureState::SetRuntime(FeatureStatus aStatus, const char* aMessage)
+{
+  // Default decision must have been made.
+  MOZ_ASSERT(mDefault.mStatus != FeatureStatus::Unused);
+
+  mRuntime.Set(aStatus, aMessage);
+}
+
+void
+FeatureState::Instance::Set(FeatureStatus aStatus, const char* aMessage /* = nullptr */)
+{
+  mStatus = aStatus;
+  if (aMessage) {
+    PR_snprintf(mMessage, sizeof(mMessage), "%s", aMessage);
+  }
+}
+
+} // namespace gfx
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/gfx/config/gfxFeature.h
@@ -0,0 +1,66 @@
+/* -*- Mode: C++; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set sts=2 ts=8 sw=2 tw=99 et: */
+/* 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/. */
+#ifndef mozilla_gfx_config_gfxFeature_h
+#define mozilla_gfx_config_gfxFeature_h
+
+#include <stdint.h>
+#include "gfxTelemetry.h"
+#include "mozilla/Assertions.h"
+
+namespace mozilla {
+namespace gfx {
+
+#define GFX_FEATURE_MAP(_)                                                        \
+  /* Name,                        Type,         Description */                    \
+  _(HW_COMPOSITING,               Feature,      "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
+};
+
+class FeatureState
+{
+ public:
+  FeatureStatus GetValue() const;
+
+  void EnableByDefault();
+  void DisableByDefault(FeatureStatus aStatus, const char* aMessage);
+  void SetUser(FeatureStatus aStatus, const char* aMessage);
+  void SetRuntime(FeatureStatus aStatus, const char* aMessage);
+  bool IsForcedOnByUser() const;
+  bool DisabledByDefault() const;
+
+  void AssertInitialized() const {
+    MOZ_ASSERT(mDefault.mStatus != FeatureStatus::Unused);
+  }
+
+ private:
+  struct Instance {
+    char mMessage[64];
+    FeatureStatus mStatus;
+
+    void Set(FeatureStatus aStatus, const char* aMessage = nullptr);
+  };
+
+  // The default state is the state we decide on startup, based on default
+  // the system, environment, and default preferences.
+  //
+  // The user state factors in any changes to preferences that the user made.
+  //
+  // The runtime state factors in any problems discovered at runtime.
+  Instance mDefault;
+  Instance mUser;
+  Instance mRuntime;
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif // mozilla_gfx_config_gfxFeature_h
new file mode 100644
--- /dev/null
+++ b/gfx/config/moz.build
@@ -0,0 +1,18 @@
+# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+EXPORTS += [
+  'gfxConfig.h',
+  'gfxFallback.h',
+  'gfxFeature.h',
+]
+
+UNIFIED_SOURCES += [
+  'gfxConfig.cpp',
+  'gfxFeature.cpp',
+]
+
+FINAL_LIBRARY = 'xul'
--- a/gfx/layers/d3d11/CompositorD3D11.cpp
+++ b/gfx/layers/d3d11/CompositorD3D11.cpp
@@ -191,19 +191,17 @@ CompositorD3D11::~CompositorD3D11()
       delete attachments;
     }
   }
 }
 
 bool
 CompositorD3D11::Initialize()
 {
-  bool force = gfxPrefs::LayersAccelerationForceEnabled();
-
-  ScopedGfxFeatureReporter reporter("D3D11 Layers", force);
+  ScopedGfxFeatureReporter reporter("D3D11 Layers");
 
   MOZ_ASSERT(gfxPlatform::CanUseDirect3D11());
 
   HRESULT hr;
 
   if (!gfxWindowsPlatform::GetPlatform()->GetD3D11Device(&mDevice)) {
     return false;
   }
--- a/gfx/layers/d3d9/CompositorD3D9.cpp
+++ b/gfx/layers/d3d9/CompositorD3D9.cpp
@@ -35,19 +35,17 @@ CompositorD3D9::~CompositorD3D9()
 {
   mSwapChain = nullptr;
   mDeviceManager = nullptr;
 }
 
 bool
 CompositorD3D9::Initialize()
 {
-  bool force = gfxPrefs::LayersAccelerationForceEnabled();
-
-  ScopedGfxFeatureReporter reporter("D3D9 Layers", force);
+  ScopedGfxFeatureReporter reporter("D3D9 Layers");
 
   MOZ_ASSERT(gfxPlatform::CanUseDirect3D9());
 
   mDeviceManager = gfxWindowsPlatform::GetPlatform()->GetD3D9DeviceManager();
   if (!mDeviceManager) {
     return false;
   }
 
--- a/gfx/layers/opengl/CompositorOGL.cpp
+++ b/gfx/layers/opengl/CompositorOGL.cpp
@@ -220,19 +220,17 @@ CompositorOGL::CleanupResources()
   mGLContext->MarkDestroyed();
 
   mGLContext = nullptr;
 }
 
 bool
 CompositorOGL::Initialize()
 {
-  bool force = gfxPrefs::LayersAccelerationForceEnabled();
-
-  ScopedGfxFeatureReporter reporter("GL Layers", force);
+  ScopedGfxFeatureReporter reporter("GL Layers");
 
   // Do not allow double initialization
   MOZ_ASSERT(mGLContext == nullptr, "Don't reinitialize CompositorOGL");
 
   mGLContext = CreateContext();
 
 #ifdef MOZ_WIDGET_ANDROID
   if (!mGLContext)
--- a/gfx/moz.build
+++ b/gfx/moz.build
@@ -16,16 +16,17 @@ DIRS += [
     'gl',
     'layers',
     'graphite2/src',
     'harfbuzz/src',
     'ots/src',
     'thebes',
     'ipc',
     'vr',
+    'config',
 ]
 
 if CONFIG['MOZ_ENABLE_SKIA']:
     DIRS += ['skia']
 
 if CONFIG['ENABLE_TESTS']:
     DIRS += ['tests/gtest']
 
--- a/gfx/src/gfxTelemetry.cpp
+++ b/gfx/src/gfxTelemetry.cpp
@@ -23,23 +23,26 @@ FeatureStatusToString(FeatureStatus aSta
     case FeatureStatus::Blacklisted:
       return "blacklisted";
     case FeatureStatus::Failed:
       return "failed";
     case FeatureStatus::Disabled:
       return "disabled";
     case FeatureStatus::Available:
       return "available";
+    case FeatureStatus::ForceEnabled:
+      return "force_enabled";
     default:
       MOZ_ASSERT_UNREACHABLE("missing status case");
       return "unknown";
   }
 }
 
 bool
 IsFeatureStatusFailure(FeatureStatus aStatus)
 {
-  return aStatus != FeatureStatus::Unused &&
-         aStatus != FeatureStatus::Available;
+  return !(aStatus == FeatureStatus::Unused ||
+           aStatus == FeatureStatus::Available ||
+           aStatus == FeatureStatus::ForceEnabled);
 }
 
 } // namespace gfx
 } // namespace mozilla
--- a/gfx/src/gfxTelemetry.h
+++ b/gfx/src/gfxTelemetry.h
@@ -33,17 +33,20 @@ enum class FeatureStatus
 
   // This feature was attempted but failed to activate.
   Failed,
 
   // This feature was explicitly disabled by the user.
   Disabled,
 
   // This feature is available for use.
-  Available
+  Available,
+
+  // This feature was explicitly force-enabled by the user.
+  ForceEnabled
 };
 
 const char* FeatureStatusToString(FeatureStatus aStatus);
 bool IsFeatureStatusFailure(FeatureStatus aStatus);
 
 } // namespace gfx
 } // namespace mozilla
 
--- a/gfx/thebes/gfxPlatform.cpp
+++ b/gfx/thebes/gfxPlatform.cpp
@@ -17,16 +17,17 @@
 #include "mozilla/Services.h"
 #include "prprf.h"
 
 #include "gfxCrashReporterUtils.h"
 #include "gfxPlatform.h"
 #include "gfxPrefs.h"
 #include "gfxEnv.h"
 #include "gfxTextRun.h"
+#include "gfxConfig.h"
 
 #ifdef XP_WIN
 #include <process.h>
 #define getpid _getpid
 #else
 #include <unistd.h>
 #endif
 
@@ -146,16 +147,17 @@ void InitGralloc();
 #endif
 void ShutdownTileCache();
 } // namespace layers
 } // namespace mozilla
 
 using namespace mozilla;
 using namespace mozilla::layers;
 using namespace mozilla::gl;
+using namespace mozilla::gfx;
 
 gfxPlatform *gPlatform = nullptr;
 static bool gEverInitialized = false;
 
 static Mutex* gGfxPlatformPrefsLock = nullptr;
 
 // These two may point to the same profile
 static qcms_profile *gCMSOutputProfile = nullptr;
@@ -594,18 +596,18 @@ gfxPlatform::Init()
       // D2D prefs
       forcedPrefs.AppendPrintf("FP(D%d%d%d",
                                gfxPrefs::Direct2DDisabled(),
                                gfxPrefs::Direct2DForceEnabled(),
                                gfxPrefs::DirectWriteFontRenderingForceEnabled());
       // Layers prefs
       forcedPrefs.AppendPrintf("-L%d%d%d%d%d",
                                gfxPrefs::LayersAMDSwitchableGfxEnabled(),
-                               gfxPrefs::LayersAccelerationDisabled(),
-                               gfxPrefs::LayersAccelerationForceEnabled(),
+                               gfxPrefs::LayersAccelerationDisabledDoNotUseDirectly(),
+                               gfxPrefs::LayersAccelerationForceEnabledDoNotUseDirectly(),
                                gfxPrefs::LayersD3D11DisableWARP(),
                                gfxPrefs::LayersD3D11ForceWARP());
       // WebGL prefs
       forcedPrefs.AppendPrintf("-W%d%d%d%d%d%d%d%d",
                                gfxPrefs::WebGLANGLEForceD3D11(),
                                gfxPrefs::WebGLANGLEForceWARP(),
                                gfxPrefs::WebGLDisabled(),
                                gfxPrefs::WebGLDisableANGLE(),
@@ -2061,37 +2063,39 @@ static bool sLayersAccelerationPrefsInit
 
 void
 gfxPlatform::InitAcceleration()
 {
   if (sLayersAccelerationPrefsInitialized) {
     return;
   }
 
+  InitCompositorAccelerationPrefs();
+
   // If this is called for the first time on a non-main thread, we're screwed.
   // At the moment there's no explicit guarantee that the main thread calls
   // this before the compositor thread, but let's at least make the assumption
   // explicit.
   MOZ_ASSERT(NS_IsMainThread(), "can only initialize prefs on the main thread");
 
   gfxPrefs::GetSingleton();
   sPrefBrowserTabsRemoteAutostart = BrowserTabsRemoteAutostart();
 
   nsCOMPtr<nsIGfxInfo> gfxInfo = services::GetGfxInfo();
   nsCString discardFailureId;
   int32_t status;
 #ifdef XP_WIN
-  if (gfxPrefs::LayersAccelerationForceEnabled()) {
+  if (gfxConfig::IsForcedOnByUser(Feature::HW_COMPOSITING)) {
     sLayersSupportsD3D9 = true;
     sLayersSupportsD3D11 = true;
-  } else if (!gfxPrefs::LayersAccelerationDisabled() && gfxInfo) {
+  } 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;
+        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()) {
@@ -2119,16 +2123,51 @@ gfxPlatform::InitAcceleration()
 
   Preferences::AddBoolVarCache(&sLayersHardwareVideoDecodingFailed,
                                "media.hardware-video-decoding.failed",
                                false);
 
   sLayersAccelerationPrefsInitialized = true;
 }
 
+void
+gfxPlatform::InitCompositorAccelerationPrefs()
+{
+  const char *acceleratedEnv = PR_GetEnv("MOZ_ACCELERATED");
+
+  // Base value - does the platform allow acceleration?
+  if (gfxConfig::SetDefault(Feature::HW_COMPOSITING,
+                            AccelerateLayersByDefault(),
+                            FeatureStatus::Blocked,
+                            "Acceleration blocked by platform"))
+  {
+    if (gfxPrefs::LayersAccelerationDisabledDoNotUseDirectly()) {
+      gfxConfig::UserDisable(Feature::HW_COMPOSITING, "Disabled by pref");
+    } else if (acceleratedEnv && *acceleratedEnv == '0') {
+      gfxConfig::UserDisable(Feature::HW_COMPOSITING, "Disabled by envvar");
+    }
+  } else {
+    if (acceleratedEnv && *acceleratedEnv == '1') {
+      gfxConfig::UserEnable(Feature::HW_COMPOSITING, "Enabled by envvar");
+    }
+  }
+
+  // This has specific meaning elsewhere, so we always record it.
+  if (gfxPrefs::LayersAccelerationForceEnabledDoNotUseDirectly()) {
+    gfxConfig::UserForceEnable(Feature::HW_COMPOSITING, "Force-enabled by pref");
+  }
+
+  // Safe mode trumps everything, so we update it as a runtime status.
+  gfxConfig::UpdateIfFailed(
+    Feature::HW_COMPOSITING,
+    !InSafeMode(),
+    FeatureStatus::Blocked,
+    "Acceleration blocked by safe-mode");
+}
+
 bool
 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;
 }
@@ -2154,38 +2193,16 @@ gfxPlatform::CanUseHardwareVideoDecoding
 bool
 gfxPlatform::CanUseDirect3D11ANGLE()
 {
   MOZ_ASSERT(sLayersAccelerationPrefsInitialized);
   return gANGLESupportsD3D11;
 }
 
 bool
-gfxPlatform::ShouldUseLayersAcceleration()
-{
-  const char *acceleratedEnv = PR_GetEnv("MOZ_ACCELERATED");
-  if (gfxPrefs::LayersAccelerationDisabled() ||
-      InSafeMode() ||
-      (acceleratedEnv && *acceleratedEnv == '0'))
-  {
-    return false;
-  }
-  if (gfxPrefs::LayersAccelerationForceEnabled()) {
-    return true;
-  }
-  if (AccelerateLayersByDefault()) {
-    return true;
-  }
-  if (acceleratedEnv && *acceleratedEnv != '0') {
-    return true;
-  }
-  return false;
-}
-
-bool
 gfxPlatform::AccelerateLayersByDefault()
 {
 #if defined(MOZ_GL_PROVIDER) || defined(MOZ_WIDGET_UIKIT)
   return true;
 #else
   return false;
 #endif
 }
@@ -2227,17 +2244,17 @@ gfxPlatform::UsesOffMainThreadCompositin
 
   if (firstTime) {
     MOZ_ASSERT(sLayersAccelerationPrefsInitialized);
     result =
       sPrefBrowserTabsRemoteAutostart ||
       !gfxPrefs::LayersOffMainThreadCompositionForceDisabled();
 #if defined(MOZ_WIDGET_GTK)
     // Linux users who chose OpenGL are being grandfathered in to OMTC
-    result |= gfxPrefs::LayersAccelerationForceEnabled();
+    result |= gfxPrefs::LayersAccelerationForceEnabledDoNotUseDirectly();
 
 #endif
     firstTime = false;
   }
 
   return result;
 }
 
@@ -2357,17 +2374,17 @@ AllowOpenGL(bool* aWhitelisted)
     if (NS_SUCCEEDED(gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_OPENGL_LAYERS, discardFailureId, &status))) {
       if (status == nsIGfxInfo::FEATURE_STATUS_OK) {
         *aWhitelisted = true;
         return true;
       }
     }
   }
 
-  return gfxPrefs::LayersAccelerationForceEnabled();
+  return gfxConfig::IsForcedOnByUser(Feature::HW_COMPOSITING);
 }
 
 void
 gfxPlatform::GetAcceleratedCompositorBackends(nsTArray<LayersBackend>& aBackends)
 {
   // Being whitelisted is not enough to accelerate, but not being whitelisted is
   // enough not to:
   bool whitelisted = false;
@@ -2420,17 +2437,17 @@ gfxPlatform::NotifyCompositorCreated(Lay
     }
   }));
 }
 
 void
 gfxPlatform::GetDeviceInitData(mozilla::gfx::DeviceInitData* aOut)
 {
   MOZ_ASSERT(XRE_IsParentProcess());
-  aOut->useAcceleration() = ShouldUseLayersAcceleration();
+  aOut->useAcceleration() = gfxConfig::IsEnabled(Feature::HW_COMPOSITING);
 }
 
 void
 gfxPlatform::UpdateDeviceInitData()
 {
   if (XRE_IsParentProcess()) {
     // The parent process figures out device initialization on its own.
     return;
--- a/gfx/thebes/gfxPlatform.h
+++ b/gfx/thebes/gfxPlatform.h
@@ -449,20 +449,16 @@ public:
 
     static bool OffMainThreadCompositingEnabled();
 
     static bool CanUseDirect3D9();
     static bool CanUseDirect3D11();
     virtual bool CanUseHardwareVideoDecoding();
     static bool CanUseDirect3D11ANGLE();
 
-    // Returns whether or not layers acceleration should be used. This should
-    // only be called on the parent process.
-    bool ShouldUseLayersAcceleration();
-
     // 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
      * check the preference, but also allow for the override to
      * disable it using DisableBufferRotation.
      */
@@ -775,16 +771,18 @@ private:
      */
     void ComputeTileSize();
 
     /**
      * This uses nsIScreenManager to determine the screen size and color depth
      */
     void PopulateScreenInfo();
 
+    void InitCompositorAccelerationPrefs();
+
     RefPtr<gfxASurface> mScreenReferenceSurface;
     nsCOMPtr<nsIObserver> mSRGBOverrideObserver;
     nsCOMPtr<nsIObserver> mFontPrefsObserver;
     nsCOMPtr<nsIObserver> mMemoryPressureObserver;
 
     // The preferred draw target backend to use for canvas
     mozilla::gfx::BackendType mPreferredCanvasBackend;
     // The fallback draw target backend to use for canvas, if the preferred backend fails
--- a/gfx/thebes/gfxPrefs.h
+++ b/gfx/thebes/gfxPrefs.h
@@ -320,21 +320,21 @@ private:
   DECL_GFX_PREF(Once, "image.mem.surfacecache.max_size_kb",    ImageMemSurfaceCacheMaxSizeKB, uint32_t, 100 * 1024);
   DECL_GFX_PREF(Once, "image.mem.surfacecache.min_expiration_ms", ImageMemSurfaceCacheMinExpirationMS, uint32_t, 60*1000);
   DECL_GFX_PREF(Once, "image.mem.surfacecache.size_factor",    ImageMemSurfaceCacheSizeFactor, uint32_t, 64);
   DECL_GFX_PREF(Live, "image.mozsamplesize.enabled",           ImageMozSampleSizeEnabled, bool, false);
   DECL_GFX_PREF(Once, "image.multithreaded_decoding.limit",    ImageMTDecodingLimit, int32_t, -1);
   DECL_GFX_PREF(Live, "image.single-color-optimization.enabled", ImageSingleColorOptimizationEnabled, bool, true);
 
   DECL_GFX_PREF(Live, "layers.child-process-shutdown",         ChildProcessShutdown, bool, true);
-  DECL_GFX_PREF(Once, "layers.acceleration.disabled",          LayersAccelerationDisabled, bool, false);
+  DECL_GFX_PREF(Once, "layers.acceleration.disabled",          LayersAccelerationDisabledDoNotUseDirectly, bool, false);
   DECL_GFX_PREF(Live, "layers.acceleration.draw-fps",          LayersDrawFPS, bool, false);
   DECL_GFX_PREF(Live, "layers.acceleration.draw-fps.print-histogram",  FPSPrintHistogram, bool, false);
   DECL_GFX_PREF(Live, "layers.acceleration.draw-fps.write-to-file", WriteFPSToFile, bool, false);
-  DECL_GFX_PREF(Once, "layers.acceleration.force-enabled",     LayersAccelerationForceEnabled, bool, false);
+  DECL_GFX_PREF(Once, "layers.acceleration.force-enabled",     LayersAccelerationForceEnabledDoNotUseDirectly, bool, false);
   DECL_GFX_PREF(Once, "layers.allow-d3d9-fallback",            LayersAllowD3D9Fallback, bool, false);
   DECL_GFX_PREF(Once, "layers.amd-switchable-gfx.enabled",     LayersAMDSwitchableGfxEnabled, bool, false);
   DECL_GFX_PREF(Once, "layers.async-pan-zoom.enabled",         AsyncPanZoomEnabledDoNotUseDirectly, bool, true);
   DECL_GFX_PREF(Once, "layers.async-pan-zoom.separate-event-thread", AsyncPanZoomSeparateEventThread, bool, false);
   DECL_GFX_PREF(Live, "layers.bench.enabled",                  LayersBenchEnabled, bool, false);
   DECL_GFX_PREF(Once, "layers.bufferrotation.enabled",         BufferRotationEnabled, bool, true);
 #ifdef MOZ_GFX_OPTIMIZE_MOBILE
   // If MOZ_GFX_OPTIMIZE_MOBILE is defined, we force component alpha off
--- a/gfx/thebes/gfxWindowsPlatform.cpp
+++ b/gfx/thebes/gfxWindowsPlatform.cpp
@@ -64,16 +64,17 @@
 #include <d3d11.h>
 
 #include "nsIMemoryReporter.h"
 #include <winternl.h>
 #include "d3dkmtQueryStatistics.h"
 
 #include "SurfaceCache.h"
 #include "gfxPrefs.h"
+#include "gfxConfig.h"
 
 #include "VsyncSource.h"
 #include "DriverCrashGuard.h"
 #include "mozilla/dom/ContentParent.h"
 
 using namespace mozilla;
 using namespace mozilla::gfx;
 using namespace mozilla::layers;
@@ -1562,17 +1563,17 @@ bool DoesD3D11DeviceWork()
   static bool checked = false;
   static bool result = false;
 
   if (checked)
       return result;
   checked = true;
 
   if (gfxPrefs::Direct2DForceEnabled() ||
-      gfxPrefs::LayersAccelerationForceEnabled())
+      gfxConfig::IsForcedOnByUser(Feature::HW_COMPOSITING))
   {
     result = true;
     return true;
   }
 
   if (GetModuleHandleW(L"igd10umd32.dll")) {
     const wchar_t* checkModules[] = {L"dlumd32.dll",
                                      L"dlumd11.dll",
@@ -1761,17 +1762,17 @@ bool DoesD3D11TextureSharingWorkInternal
 {
   // CreateTexture2D is known to crash on lower feature levels, see bugs
   // 1170211 and 1089413.
   if (device->GetFeatureLevel() < D3D_FEATURE_LEVEL_10_0) {
     return false;
   }
 
   if (gfxPrefs::Direct2DForceEnabled() ||
-      gfxPrefs::LayersAccelerationForceEnabled())
+      gfxConfig::IsForcedOnByUser(Feature::HW_COMPOSITING))
   {
     return true;
   }
 
   if (GetModuleHandleW(L"atidxx32.dll")) {
     nsCOMPtr<nsIGfxInfo> gfxInfo = services::GetGfxInfo();
     if (gfxInfo) {
       nsString vendorID, vendorID2;
@@ -1964,17 +1965,17 @@ gfxWindowsPlatform::CheckD3D11Support(bo
     *aCanUseHardware = !GetParentDevicePrefs().useD3D11WARP();
     return FeatureStatus::Available;
   }
 
   if (gfxPrefs::LayersD3D11ForceWARP()) {
     *aCanUseHardware = false;
     return FeatureStatus::Available;
   }
-  if (gfxPrefs::LayersAccelerationForceEnabled()) {
+  if (gfxConfig::IsForcedOnByUser(Feature::HW_COMPOSITING)) {
     *aCanUseHardware = true;
     return FeatureStatus::Available;
   }
 
   if (nsCOMPtr<nsIGfxInfo> gfxInfo = services::GetGfxInfo()) {
     int32_t status;
     nsCString discardFailureId;
     if (NS_SUCCEEDED(gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_DIRECT3D_11_LAYERS, discardFailureId, &status))) {
@@ -2275,16 +2276,18 @@ void
 gfxWindowsPlatform::InitializeDevices()
 {
   // If acceleration is disabled, we refuse to initialize anything.
   mAcceleration = CheckAccelerationSupport();
   if (IsFeatureStatusFailure(mAcceleration)) {
     return;
   }
 
+  MOZ_ASSERT(!InSafeMode());
+
   // If we previously crashed initializing devices, bail out now. This is
   // effectively a parent-process only check, since the content process
   // cannot create a lock file.
   D3D11LayersCrashGuard detectCrashes;
   if (detectCrashes.Crashed()) {
     mAcceleration = FeatureStatus::Blocked;
     return;
   }
@@ -2321,23 +2324,17 @@ gfxWindowsPlatform::CheckAccelerationSup
   if (IsFeatureStatusFailure(mAcceleration)) {
     return mAcceleration;
   }
   if (XRE_IsContentProcess()) {
     return GetParentDevicePrefs().useAcceleration()
            ? FeatureStatus::Available
            : FeatureStatus::Blocked;
   }
-  if (InSafeMode()) {
-    return FeatureStatus::Blocked;
-  }
-  if (!ShouldUseLayersAcceleration()) {
-    return FeatureStatus::Disabled;
-  }
-  return FeatureStatus::Available;
+  return gfxConfig::GetValue(Feature::HW_COMPOSITING);
 }
 
 bool
 gfxWindowsPlatform::CanUseD3D11ImageBridge()
 {
   if (XRE_IsContentProcess()) {
     if (!GetParentDevicePrefs().useD3D11ImageBridge()) {
       return false;
--- a/widget/nsBaseWidget.cpp
+++ b/widget/nsBaseWidget.cpp
@@ -64,16 +64,17 @@
 #include "nsRefPtrHashtable.h"
 #include "TouchEvents.h"
 #include "WritingModes.h"
 #include "InputData.h"
 #include "FrameLayerBuilder.h"
 #ifdef ACCESSIBILITY
 #include "nsAccessibilityService.h"
 #endif
+#include "gfxConfig.h"
 
 #ifdef DEBUG
 #include "nsIObserver.h"
 
 static void debug_RegisterPrefCallbacks();
 
 #endif
 
@@ -921,17 +922,17 @@ nsBaseWidget::AutoLayerManagerSetup::~Au
     mLayerManager->SetDefaultTarget(nullptr);
     mLayerManager->SetDefaultTargetConfiguration(mozilla::layers::BufferMode::BUFFER_NONE, ROTATION_0);
   }
 }
 
 bool
 nsBaseWidget::ComputeShouldAccelerate()
 {
-  return gfxPlatform::GetPlatform()->ShouldUseLayersAcceleration();
+  return gfx::gfxConfig::IsEnabled(gfx::Feature::HW_COMPOSITING);
 }
 
 CompositorBridgeParent* nsBaseWidget::NewCompositorBridgeParent(int aSurfaceWidth,
                                                     int aSurfaceHeight)
 {
   return new CompositorBridgeParent(this, false, aSurfaceWidth, aSurfaceHeight);
 }
 
--- a/widget/windows/nsWindow.cpp
+++ b/widget/windows/nsWindow.cpp
@@ -133,16 +133,18 @@
 #include "nsToolkitCompsCID.h"
 #include "nsIAppStartup.h"
 #include "mozilla/WindowsVersion.h"
 #include "mozilla/TextEvents.h" // For WidgetKeyboardEvent
 #include "mozilla/TextEventDispatcherListener.h"
 #include "mozilla/widget/WinNativeEventData.h"
 #include "nsThemeConstants.h"
 #include "nsBidiKeyboard.h"
+#include "nsThemeConstants.h"
+#include "gfxConfig.h"
 
 #include "nsIGfxInfo.h"
 #include "nsUXThemeConstants.h"
 #include "KeyboardLayout.h"
 #include "nsNativeDragTarget.h"
 #include <mmsystem.h> // needed for WIN32_LEAN_AND_MEAN
 #include <zmouse.h>
 #include <richedit.h>
@@ -6853,27 +6855,27 @@ nsWindow::HasBogusPopupsDropShadowOnMult
   if (sHasBogusPopupsDropShadowOnMultiMonitor == TRI_UNKNOWN) {
     // Since any change in the preferences requires a restart, this can be
     // done just once.
     // Check for Direct2D first.
     sHasBogusPopupsDropShadowOnMultiMonitor =
       gfxWindowsPlatform::GetPlatform()->IsDirect2DBackend() ? TRI_TRUE : TRI_FALSE;
     if (!sHasBogusPopupsDropShadowOnMultiMonitor) {
       // Otherwise check if Direct3D 9 may be used.
-      if (gfxPlatform::GetPlatform()->ShouldUseLayersAcceleration() &&
+      if (gfxConfig::IsEnabled(Feature::HW_COMPOSITING) &&
           !gfxPrefs::LayersPreferOpenGL())
       {
         nsCOMPtr<nsIGfxInfo> gfxInfo = services::GetGfxInfo();
         if (gfxInfo) {
           int32_t status;
           nsCString discardFailureId;
           if (NS_SUCCEEDED(gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_DIRECT3D_9_LAYERS,
                                                      discardFailureId, &status))) {
             if (status == nsIGfxInfo::FEATURE_STATUS_OK ||
-                gfxPrefs::LayersAccelerationForceEnabled())
+                gfxConfig::IsForcedOnByUser(Feature::HW_COMPOSITING))
             {
               sHasBogusPopupsDropShadowOnMultiMonitor = TRI_TRUE;
             }
           }
         }
       }
     }
   }