Bug 969831 - Share code for checking minumum d3d feature level required for running metro and expose this information (including cached check results) via nsIWinMetroUtils. r=bbondy
authorJim Mathies <jmathies@mozilla.com>
Fri, 28 Feb 2014 07:37:30 -0600
changeset 171613 8d0fa796f925b578cc57328980eca732379e3998
parent 171541 e91388c7698cbb99369fd5fef7567cfd602a54d2
child 171614 a905fb8f85b0deff96833603ac8a4df31ae38c4a
push id270
push userpvanderbeken@mozilla.com
push dateThu, 06 Mar 2014 09:24:21 +0000
reviewersbbondy
bugs969831
milestone30.0a1
Bug 969831 - Share code for checking minumum d3d feature level required for running metro and expose this information (including cached check results) via nsIWinMetroUtils. r=bbondy
browser/metro/shell/commandexecutehandler/CEHHelper.cpp
browser/metro/shell/commandexecutehandler/CEHHelper.h
browser/metro/shell/commandexecutehandler/CommandExecuteHandler.cpp
widget/nsIWinMetroUtils.idl
widget/windows/winrt/MetroD3DCheckHelper.h
widget/windows/winrt/moz.build
widget/windows/winrt/nsWinMetroUtils.cpp
--- a/browser/metro/shell/commandexecutehandler/CEHHelper.cpp
+++ b/browser/metro/shell/commandexecutehandler/CEHHelper.cpp
@@ -1,15 +1,16 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* 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 "CEHHelper.h"
 #include <tlhelp32.h>
+#include "mozilla/widget/MetroD3DCheckHelper.h"
 
 #ifdef SHOW_CONSOLE
 #include <io.h> // _open_osfhandle
 #endif
 
 HANDLE sCon;
 LPCWSTR metroDX10Available = L"MetroD3DAvailable";
 LPCWSTR metroLastAHE = L"MetroLastAHE";
