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 334211 2de214d5d1e098230aa00d80813615104a27f067
parent 334210 f2056259d227bb92c7712e84f61c90f65d502fba
child 334212 64e58f9625ebb834fb232f01311bf4e17252ca6c
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)
reviewersmilan
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
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;
             }
           }
         }
       }
     }
   }