summaryrefslogtreecommitdiffstats
path: root/gfx/config
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
commit36d22d82aa202bb199967e9512281e9a53db42c9 (patch)
tree105e8c98ddea1c1e4784a60a5a6410fa416be2de /gfx/config
parentInitial commit. (diff)
downloadfirefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz
firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip
Adding upstream version 115.7.0esr.upstream/115.7.0esr
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'gfx/config')
-rw-r--r--gfx/config/gfxConfig.cpp286
-rw-r--r--gfx/config/gfxConfig.h224
-rw-r--r--gfx/config/gfxConfigManager.cpp353
-rw-r--r--gfx/config/gfxConfigManager.h119
-rw-r--r--gfx/config/gfxFallback.h30
-rw-r--r--gfx/config/gfxFeature.cpp307
-rw-r--r--gfx/config/gfxFeature.h167
-rw-r--r--gfx/config/gfxVarReceiver.h24
-rw-r--r--gfx/config/gfxVars.cpp149
-rw-r--r--gfx/config/gfxVars.h253
-rw-r--r--gfx/config/moz.build28
11 files changed, 1940 insertions, 0 deletions
diff --git a/gfx/config/gfxConfig.cpp b/gfx/config/gfxConfig.cpp
new file mode 100644
index 0000000000..6281ffbbb9
--- /dev/null
+++ b/gfx/config/gfxConfig.cpp
@@ -0,0 +1,286 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/Unused.h"
+#include "mozilla/gfx/GPUParent.h"
+#include "mozilla/gfx/GraphicsMessages.h"
+#include "plstr.h"
+
+namespace mozilla {
+namespace gfx {
+
+static UniquePtr<gfxConfig> sConfig;
+
+/* static */ FeatureState& gfxConfig::GetFeature(Feature aFeature) {
+ return sConfig->GetState(aFeature);
+}
+
+/* static */
+bool gfxConfig::IsEnabled(Feature aFeature) {
+ const FeatureState& state = sConfig->GetState(aFeature);
+ return state.IsEnabled();
+}
+
+/* static */
+bool gfxConfig::IsDisabledByDefault(Feature aFeature) {
+ const FeatureState& state = sConfig->GetState(aFeature);
+ return state.DisabledByDefault();
+}
+
+/* static */
+bool gfxConfig::IsForcedOnByUser(Feature aFeature) {
+ const FeatureState& state = sConfig->GetState(aFeature);
+ return state.IsForcedOnByUser();
+}
+
+/* static */
+FeatureStatus gfxConfig::GetValue(Feature 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);
+ return state.SetDefault(aEnable, aDisableStatus, aDisableMessage);
+}
+
+/* static */
+void gfxConfig::DisableByDefault(Feature aFeature, FeatureStatus aDisableStatus,
+ const char* aDisableMessage,
+ const nsACString& aFailureId) {
+ FeatureState& state = sConfig->GetState(aFeature);
+ state.DisableByDefault(aDisableStatus, aDisableMessage, aFailureId);
+}
+
+/* static */
+void gfxConfig::EnableByDefault(Feature aFeature) {
+ FeatureState& state = sConfig->GetState(aFeature);
+ state.EnableByDefault();
+}
+
+/* static */
+void gfxConfig::SetDefaultFromPref(Feature aFeature, const char* aPrefName,
+ bool aIsEnablePref, bool aDefaultValue) {
+ FeatureState& state = sConfig->GetState(aFeature);
+ return state.SetDefaultFromPref(aPrefName, aIsEnablePref, aDefaultValue);
+}
+
+/* static */
+bool gfxConfig::InitOrUpdate(Feature aFeature, bool aEnable,
+ FeatureStatus aDisableStatus,
+ const char* aDisableMessage) {
+ FeatureState& state = sConfig->GetState(aFeature);
+ return state.InitOrUpdate(aEnable, aDisableStatus, aDisableMessage);
+}
+
+/* static */
+void gfxConfig::SetFailed(Feature aFeature, FeatureStatus aStatus,
+ const char* aMessage, const nsACString& aFailureId) {
+ FeatureState& state = sConfig->GetState(aFeature);
+ state.SetFailed(aStatus, aMessage, aFailureId);
+}
+
+/* static */
+void gfxConfig::Disable(Feature aFeature, FeatureStatus aStatus,
+ const char* aMessage, const nsACString& aFailureId) {
+ FeatureState& state = sConfig->GetState(aFeature);
+ state.Disable(aStatus, aMessage, aFailureId);
+}
+
+/* static */
+void gfxConfig::UserEnable(Feature aFeature, const char* aMessage) {
+ FeatureState& state = sConfig->GetState(aFeature);
+ state.UserEnable(aMessage);
+}
+
+/* static */
+void gfxConfig::UserForceEnable(Feature aFeature, const char* aMessage) {
+ FeatureState& state = sConfig->GetState(aFeature);
+ state.UserForceEnable(aMessage);
+}
+
+/* static */
+void gfxConfig::UserDisable(Feature aFeature, const char* aMessage,
+ const nsACString& aFailureId) {
+ FeatureState& state = sConfig->GetState(aFeature);
+ state.UserDisable(aMessage, aFailureId);
+}
+
+/* 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, nsCString());
+}
+
+/* static */
+void gfxConfig::Reset(Feature aFeature) {
+ FeatureState& state = sConfig->GetState(aFeature);
+ state.Reset();
+}
+
+/* static */
+void gfxConfig::Inherit(Feature aFeature, FeatureStatus aStatus) {
+ FeatureState& state = sConfig->GetState(aFeature);
+
+ state.Reset();
+
+ switch (aStatus) {
+ case FeatureStatus::Unused:
+ break;
+ case FeatureStatus::Available:
+ gfxConfig::EnableByDefault(aFeature);
+ break;
+ case FeatureStatus::ForceEnabled:
+ gfxConfig::EnableByDefault(aFeature);
+ gfxConfig::UserForceEnable(aFeature, "Inherited from parent process");
+ break;
+ default:
+ gfxConfig::SetDefault(aFeature, false, aStatus,
+ "Disabled in parent process");
+ break;
+ }
+}
+
+/* static */
+void gfxConfig::Inherit(EnumSet<Feature> aFeatures,
+ const DevicePrefs& aDevicePrefs) {
+ for (Feature feature : aFeatures) {
+ FeatureStatus status = FeatureStatus::Unused;
+ switch (feature) {
+ case Feature::HW_COMPOSITING:
+ status = aDevicePrefs.hwCompositing();
+ break;
+ case Feature::D3D11_COMPOSITING:
+ status = aDevicePrefs.d3d11Compositing();
+ break;
+ case Feature::OPENGL_COMPOSITING:
+ status = aDevicePrefs.oglCompositing();
+ break;
+ case Feature::DIRECT2D:
+ status = aDevicePrefs.useD2D1();
+ break;
+ default:
+ break;
+ }
+ gfxConfig::Inherit(feature, status);
+ }
+}
+
+/* static */
+bool gfxConfig::UseFallback(Fallback aFallback) {
+ return sConfig->UseFallbackImpl(aFallback);
+}
+
+/* static */
+void gfxConfig::EnableFallback(Fallback aFallback, const char* aMessage) {
+ if (!NS_IsMainThread()) {
+ nsCString message(aMessage);
+ NS_DispatchToMainThread(
+ NS_NewRunnableFunction("gfxConfig::EnableFallback", [=]() -> void {
+ gfxConfig::EnableFallback(aFallback, message.get());
+ }));
+ return;
+ }
+
+ if (XRE_IsGPUProcess()) {
+ nsCString message(aMessage);
+ Unused << GPUParent::GetSingleton()->SendUsedFallback(aFallback, message);
+ return;
+ }
+
+ sConfig->EnableFallbackImpl(aFallback, aMessage);
+}
+
+bool gfxConfig::UseFallbackImpl(Fallback aFallback) const {
+ return !!(mFallbackBits & (uint64_t(1) << uint64_t(aFallback)));
+}
+
+void gfxConfig::EnableFallbackImpl(Fallback aFallback, const char* aMessage) {
+ if (!UseFallbackImpl(aFallback)) {
+ MOZ_ASSERT(mNumFallbackLogEntries < kNumFallbacks);
+
+ FallbackLogEntry& entry = mFallbackLog[mNumFallbackLogEntries];
+ mNumFallbackLogEntries++;
+
+ entry.mFallback = aFallback;
+ PL_strncpyz(entry.mMessage, aMessage, sizeof(entry.mMessage));
+ }
+ mFallbackBits |= (uint64_t(1) << uint64_t(aFallback));
+}
+
+struct FeatureInfo {
+ const char* name;
+ const char* description;
+};
+static const FeatureInfo sFeatureInfo[] = {
+#define FOR_EACH_FEATURE(name, type, desc) {#name, desc},
+ GFX_FEATURE_MAP(FOR_EACH_FEATURE)
+#undef FOR_EACH_FEATURE
+ {nullptr, nullptr}};
+
+/* static */
+void gfxConfig::ForEachFeature(const FeatureIterCallback& aCallback) {
+ for (size_t i = 0; i < kNumFeatures; i++) {
+ FeatureState& state = GetFeature(static_cast<Feature>(i));
+ if (!state.IsInitialized()) {
+ continue;
+ }
+
+ aCallback(sFeatureInfo[i].name, sFeatureInfo[i].description, state);
+ }
+}
+
+static const char* sFallbackNames[] = {
+#define FOR_EACH_FALLBACK(name) #name,
+ GFX_FALLBACK_MAP(FOR_EACH_FALLBACK)
+#undef FOR_EACH_FALLBACK
+ nullptr};
+
+/* static */
+void gfxConfig::ForEachFallback(const FallbackIterCallback& aCallback) {
+ sConfig->ForEachFallbackImpl(aCallback);
+}
+
+void gfxConfig::ForEachFallbackImpl(const FallbackIterCallback& aCallback) {
+ for (size_t i = 0; i < mNumFallbackLogEntries; i++) {
+ const FallbackLogEntry& entry = mFallbackLog[i];
+ aCallback(sFallbackNames[size_t(entry.mFallback)], entry.mMessage);
+ }
+}
+
+/* static */ const nsCString& gfxConfig::GetFailureId(Feature aFeature) {
+ const FeatureState& state = sConfig->GetState(aFeature);
+ return state.GetFailureId();
+}
+
+/* static */
+void gfxConfig::ImportChange(Feature aFeature,
+ const Maybe<FeatureFailure>& aChange) {
+ if (aChange.isNothing()) {
+ return;
+ }
+
+ const FeatureFailure& failure = aChange.ref();
+ gfxConfig::SetFailed(aFeature, failure.status(), failure.message().get(),
+ failure.failureId());
+}
+
+/* static */
+void gfxConfig::Init() { sConfig = mozilla::MakeUnique<gfxConfig>(); }
+
+/* static */
+void gfxConfig::Shutdown() { sConfig = nullptr; }
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/gfx/config/gfxConfig.h b/gfx/config/gfxConfig.h
new file mode 100644
index 0000000000..c6a39d373e
--- /dev/null
+++ b/gfx/config/gfxConfig.h
@@ -0,0 +1,224 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 <functional>
+#include "gfxFeature.h"
+#include "gfxFallback.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/EnumSet.h"
+#include "mozilla/Maybe.h"
+
+namespace mozilla {
+namespace gfx {
+
+// Defined in GraphicsMessages.ipdlh.
+class DevicePrefs;
+class FeatureFailure;
+
+// Manages the history and state of a graphics feature. The flow of a feature
+// is:
+// - A default value, set by all.js, or gfxPlatform.
+// - A user value, set by an external value or user pref.
+// - An environment value, determined by system/hardware factors or
+// nsIGfxInfo.
+// - A runtime value, determined by any failures encountered after enabling
+// the feature.
+//
+// Each state change for a feature is recorded in this class.
+class gfxConfig {
+ public:
+ // Return the full state history of a feature.
+ static FeatureState& GetFeature(Feature aFeature);
+
+ // 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 feature, return false.
+ // 2. If the user force-enabled the feature, return true.
+ // 3. If the environment disabled the feature, return false.
+ // 4. If the user specified a decision, return it.
+ // 5. Return the base setting for the feature.
+ 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);
+
+ // This returns true if the feature was disabled by default, or was never
+ // initialized to begin with.
+ static bool IsDisabledByDefault(Feature aFeature);
+
+ // Query the status value of a parameter. This is computed similar to
+ // IsEnabled:
+ //
+ // 1. If a runtime failure was set, return it.
+ // 2. If the user force-enabled the feature, return ForceEnabled.
+ // 3. If an environment status was set, return it.
+ // 4. If a user status was set, return it.
+ // 5. Return the default status.
+ static FeatureStatus GetValue(Feature aFeature);
+
+ // Reset the entire state of a feature.
+ static void Reset(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);
+ static void DisableByDefault(Feature aFeature, FeatureStatus aDisableStatus,
+ const char* aDisableMessage,
+ const nsACString& aFailureId = ""_ns);
+ static void EnableByDefault(Feature aFeature);
+
+ // Inherit a computed value from another process.
+ static void Inherit(Feature aFeature, FeatureStatus aStatus);
+
+ // Inherit a set of computed values from another process.
+ static void Inherit(EnumSet<Feature> aFeatures,
+ const DevicePrefs& aDevicePrefs);
+
+ // Set a environment status that overrides both the default and user
+ // statuses; this should be used to disable features based on system
+ // or hardware problems that can be determined up-front. The only
+ // status that can override this decision is the user force-enabling
+ // the feature.
+ static void Disable(Feature aFeature, FeatureStatus aStatus,
+ const char* aMessage,
+ const nsACString& aFailureId = ""_ns);
+
+ // Given a preference name, infer the default value and whether or not the
+ // user has changed it. |aIsEnablePref| specifies whether or not the pref
+ // is intended to enable a feature (true), or disable it (false).
+ static void SetDefaultFromPref(Feature aFeature, const char* aPrefName,
+ bool aIsEnablePref, bool aDefaultValue);
+
+ // Disable a parameter based on a runtime decision. This permanently
+ // disables the feature, since runtime decisions override all other
+ // decisions.
+ static void SetFailed(Feature aFeature, FeatureStatus aStatus,
+ const char* aMessage,
+ const nsACString& aFailureId = ""_ns);
+
+ // Force a feature to be disabled permanently. This is the same as
+ // SetFailed(), but the name may be clearer depending on the context.
+ static void ForceDisable(Feature aFeature, FeatureStatus aStatus,
+ const char* aMessage,
+ const nsACString& aFailureId = ""_ns) {
+ SetFailed(aFeature, aStatus, aMessage, aFailureId);
+ }
+
+ // Convenience helpers for SetFailed().
+ static bool MaybeSetFailed(Feature aFeature, bool aEnable,
+ FeatureStatus aDisableStatus,
+ const char* aDisableMessage,
+ const nsACString& aFailureId = ""_ns) {
+ if (!aEnable) {
+ SetFailed(aFeature, aDisableStatus, aDisableMessage, aFailureId);
+ return false;
+ }
+ return true;
+ }
+
+ // Convenience helper for SetFailed().
+ static bool MaybeSetFailed(Feature aFeature, FeatureStatus aStatus,
+ const char* aDisableMessage,
+ const nsACString& aFailureId = ""_ns) {
+ return MaybeSetFailed(aFeature,
+ (aStatus != FeatureStatus::Available &&
+ aStatus != FeatureStatus::ForceEnabled),
+ aStatus, aDisableMessage, aFailureId);
+ }
+
+ // 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);
+
+ // 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,
+ const nsACString& aFailureId = ""_ns);
+
+ // Query whether a fallback has been toggled.
+ static bool UseFallback(Fallback aFallback);
+
+ // Add a log entry denoting that a given fallback had to be used. This can
+ // be called from any thread in the UI or GPU process.
+ static void EnableFallback(Fallback aFallback, const char* aMessage);
+
+ // Run a callback for each initialized FeatureState.
+ typedef std::function<void(const char* aName, const char* aDescription,
+ FeatureState& aFeature)>
+ FeatureIterCallback;
+ static void ForEachFeature(const FeatureIterCallback& aCallback);
+
+ // Run a callback for each enabled fallback.
+ typedef std::function<void(const char* aName, const char* aMsg)>
+ FallbackIterCallback;
+ static void ForEachFallback(const FallbackIterCallback& aCallback);
+
+ // Get the most descriptive failure id message for this feature.
+ static const nsCString& GetFailureId(Feature aFeature);
+
+ static void ImportChange(Feature aFeature,
+ const Maybe<FeatureFailure>& aChange);
+
+ static void Init();
+ static void Shutdown();
+
+ private:
+ void ForEachFallbackImpl(const FallbackIterCallback& aCallback);
+
+ 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, const char* aMessage);
+
+ private:
+ static const size_t kNumFeatures = size_t(Feature::NumValues);
+ static const size_t kNumFallbacks = size_t(Fallback::NumValues);
+
+ private:
+ FeatureState mFeatures[kNumFeatures];
+ uint64_t mFallbackBits;
+
+ private:
+ struct FallbackLogEntry {
+ Fallback mFallback;
+ char mMessage[80];
+ };
+
+ FallbackLogEntry mFallbackLog[kNumFallbacks];
+ size_t mNumFallbackLogEntries;
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif // mozilla_gfx_config_gfxConfig_h
diff --git a/gfx/config/gfxConfigManager.cpp b/gfx/config/gfxConfigManager.cpp
new file mode 100644
index 0000000000..644ad30007
--- /dev/null
+++ b/gfx/config/gfxConfigManager.cpp
@@ -0,0 +1,353 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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/gfx/gfxConfigManager.h"
+#include "mozilla/gfx/gfxVars.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/Components.h"
+#include "mozilla/StaticPrefs_gfx.h"
+#include "mozilla/StaticPrefs_layers.h"
+#include "gfxConfig.h"
+#include "gfxPlatform.h"
+#include "nsIGfxInfo.h"
+#include "nsPrintfCString.h"
+#include "nsXULAppAPI.h"
+
+#ifdef XP_WIN
+# include "mozilla/WindowsVersion.h"
+# include "mozilla/gfx/DeviceManagerDx.h"
+# include "mozilla/gfx/DisplayConfigWindows.h"
+#endif
+
+namespace mozilla {
+namespace gfx {
+
+void gfxConfigManager::Init() {
+ MOZ_ASSERT(XRE_IsParentProcess());
+
+ EmplaceUserPref("gfx.webrender.compositor", mWrCompositorEnabled);
+ mWrForceEnabled = gfxPlatform::WebRenderPrefEnabled();
+ mWrSoftwareForceEnabled = StaticPrefs::gfx_webrender_software_AtStartup();
+ mWrCompositorForceEnabled =
+ StaticPrefs::gfx_webrender_compositor_force_enabled_AtStartup();
+ mGPUProcessAllowSoftware =
+ StaticPrefs::layers_gpu_process_allow_software_AtStartup();
+ mWrForcePartialPresent =
+ StaticPrefs::gfx_webrender_force_partial_present_AtStartup();
+ mWrPartialPresent =
+ StaticPrefs::gfx_webrender_max_partial_present_rects_AtStartup() > 0;
+ EmplaceUserPref(StaticPrefs::GetPrefName_gfx_webrender_program_binary_disk(),
+ mWrShaderCache);
+ mWrOptimizedShaders =
+ StaticPrefs::gfx_webrender_use_optimized_shaders_AtStartup();
+ mWrScissoredCacheClearsEnabled =
+ StaticPrefs::gfx_webrender_scissored_cache_clears_enabled_AtStartup();
+ mWrScissoredCacheClearsForceEnabled = StaticPrefs::
+ gfx_webrender_scissored_cache_clears_force_enabled_AtStartup();
+#ifdef XP_WIN
+ mWrForceAngle = StaticPrefs::gfx_webrender_force_angle_AtStartup();
+ mWrForceAngleNoGPUProcess = StaticPrefs::
+ gfx_webrender_enabled_no_gpu_process_with_angle_win_AtStartup();
+ mWrDCompWinEnabled =
+ Preferences::GetBool("gfx.webrender.dcomp-win.enabled", false);
+#endif
+
+ mWrEnvForceEnabled = gfxPlatform::WebRenderEnvvarEnabled();
+
+#ifdef XP_WIN
+ DeviceManagerDx::Get()->CheckHardwareStretchingSupport(mHwStretchingSupport);
+ mScaledResolution = HasScaledResolution();
+ mIsWin10OrLater = IsWin10OrLater();
+ mIsWin11OrLater = IsWin11OrLater();
+ mWrCompositorDCompRequired = true;
+#else
+ ++mHwStretchingSupport.mBoth;
+#endif
+
+#ifdef MOZ_WIDGET_GTK
+ mDisableHwCompositingNoWr = true;
+#endif
+
+#ifdef NIGHTLY_BUILD
+ mIsNightly = true;
+#endif
+#ifdef EARLY_BETA_OR_EARLIER
+ mIsEarlyBetaOrEarlier = true;
+#endif
+ mSafeMode = gfxPlatform::InSafeMode();
+
+ mGfxInfo = components::GfxInfo::Service();
+
+ mFeatureWr = &gfxConfig::GetFeature(Feature::WEBRENDER);
+ mFeatureWrCompositor = &gfxConfig::GetFeature(Feature::WEBRENDER_COMPOSITOR);
+ mFeatureWrAngle = &gfxConfig::GetFeature(Feature::WEBRENDER_ANGLE);
+ mFeatureWrDComp = &gfxConfig::GetFeature(Feature::WEBRENDER_DCOMP_PRESENT);
+ mFeatureWrPartial = &gfxConfig::GetFeature(Feature::WEBRENDER_PARTIAL);
+ mFeatureWrShaderCache =
+ &gfxConfig::GetFeature(Feature::WEBRENDER_SHADER_CACHE);
+ mFeatureWrOptimizedShaders =
+ &gfxConfig::GetFeature(Feature::WEBRENDER_OPTIMIZED_SHADERS);
+ mFeatureWrScissoredCacheClears =
+ &gfxConfig::GetFeature(Feature::WEBRENDER_SCISSORED_CACHE_CLEARS);
+
+ mFeatureHwCompositing = &gfxConfig::GetFeature(Feature::HW_COMPOSITING);
+#ifdef XP_WIN
+ mFeatureD3D11HwAngle = &gfxConfig::GetFeature(Feature::D3D11_HW_ANGLE);
+ mFeatureD3D11Compositing = &gfxConfig::GetFeature(Feature::D3D11_COMPOSITING);
+#endif
+ mFeatureGPUProcess = &gfxConfig::GetFeature(Feature::GPU_PROCESS);
+}
+
+void gfxConfigManager::EmplaceUserPref(const char* aPrefName,
+ Maybe<bool>& aValue) {
+ if (Preferences::HasUserValue(aPrefName)) {
+ aValue.emplace(Preferences::GetBool(aPrefName, false));
+ }
+}
+
+void gfxConfigManager::ConfigureFromBlocklist(long aFeature,
+ FeatureState* aFeatureState) {
+ MOZ_ASSERT(aFeatureState);
+
+ nsCString blockId;
+ int32_t status;
+ if (!NS_SUCCEEDED(mGfxInfo->GetFeatureStatus(aFeature, blockId, &status))) {
+ aFeatureState->Disable(FeatureStatus::BlockedNoGfxInfo, "gfxInfo is broken",
+ "FEATURE_FAILURE_NO_GFX_INFO"_ns);
+ return;
+ }
+
+ switch (status) {
+ case nsIGfxInfo::FEATURE_STATUS_OK:
+ case nsIGfxInfo::FEATURE_ALLOW_ALWAYS:
+ break;
+ case nsIGfxInfo::FEATURE_ALLOW_QUALIFIED:
+ MOZ_ASSERT_UNREACHABLE("Allowing only qualified, but needs experiment?");
+ break;
+ case nsIGfxInfo::FEATURE_DENIED:
+ aFeatureState->Disable(FeatureStatus::Denied, "Not on allowlist",
+ blockId);
+ break;
+ default:
+ aFeatureState->Disable(FeatureStatus::Blocklisted,
+ "Blocklisted by gfxInfo", blockId);
+ break;
+ }
+}
+
+void gfxConfigManager::ConfigureWebRender() {
+ MOZ_ASSERT(XRE_IsParentProcess());
+ MOZ_ASSERT(mFeatureWr);
+ MOZ_ASSERT(mFeatureWrCompositor);
+ MOZ_ASSERT(mFeatureWrAngle);
+ MOZ_ASSERT(mFeatureWrDComp);
+ MOZ_ASSERT(mFeatureWrPartial);
+ MOZ_ASSERT(mFeatureWrShaderCache);
+ MOZ_ASSERT(mFeatureWrOptimizedShaders);
+ MOZ_ASSERT(mFeatureWrScissoredCacheClears);
+ MOZ_ASSERT(mFeatureHwCompositing);
+ MOZ_ASSERT(mFeatureGPUProcess);
+
+ // Initialize WebRender native compositor usage
+ mFeatureWrCompositor->SetDefaultFromPref("gfx.webrender.compositor", true,
+ false, mWrCompositorEnabled);
+
+ if (mWrCompositorForceEnabled) {
+ mFeatureWrCompositor->UserForceEnable("Force enabled by pref");
+ }
+
+ ConfigureFromBlocklist(nsIGfxInfo::FEATURE_WEBRENDER_COMPOSITOR,
+ mFeatureWrCompositor);
+
+ // Disable native compositor when hardware stretching is not supported. It is
+ // for avoiding a problem like Bug 1618370.
+ // XXX Is there a better check for Bug 1618370?
+ if (!mHwStretchingSupport.IsFullySupported() && mScaledResolution) {
+ nsPrintfCString failureId(
+ "FEATURE_FAILURE_NO_HARDWARE_STRETCHING_B%uW%uF%uN%uE%u",
+ mHwStretchingSupport.mBoth, mHwStretchingSupport.mWindowOnly,
+ mHwStretchingSupport.mFullScreenOnly, mHwStretchingSupport.mNone,
+ mHwStretchingSupport.mError);
+ mFeatureWrCompositor->Disable(FeatureStatus::Unavailable,
+ "No hardware stretching support", failureId);
+ }
+
+ mFeatureWr->EnableByDefault();
+
+ // envvar works everywhere; note that we need this for testing in CI.
+ // Prior to bug 1523788, the `prefEnabled` check was only done on Nightly,
+ // so as to prevent random users from easily enabling WebRender on
+ // unqualified hardware in beta/release.
+ if (mWrSoftwareForceEnabled) {
+ mFeatureWr->UserDisable("User force-enabled software WR",
+ "FEATURE_FAILURE_USER_FORCE_ENABLED_SW_WR"_ns);
+ } else if (mWrEnvForceEnabled) {
+ mFeatureWr->UserForceEnable("Force enabled by envvar");
+ } else if (mWrForceEnabled) {
+ mFeatureWr->UserForceEnable("Force enabled by pref");
+ }
+
+ ConfigureFromBlocklist(nsIGfxInfo::FEATURE_WEBRENDER, mFeatureWr);
+
+ // HW_COMPOSITING being disabled implies interfacing with the GPU might break
+ if (!mFeatureHwCompositing->IsEnabled()) {
+ mFeatureWr->ForceDisable(FeatureStatus::UnavailableNoHwCompositing,
+ "Hardware compositing is disabled",
+ "FEATURE_FAILURE_WEBRENDER_NEED_HWCOMP"_ns);
+ }
+
+ if (mSafeMode) {
+ mFeatureWr->ForceDisable(FeatureStatus::UnavailableInSafeMode,
+ "Safe-mode is enabled",
+ "FEATURE_FAILURE_SAFE_MODE"_ns);
+ }
+
+ mFeatureWrAngle->EnableByDefault();
+ if (mFeatureD3D11HwAngle) {
+ if (mWrForceAngle) {
+ if (!mFeatureD3D11HwAngle->IsEnabled()) {
+ mFeatureWrAngle->ForceDisable(FeatureStatus::UnavailableNoAngle,
+ "ANGLE is disabled",
+ mFeatureD3D11HwAngle->GetFailureId());
+ } else if (!mFeatureGPUProcess->IsEnabled() &&
+ !mWrForceAngleNoGPUProcess) {
+ // WebRender with ANGLE relies on the GPU process when on Windows
+ mFeatureWrAngle->ForceDisable(
+ FeatureStatus::UnavailableNoGpuProcess, "GPU Process is disabled",
+ "FEATURE_FAILURE_GPU_PROCESS_DISABLED"_ns);
+ }
+ } else {
+ mFeatureWrAngle->Disable(FeatureStatus::Disabled, "ANGLE is not forced",
+ "FEATURE_FAILURE_ANGLE_NOT_FORCED"_ns);
+ }
+ } else {
+ mFeatureWrAngle->Disable(FeatureStatus::Unavailable, "OS not supported",
+ "FEATURE_FAILURE_OS_NOT_SUPPORTED"_ns);
+ }
+
+ if (mWrForceAngle && mFeatureWr->IsEnabled() &&
+ !mFeatureWrAngle->IsEnabled()) {
+ // Ensure we disable WebRender if ANGLE is unavailable and it is required.
+ mFeatureWr->ForceDisable(FeatureStatus::UnavailableNoAngle,
+ "ANGLE is disabled",
+ mFeatureWrAngle->GetFailureId());
+ }
+
+ if (!mFeatureWr->IsEnabled() && mDisableHwCompositingNoWr) {
+ if (mFeatureHwCompositing->IsEnabled()) {
+ // Hardware compositing should be disabled by default if we aren't using
+ // WebRender. We had to check if it is enabled at all, because it may
+ // already have been forced disabled (e.g. safe mode, headless). It may
+ // still be forced on by the user, and if so, this should have no effect.
+ mFeatureHwCompositing->Disable(FeatureStatus::Blocked,
+ "Acceleration blocked by platform", ""_ns);
+ }
+
+ if (!mFeatureHwCompositing->IsEnabled() &&
+ mFeatureGPUProcess->IsEnabled() && !mGPUProcessAllowSoftware) {
+ // We have neither WebRender nor OpenGL, we don't allow the GPU process
+ // for basic compositor, and it wasn't disabled already.
+ mFeatureGPUProcess->Disable(FeatureStatus::Unavailable,
+ "Hardware compositing is unavailable.",
+ ""_ns);
+ }
+ }
+
+ mFeatureWrDComp->EnableByDefault();
+ if (!mWrDCompWinEnabled) {
+ mFeatureWrDComp->UserDisable("User disabled via pref",
+ "FEATURE_FAILURE_DCOMP_PREF_DISABLED"_ns);
+ }
+
+ if (!mIsWin10OrLater) {
+ // XXX relax win version to windows 8.
+ mFeatureWrDComp->Disable(FeatureStatus::Unavailable,
+ "Requires Windows 10 or later",
+ "FEATURE_FAILURE_DCOMP_NOT_WIN10"_ns);
+ }
+
+ if (!mFeatureGPUProcess->IsEnabled()) {
+ mFeatureWrDComp->Disable(FeatureStatus::Unavailable, "Requires GPU process",
+ "FEATURE_FAILURE_NO_GPU_PROCESS"_ns);
+ }
+
+ if (mIsWin10OrLater && !mIsWin11OrLater) {
+ // Disable DirectComposition for NVIDIA users on Windows 10 with high/mixed
+ // refresh rate monitors due to rendering artifacts. (See bug 1638709.)
+ nsAutoString adapterVendorID;
+ mGfxInfo->GetAdapterVendorID(adapterVendorID);
+ if (adapterVendorID == u"0x10de") {
+ bool mixed = false;
+ int32_t maxRefreshRate = mGfxInfo->GetMaxRefreshRate(&mixed);
+ if (maxRefreshRate > 60 && mixed) {
+ mFeatureWrDComp->Disable(FeatureStatus::Blocked,
+ "Monitor refresh rate too high/mixed",
+ "NVIDIA_REFRESH_RATE_MIXED"_ns);
+ }
+ }
+ }
+
+ mFeatureWrDComp->MaybeSetFailed(
+ mFeatureWr->IsEnabled(), FeatureStatus::Unavailable, "Requires WebRender",
+ "FEATURE_FAILURE_DCOMP_NOT_WR"_ns);
+ mFeatureWrDComp->MaybeSetFailed(mFeatureWrAngle->IsEnabled(),
+ FeatureStatus::Unavailable, "Requires ANGLE",
+ "FEATURE_FAILURE_DCOMP_NOT_ANGLE"_ns);
+
+ if (!mFeatureWrDComp->IsEnabled() && mWrCompositorDCompRequired) {
+ mFeatureWrCompositor->ForceDisable(FeatureStatus::Unavailable,
+ "No DirectComposition usage",
+ mFeatureWrDComp->GetFailureId());
+ }
+
+ // Initialize WebRender partial present config.
+ // Partial present is used only when WebRender compositor is not used.
+ mFeatureWrPartial->SetDefault(mWrPartialPresent, FeatureStatus::Disabled,
+ "User disabled via pref");
+ if (mWrForcePartialPresent) {
+ mFeatureWrPartial->UserForceEnable("Force enabled by pref");
+ }
+
+ ConfigureFromBlocklist(nsIGfxInfo::FEATURE_WEBRENDER_PARTIAL_PRESENT,
+ mFeatureWrPartial);
+
+ mFeatureWrShaderCache->SetDefaultFromPref(
+ StaticPrefs::GetPrefName_gfx_webrender_program_binary_disk(), true,
+ StaticPrefs::GetPrefDefault_gfx_webrender_program_binary_disk(),
+ mWrShaderCache);
+ ConfigureFromBlocklist(nsIGfxInfo::FEATURE_WEBRENDER_SHADER_CACHE,
+ mFeatureWrShaderCache);
+ if (!mFeatureWr->IsEnabled()) {
+ mFeatureWrShaderCache->ForceDisable(FeatureStatus::Unavailable,
+ "WebRender disabled",
+ "FEATURE_FAILURE_WR_DISABLED"_ns);
+ }
+
+ mFeatureWrOptimizedShaders->EnableByDefault();
+ if (!mWrOptimizedShaders) {
+ mFeatureWrOptimizedShaders->UserDisable("User disabled via pref",
+ "FEATURE_FAILURE_PREF_DISABLED"_ns);
+ }
+ ConfigureFromBlocklist(nsIGfxInfo::FEATURE_WEBRENDER_OPTIMIZED_SHADERS,
+ mFeatureWrOptimizedShaders);
+ if (!mFeatureWr->IsEnabled()) {
+ mFeatureWrOptimizedShaders->ForceDisable(FeatureStatus::Unavailable,
+ "WebRender disabled",
+ "FEATURE_FAILURE_WR_DISABLED"_ns);
+ }
+
+ mFeatureWrScissoredCacheClears->SetDefault(mWrScissoredCacheClearsEnabled,
+ FeatureStatus::Disabled,
+ "User disabled via pref");
+ if (mWrScissoredCacheClearsForceEnabled) {
+ mFeatureWrScissoredCacheClears->UserForceEnable("Force enabled by pref");
+ }
+ ConfigureFromBlocklist(nsIGfxInfo::FEATURE_WEBRENDER_SCISSORED_CACHE_CLEARS,
+ mFeatureWrScissoredCacheClears);
+}
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/gfx/config/gfxConfigManager.h b/gfx/config/gfxConfigManager.h
new file mode 100644
index 0000000000..303b7fb85f
--- /dev/null
+++ b/gfx/config/gfxConfigManager.h
@@ -0,0 +1,119 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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_gfxConfigManager_h
+#define mozilla_gfx_config_gfxConfigManager_h
+
+#include "gfxFeature.h"
+#include "gfxTypes.h"
+#include "nsCOMPtr.h"
+
+class nsIGfxInfo;
+
+namespace mozilla {
+namespace gfx {
+
+class gfxConfigManager {
+ public:
+ gfxConfigManager()
+ : mFeatureWr(nullptr),
+ mFeatureWrCompositor(nullptr),
+ mFeatureWrAngle(nullptr),
+ mFeatureWrDComp(nullptr),
+ mFeatureWrPartial(nullptr),
+ mFeatureWrShaderCache(nullptr),
+ mFeatureWrOptimizedShaders(nullptr),
+ mFeatureWrScissoredCacheClears(nullptr),
+ mFeatureHwCompositing(nullptr),
+ mFeatureD3D11HwAngle(nullptr),
+ mFeatureD3D11Compositing(nullptr),
+ mFeatureGPUProcess(nullptr),
+ mWrForceEnabled(false),
+ mWrSoftwareForceEnabled(false),
+ mWrCompositorForceEnabled(false),
+ mWrForceAngle(false),
+ mWrForceAngleNoGPUProcess(false),
+ mWrDCompWinEnabled(false),
+ mWrCompositorDCompRequired(false),
+ mWrForcePartialPresent(false),
+ mWrPartialPresent(false),
+ mWrOptimizedShaders(false),
+ mWrScissoredCacheClearsEnabled(false),
+ mWrScissoredCacheClearsForceEnabled(false),
+ mGPUProcessAllowSoftware(false),
+ mWrEnvForceEnabled(false),
+ mScaledResolution(false),
+ mDisableHwCompositingNoWr(false),
+ mIsNightly(false),
+ mIsEarlyBetaOrEarlier(false),
+ mSafeMode(false),
+ mIsWin10OrLater(false) {}
+
+ void Init();
+
+ void ConfigureWebRender();
+ void ConfigureFromBlocklist(long aFeature, FeatureState* aFeatureState);
+
+ protected:
+ void EmplaceUserPref(const char* aPrefName, Maybe<bool>& aValue);
+ void ConfigureWebRenderQualified();
+
+ nsCOMPtr<nsIGfxInfo> mGfxInfo;
+
+ FeatureState* mFeatureWr;
+ FeatureState* mFeatureWrCompositor;
+ FeatureState* mFeatureWrAngle;
+ FeatureState* mFeatureWrDComp;
+ FeatureState* mFeatureWrPartial;
+ FeatureState* mFeatureWrShaderCache;
+ FeatureState* mFeatureWrOptimizedShaders;
+ FeatureState* mFeatureWrScissoredCacheClears;
+
+ FeatureState* mFeatureHwCompositing;
+ FeatureState* mFeatureD3D11HwAngle;
+ FeatureState* mFeatureD3D11Compositing;
+ FeatureState* mFeatureGPUProcess;
+
+ /**
+ * Prefs
+ */
+ Maybe<bool> mWrCompositorEnabled;
+ bool mWrForceEnabled;
+ bool mWrSoftwareForceEnabled;
+ bool mWrCompositorForceEnabled;
+ bool mWrForceAngle;
+ bool mWrForceAngleNoGPUProcess;
+ bool mWrDCompWinEnabled;
+ bool mWrCompositorDCompRequired;
+ bool mWrForcePartialPresent;
+ bool mWrPartialPresent;
+ Maybe<bool> mWrShaderCache;
+ bool mWrOptimizedShaders;
+ bool mWrScissoredCacheClearsEnabled;
+ bool mWrScissoredCacheClearsForceEnabled;
+ bool mGPUProcessAllowSoftware;
+
+ /**
+ * Environment variables
+ */
+ bool mWrEnvForceEnabled;
+
+ /**
+ * System support
+ */
+ HwStretchingSupport mHwStretchingSupport;
+ bool mScaledResolution;
+ bool mDisableHwCompositingNoWr;
+ bool mIsNightly;
+ bool mIsEarlyBetaOrEarlier;
+ bool mSafeMode;
+ bool mIsWin10OrLater;
+ bool mIsWin11OrLater;
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif // mozilla_gfx_config_gfxConfigParams_h
diff --git a/gfx/config/gfxFallback.h b/gfx/config/gfxFallback.h
new file mode 100644
index 0000000000..6875558856
--- /dev/null
+++ b/gfx/config/gfxFallback.h
@@ -0,0 +1,30 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 */ \
+ _(NO_CONSTANT_BUFFER_OFFSETTING) \
+ /* 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
diff --git a/gfx/config/gfxFeature.cpp b/gfx/config/gfxFeature.cpp
new file mode 100644
index 0000000000..71517e563c
--- /dev/null
+++ b/gfx/config/gfxFeature.cpp
@@ -0,0 +1,307 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 "mozilla/Preferences.h"
+#include "mozilla/Sprintf.h"
+#include "nsString.h"
+
+namespace mozilla {
+namespace gfx {
+
+bool FeatureState::IsEnabled() const {
+ return IsInitialized() && IsFeatureStatusSuccess(GetValue());
+}
+
+FeatureStatus FeatureState::GetValue() const {
+ if (!IsInitialized()) {
+ return FeatureStatus::Unused;
+ }
+
+ if (mRuntime.mStatus != FeatureStatus::Unused) {
+ return mRuntime.mStatus;
+ }
+ if (mUser.mStatus == FeatureStatus::ForceEnabled) {
+ return FeatureStatus::ForceEnabled;
+ }
+ if (mEnvironment.mStatus != FeatureStatus::Unused) {
+ return mEnvironment.mStatus;
+ }
+ if (mUser.mStatus != FeatureStatus::Unused) {
+ return mUser.mStatus;
+ }
+ return mDefault.mStatus;
+}
+
+bool FeatureState::SetDefault(bool aEnable, FeatureStatus aDisableStatus,
+ const char* aDisableMessage) {
+ if (!aEnable) {
+ DisableByDefault(aDisableStatus, aDisableMessage,
+ "FEATURE_FAILURE_DISABLED"_ns);
+ return false;
+ }
+ EnableByDefault();
+ return true;
+}
+
+void FeatureState::SetDefaultFromPref(const char* aPrefName, bool aIsEnablePref,
+ bool aDefaultValue,
+ Maybe<bool> aUserValue) {
+ bool baseValue =
+ Preferences::GetBool(aPrefName, aDefaultValue, PrefValueKind::Default);
+ SetDefault(baseValue == aIsEnablePref, FeatureStatus::Disabled,
+ "Disabled by default");
+
+ if (aUserValue) {
+ if (*aUserValue == aIsEnablePref) {
+ nsCString message("Enabled via ");
+ message.AppendASCII(aPrefName);
+ UserEnable(message.get());
+ } else {
+ nsCString message("Disabled via ");
+ message.AppendASCII(aPrefName);
+ UserDisable(message.get(), "FEATURE_FAILURE_PREF_OFF"_ns);
+ }
+ }
+}
+
+void FeatureState::SetDefaultFromPref(const char* aPrefName, bool aIsEnablePref,
+ bool aDefaultValue) {
+ Maybe<bool> userValue;
+ if (Preferences::HasUserValue(aPrefName)) {
+ userValue.emplace(Preferences::GetBool(aPrefName, aDefaultValue));
+ }
+
+ SetDefaultFromPref(aPrefName, aIsEnablePref, aDefaultValue, userValue);
+}
+
+bool FeatureState::InitOrUpdate(bool aEnable, FeatureStatus aDisableStatus,
+ const char* aDisableMessage) {
+ if (!IsInitialized()) {
+ return SetDefault(aEnable, aDisableStatus, aDisableMessage);
+ }
+ return MaybeSetFailed(aEnable, aDisableStatus, aDisableMessage, nsCString());
+}
+
+void FeatureState::UserEnable(const char* aMessage) {
+ AssertInitialized();
+ SetUser(FeatureStatus::Available, aMessage, nsCString());
+}
+
+void FeatureState::UserForceEnable(const char* aMessage) {
+ AssertInitialized();
+ SetUser(FeatureStatus::ForceEnabled, aMessage, nsCString());
+}
+
+void FeatureState::UserDisable(const char* aMessage,
+ const nsACString& aFailureId) {
+ AssertInitialized();
+ SetUser(FeatureStatus::Disabled, aMessage, aFailureId);
+}
+
+void FeatureState::Disable(FeatureStatus aStatus, const char* aMessage,
+ const nsACString& aFailureId) {
+ AssertInitialized();
+
+ // We should never bother setting an environment status to "enabled," since
+ // it could override an explicit user decision to disable it.
+ MOZ_ASSERT(IsFeatureStatusFailure(aStatus));
+
+ SetEnvironment(aStatus, aMessage, aFailureId);
+}
+
+void FeatureState::SetFailed(FeatureStatus aStatus, const char* aMessage,
+ const nsACString& aFailureId) {
+ AssertInitialized();
+
+ // We should never bother setting a runtime status to "enabled," since it
+ // could override an explicit user decision to disable it.
+ MOZ_ASSERT(IsFeatureStatusFailure(aStatus));
+
+ SetRuntime(aStatus, aMessage, aFailureId);
+}
+
+bool FeatureState::MaybeSetFailed(bool aEnable, FeatureStatus aStatus,
+ const char* aMessage,
+ const nsACString& aFailureId) {
+ if (!aEnable) {
+ SetFailed(aStatus, aMessage, aFailureId);
+ return false;
+ }
+ return true;
+}
+
+bool FeatureState::MaybeSetFailed(FeatureStatus aStatus, const char* aMessage,
+ const nsACString& aFailureId) {
+ return MaybeSetFailed(IsFeatureStatusSuccess(aStatus), aStatus, aMessage,
+ aFailureId);
+}
+
+bool FeatureState::DisabledByDefault() const {
+ return mDefault.mStatus != FeatureStatus::Available;
+}
+
+bool FeatureState::IsForcedOnByUser() const {
+ AssertInitialized();
+ return mUser.mStatus == FeatureStatus::ForceEnabled;
+}
+
+void FeatureState::EnableByDefault() {
+ // User/runtime decisions should not have been made yet.
+ MOZ_ASSERT(!mUser.IsInitialized());
+ MOZ_ASSERT(!mEnvironment.IsInitialized());
+ MOZ_ASSERT(!mRuntime.IsInitialized());
+
+ mDefault.Set(FeatureStatus::Available);
+}
+
+void FeatureState::DisableByDefault(FeatureStatus aStatus, const char* aMessage,
+ const nsACString& aFailureId) {
+ // User/runtime decisions should not have been made yet.
+ MOZ_ASSERT(!mUser.IsInitialized());
+ MOZ_ASSERT(!mEnvironment.IsInitialized());
+ MOZ_ASSERT(!mRuntime.IsInitialized());
+
+ // Make sure that when disabling we actually use a failure status.
+ MOZ_ASSERT(IsFeatureStatusFailure(aStatus));
+
+ mDefault.Set(aStatus, aMessage, aFailureId);
+}
+
+void FeatureState::SetUser(FeatureStatus aStatus, const char* aMessage,
+ const nsACString& aFailureId) {
+ // Default decision must have been made, but not runtime or environment.
+ MOZ_ASSERT(mDefault.IsInitialized());
+ MOZ_ASSERT(!mEnvironment.IsInitialized());
+ MOZ_ASSERT(!mRuntime.IsInitialized());
+
+ mUser.Set(aStatus, aMessage, aFailureId);
+}
+
+void FeatureState::SetEnvironment(FeatureStatus aStatus, const char* aMessage,
+ const nsACString& aFailureId) {
+ // Default decision must have been made, but not runtime.
+ MOZ_ASSERT(mDefault.IsInitialized());
+ MOZ_ASSERT(!mRuntime.IsInitialized());
+
+ mEnvironment.Set(aStatus, aMessage, aFailureId);
+}
+
+void FeatureState::SetRuntime(FeatureStatus aStatus, const char* aMessage,
+ const nsACString& aFailureId) {
+ AssertInitialized();
+
+ mRuntime.Set(aStatus, aMessage, aFailureId);
+}
+
+const char* FeatureState::GetRuntimeMessage() const {
+ MOZ_ASSERT(IsFeatureStatusFailure(mRuntime.mStatus));
+ return mRuntime.mMessage;
+}
+
+void FeatureState::ForEachStatusChange(
+ const StatusIterCallback& aCallback) const {
+ AssertInitialized();
+
+ aCallback("default", mDefault.mStatus, mDefault.MessageOrNull(),
+ mDefault.FailureId());
+ if (mUser.IsInitialized()) {
+ aCallback("user", mUser.mStatus, mUser.Message(), mUser.FailureId());
+ }
+ if (mEnvironment.IsInitialized()) {
+ aCallback("env", mEnvironment.mStatus, mEnvironment.Message(),
+ mEnvironment.FailureId());
+ }
+ if (mRuntime.IsInitialized()) {
+ aCallback("runtime", mRuntime.mStatus, mRuntime.Message(),
+ mRuntime.FailureId());
+ }
+}
+
+const char* FeatureState::GetFailureMessage() const {
+ AssertInitialized();
+ MOZ_ASSERT(!IsEnabled());
+
+ if (mRuntime.mStatus != FeatureStatus::Unused &&
+ IsFeatureStatusFailure(mRuntime.mStatus)) {
+ return mRuntime.mMessage;
+ }
+ if (mEnvironment.mStatus != FeatureStatus::Unused &&
+ IsFeatureStatusFailure(mEnvironment.mStatus)) {
+ return mEnvironment.mMessage;
+ }
+ if (mUser.mStatus != FeatureStatus::Unused &&
+ IsFeatureStatusFailure(mUser.mStatus)) {
+ return mUser.mMessage;
+ }
+
+ MOZ_ASSERT(IsFeatureStatusFailure(mDefault.mStatus));
+ return mDefault.mMessage;
+}
+
+const nsCString& FeatureState::GetFailureId() const {
+ MOZ_ASSERT(!IsEnabled());
+
+ if (mRuntime.mStatus != FeatureStatus::Unused) {
+ return mRuntime.mFailureId;
+ }
+ if (mEnvironment.mStatus != FeatureStatus::Unused) {
+ return mEnvironment.mFailureId;
+ }
+ if (mUser.mStatus != FeatureStatus::Unused) {
+ return mUser.mFailureId;
+ }
+
+ return mDefault.mFailureId;
+}
+
+nsCString FeatureState::GetStatusAndFailureIdString() const {
+ nsCString status;
+ auto value = GetValue();
+ switch (value) {
+ case FeatureStatus::Blocklisted:
+ case FeatureStatus::Disabled:
+ case FeatureStatus::Unavailable:
+ case FeatureStatus::UnavailableNoAngle:
+ case FeatureStatus::Blocked:
+ status.AppendPrintf("%s:%s", FeatureStatusToString(value),
+ GetFailureId().get());
+ break;
+ default:
+ status.Append(FeatureStatusToString(value));
+ break;
+ }
+
+ return status;
+}
+
+void FeatureState::Reset() {
+ mDefault.Set(FeatureStatus::Unused);
+ mUser.Set(FeatureStatus::Unused);
+ mEnvironment.Set(FeatureStatus::Unused);
+ mRuntime.Set(FeatureStatus::Unused);
+}
+
+void FeatureState::Instance::Set(FeatureStatus aStatus) {
+ mStatus = aStatus;
+ mMessage[0] = '\0';
+ mFailureId.Truncate();
+}
+
+void FeatureState::Instance::Set(FeatureStatus aStatus, const char* aMessage,
+ const nsACString& aFailureId) {
+ mStatus = aStatus;
+ if (aMessage) {
+ SprintfLiteral(mMessage, "%s", aMessage);
+ } else {
+ mMessage[0] = '\0';
+ }
+ mFailureId.Assign(aFailureId);
+}
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/gfx/config/gfxFeature.h b/gfx/config/gfxFeature.h
new file mode 100644
index 0000000000..edebb5759d
--- /dev/null
+++ b/gfx/config/gfxFeature.h
@@ -0,0 +1,167 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 <functional>
+#include <stdint.h>
+#include "gfxTelemetry.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/Maybe.h"
+#include "nsString.h"
+
+namespace mozilla {
+namespace gfx {
+
+#define GFX_FEATURE_MAP(_) \
+ /* Name, Type, Description */ \
+ _(HW_COMPOSITING, Feature, "Compositing") \
+ _(D3D11_COMPOSITING, Feature, "Direct3D11 Compositing") \
+ _(OPENGL_COMPOSITING, Feature, "OpenGL Compositing") \
+ _(DIRECT2D, Feature, "Direct2D") \
+ _(D3D11_HW_ANGLE, Feature, "Direct3D11 hardware ANGLE") \
+ _(DIRECT_DRAW, Feature, "DirectDraw") \
+ _(GPU_PROCESS, Feature, "GPU Process") \
+ _(WEBRENDER, Feature, "WebRender") \
+ _(WEBRENDER_COMPOSITOR, Feature, "WebRender native compositor") \
+ _(WEBRENDER_PARTIAL, Feature, "WebRender partial present") \
+ _(WEBRENDER_SHADER_CACHE, Feature, "WebRender shader disk cache") \
+ _(WEBRENDER_OPTIMIZED_SHADERS, Feature, "WebRender optimized shaders") \
+ _(WEBRENDER_ANGLE, Feature, "WebRender ANGLE") \
+ _(WEBRENDER_DCOMP_PRESENT, Feature, "WebRender DirectComposition") \
+ _(WEBRENDER_SCISSORED_CACHE_CLEARS, Feature, \
+ "WebRender scissored cache clears") \
+ _(OMTP, Feature, "Off Main Thread Painting") \
+ _(WEBGPU, Feature, "WebGPU") \
+ _(X11_EGL, Feature, "X11 EGL") \
+ _(DMABUF, Feature, "DMABUF") \
+ _(WINDOW_OCCLUSION, Feature, "WINDOW_OCCLUSION") \
+ _(HARDWARE_VIDEO_DECODING, Feature, "Hardware video decoding") \
+ _(VIDEO_OVERLAY, Feature, "video overlay") \
+ _(HW_DECODED_VIDEO_ZERO_COPY, Feature, "Hardware decoded video zero copy") \
+ _(VP8_HW_DECODE, Feature, "VP8 hardware decoding") \
+ _(VP9_HW_DECODE, Feature, "VP9 hardware decoding") \
+ _(DMABUF_SURFACE_EXPORT, Feature, "WebGL DMABuf surface export") \
+ _(REUSE_DECODER_DEVICE, Feature, "Reuse decoder device") \
+ _(BACKDROP_FILTER, Feature, "Backdrop filter") \
+ _(CANVAS_RENDERER_THREAD, Feature, "canvas renderer thread") \
+ _(ACCELERATED_CANVAS2D, Feature, "Accelerated Canvas2D") \
+ _(H264_HW_DECODE, Feature, "H.264 hardware decoding") \
+ _(AV1_HW_DECODE, Feature, "AV1 hardware decoding") \
+ /* 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 {
+ friend class gfxConfig;
+ friend class GfxConfigManager; // for testing
+
+ public:
+ FeatureState() { Reset(); }
+
+ bool IsEnabled() const;
+ FeatureStatus GetValue() const;
+
+ void EnableByDefault();
+ void DisableByDefault(FeatureStatus aStatus, const char* aMessage,
+ const nsACString& aFailureId);
+ bool SetDefault(bool aEnable, FeatureStatus aDisableStatus,
+ const char* aDisableMessage);
+ bool InitOrUpdate(bool aEnable, FeatureStatus aDisableStatus,
+ const char* aMessage);
+ void SetDefaultFromPref(const char* aPrefName, bool aIsEnablePref,
+ bool aDefaultValue, Maybe<bool> aUserValue);
+ void SetDefaultFromPref(const char* aPrefName, bool aIsEnablePref,
+ bool aDefaultValue);
+ void UserEnable(const char* aMessage);
+ void UserForceEnable(const char* aMessage);
+ void UserDisable(const char* aMessage, const nsACString& aFailureId);
+ void Disable(FeatureStatus aStatus, const char* aMessage,
+ const nsACString& aFailureId);
+ void ForceDisable(FeatureStatus aStatus, const char* aMessage,
+ const nsACString& aFailureId) {
+ SetFailed(aStatus, aMessage, aFailureId);
+ }
+ void SetFailed(FeatureStatus aStatus, const char* aMessage,
+ const nsACString& aFailureId);
+ bool MaybeSetFailed(bool aEnable, FeatureStatus aStatus, const char* aMessage,
+ const nsACString& aFailureId);
+ bool MaybeSetFailed(FeatureStatus aStatus, const char* aMessage,
+ const nsACString& aFailureId);
+
+ // aType is "base", "user", "env", or "runtime".
+ // aMessage may be null.
+ typedef std::function<void(const char* aType, FeatureStatus aStatus,
+ const char* aMessage, const nsCString& aFailureId)>
+ StatusIterCallback;
+ void ForEachStatusChange(const StatusIterCallback& aCallback) const;
+
+ const char* GetFailureMessage() const;
+ const nsCString& GetFailureId() const;
+ nsCString GetStatusAndFailureIdString() const;
+
+ bool DisabledByDefault() const;
+
+ // Clear all state.
+ void Reset();
+
+ private:
+ void SetUser(FeatureStatus aStatus, const char* aMessage,
+ const nsACString& aFailureId);
+ void SetEnvironment(FeatureStatus aStatus, const char* aMessage,
+ const nsACString& aFailureId);
+ void SetRuntime(FeatureStatus aStatus, const char* aMessage,
+ const nsACString& aFailureId);
+ bool IsForcedOnByUser() const;
+ const char* GetRuntimeMessage() const;
+ bool IsInitialized() const { return mDefault.IsInitialized(); }
+
+ void AssertInitialized() const { MOZ_ASSERT(IsInitialized()); }
+
+ private:
+ struct Instance {
+ char mMessage[64];
+ FeatureStatus mStatus;
+ nsCString mFailureId;
+
+ void Set(FeatureStatus aStatus);
+ void Set(FeatureStatus aStatus, const char* aMessage,
+ const nsACString& aFailureId);
+ bool IsInitialized() const { return mStatus != FeatureStatus::Unused; }
+ const char* MessageOrNull() const {
+ return mMessage[0] != '\0' ? mMessage : nullptr;
+ }
+ const char* Message() const {
+ MOZ_ASSERT(MessageOrNull());
+ return mMessage;
+ }
+ const nsCString& FailureId() const { return mFailureId; }
+ };
+
+ // The default state is the state we decide on startup, based on the operating
+ // system or a base preference.
+ //
+ // The user state factors in any changes to preferences that the user made.
+ //
+ // The environment state factors in any additional decisions made, such as
+ // availability or blocklisting.
+ //
+ // The runtime state factors in any problems discovered at runtime.
+ Instance mDefault;
+ Instance mUser;
+ Instance mEnvironment;
+ Instance mRuntime;
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif // mozilla_gfx_config_gfxFeature_h
diff --git a/gfx/config/gfxVarReceiver.h b/gfx/config/gfxVarReceiver.h
new file mode 100644
index 0000000000..ab026f2eb6
--- /dev/null
+++ b/gfx/config/gfxVarReceiver.h
@@ -0,0 +1,24 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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_gfxVarReceiver_h
+#define mozilla_gfx_config_gfxVarReceiver_h
+
+namespace mozilla {
+namespace gfx {
+
+class GfxVarUpdate;
+
+// This allows downstream processes (such as PContent, PGPU) to listen for
+// updates on gfxVarReceiver.
+class gfxVarReceiver {
+ public:
+ virtual void OnVarChanged(const GfxVarUpdate& aVar) = 0;
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif // mozilla_gfx_config_gfxVarReceiver_h
diff --git a/gfx/config/gfxVars.cpp b/gfx/config/gfxVars.cpp
new file mode 100644
index 0000000000..77a8470c3c
--- /dev/null
+++ b/gfx/config/gfxVars.cpp
@@ -0,0 +1,149 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 "gfxVars.h"
+#include "gfxVarReceiver.h"
+#include "mozilla/dom/ContentChild.h"
+
+namespace mozilla {
+namespace gfx {
+
+StaticAutoPtr<gfxVars> gfxVars::sInstance;
+StaticAutoPtr<nsTArray<gfxVars::VarBase*>> gfxVars::sVarList;
+
+StaticAutoPtr<nsTArray<GfxVarUpdate>> gGfxVarInitUpdates;
+
+void gfxVars::SetValuesForInitialize(
+ const nsTArray<GfxVarUpdate>& aInitUpdates) {
+ // This should only be called once
+ MOZ_RELEASE_ASSERT(!gGfxVarInitUpdates);
+
+ // We expect aInitUpdates to be provided before any other gfxVars operation,
+ // and for sInstance to be null here, but handle the alternative.
+ if (sInstance) {
+ // Apply the updates, the object has been created already
+ for (const auto& varUpdate : aInitUpdates) {
+ ApplyUpdate(varUpdate);
+ }
+ } else {
+ // Save the values for Initialize call
+ gGfxVarInitUpdates = new nsTArray<GfxVarUpdate>(aInitUpdates.Clone());
+ }
+}
+
+void gfxVars::Initialize() {
+ if (sInstance) {
+ MOZ_RELEASE_ASSERT(
+ !gGfxVarInitUpdates,
+ "Initial updates should not be present after any gfxVars operation");
+ return;
+ }
+
+ // sVarList must be initialized first since it's used in the constructor for
+ // sInstance.
+ sVarList = new nsTArray<gfxVars::VarBase*>();
+ sInstance = new gfxVars;
+
+ // Note the GPU process is not handled here - it cannot send sync
+ // messages, so instead the initial data is pushed down.
+ if (XRE_IsContentProcess()) {
+ MOZ_ASSERT(gGfxVarInitUpdates,
+ "Initial updates should be provided in content process");
+ if (!gGfxVarInitUpdates) {
+ // No provided initial updates, sync-request them from parent.
+ nsTArray<GfxVarUpdate> initUpdates;
+ dom::ContentChild::GetSingleton()->SendGetGfxVars(&initUpdates);
+ gGfxVarInitUpdates = new nsTArray<GfxVarUpdate>(std::move(initUpdates));
+ }
+ for (const auto& varUpdate : *gGfxVarInitUpdates) {
+ ApplyUpdate(varUpdate);
+ }
+ gGfxVarInitUpdates = nullptr;
+ }
+}
+
+gfxVars::gfxVars() = default;
+
+void gfxVars::Shutdown() {
+ sInstance = nullptr;
+ sVarList = nullptr;
+ gGfxVarInitUpdates = nullptr;
+}
+
+/* static */
+void gfxVars::ApplyUpdate(const GfxVarUpdate& aUpdate) {
+ // Only subprocesses receive updates and apply them locally.
+ MOZ_ASSERT(!XRE_IsParentProcess());
+ MOZ_DIAGNOSTIC_ASSERT(sVarList || gGfxVarInitUpdates);
+ if (sVarList) {
+ sVarList->ElementAt(aUpdate.index())->SetValue(aUpdate.value());
+ } else if (gGfxVarInitUpdates) {
+ // Too early, we haven't been initialized, so just add to
+ // the array waiting for the initialization...
+ gGfxVarInitUpdates->AppendElement(aUpdate);
+ }
+}
+
+/* static */
+void gfxVars::AddReceiver(gfxVarReceiver* aReceiver) {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ // Don't double-add receivers, in case a broken content process sends two
+ // init messages.
+ if (!sInstance->mReceivers.Contains(aReceiver)) {
+ sInstance->mReceivers.AppendElement(aReceiver);
+ }
+}
+
+/* static */
+void gfxVars::RemoveReceiver(gfxVarReceiver* aReceiver) {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ if (sInstance) {
+ sInstance->mReceivers.RemoveElement(aReceiver);
+ }
+}
+
+/* static */
+nsTArray<GfxVarUpdate> gfxVars::FetchNonDefaultVars() {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(sVarList);
+
+ nsTArray<GfxVarUpdate> updates;
+ for (size_t i = 0; i < sVarList->Length(); i++) {
+ VarBase* var = sVarList->ElementAt(i);
+ if (var->HasDefaultValue()) {
+ continue;
+ }
+
+ GfxVarValue value;
+ var->GetValue(&value);
+
+ updates.AppendElement(GfxVarUpdate(i, value));
+ }
+
+ return updates;
+}
+
+gfxVars::VarBase::VarBase() {
+ mIndex = gfxVars::sVarList->Length();
+ gfxVars::sVarList->AppendElement(this);
+}
+
+void gfxVars::NotifyReceivers(VarBase* aVar) {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ GfxVarValue value;
+ aVar->GetValue(&value);
+
+ GfxVarUpdate update(aVar->Index(), value);
+ for (auto& receiver : mReceivers) {
+ receiver->OnVarChanged(update);
+ }
+}
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/gfx/config/gfxVars.h b/gfx/config/gfxVars.h
new file mode 100644
index 0000000000..087540ad96
--- /dev/null
+++ b/gfx/config/gfxVars.h
@@ -0,0 +1,253 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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_gfxVars_h
+#define mozilla_gfx_config_gfxVars_h
+
+#include <stdint.h>
+#include "mozilla/Assertions.h"
+#include "mozilla/StaticPtr.h"
+#include "mozilla/gfx/GraphicsMessages.h"
+#include "mozilla/gfx/Point.h"
+#include "mozilla/gfx/Types.h"
+#include "nsTArray.h"
+#include "nsXULAppAPI.h"
+
+namespace mozilla::gfx {
+
+class gfxVarReceiver;
+
+// Generator for graphics vars.
+#define GFX_VARS_LIST(_) \
+ /* C++ Name, Data Type, Default Value */ \
+ _(AllowEglRbab, bool, true) \
+ _(AllowWebgl2, bool, true) \
+ _(AllowWebglAccelAngle, bool, true) \
+ _(AllowWebglOop, bool, true) \
+ _(BrowserTabsRemoteAutostart, bool, false) \
+ _(ContentBackend, BackendType, BackendType::NONE) \
+ _(SoftwareBackend, BackendType, BackendType::NONE) \
+ _(OffscreenFormat, gfxImageFormat, \
+ mozilla::gfx::SurfaceFormat::X8R8G8B8_UINT32) \
+ _(RequiresAcceleratedGLContextForCompositorOGL, bool, false) \
+ _(CanUseHardwareVideoDecoding, bool, false) \
+ _(DXInterop2Blocked, bool, false) \
+ _(DXNV12Blocked, bool, false) \
+ _(DXP010Blocked, bool, false) \
+ _(DXP016Blocked, bool, false) \
+ _(UseWebRenderANGLE, bool, false) \
+ _(UseWebRenderFlipSequentialWin, bool, false) \
+ _(UseWebRenderDCompWin, bool, false) \
+ _(UseWebRenderDCompVideoOverlayWin, bool, false) \
+ _(UseWebRenderTripleBufferingWin, bool, false) \
+ _(UseWebRenderCompositor, bool, false) \
+ _(UseWebRenderProgramBinaryDisk, bool, false) \
+ _(UseWebRenderOptimizedShaders, bool, false) \
+ _(UseWebRenderScissoredCacheClears, bool, true) \
+ _(WebRenderProfilerUI, nsCString, nsCString()) \
+ _(WebglAllowCoreProfile, bool, true) \
+ _(WebglAllowWindowsNativeGl, bool, false) \
+ _(WebRenderMaxPartialPresentRects, int32_t, 0) \
+ _(WebRenderDebugFlags, int32_t, 0) \
+ _(WebRenderBoolParameters, int32_t, 0) \
+ _(WebRenderBatchingLookback, int32_t, 10) \
+ _(WebRenderBlobTileSize, int32_t, 256) \
+ _(WebRenderBatchedUploadThreshold, int32_t, 512 * 512) \
+ _(UseSoftwareWebRender, bool, false) \
+ _(AllowSoftwareWebRenderD3D11, bool, false) \
+ _(ScreenDepth, int32_t, 0) \
+ _(GREDirectory, nsString, nsString()) \
+ _(ProfDirectory, nsString, nsString()) \
+ _(AllowD3D11KeyedMutex, bool, false) \
+ _(SwapIntervalGLX, bool, false) \
+ _(SwapIntervalEGL, bool, false) \
+ _(SystemTextQuality, int32_t, 5 /* CLEARTYPE_QUALITY */) \
+ _(SystemTextClearTypeLevel, float, 1.0f) \
+ _(SystemTextEnhancedContrast, float, 1.0f) \
+ _(SystemTextGamma, float, 2.2f) \
+ _(SystemTextPixelGeometry, int32_t, 1 /* pixel geometry RGB */) \
+ _(SystemTextRenderingMode, int32_t, 0) \
+ _(SystemGDIGamma, float, 1.4f) \
+ _(LayersWindowRecordingPath, nsCString, nsCString()) \
+ _(RemoteCanvasEnabled, bool, false) \
+ _(UseDoubleBufferingWithCompositor, bool, false) \
+ _(UseGLSwizzle, bool, true) \
+ _(ForceSubpixelAAWherePossible, bool, false) \
+ _(DwmCompositionEnabled, bool, true) \
+ _(FxREmbedded, bool, false) \
+ _(UseAHardwareBufferSharedSurfaceWebglOop, bool, false) \
+ _(UseEGL, bool, false) \
+ _(DrmRenderDevice, nsCString, nsCString()) \
+ _(UseDMABuf, bool, false) \
+ _(DMABufModifiersXRGB, ArrayOfuint64_t, nsTArray<uint64_t>()) \
+ _(DMABufModifiersARGB, ArrayOfuint64_t, nsTArray<uint64_t>()) \
+ _(CodecSupportInfo, nsCString, nsCString()) \
+ _(WebRenderRequiresHardwareDriver, bool, false) \
+ _(SupportsThreadsafeGL, bool, false) \
+ _(AllowWebGPU, bool, false) \
+ _(UseVP8HwDecode, bool, false) \
+ _(UseVP9HwDecode, bool, false) \
+ _(UseAV1HwDecode, bool, false) \
+ _(UseH264HwDecode, bool, false) \
+ _(HwDecodedVideoZeroCopy, bool, false) \
+ _(UseDMABufSurfaceExport, bool, true) \
+ _(ReuseDecoderDevice, bool, false) \
+ _(UseCanvasRenderThread, bool, false) \
+ _(AllowBackdropFilter, bool, true) \
+ _(WebglOopAsyncPresentForceSync, bool, true) \
+ _(UseAcceleratedCanvas2D, bool, false) \
+ _(UseWebRenderDCompSwVideoOverlayWin, bool, false) \
+ _(WebglUseHardware, bool, true)
+
+/* Add new entries above this line. */
+
+// Some graphics settings are computed on the UI process and must be
+// communicated to content and GPU processes. gfxVars helps facilitate
+// this. Its function is similar to StaticPrefs, except rather than hold
+// user preferences, it holds dynamically computed values.
+//
+// Each variable in GFX_VARS_LIST exposes the following static methods:
+//
+// const DataType& CxxName();
+// void SetCxxName(const DataType& aValue);
+//
+// Note that the setter may only be called in the UI process; a gfxVar must be
+// a variable that is determined in the UI process and pushed to child
+// processes.
+class gfxVars final {
+ public:
+ // These values will be used during the Initialize() call if set. Any
+ // updates that come before initialization will get added to this array.
+ static void SetValuesForInitialize(
+ const nsTArray<GfxVarUpdate>& aInitUpdates);
+
+ static void Initialize();
+ static void Shutdown();
+
+ static void ApplyUpdate(const GfxVarUpdate& aUpdate);
+ static void AddReceiver(gfxVarReceiver* aReceiver);
+ static void RemoveReceiver(gfxVarReceiver* aReceiver);
+
+ // Return a list of updates for all variables with non-default values.
+ static nsTArray<GfxVarUpdate> FetchNonDefaultVars();
+
+ private:
+ template <typename U>
+ static U CloneVarValue(const U& aValue) {
+ return aValue;
+ }
+
+ template <typename U>
+ static nsTArray<U> CloneVarValue(const nsTArray<U>& aValue) {
+ return aValue.Clone();
+ }
+
+ public:
+ // Each variable must expose Set and Get methods for IPDL.
+ class VarBase {
+ public:
+ VarBase();
+ virtual void SetValue(const GfxVarValue& aValue) = 0;
+ virtual void GetValue(GfxVarValue* aOutValue) = 0;
+ virtual bool HasDefaultValue() const = 0;
+ size_t Index() const { return mIndex; }
+
+ private:
+ size_t mIndex;
+ };
+
+ // Whether the gfxVars singleton instance has been initialized. Most gfx code
+ // doesn't need to check this, but code that can potentially run before
+ // gfxPlatform initialization can use this to check whether gfxVars are
+ // available yet.
+ static bool IsInitialized() { return sInstance != nullptr; }
+
+ private:
+ static StaticAutoPtr<gfxVars> sInstance;
+ static StaticAutoPtr<nsTArray<VarBase*>> sVarList;
+
+ template <typename T, T Default(), T GetFrom(const GfxVarValue& aValue)>
+ class VarImpl final : public VarBase {
+ public:
+ VarImpl() : mValue(Default()) {}
+ void SetValue(const GfxVarValue& aValue) override {
+ mValue = GetFrom(aValue);
+ if (mListener) {
+ mListener();
+ }
+ }
+ void GetValue(GfxVarValue* aOutValue) override {
+ *aOutValue = GfxVarValue(mValue);
+ }
+ bool HasDefaultValue() const override { return mValue == Default(); }
+ const T& Get() const { return mValue; }
+
+ // Return true if the value changed, false otherwise.
+ bool Set(const T& aValue) {
+ MOZ_ASSERT(XRE_IsParentProcess());
+ if (mValue == aValue) {
+ return false;
+ }
+ mValue = CloneVarValue(aValue);
+ if (mListener) {
+ mListener();
+ }
+ return true;
+ }
+
+ void SetListener(const std::function<void()>& aListener) {
+ mListener = aListener;
+ }
+
+ private:
+ T mValue;
+ std::function<void()> mListener;
+ };
+
+#define GFX_VAR_DECL(CxxName, DataType, DefaultValue) \
+ private: \
+ static DataType Get##CxxName##Default() { return DefaultValue; } \
+ static DataType Get##CxxName##From(const GfxVarValue& aValue) { \
+ return CloneVarValue(aValue.get_##DataType()); \
+ } \
+ VarImpl<DataType, Get##CxxName##Default, Get##CxxName##From> mVar##CxxName; \
+ \
+ public: \
+ static const DataType& CxxName() { return sInstance->mVar##CxxName.Get(); } \
+ static DataType Get##CxxName##OrDefault() { \
+ if (!sInstance) { \
+ return DefaultValue; \
+ } \
+ return CloneVarValue(sInstance->mVar##CxxName.Get()); \
+ } \
+ static void Set##CxxName(const DataType& aValue) { \
+ if (sInstance->mVar##CxxName.Set(aValue)) { \
+ sInstance->NotifyReceivers(&sInstance->mVar##CxxName); \
+ } \
+ } \
+ \
+ static void Set##CxxName##Listener(const std::function<void()>& aListener) { \
+ sInstance->mVar##CxxName.SetListener(aListener); \
+ }
+
+ using ArrayOfuint64_t = nsTArray<uint64_t>;
+
+ GFX_VARS_LIST(GFX_VAR_DECL)
+#undef GFX_VAR_DECL
+
+ private:
+ gfxVars();
+
+ void NotifyReceivers(VarBase* aVar);
+
+ private:
+ nsTArray<gfxVarReceiver*> mReceivers;
+};
+
+#undef GFX_VARS_LIST
+
+} // namespace mozilla::gfx
+
+#endif // mozilla_gfx_config_gfxVars_h
diff --git a/gfx/config/moz.build b/gfx/config/moz.build
new file mode 100644
index 0000000000..a19b39a592
--- /dev/null
+++ b/gfx/config/moz.build
@@ -0,0 +1,28 @@
+# -*- Mode: python; 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",
+]
+
+EXPORTS.mozilla.gfx += [
+ "gfxConfigManager.h",
+ "gfxVarReceiver.h",
+ "gfxVars.h",
+]
+
+UNIFIED_SOURCES += [
+ "gfxConfig.cpp",
+ "gfxConfigManager.cpp",
+ "gfxFeature.cpp",
+ "gfxVars.cpp",
+]
+
+include("/ipc/chromium/chromium-config.mozbuild")
+
+FINAL_LIBRARY = "xul"