1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #include "mozilla/gfx/gfxConfigManager.h"
8 #include "mozilla/gfx/gfxVars.h"
9 #include "mozilla/Preferences.h"
10 #include "mozilla/Components.h"
11 #include "mozilla/StaticPrefs_gfx.h"
12 #include "mozilla/StaticPrefs_layers.h"
13 #include "gfxConfig.h"
14 #include "gfxPlatform.h"
15 #include "nsIGfxInfo.h"
16 #include "nsPrintfCString.h"
17 #include "nsXULAppAPI.h"
18 
19 #ifdef XP_WIN
20 #  include "mozilla/WindowsVersion.h"
21 #  include "mozilla/gfx/DeviceManagerDx.h"
22 #  include "mozilla/gfx/DisplayConfigWindows.h"
23 #endif
24 
25 namespace mozilla {
26 namespace gfx {
27 
Init()28 void gfxConfigManager::Init() {
29   MOZ_ASSERT(XRE_IsParentProcess());
30 
31   EmplaceUserPref("gfx.webrender.compositor", mWrCompositorEnabled);
32   mWrForceEnabled = gfxPlatform::WebRenderPrefEnabled();
33   mWrSoftwareForceEnabled = StaticPrefs::gfx_webrender_software_AtStartup();
34   mWrCompositorForceEnabled =
35       StaticPrefs::gfx_webrender_compositor_force_enabled_AtStartup();
36   mGPUProcessAllowSoftware =
37       StaticPrefs::layers_gpu_process_allow_software_AtStartup();
38   mWrForcePartialPresent =
39       StaticPrefs::gfx_webrender_force_partial_present_AtStartup();
40   mWrPartialPresent =
41       StaticPrefs::gfx_webrender_max_partial_present_rects_AtStartup() > 0;
42   EmplaceUserPref(StaticPrefs::GetPrefName_gfx_webrender_program_binary_disk(),
43                   mWrShaderCache);
44   mWrOptimizedShaders =
45       StaticPrefs::gfx_webrender_use_optimized_shaders_AtStartup();
46 #ifdef XP_WIN
47   mWrForceAngle = StaticPrefs::gfx_webrender_force_angle_AtStartup();
48   mWrForceAngleNoGPUProcess = StaticPrefs::
49       gfx_webrender_enabled_no_gpu_process_with_angle_win_AtStartup();
50   mWrDCompWinEnabled =
51       Preferences::GetBool("gfx.webrender.dcomp-win.enabled", false);
52 #endif
53 
54   mWrEnvForceEnabled = gfxPlatform::WebRenderEnvvarEnabled();
55 
56 #ifdef XP_WIN
57   DeviceManagerDx::Get()->CheckHardwareStretchingSupport(mHwStretchingSupport);
58   mScaledResolution = HasScaledResolution();
59   mIsWin10OrLater = IsWin10OrLater();
60   mWrCompositorDCompRequired = true;
61 #else
62   ++mHwStretchingSupport.mBoth;
63 #endif
64 
65 #ifdef MOZ_WIDGET_GTK
66   mDisableHwCompositingNoWr = true;
67 #endif
68 
69 #ifdef NIGHTLY_BUILD
70   mIsNightly = true;
71 #endif
72 #ifdef EARLY_BETA_OR_EARLIER
73   mIsEarlyBetaOrEarlier = true;
74 #endif
75   mSafeMode = gfxPlatform::InSafeMode();
76 
77   mGfxInfo = components::GfxInfo::Service();
78 
79   mFeatureWr = &gfxConfig::GetFeature(Feature::WEBRENDER);
80   mFeatureWrQualified = &gfxConfig::GetFeature(Feature::WEBRENDER_QUALIFIED);
81   mFeatureWrCompositor = &gfxConfig::GetFeature(Feature::WEBRENDER_COMPOSITOR);
82   mFeatureWrAngle = &gfxConfig::GetFeature(Feature::WEBRENDER_ANGLE);
83   mFeatureWrDComp = &gfxConfig::GetFeature(Feature::WEBRENDER_DCOMP_PRESENT);
84   mFeatureWrPartial = &gfxConfig::GetFeature(Feature::WEBRENDER_PARTIAL);
85   mFeatureWrShaderCache =
86       &gfxConfig::GetFeature(Feature::WEBRENDER_SHADER_CACHE);
87   mFeatureWrOptimizedShaders =
88       &gfxConfig::GetFeature(Feature::WEBRENDER_OPTIMIZED_SHADERS);
89   mFeatureWrSoftware = &gfxConfig::GetFeature(Feature::WEBRENDER_SOFTWARE);
90 
91   mFeatureHwCompositing = &gfxConfig::GetFeature(Feature::HW_COMPOSITING);
92 #ifdef XP_WIN
93   mFeatureD3D11HwAngle = &gfxConfig::GetFeature(Feature::D3D11_HW_ANGLE);
94   mFeatureD3D11Compositing = &gfxConfig::GetFeature(Feature::D3D11_COMPOSITING);
95 #endif
96   mFeatureGPUProcess = &gfxConfig::GetFeature(Feature::GPU_PROCESS);
97 }
98 
EmplaceUserPref(const char * aPrefName,Maybe<bool> & aValue)99 void gfxConfigManager::EmplaceUserPref(const char* aPrefName,
100                                        Maybe<bool>& aValue) {
101   if (Preferences::HasUserValue(aPrefName)) {
102     aValue.emplace(Preferences::GetBool(aPrefName, false));
103   }
104 }
105 
ConfigureFromBlocklist(long aFeature,FeatureState * aFeatureState)106 void gfxConfigManager::ConfigureFromBlocklist(long aFeature,
107                                               FeatureState* aFeatureState) {
108   MOZ_ASSERT(aFeatureState);
109 
110   nsCString blockId;
111   int32_t status;
112   if (!NS_SUCCEEDED(mGfxInfo->GetFeatureStatus(aFeature, blockId, &status))) {
113     aFeatureState->Disable(FeatureStatus::BlockedNoGfxInfo, "gfxInfo is broken",
114                            "FEATURE_FAILURE_NO_GFX_INFO"_ns);
115 
116   } else {
117     if (status != nsIGfxInfo::FEATURE_STATUS_OK) {
118       aFeatureState->Disable(FeatureStatus::Blocklisted,
119                              "Blocklisted by gfxInfo", blockId);
120     }
121   }
122 }
123 
ConfigureWebRenderQualified()124 void gfxConfigManager::ConfigureWebRenderQualified() {
125   MOZ_ASSERT(mFeatureWrQualified);
126   MOZ_ASSERT(mFeatureWrCompositor);
127 
128   mFeatureWrQualified->EnableByDefault();
129 
130   nsCString failureId;
131   int32_t status;
132   if (NS_FAILED(mGfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_WEBRENDER,
133                                            failureId, &status))) {
134     mFeatureWrQualified->Disable(FeatureStatus::BlockedNoGfxInfo,
135                                  "gfxInfo is broken",
136                                  "FEATURE_FAILURE_WR_NO_GFX_INFO"_ns);
137     return;
138   }
139 
140   switch (status) {
141     case nsIGfxInfo::FEATURE_ALLOW_ALWAYS:
142     case nsIGfxInfo::FEATURE_ALLOW_QUALIFIED:
143       break;
144     case nsIGfxInfo::FEATURE_DENIED:
145       mFeatureWrQualified->Disable(FeatureStatus::Denied, "Not on allowlist",
146                                    failureId);
147       break;
148     default:
149       mFeatureWrQualified->Disable(FeatureStatus::Blocklisted,
150                                    "No qualified hardware", failureId);
151       break;
152     case nsIGfxInfo::FEATURE_STATUS_OK:
153       MOZ_ASSERT_UNREACHABLE("We should still be rolling out WebRender!");
154       mFeatureWrQualified->Disable(FeatureStatus::Blocked,
155                                    "Not controlled by rollout", failureId);
156       break;
157   }
158 }
159 
ConfigureWebRender()160 void gfxConfigManager::ConfigureWebRender() {
161   MOZ_ASSERT(XRE_IsParentProcess());
162   MOZ_ASSERT(mFeatureWr);
163   MOZ_ASSERT(mFeatureWrQualified);
164   MOZ_ASSERT(mFeatureWrCompositor);
165   MOZ_ASSERT(mFeatureWrAngle);
166   MOZ_ASSERT(mFeatureWrDComp);
167   MOZ_ASSERT(mFeatureWrPartial);
168   MOZ_ASSERT(mFeatureWrShaderCache);
169   MOZ_ASSERT(mFeatureWrOptimizedShaders);
170   MOZ_ASSERT(mFeatureWrSoftware);
171   MOZ_ASSERT(mFeatureHwCompositing);
172   MOZ_ASSERT(mFeatureGPUProcess);
173 
174   // Initialize WebRender native compositor usage
175   mFeatureWrCompositor->SetDefaultFromPref("gfx.webrender.compositor", true,
176                                            false, mWrCompositorEnabled);
177 
178   if (mWrCompositorForceEnabled) {
179     mFeatureWrCompositor->UserForceEnable("Force enabled by pref");
180   }
181 
182   ConfigureFromBlocklist(nsIGfxInfo::FEATURE_WEBRENDER_COMPOSITOR,
183                          mFeatureWrCompositor);
184 
185   // Disable native compositor when hardware stretching is not supported. It is
186   // for avoiding a problem like Bug 1618370.
187   // XXX Is there a better check for Bug 1618370?
188   if (!mHwStretchingSupport.IsFullySupported() && mScaledResolution) {
189     nsPrintfCString failureId(
190         "FEATURE_FAILURE_NO_HARDWARE_STRETCHING_B%uW%uF%uN%uE%u",
191         mHwStretchingSupport.mBoth, mHwStretchingSupport.mWindowOnly,
192         mHwStretchingSupport.mFullScreenOnly, mHwStretchingSupport.mNone,
193         mHwStretchingSupport.mError);
194     mFeatureWrCompositor->Disable(FeatureStatus::Unavailable,
195                                   "No hardware stretching support", failureId);
196   }
197 
198   mFeatureWr->EnableByDefault();
199   mFeatureWrSoftware->EnableByDefault();
200   ConfigureWebRenderQualified();
201 
202   // envvar works everywhere; note that we need this for testing in CI.
203   // Prior to bug 1523788, the `prefEnabled` check was only done on Nightly,
204   // so as to prevent random users from easily enabling WebRender on
205   // unqualified hardware in beta/release.
206   if (mWrSoftwareForceEnabled) {
207     MOZ_ASSERT(mFeatureWrSoftware->IsEnabled());
208     mFeatureWr->UserDisable("User force-enabled software WR",
209                             "FEATURE_FAILURE_USER_FORCE_ENABLED_SW_WR"_ns);
210   } else if (mWrEnvForceEnabled) {
211     mFeatureWr->UserForceEnable("Force enabled by envvar");
212   } else if (mWrForceEnabled) {
213     mFeatureWr->UserForceEnable("Force enabled by pref");
214   }
215 
216   if (!mFeatureWrQualified->IsEnabled()) {
217     // No qualified hardware. If we haven't allowed software fallback,
218     // then we need to disable WR.
219     mFeatureWr->Disable(FeatureStatus::Disabled, "Not qualified",
220                         "FEATURE_FAILURE_NOT_QUALIFIED"_ns);
221   }
222 
223   // HW_COMPOSITING being disabled implies interfacing with the GPU might break
224   if (!mFeatureHwCompositing->IsEnabled()) {
225     mFeatureWr->ForceDisable(FeatureStatus::UnavailableNoHwCompositing,
226                              "Hardware compositing is disabled",
227                              "FEATURE_FAILURE_WEBRENDER_NEED_HWCOMP"_ns);
228   }
229 
230   if (mSafeMode) {
231     mFeatureWr->ForceDisable(FeatureStatus::UnavailableInSafeMode,
232                              "Safe-mode is enabled",
233                              "FEATURE_FAILURE_SAFE_MODE"_ns);
234   }
235 
236   mFeatureWrAngle->EnableByDefault();
237   if (mFeatureD3D11HwAngle) {
238     if (mWrForceAngle) {
239       if (!mFeatureD3D11HwAngle->IsEnabled()) {
240         mFeatureWrAngle->ForceDisable(FeatureStatus::UnavailableNoAngle,
241                                       "ANGLE is disabled",
242                                       mFeatureD3D11HwAngle->GetFailureId());
243       } else if (!mFeatureGPUProcess->IsEnabled() &&
244                  !mWrForceAngleNoGPUProcess) {
245         // WebRender with ANGLE relies on the GPU process when on Windows
246         mFeatureWrAngle->ForceDisable(
247             FeatureStatus::UnavailableNoGpuProcess, "GPU Process is disabled",
248             "FEATURE_FAILURE_GPU_PROCESS_DISABLED"_ns);
249       } else if (!mFeatureWr->IsEnabled() && !mFeatureWrSoftware->IsEnabled()) {
250         mFeatureWrAngle->ForceDisable(FeatureStatus::Unavailable,
251                                       "WebRender disabled",
252                                       "FEATURE_FAILURE_WR_DISABLED"_ns);
253       }
254     } else {
255       mFeatureWrAngle->Disable(FeatureStatus::Disabled, "ANGLE is not forced",
256                                "FEATURE_FAILURE_ANGLE_NOT_FORCED"_ns);
257     }
258   } else {
259     mFeatureWrAngle->Disable(FeatureStatus::Unavailable, "OS not supported",
260                              "FEATURE_FAILURE_OS_NOT_SUPPORTED"_ns);
261   }
262 
263   if (mWrForceAngle && mFeatureWr->IsEnabled() &&
264       !mFeatureWrAngle->IsEnabled()) {
265     // Ensure we disable WebRender if ANGLE is unavailable and it is required.
266     mFeatureWr->ForceDisable(FeatureStatus::UnavailableNoAngle,
267                              "ANGLE is disabled",
268                              mFeatureWrAngle->GetFailureId());
269   }
270 
271   if (!mFeatureWr->IsEnabled() && mDisableHwCompositingNoWr) {
272     if (mFeatureHwCompositing->IsEnabled()) {
273       // Hardware compositing should be disabled by default if we aren't using
274       // WebRender. We had to check if it is enabled at all, because it may
275       // already have been forced disabled (e.g. safe mode, headless). It may
276       // still be forced on by the user, and if so, this should have no effect.
277       mFeatureHwCompositing->Disable(FeatureStatus::Blocked,
278                                      "Acceleration blocked by platform", ""_ns);
279     }
280 
281     if (!mFeatureHwCompositing->IsEnabled() &&
282         mFeatureGPUProcess->IsEnabled() && !mGPUProcessAllowSoftware) {
283       // We have neither WebRender nor OpenGL, we don't allow the GPU process
284       // for basic compositor, and it wasn't disabled already.
285       mFeatureGPUProcess->Disable(FeatureStatus::Unavailable,
286                                   "Hardware compositing is unavailable.",
287                                   ""_ns);
288     }
289   }
290 
291   mFeatureWrDComp->EnableByDefault();
292   if (!mWrDCompWinEnabled) {
293     mFeatureWrDComp->UserDisable("User disabled via pref",
294                                  "FEATURE_FAILURE_DCOMP_PREF_DISABLED"_ns);
295   }
296 
297   if (!mIsWin10OrLater) {
298     // XXX relax win version to windows 8.
299     mFeatureWrDComp->Disable(FeatureStatus::Unavailable,
300                              "Requires Windows 10 or later",
301                              "FEATURE_FAILURE_DCOMP_NOT_WIN10"_ns);
302   }
303 
304   if (!mIsNightly) {
305     // Disable DirectComposition for NVIDIA users with high/mixed refresh rate
306     // monitors due to rendering artifacts.
307     nsAutoString adapterVendorID;
308     mGfxInfo->GetAdapterVendorID(adapterVendorID);
309     if (adapterVendorID == u"0x10de") {
310       bool mixed = false;
311       int32_t maxRefreshRate = mGfxInfo->GetMaxRefreshRate(&mixed);
312       if (maxRefreshRate > 60 && mixed) {
313         mFeatureWrDComp->Disable(FeatureStatus::Blocked,
314                                  "Monitor refresh rate too high/mixed",
315                                  "NVIDIA_REFRESH_RATE_MIXED"_ns);
316       }
317     }
318   }
319 
320   mFeatureWrDComp->MaybeSetFailed(
321       mFeatureWr->IsEnabled(), FeatureStatus::Unavailable, "Requires WebRender",
322       "FEATURE_FAILURE_DCOMP_NOT_WR"_ns);
323   mFeatureWrDComp->MaybeSetFailed(mFeatureWrAngle->IsEnabled(),
324                                   FeatureStatus::Unavailable, "Requires ANGLE",
325                                   "FEATURE_FAILURE_DCOMP_NOT_ANGLE"_ns);
326 
327   if (!mFeatureWrDComp->IsEnabled() && mWrCompositorDCompRequired) {
328     mFeatureWrCompositor->ForceDisable(FeatureStatus::Unavailable,
329                                        "No DirectComposition usage",
330                                        mFeatureWrDComp->GetFailureId());
331   }
332 
333   // Initialize WebRender partial present config.
334   // Partial present is used only when WebRender compositor is not used.
335   mFeatureWrPartial->SetDefault(mWrPartialPresent, FeatureStatus::Disabled,
336                                 "User disabled via pref");
337   if (mWrForcePartialPresent) {
338     mFeatureWrPartial->UserForceEnable("Force enabled by pref");
339   }
340 
341   ConfigureFromBlocklist(nsIGfxInfo::FEATURE_WEBRENDER_PARTIAL_PRESENT,
342                          mFeatureWrPartial);
343 
344   mFeatureWrShaderCache->SetDefaultFromPref(
345       StaticPrefs::GetPrefName_gfx_webrender_program_binary_disk(), true,
346       StaticPrefs::GetPrefDefault_gfx_webrender_program_binary_disk(),
347       mWrShaderCache);
348   ConfigureFromBlocklist(nsIGfxInfo::FEATURE_WEBRENDER_SHADER_CACHE,
349                          mFeatureWrShaderCache);
350   if (!mFeatureWr->IsEnabled()) {
351     mFeatureWrShaderCache->ForceDisable(FeatureStatus::Unavailable,
352                                         "WebRender disabled",
353                                         "FEATURE_FAILURE_WR_DISABLED"_ns);
354   }
355 
356   mFeatureWrOptimizedShaders->EnableByDefault();
357   if (!mWrOptimizedShaders) {
358     mFeatureWrOptimizedShaders->UserDisable("User disabled via pref",
359                                             "FEATURE_FAILURE_PREF_DISABLED"_ns);
360   }
361   ConfigureFromBlocklist(nsIGfxInfo::FEATURE_WEBRENDER_OPTIMIZED_SHADERS,
362                          mFeatureWrOptimizedShaders);
363   if (!mFeatureWr->IsEnabled()) {
364     mFeatureWrOptimizedShaders->ForceDisable(FeatureStatus::Unavailable,
365                                              "WebRender disabled",
366                                              "FEATURE_FAILURE_WR_DISABLED"_ns);
367   }
368 }
369 
370 }  // namespace gfx
371 }  // namespace mozilla
372