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   mWrForceDisabled = StaticPrefs::gfx_webrender_force_disabled_AtStartup();
34   mWrSoftwareForceEnabled = StaticPrefs::gfx_webrender_software_AtStartup();
35   mWrCompositorForceEnabled =
36       StaticPrefs::gfx_webrender_compositor_force_enabled_AtStartup();
37   mGPUProcessAllowSoftware =
38       StaticPrefs::layers_gpu_process_allow_software_AtStartup();
39   mWrPartialPresent =
40       StaticPrefs::gfx_webrender_max_partial_present_rects_AtStartup() > 0;
41   EmplaceUserPref(StaticPrefs::GetPrefName_gfx_webrender_program_binary_disk(),
42                   mWrShaderCache);
43   mWrOptimizedShaders =
44       StaticPrefs::gfx_webrender_use_optimized_shaders_AtStartup();
45 #ifdef XP_WIN
46   mWrForceAngle = StaticPrefs::gfx_webrender_force_angle_AtStartup();
47   mWrForceAngleNoGPUProcess = StaticPrefs::
48       gfx_webrender_enabled_no_gpu_process_with_angle_win_AtStartup();
49   mWrDCompWinEnabled =
50       Preferences::GetBool("gfx.webrender.dcomp-win.enabled", false);
51 #endif
52 
53   mWrEnvForceEnabled = gfxPlatform::WebRenderEnvvarEnabled();
54   mWrEnvForceDisabled = gfxPlatform::WebRenderEnvvarDisabled();
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   mXRenderEnabled = mozilla::Preferences::GetBool("gfx.xrender.enabled");
68 #endif
69 
70 #ifdef NIGHTLY_BUILD
71   mIsNightly = true;
72 #endif
73 #ifdef EARLY_BETA_OR_EARLIER
74   mIsEarlyBetaOrEarlier = true;
75 #endif
76   mSafeMode = gfxPlatform::InSafeMode();
77 
78   mGfxInfo = components::GfxInfo::Service();
79 
80   mFeatureWr = &gfxConfig::GetFeature(Feature::WEBRENDER);
81   mFeatureWrQualified = &gfxConfig::GetFeature(Feature::WEBRENDER_QUALIFIED);
82   mFeatureWrCompositor = &gfxConfig::GetFeature(Feature::WEBRENDER_COMPOSITOR);
83   mFeatureWrAngle = &gfxConfig::GetFeature(Feature::WEBRENDER_ANGLE);
84   mFeatureWrDComp = &gfxConfig::GetFeature(Feature::WEBRENDER_DCOMP_PRESENT);
85   mFeatureWrPartial = &gfxConfig::GetFeature(Feature::WEBRENDER_PARTIAL);
86   mFeatureWrShaderCache =
87       &gfxConfig::GetFeature(Feature::WEBRENDER_SHADER_CACHE);
88   mFeatureWrOptimizedShaders =
89       &gfxConfig::GetFeature(Feature::WEBRENDER_OPTIMIZED_SHADERS);
90   mFeatureWrSoftware = &gfxConfig::GetFeature(Feature::WEBRENDER_SOFTWARE);
91 
92   mFeatureHwCompositing = &gfxConfig::GetFeature(Feature::HW_COMPOSITING);
93 #ifdef XP_WIN
94   mFeatureD3D11HwAngle = &gfxConfig::GetFeature(Feature::D3D11_HW_ANGLE);
95   mFeatureD3D11Compositing = &gfxConfig::GetFeature(Feature::D3D11_COMPOSITING);
96 #endif
97   mFeatureGPUProcess = &gfxConfig::GetFeature(Feature::GPU_PROCESS);
98 }
99 
EmplaceUserPref(const char * aPrefName,Maybe<bool> & aValue)100 void gfxConfigManager::EmplaceUserPref(const char* aPrefName,
101                                        Maybe<bool>& aValue) {
102   if (Preferences::HasUserValue(aPrefName)) {
103     aValue.emplace(Preferences::GetBool(aPrefName, false));
104   }
105 }
106 
ConfigureFromBlocklist(long aFeature,FeatureState * aFeatureState)107 void gfxConfigManager::ConfigureFromBlocklist(long aFeature,
108                                               FeatureState* aFeatureState) {
109   MOZ_ASSERT(aFeatureState);
110 
111   nsCString blockId;
112   int32_t status;
113   if (!NS_SUCCEEDED(mGfxInfo->GetFeatureStatus(aFeature, blockId, &status))) {
114     aFeatureState->Disable(FeatureStatus::BlockedNoGfxInfo, "gfxInfo is broken",
115                            "FEATURE_FAILURE_NO_GFX_INFO"_ns);
116 
117   } else {
118     if (status != nsIGfxInfo::FEATURE_STATUS_OK) {
119       aFeatureState->Disable(FeatureStatus::Blocklisted,
120                              "Blocklisted by gfxInfo", blockId);
121     }
122   }
123 }
124 
ConfigureWebRenderSoftware()125 void gfxConfigManager::ConfigureWebRenderSoftware() {
126   MOZ_ASSERT(mFeatureWrSoftware);
127 
128   mFeatureWrSoftware->EnableByDefault();
129 
130   // Note that for testing in CI, software WebRender uses gfx.webrender.software
131   // to force enable WebRender Software. As a result, we need to prefer that
132   // over the MOZ_WEBRENDER envvar which is used to otherwise force on WebRender
133   // (hardware). See bug 1656811.
134   if (mWrSoftwareForceEnabled) {
135     mFeatureWrSoftware->UserForceEnable("Force enabled by pref");
136   } else if (mWrForceDisabled || mWrEnvForceDisabled) {
137     // If the user set the pref to force-disable, let's do that. This
138     // will override all the other enabling prefs
139     mFeatureWrSoftware->UserDisable("User force-disabled WR",
140                                     "FEATURE_FAILURE_USER_FORCE_DISABLED"_ns);
141   } else if (gfxPlatform::DoesFissionForceWebRender()) {
142     mFeatureWrSoftware->UserForceEnable("Force enabled by fission");
143   }
144 
145   if (!mHasWrSoftwareBlocklist) {
146     return;
147   }
148 
149   nsCString failureId;
150   int32_t status;
151   if (NS_FAILED(mGfxInfo->GetFeatureStatus(
152           nsIGfxInfo::FEATURE_WEBRENDER_SOFTWARE, failureId, &status))) {
153     mFeatureWrSoftware->Disable(FeatureStatus::BlockedNoGfxInfo,
154                                 "gfxInfo is broken",
155                                 "FEATURE_FAILURE_WR_NO_GFX_INFO"_ns);
156     return;
157   }
158 
159   switch (status) {
160     case nsIGfxInfo::FEATURE_ALLOW_ALWAYS:
161     case nsIGfxInfo::FEATURE_ALLOW_QUALIFIED:
162       break;
163     case nsIGfxInfo::FEATURE_DENIED:
164       mFeatureWrSoftware->Disable(FeatureStatus::Denied, "Not on allowlist",
165                                   failureId);
166       break;
167     default:
168       mFeatureWrSoftware->Disable(FeatureStatus::Blocklisted,
169                                   "No qualified hardware", failureId);
170       break;
171     case nsIGfxInfo::FEATURE_STATUS_OK:
172       MOZ_ASSERT_UNREACHABLE("We should still be rolling out WebRender!");
173       mFeatureWrSoftware->Disable(FeatureStatus::Blocked,
174                                   "Not controlled by rollout", failureId);
175       break;
176   }
177 }
178 
ConfigureWebRenderQualified()179 void gfxConfigManager::ConfigureWebRenderQualified() {
180   MOZ_ASSERT(mFeatureWrQualified);
181   MOZ_ASSERT(mFeatureWrCompositor);
182 
183   mFeatureWrQualified->EnableByDefault();
184 
185   nsCString failureId;
186   int32_t status;
187   if (NS_FAILED(mGfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_WEBRENDER,
188                                            failureId, &status))) {
189     mFeatureWrQualified->Disable(FeatureStatus::BlockedNoGfxInfo,
190                                  "gfxInfo is broken",
191                                  "FEATURE_FAILURE_WR_NO_GFX_INFO"_ns);
192     return;
193   }
194 
195   switch (status) {
196     case nsIGfxInfo::FEATURE_ALLOW_ALWAYS:
197     case nsIGfxInfo::FEATURE_ALLOW_QUALIFIED:
198       break;
199     case nsIGfxInfo::FEATURE_DENIED:
200       mFeatureWrQualified->Disable(FeatureStatus::Denied, "Not on allowlist",
201                                    failureId);
202       break;
203     default:
204       mFeatureWrQualified->Disable(FeatureStatus::Blocklisted,
205                                    "No qualified hardware", failureId);
206       break;
207     case nsIGfxInfo::FEATURE_STATUS_OK:
208       MOZ_ASSERT_UNREACHABLE("We should still be rolling out WebRender!");
209       mFeatureWrQualified->Disable(FeatureStatus::Blocked,
210                                    "Not controlled by rollout", failureId);
211       break;
212   }
213 }
214 
ConfigureWebRender()215 void gfxConfigManager::ConfigureWebRender() {
216   MOZ_ASSERT(XRE_IsParentProcess());
217   MOZ_ASSERT(mFeatureWr);
218   MOZ_ASSERT(mFeatureWrQualified);
219   MOZ_ASSERT(mFeatureWrCompositor);
220   MOZ_ASSERT(mFeatureWrAngle);
221   MOZ_ASSERT(mFeatureWrDComp);
222   MOZ_ASSERT(mFeatureWrPartial);
223   MOZ_ASSERT(mFeatureWrShaderCache);
224   MOZ_ASSERT(mFeatureWrOptimizedShaders);
225   MOZ_ASSERT(mFeatureWrSoftware);
226   MOZ_ASSERT(mFeatureHwCompositing);
227   MOZ_ASSERT(mFeatureGPUProcess);
228 
229   // Initialize WebRender native compositor usage
230   mFeatureWrCompositor->SetDefaultFromPref("gfx.webrender.compositor", true,
231                                            false, mWrCompositorEnabled);
232 
233   if (mWrCompositorForceEnabled) {
234     mFeatureWrCompositor->UserForceEnable("Force enabled by pref");
235   }
236 
237   ConfigureFromBlocklist(nsIGfxInfo::FEATURE_WEBRENDER_COMPOSITOR,
238                          mFeatureWrCompositor);
239 
240   // Disable native compositor when hardware stretching is not supported. It is
241   // for avoiding a problem like Bug 1618370.
242   // XXX Is there a better check for Bug 1618370?
243   if (!mHwStretchingSupport.IsFullySupported() && mScaledResolution) {
244     nsPrintfCString failureId(
245         "FEATURE_FAILURE_NO_HARDWARE_STRETCHING_B%uW%uF%uN%uE%u",
246         mHwStretchingSupport.mBoth, mHwStretchingSupport.mWindowOnly,
247         mHwStretchingSupport.mFullScreenOnly, mHwStretchingSupport.mNone,
248         mHwStretchingSupport.mError);
249     mFeatureWrCompositor->Disable(FeatureStatus::Unavailable,
250                                   "No hardware stretching support", failureId);
251   }
252 
253   ConfigureWebRenderSoftware();
254   ConfigureWebRenderQualified();
255 
256   mFeatureWr->EnableByDefault();
257 
258   // envvar works everywhere; note that we need this for testing in CI.
259   // Prior to bug 1523788, the `prefEnabled` check was only done on Nightly,
260   // so as to prevent random users from easily enabling WebRender on
261   // unqualified hardware in beta/release.
262   if (mWrSoftwareForceEnabled) {
263     MOZ_ASSERT(mFeatureWrSoftware->IsEnabled());
264     mFeatureWr->UserDisable("User force-enabled software WR",
265                             "FEATURE_FAILURE_USER_FORCE_ENABLED_SW_WR"_ns);
266   } else if (mWrEnvForceEnabled) {
267     mFeatureWr->UserForceEnable("Force enabled by envvar");
268   } else if (mWrForceDisabled || mWrEnvForceDisabled) {
269     // If the user set the pref to force-disable, let's do that. This
270     // will override all the other enabling prefs
271     // (gfx.webrender.enabled, gfx.webrender.all, and
272     // gfx.webrender.all.qualified).
273     mFeatureWr->UserDisable("User force-disabled WR",
274                             "FEATURE_FAILURE_USER_FORCE_DISABLED"_ns);
275   } else if (mWrForceEnabled) {
276     mFeatureWr->UserForceEnable("Force enabled by pref");
277   }
278 
279   if (!mFeatureWrQualified->IsEnabled()) {
280     // No qualified hardware. If we haven't allowed software fallback,
281     // then we need to disable WR.
282     mFeatureWr->Disable(FeatureStatus::Disabled, "Not qualified",
283                         "FEATURE_FAILURE_NOT_QUALIFIED"_ns);
284   }
285 
286   // HW_COMPOSITING being disabled implies interfacing with the GPU might break
287   if (!mFeatureHwCompositing->IsEnabled()) {
288     mFeatureWr->ForceDisable(FeatureStatus::UnavailableNoHwCompositing,
289                              "Hardware compositing is disabled",
290                              "FEATURE_FAILURE_WEBRENDER_NEED_HWCOMP"_ns);
291   }
292 
293   if (mSafeMode) {
294     mFeatureWr->ForceDisable(FeatureStatus::UnavailableInSafeMode,
295                              "Safe-mode is enabled",
296                              "FEATURE_FAILURE_SAFE_MODE"_ns);
297     mFeatureWrSoftware->ForceDisable(FeatureStatus::UnavailableInSafeMode,
298                                      "Safe-mode is enabled",
299                                      "FEATURE_FAILURE_SAFE_MODE"_ns);
300   }
301 
302   if (mXRenderEnabled) {
303     // XRender and WebRender don't play well together. XRender is disabled by
304     // default. If the user opts into it don't enable webrender.
305     mFeatureWr->ForceDisable(FeatureStatus::Blocked, "XRender is enabled",
306                              "FEATURE_FAILURE_XRENDER"_ns);
307     mFeatureWrSoftware->ForceDisable(FeatureStatus::Blocked,
308                                      "XRender is enabled",
309                                      "FEATURE_FAILURE_XRENDER"_ns);
310   }
311 
312   mFeatureWrAngle->EnableByDefault();
313   if (mFeatureD3D11HwAngle) {
314     if (mWrForceAngle) {
315       if (!mFeatureD3D11HwAngle->IsEnabled()) {
316         mFeatureWrAngle->ForceDisable(FeatureStatus::UnavailableNoAngle,
317                                       "ANGLE is disabled",
318                                       mFeatureD3D11HwAngle->GetFailureId());
319       } else if (!mFeatureGPUProcess->IsEnabled() &&
320                  !mWrForceAngleNoGPUProcess) {
321         // WebRender with ANGLE relies on the GPU process when on Windows
322         mFeatureWrAngle->ForceDisable(
323             FeatureStatus::UnavailableNoGpuProcess, "GPU Process is disabled",
324             "FEATURE_FAILURE_GPU_PROCESS_DISABLED"_ns);
325       } else if (!mFeatureWr->IsEnabled() && !mFeatureWrSoftware->IsEnabled()) {
326         mFeatureWrAngle->ForceDisable(FeatureStatus::Unavailable,
327                                       "WebRender disabled",
328                                       "FEATURE_FAILURE_WR_DISABLED"_ns);
329       }
330     } else {
331       mFeatureWrAngle->Disable(FeatureStatus::Disabled, "ANGLE is not forced",
332                                "FEATURE_FAILURE_ANGLE_NOT_FORCED"_ns);
333     }
334   } else {
335     mFeatureWrAngle->Disable(FeatureStatus::Unavailable, "OS not supported",
336                              "FEATURE_FAILURE_OS_NOT_SUPPORTED"_ns);
337   }
338 
339   if (mWrForceAngle && mFeatureWr->IsEnabled() &&
340       !mFeatureWrAngle->IsEnabled()) {
341     // Ensure we disable WebRender if ANGLE is unavailable and it is required.
342     mFeatureWr->ForceDisable(FeatureStatus::UnavailableNoAngle,
343                              "ANGLE is disabled",
344                              mFeatureWrAngle->GetFailureId());
345   }
346 
347   if (!mFeatureWr->IsEnabled() && mDisableHwCompositingNoWr) {
348     if (mFeatureHwCompositing->IsEnabled()) {
349       // Hardware compositing should be disabled by default if we aren't using
350       // WebRender. We had to check if it is enabled at all, because it may
351       // already have been forced disabled (e.g. safe mode, headless). It may
352       // still be forced on by the user, and if so, this should have no effect.
353       mFeatureHwCompositing->Disable(FeatureStatus::Blocked,
354                                      "Acceleration blocked by platform", ""_ns);
355     }
356 
357     if (!mFeatureHwCompositing->IsEnabled() &&
358         mFeatureGPUProcess->IsEnabled() && !mGPUProcessAllowSoftware) {
359       // We have neither WebRender nor OpenGL, we don't allow the GPU process
360       // for basic compositor, and it wasn't disabled already.
361       mFeatureGPUProcess->Disable(FeatureStatus::Unavailable,
362                                   "Hardware compositing is unavailable.",
363                                   ""_ns);
364     }
365   }
366 
367   mFeatureWrDComp->EnableByDefault();
368   if (!mWrDCompWinEnabled) {
369     mFeatureWrDComp->UserDisable("User disabled via pref",
370                                  "FEATURE_FAILURE_DCOMP_PREF_DISABLED"_ns);
371   }
372 
373   if (!mIsWin10OrLater) {
374     // XXX relax win version to windows 8.
375     mFeatureWrDComp->Disable(FeatureStatus::Unavailable,
376                              "Requires Windows 10 or later",
377                              "FEATURE_FAILURE_DCOMP_NOT_WIN10"_ns);
378   }
379 
380   if (!mIsNightly) {
381     // Disable DirectComposition for NVIDIA users with high/mixed refresh rate
382     // monitors due to rendering artifacts.
383     nsAutoString adapterVendorID;
384     mGfxInfo->GetAdapterVendorID(adapterVendorID);
385     if (adapterVendorID == u"0x10de") {
386       bool mixed = false;
387       int32_t maxRefreshRate = mGfxInfo->GetMaxRefreshRate(&mixed);
388       if (maxRefreshRate > 60 && mixed) {
389         mFeatureWrDComp->Disable(FeatureStatus::Blocked,
390                                  "Monitor refresh rate too high/mixed",
391                                  "NVIDIA_REFRESH_RATE_MIXED"_ns);
392       }
393     }
394   }
395 
396   mFeatureWrDComp->MaybeSetFailed(
397       mFeatureWr->IsEnabled(), FeatureStatus::Unavailable, "Requires WebRender",
398       "FEATURE_FAILURE_DCOMP_NOT_WR"_ns);
399   mFeatureWrDComp->MaybeSetFailed(mFeatureWrAngle->IsEnabled(),
400                                   FeatureStatus::Unavailable, "Requires ANGLE",
401                                   "FEATURE_FAILURE_DCOMP_NOT_ANGLE"_ns);
402 
403   if (!mFeatureWrDComp->IsEnabled() && mWrCompositorDCompRequired) {
404     mFeatureWrCompositor->ForceDisable(FeatureStatus::Unavailable,
405                                        "No DirectComposition usage",
406                                        mFeatureWrDComp->GetFailureId());
407   }
408 
409   // Initialize WebRender partial present config.
410   // Partial present is used only when WebRender compositor is not used.
411   if (mWrPartialPresent) {
412     if (mFeatureWr->IsEnabled() || mFeatureWrSoftware->IsEnabled()) {
413       mFeatureWrPartial->EnableByDefault();
414 
415       nsString adapter;
416       mGfxInfo->GetAdapterDeviceID(adapter);
417       // Block partial present on some devices due to rendering issues.
418       // On Mali-Txxx due to bug 1680087 and bug 1707815.
419       // On Adreno 3xx GPUs due to bug 1695771.
420       if (adapter.Find("Mali-T", /*ignoreCase*/ true) >= 0 ||
421           adapter.Find("Adreno (TM) 3", /*ignoreCase*/ true) >= 0) {
422         mFeatureWrPartial->Disable(
423             FeatureStatus::Blocked, "Partial present blocked",
424             "FEATURE_FAILURE_PARTIAL_PRESENT_BLOCKED"_ns);
425       }
426     }
427   }
428 
429   mFeatureWrShaderCache->SetDefaultFromPref(
430       StaticPrefs::GetPrefName_gfx_webrender_program_binary_disk(), true,
431       StaticPrefs::GetPrefDefault_gfx_webrender_program_binary_disk(),
432       mWrShaderCache);
433   ConfigureFromBlocklist(nsIGfxInfo::FEATURE_WEBRENDER_SHADER_CACHE,
434                          mFeatureWrShaderCache);
435   if (!mFeatureWr->IsEnabled()) {
436     mFeatureWrShaderCache->ForceDisable(FeatureStatus::Unavailable,
437                                         "WebRender disabled",
438                                         "FEATURE_FAILURE_WR_DISABLED"_ns);
439   }
440 
441   mFeatureWrOptimizedShaders->EnableByDefault();
442   if (!mWrOptimizedShaders) {
443     mFeatureWrOptimizedShaders->UserDisable("User disabled via pref",
444                                             "FEATURE_FAILURE_PREF_DISABLED"_ns);
445   }
446   ConfigureFromBlocklist(nsIGfxInfo::FEATURE_WEBRENDER_OPTIMIZED_SHADERS,
447                          mFeatureWrOptimizedShaders);
448   if (!mFeatureWr->IsEnabled()) {
449     mFeatureWrOptimizedShaders->ForceDisable(FeatureStatus::Unavailable,
450                                              "WebRender disabled",
451                                              "FEATURE_FAILURE_WR_DISABLED"_ns);
452   }
453 }
454 
455 }  // namespace gfx
456 }  // namespace mozilla
457