Bug 1273691 - Implement 'media.wmf.disable-d3d11-for-dlls' pref - r=cpearce,a=ritu
authorGerald Squelart <gsquelart@mozilla.com>
Wed, 18 May 2016 15:44:42 +1000
changeset 332948 fc50dd4bc0f948f1eba7c05fb05a15f9754e59cc
parent 332947 9ba18aae69e626a3f83560df321d91e42316083a
child 332949 602f7e05f13032a3523a715bc82e2ba0f2997196
push id6048
push userkmoir@mozilla.com
push dateMon, 06 Jun 2016 19:02:08 +0000
treeherdermozilla-beta@46d72a56c57d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerscpearce, ritu
bugs1273691, 1268905, 1269204, 1273406
milestone48.0a2
Bug 1273691 - Implement 'media.wmf.disable-d3d11-for-dlls' pref - r=cpearce,a=ritu Format: "dll1.dll: 1.2.3.4[, more versions...][; more dlls...]" I.e., DLLs are separated by semicolons, a DLL and all its versions are separated by a single colon, and versions are separated by commas. Whitespace between names&numbers is ignored. Seeding pref with blacklistings from bug 1268905, bug 1269204, and bug 1273406. MozReview-Commit-ID: 8auw4GPNyb9
dom/media/platforms/wmf/WMFVideoMFTManager.cpp
modules/libpref/init/all.js
--- a/dom/media/platforms/wmf/WMFVideoMFTManager.cpp
+++ b/dom/media/platforms/wmf/WMFVideoMFTManager.cpp
@@ -8,27 +8,30 @@
 #include "WMFVideoMFTManager.h"
 #include "MediaDecoderReader.h"
 #include "WMFUtils.h"
 #include "ImageContainer.h"
 #include "VideoUtils.h"
 #include "DXVA2Manager.h"
 #include "nsThreadUtils.h"
 #include "Layers.h"
+#include "mozilla/ClearOnShutdown.h"
 #include "mozilla/layers/LayersTypes.h"
 #include "MediaInfo.h"
 #include "mozilla/Logging.h"
+#include "mozilla/Preferences.h"
 #include "gfx2DGlue.h"
 #include "gfxWindowsPlatform.h"
 #include "IMFYCbCrImage.h"
 #include "mozilla/WindowsVersion.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Telemetry.h"
 #include "nsPrintfCString.h"
 #include "MediaTelemetryConstants.h"
+#include "GMPUtils.h" // For SplitAt. TODO: Move SplitAt to a central place.
 
 extern mozilla::LogModule* GetPDMLog();
 #define LOG(...) MOZ_LOG(GetPDMLog(), mozilla::LogLevel::Debug, (__VA_ARGS__))
 
 using mozilla::layers::Image;
 using mozilla::layers::IMFYCbCrImage;
 using mozilla::layers::LayerManager;
 using mozilla::layers::LayersBackend;
@@ -144,33 +147,161 @@ WMFVideoMFTManager::GetMediaSubtypeGUID(
   switch (mStreamType) {
     case H264: return MFVideoFormat_H264;
     case VP8: return MFVideoFormat_VP80;
     case VP9: return MFVideoFormat_VP90;
     default: return GUID_NULL;
   };
 }
 