@@ -134,83 +135,19 @@ GetLastAHE()
 
 bool
 IsDX10Available()
 {
   DWORD isDX10Available;
   if (GetDWORDRegKey(metroDX10Available, isDX10Available)) {
     return isDX10Available;
   }
-
-  HMODULE dxgiModule = LoadLibraryA("dxgi.dll");
-  if (!dxgiModule) {
-    SetDWORDRegKey(metroDX10Available, 0);
-    return false;
-  }
-  decltype(CreateDXGIFactory1)* createDXGIFactory1 =
-    (decltype(CreateDXGIFactory1)*) GetProcAddress(dxgiModule, "CreateDXGIFactory1");
-  if (!createDXGIFactory1) {
-    SetDWORDRegKey(metroDX10Available, 0);
-    return false;
-  }
-
-  HMODULE d3d10module = LoadLibraryA("d3d10_1.dll");
-  if (!d3d10module) {
-    SetDWORDRegKey(metroDX10Available, 0);
-    return false;
-  }
-  decltype(D3D10CreateDevice1)* createD3DDevice =
-    (decltype(D3D10CreateDevice1)*) GetProcAddress(d3d10module,
-                                                   "D3D10CreateDevice1");
-  if (!createD3DDevice) {
-    SetDWORDRegKey(metroDX10Available, 0);
-    return false;
-  }
-
-  CComPtr<IDXGIFactory1> factory1;
-  if (FAILED(createDXGIFactory1(__uuidof(IDXGIFactory1), (void**)&factory1))) {
-    SetDWORDRegKey(metroDX10Available, 0);
-    return false;
-  }
-
-  CComPtr<IDXGIAdapter1> adapter1;
-  if (FAILED(factory1->EnumAdapters1(0, &adapter1))) {
-    SetDWORDRegKey(metroDX10Available, 0);
-    return false;
-  }
-
-  CComPtr<ID3D10Device1> device;
-  // Try for DX10.1
-  if (FAILED(createD3DDevice(adapter1, D3D10_DRIVER_TYPE_HARDWARE, nullptr,
-                             D3D10_CREATE_DEVICE_BGRA_SUPPORT |
-                             D3D10_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS,
-                             D3D10_FEATURE_LEVEL_10_1,
-                             D3D10_1_SDK_VERSION, &device))) {
-    // Try for DX10
-    if (FAILED(createD3DDevice(adapter1, D3D10_DRIVER_TYPE_HARDWARE, nullptr,
-                               D3D10_CREATE_DEVICE_BGRA_SUPPORT |
-                               D3D10_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS,
-                               D3D10_FEATURE_LEVEL_10_0,
-                               D3D10_1_SDK_VERSION, &device))) {
-      // Try for DX9.3 (we fall back to cairo and cairo has support for D3D 9.3)
-      if (FAILED(createD3DDevice(adapter1, D3D10_DRIVER_TYPE_HARDWARE, nullptr,
-                                 D3D10_CREATE_DEVICE_BGRA_SUPPORT |
-                                 D3D10_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS,
-                                 D3D10_FEATURE_LEVEL_9_3,
-                                 D3D10_1_SDK_VERSION, &device))) {
-
-        SetDWORDRegKey(metroDX10Available, 0);
-        return false;
-      }
-    }
-  }
-  
-
-  SetDWORDRegKey(metroDX10Available, 1);
-  return true;
+  bool check = D3DFeatureLevelCheck();
+  SetDWORDRegKey(metroDX10Available, check);
+  return check;
 }
 
 bool
 GetDWORDRegKey(LPCWSTR name, DWORD &value)
 {
   HKEY key;
   LONG result = RegOpenKeyExW(HKEY_CURRENT_USER, L"SOFTWARE\\Mozilla\\Firefox",
                               0, KEY_READ, &key);
--- a/browser/metro/shell/commandexecutehandler/CEHHelper.h
+++ b/browser/metro/shell/commandexecutehandler/CEHHelper.h
@@ -4,25 +4,21 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #undef WINVER
 #undef _WIN32_WINNT
 #define WINVER 0x602
 #define _WIN32_WINNT 0x602
 
 #include <windows.h>
-#include <d3d10_1.h>
-#include <dxgi.h>
-#include <d3d10misc.h>
 #include <atlbase.h>
 #include <shlobj.h>
 
 //#define SHOW_CONSOLE 1
 extern HANDLE sCon;
-extern LPCWSTR metroDX10Available;
 
 void Log(const wchar_t *fmt, ...);
 
 #if defined(SHOW_CONSOLE)
 void SetupConsole();
 #endif
 
 AHE_TYPE GetLastAHE();
--- a/browser/metro/shell/commandexecutehandler/CommandExecuteHandler.cpp
+++ b/browser/metro/shell/commandexecutehandler/CommandExecuteHandler.cpp
@@ -760,16 +760,21 @@ IFACEMETHODIMP CExecuteCommandVerb::Exec
   Log(L"Execute()");
 
   if (!mTarget.GetLength()) {
     // We shut down when this flips to true
     SetRequestMet();
     return E_FAIL;
   }
 
+  if (!IsDX10Available()) {
+    Log(L"Can't launch in metro due to missing hardware acceleration features.");
+    mRequestType = DESKTOP_RESTART;
+  } 
+
   // Deal with metro restart for an update - launch desktop with a command
   // that tells it to run updater then launch the metro browser.
   if (mRequestType == METRO_UPDATE) {
     // We'll complete this in the heart beat callback from the main msg loop.
     // We do this because the last browser instance makes this call to Execute
     // sync. So we want to make sure it's completely shutdown before we do
     // the update.
     mParameters = kMetroUpdateCmdLine;
--- a/widget/nsIWinMetroUtils.idl
+++ b/widget/nsIWinMetroUtils.idl
@@ -3,24 +3,30 @@
  * 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 "nsISupports.idl"
 
 /**
  * Integration with the "Metro"/"Modern" UI environment in Windows 8.
  *
- * Note: browser/metro/base/content/browser-scripts.js contains a stub
+ * Note: browser/metro/base/content/browser.js contains a stub
  * implementation of this interface for non-Windows systems, for testing and
  * development purposes only.
  */
-[scriptable, uuid(dde6eee6-ad11-475b-b7d7-bee8e46e5756)]
+[scriptable, uuid(319faae0-82ca-4c2f-8a24-2b2445e5a72a)]
 interface nsIWinMetroUtils : nsISupports
 {
   /**
+   * Determine if the current device has the hardware capabilities to run
+   * in metro mode.
+   */
+  readonly attribute boolean supported;
+
+  /**
    * Determine if the current browser is running in the metro immersive
    * environment.
    */
   readonly attribute boolean immersive;
 
   /**
    * Determine the activation URI
    */
new file mode 100644
--- /dev/null
+++ b/widget/windows/winrt/MetroD3DCheckHelper.h
@@ -0,0 +1,96 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+#pragma once
+
+/* this file is included by exe stubs, don't pull xpcom into it. */
+
+#include <d3d10_1.h>
+#include <dxgi.h>
+#include <d3d10misc.h>
+
+/*
+ * Checks to see if the d3d implementation supports feature level 9.3 or
+ * above. Metrofx can't run on systems that fail this check.
+ *
+ * Note, this can hit perf, don't call this unless you absolutely have to.
+ * Both the ceh and winrt widget code save a cached result in the registry.
+ */
+static bool D3DFeatureLevelCheck()
+{
+  HMODULE dxgiModule = LoadLibraryA("dxgi.dll");
+  if (!dxgiModule) {
+    return false;
+  }
+  decltype(CreateDXGIFactory1)* createDXGIFactory1 =
+    (decltype(CreateDXGIFactory1)*) GetProcAddress(dxgiModule, "CreateDXGIFactory1");
+  if (!createDXGIFactory1) {
+    FreeLibrary(dxgiModule);
+    return false;
+  }
+
+  HMODULE d3d10module = LoadLibraryA("d3d10_1.dll");
+  if (!d3d10module) {
+    FreeLibrary(dxgiModule);
+    return false;
+  }
+  decltype(D3D10CreateDevice1)* createD3DDevice =
+    (decltype(D3D10CreateDevice1)*) GetProcAddress(d3d10module,
+                                                   "D3D10CreateDevice1");
+  if (!createD3DDevice) {
+    FreeLibrary(d3d10module);
+    FreeLibrary(dxgiModule);
+    return false;
+  }
+
+  IDXGIFactory1* factory1;
+  if (FAILED(createDXGIFactory1(__uuidof(IDXGIFactory1), (void**)&factory1))) {
+    FreeLibrary(d3d10module);
+    FreeLibrary(dxgiModule);
+    return false;
+  }
+
+  IDXGIAdapter1* adapter1;
+  if (FAILED(factory1->EnumAdapters1(0, &adapter1))) {
+    factory1->Release();
+    FreeLibrary(d3d10module);
+    FreeLibrary(dxgiModule);
+    return false;
+  }
+
+  // Try for DX10.1
+  ID3D10Device1* device;
+  if (FAILED(createD3DDevice(adapter1, D3D10_DRIVER_TYPE_HARDWARE, nullptr,
+                             D3D10_CREATE_DEVICE_BGRA_SUPPORT |
+                             D3D10_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS,
+                             D3D10_FEATURE_LEVEL_10_1,
+                             D3D10_1_SDK_VERSION, &device))) {
+    // Try for DX10
+    if (FAILED(createD3DDevice(adapter1, D3D10_DRIVER_TYPE_HARDWARE, nullptr,
+                               D3D10_CREATE_DEVICE_BGRA_SUPPORT |
+                               D3D10_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS,
+                               D3D10_FEATURE_LEVEL_10_0,
+                               D3D10_1_SDK_VERSION, &device))) {
+      // Try for DX9.3 (we fall back to cairo and cairo has support for D3D 9.3)
+      if (FAILED(createD3DDevice(adapter1, D3D10_DRIVER_TYPE_HARDWARE, nullptr,
+                                 D3D10_CREATE_DEVICE_BGRA_SUPPORT |
+                                 D3D10_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS,
+                                 D3D10_FEATURE_LEVEL_9_3,
+                                 D3D10_1_SDK_VERSION, &device))) {
+        adapter1->Release();
+        factory1->Release();
+        FreeLibrary(d3d10module);
+        FreeLibrary(dxgiModule);
+        return false;
+      }
+    }
+  }
+  device->Release();
+  adapter1->Release();
+  factory1->Release();
+  FreeLibrary(d3d10module);
+  FreeLibrary(dxgiModule);
+  return true;
+}
--- a/widget/windows/winrt/moz.build
+++ b/widget/windows/winrt/moz.build
@@ -20,16 +20,20 @@ SOURCES += [
     'UIABridge.cpp',
 ]
 
 EXTRA_COMPONENTS += [
     'MetroUIUtils.js',
     'MetroUIUtils.manifest',
 ]
 
+EXPORTS.mozilla.widget += [
+    'MetroD3DCheckHelper.h',
+]
+
 include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'xul'
 
 LOCAL_INCLUDES += [
     '../',
     '../../shared',
     '../../xpwidgets',
--- a/widget/windows/winrt/nsWinMetroUtils.cpp
+++ b/widget/windows/winrt/nsWinMetroUtils.cpp
@@ -4,16 +4,20 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsWinMetroUtils.h"
 #include "MetroUtils.h"
 #include "nsXULAppAPI.h"
 #include "FrameworkView.h"
 #include "MetroApp.h"
 #include "ToastNotificationHandler.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/WindowsVersion.h"
+#include "nsIWindowsRegKey.h"
+#include "mozilla/widget/MetroD3DCheckHelper.h"
 
 #include <shldisp.h>
 #include <shellapi.h>
 #include <windows.ui.viewmanagement.h>
 #include <windows.ui.startscreen.h>
 
 using namespace ABI::Windows::Foundation;
 using namespace ABI::Windows::UI::StartScreen;
@@ -336,10 +340,63 @@ nsWinMetroUtils::SetUpdatePending(bool a
 
 NS_IMETHODIMP
 nsWinMetroUtils::GetForeground(bool* aForeground)
 {
   *aForeground = (::GetActiveWindow() == ::GetForegroundWindow());
   return NS_OK;
 }
 
+NS_IMETHODIMP
+nsWinMetroUtils::GetSupported(bool *aSupported)
+{
+  *aSupported = false;
+  if (!IsWin8OrLater()) {
+    return NS_OK;
+  }
+
+  // if last_used_feature_level_idx is set, we've previously created a
+  // d3d device that's compatible. See gfxEindowsPlatform for details.
+  if (Preferences::GetInt("gfx.direct3d.last_used_feature_level_idx", -1) != -1) {
+    *aSupported = true;
+    return NS_OK;
+  }
+
+  // if last_used_feature_level_idx isn't set, gfx hasn't attempted to create
+  // a device yet. This could be a case where d2d is pref'd off or blacklisted
+  // on desktop, or we tried to create a device and failed. This could also be
+  // a first run case where we haven't created an accelerated top level window
+  // yet.
+
+  NS_NAMED_LITERAL_STRING(metroRegValueName, "MetroD3DAvailable");
+  NS_NAMED_LITERAL_STRING(metroRegValuePath, "Software\\Mozilla\\Firefox");
+
+  // Check to see if the ceh launched us, it also does this check and caches
+  // a flag in the registry.
+  nsresult rv;
+  uint32_t value = 0;
+  nsCOMPtr<nsIWindowsRegKey> regKey =
+    do_CreateInstance("@mozilla.org/windows-registry-key;1", &rv);
+  if (NS_SUCCEEDED(rv)) {
+    rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER,
+                      metroRegValuePath,
+                      nsIWindowsRegKey::ACCESS_WRITE);
+    if (NS_SUCCEEDED(rv)) {
+      rv = regKey->ReadIntValue(metroRegValueName, &value);
+      if (NS_SUCCEEDED(rv)) {
+        *aSupported = (bool)value;
+        return NS_OK;
+      }
+
+      // If all else fails, do the check here. This call is costly but
+      // we shouldn't hit this except in rare situations where the
+      // ceh never launched the browser that's running. 
+      value = D3DFeatureLevelCheck();
+      regKey->WriteIntValue(metroRegValueName, value);
+      *aSupported = (bool)value;
+      return NS_OK;
+    }
+  }
+  return NS_OK;
+}
+
 } // widget
 } // mozilla