diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
commit | 36d22d82aa202bb199967e9512281e9a53db42c9 (patch) | |
tree | 105e8c98ddea1c1e4784a60a5a6410fa416be2de /gfx/config | |
parent | Initial commit. (diff) | |
download | firefox-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.cpp | 286 | ||||
-rw-r--r-- | gfx/config/gfxConfig.h | 224 | ||||
-rw-r--r-- | gfx/config/gfxConfigManager.cpp | 353 | ||||
-rw-r--r-- | gfx/config/gfxConfigManager.h | 119 | ||||
-rw-r--r-- | gfx/config/gfxFallback.h | 30 | ||||
-rw-r--r-- | gfx/config/gfxFeature.cpp | 307 | ||||
-rw-r--r-- | gfx/config/gfxFeature.h | 167 | ||||
-rw-r--r-- | gfx/config/gfxVarReceiver.h | 24 | ||||
-rw-r--r-- | gfx/config/gfxVars.cpp | 149 | ||||
-rw-r--r-- | gfx/config/gfxVars.h | 253 | ||||
-rw-r--r-- | gfx/config/moz.build | 28 |
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" |