+struct D3D11BlacklistingCache
+{
+  // D3D11-blacklist pref last seen.
+  nsCString mBlacklistPref;
+  // Non-empty if a D3D11-blacklisted DLL was found.
+  nsCString mBlacklistedDLL;
+};
+StaticAutoPtr<D3D11BlacklistingCache> sD3D11BlacklistingCache;
+
+// If a blacklisted DLL is found, return its information, otherwise "".
+static const nsACString&
+IsD3D11DLLBlacklisted()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Must be on main thread.");
+
+  if (!sD3D11BlacklistingCache) {
+    // First time here, create persistent data that will be reused in all
+    // D3D11-blacklisting checks.
+    sD3D11BlacklistingCache = new D3D11BlacklistingCache();
+    ClearOnShutdown(&sD3D11BlacklistingCache);
+  }
+
+  nsAdoptingCString blacklist =
+    Preferences::GetCString("media.wmf.disable-d3d11-for-dlls");
+  if (blacklist.IsEmpty()) {
+    // Empty blacklist -> No blacklisting.
+    sD3D11BlacklistingCache->mBlacklistPref.SetLength(0);
+    sD3D11BlacklistingCache->mBlacklistedDLL.SetLength(0);
+    return sD3D11BlacklistingCache->mBlacklistedDLL;
+  }
+
+  // Detect changes in pref.
+  if (sD3D11BlacklistingCache->mBlacklistPref.Equals(blacklist)) {
+
+    // Same blacklist -> Return same result (i.e., don't check DLLs again).
+    return sD3D11BlacklistingCache->mBlacklistedDLL;
+  }
+  // Adopt new pref now, so we don't work on it again.
+  sD3D11BlacklistingCache->mBlacklistPref = blacklist;
+
+  // media.wmf.disable-d3d11-for-dlls format: (whitespace is trimmed)
+  // "dll1.dll: 1.2.3.4[, more versions...][; more dlls...]"
+  nsTArray<nsCString> dlls;
+  SplitAt(";", blacklist, dlls);
+  for (const auto& dll : dlls) {
+    nsTArray<nsCString> nameAndVersions;
+    SplitAt(":", dll, nameAndVersions);
+    if (nameAndVersions.Length() != 2) {
+      NS_WARNING("Skipping incorrect 'media.wmf.disable-d3d11-for-dlls' dll:versions format");
+      continue;
+    }
+
+    nameAndVersions[0].CompressWhitespace();
+    NS_ConvertUTF8toUTF16 name(nameAndVersions[0]);
+    WCHAR systemPath[MAX_PATH + 1];
+    if (!ConstructSystem32Path(name.get(), systemPath, MAX_PATH + 1)) {
+      // Cannot build path -> Assume it's not the blacklisted DLL.
+      continue;
+    }
+
+    DWORD zero;
+    DWORD infoSize = GetFileVersionInfoSizeW(systemPath, &zero);
+    if (infoSize == 0) {
+      // Can't get file info -> Assume we don't have the blacklisted DLL.
+      continue;
+    }
+    // vInfo is a pointer into infoData, that's why we keep it outside of the loop.
+    auto infoData = MakeUnique<unsigned char[]>(infoSize);
+    VS_FIXEDFILEINFO *vInfo;
+    UINT vInfoLen;
+    if (!GetFileVersionInfoW(systemPath, 0, infoSize, infoData.get())
+        || !VerQueryValueW(infoData.get(), L"\\", (LPVOID*)&vInfo, &vInfoLen)
+        || !vInfo) {
+      // Can't find version -> Assume it's not blacklisted.
+      continue;
+    }
+
+    nsTArray<nsCString> versions;
+    SplitAt(",", nameAndVersions[1], versions);
+    for (const auto& version : versions) {
+      nsTArray<nsCString> numberStrings;
+      SplitAt(".", version, numberStrings);
+      if (numberStrings.Length() != 4) {
+        NS_WARNING("Skipping incorrect 'media.wmf.disable-d3d11-for-dlls' a.b.c.d version format");
+        continue;
+      }
+      DWORD numbers[4];
+      nsresult errorCode = NS_OK;
+      for (int i = 0; i < 4; ++i) {
+        numberStrings[i].CompressWhitespace();
+        numbers[i] = DWORD(numberStrings[i].ToInteger(&errorCode));
+        if (NS_FAILED(errorCode)) {
+          break;
+        }
+        if (numbers[i] > UINT16_MAX) {
+          errorCode = NS_ERROR_FAILURE;
+          break;
+        }
+      }
+
+      if (NS_FAILED(errorCode)) {
+        NS_WARNING("Skipping incorrect 'media.wmf.disable-d3d11-for-dlls' a.b.c.d version format");
+        continue;
+      }
+
+      if (vInfo->dwFileVersionMS == ((numbers[0] << 16) | numbers[1])
+          && vInfo->dwFileVersionLS == ((numbers[2] << 16) | numbers[3])) {
+        // Blacklisted! Record bad DLL.
+        sD3D11BlacklistingCache->mBlacklistedDLL.SetLength(0);
+        sD3D11BlacklistingCache->mBlacklistedDLL.AppendPrintf(
+          "%s (%lu.%lu.%lu.%lu)",
+          nameAndVersions[0].get(), numbers[0], numbers[1], numbers[2], numbers[3]);
+        return sD3D11BlacklistingCache->mBlacklistedDLL;
+      }
+    }
+  }
+
+  // No blacklisted DLL.
+  sD3D11BlacklistingCache->mBlacklistedDLL.SetLength(0);
+  return sD3D11BlacklistingCache->mBlacklistedDLL;
+}
+
 class CreateDXVAManagerEvent : public nsRunnable {
 public:
   CreateDXVAManagerEvent(LayersBackend aBackend, nsCString& aFailureReason)
     : mBackend(aBackend)
     , mFailureReason(aFailureReason)
   {}
 
   NS_IMETHOD Run() {
     NS_ASSERTION(NS_IsMainThread(), "Must be on main thread.");
     nsACString* failureReason = &mFailureReason;
     nsCString secondFailureReason;
     if (mBackend == LayersBackend::LAYERS_D3D11 &&
         Preferences::GetBool("media.windows-media-foundation.allow-d3d11-dxva", true) &&
         IsWin8OrLater()) {
-      mDXVA2Manager = DXVA2Manager::CreateD3D11DXVA(*failureReason);
-      if (mDXVA2Manager) {
-        return NS_OK;
+      const nsACString& blacklistedDLL = IsD3D11DLLBlacklisted();
+      if (!blacklistedDLL.IsEmpty()) {
+        failureReason->AppendPrintf("D3D11 blacklisted with DLL %s",
+                                    blacklistedDLL);
+      } else {
+        mDXVA2Manager = DXVA2Manager::CreateD3D11DXVA(*failureReason);
+        if (mDXVA2Manager) {
+          return NS_OK;
+        }
       }
       // Try again with d3d9, but record the failure reason
       // into a new var to avoid overwriting the d3d11 failure.
       failureReason = &secondFailureReason;
       mFailureReason.Append(NS_LITERAL_CSTRING("; "));
     }
     mDXVA2Manager = DXVA2Manager::CreateD3D9DXVA(*failureReason);
     // Make sure we include the messages from both attempts (if applicable).
@@ -196,17 +327,19 @@ WMFVideoMFTManager::InitializeDXVA(bool 
   if (mLayersBackend != LayersBackend::LAYERS_D3D9 &&
       mLayersBackend != LayersBackend::LAYERS_D3D11) {
     mDXVAFailureReason.AssignLiteral("Unsupported layers backend");
     return false;
   }
 
   // The DXVA manager must be created on the main thread.
   RefPtr<CreateDXVAManagerEvent> event =
-    new CreateDXVAManagerEvent(aForceD3D9 ? LayersBackend::LAYERS_D3D9 : mLayersBackend, mDXVAFailureReason);
+    new CreateDXVAManagerEvent(aForceD3D9 ? LayersBackend::LAYERS_D3D9
+                                          : mLayersBackend,
+                               mDXVAFailureReason);
 
   if (NS_IsMainThread()) {
     event->Run();
   } else {
     NS_DispatchToMainThread(event, NS_DISPATCH_SYNC);
   }
   mDXVA2Manager = event->mDXVA2Manager;
 
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -313,16 +313,17 @@ pref("media.mp4.enabled", true);
 // decoder works on all platforms.
 pref("media.use-blank-decoder", false);
 #ifdef MOZ_WMF
 pref("media.wmf.enabled", true);
 pref("media.wmf.decoder.thread-count", -1);
 pref("media.wmf.low-latency.enabled", false);
 pref("media.wmf.skip-blacklist", false);
 pref("media.windows-media-foundation.allow-d3d11-dxva", true);
+pref("media.wmf.disable-d3d11-for-dlls", "igd10umd32.dll: 9.17.10.2857; isonyvideoprocessor.dll: 4.1.2247.8090, 4.1.2153.6200; tosqep.dll: 1.2.15.526, 1.1.12.201, 1.0.11.318, 1.0.11.215; tosqep64.dll: 1.1.12.201, 1.0.11.215");
 #endif
 #if defined(MOZ_FFMPEG)
 #if defined(XP_MACOSX)
 pref("media.ffmpeg.enabled", false);
 #else
 pref("media.ffmpeg.enabled", true);
 #endif
 #endif