Bug 844954 - Add support for DX9.3 feature level for Metro and cleanup CreateDevice code. r=bas
authorBrian R. Bondy <netzen@gmail.com>
Fri, 22 Mar 2013 15:32:56 -0400
changeset 137256 0ed97a9289b6f331be3658a30c2f315fe2d5f529
parent 137255 cf4cd763bc4c1ef40f7385096f34d511aea81bec
child 137257 cf75954e488fa602bf19de0b44a710066d0a5c5d
push id336
push userakeybl@mozilla.com
push dateMon, 17 Jun 2013 22:53:19 +0000
treeherdermozilla-release@574a39cdf657 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbas
bugs844954
milestone22.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 844954 - Add support for DX9.3 feature level for Metro and cleanup CreateDevice code. r=bas
gfx/thebes/gfxWindowsPlatform.cpp
gfx/thebes/gfxWindowsPlatform.h
--- a/gfx/thebes/gfxWindowsPlatform.cpp
+++ b/gfx/thebes/gfxWindowsPlatform.cpp
@@ -31,30 +31,27 @@
 #ifdef CAIRO_HAS_DWRITE_FONT
 #include "gfxDWriteFontList.h"
 #include "gfxDWriteFonts.h"
 #include "gfxDWriteCommon.h"
 #include <dwrite.h>
 #endif
 
 #include "gfxUserFontSet.h"
-#ifdef MOZ_METRO
 #include "nsWindowsHelpers.h"
-#endif
 
 #include <string>
 
 using namespace mozilla;
 using namespace mozilla::gfx;
 
 #ifdef CAIRO_HAS_D2D_SURFACE
 #include "gfxD2DSurface.h"
 
 #include <d3d10_1.h>
-#include <dxgi.h>
 
 #include "mozilla/gfx/2D.h"
 
 #include "nsMemory.h"
 #endif
 
 /*
  * Required headers are not available in the current consumer preview Win8
@@ -84,16 +81,22 @@ extern "C" {
 #include <d3dkmthk.h>
 }
 #endif
 
 using namespace mozilla;
 
 #ifdef CAIRO_HAS_D2D_SURFACE
 
+static const char *kFeatureLevelPref =
+  "gfx.direct3d.last_used_feature_level_idx";
+static const int kSupportedFeatureLevels[] =
+  { D3D10_FEATURE_LEVEL_10_1, D3D10_FEATURE_LEVEL_10_0,
+    D3D10_FEATURE_LEVEL_9_3 };
+
 NS_MEMORY_REPORTER_IMPLEMENT(
     D2DCache,
     "gfx-d2d-surfacecache",
     KIND_OTHER,
     UNITS_BYTES,
     cairo_d2d_get_image_surface_cache_usage,
     "Memory used by the Direct2D internal surface cache.")
 
@@ -499,179 +502,128 @@ gfxWindowsPlatform::UpdateRenderMode()
       canvasMask |= 1 << BACKEND_DIRECT2D;
       contentMask |= 1 << BACKEND_DIRECT2D;
     } else {
       canvasMask |= 1 << BACKEND_SKIA;
     }
     InitBackendPrefs(canvasMask, contentMask);
 }
 
+#ifdef CAIRO_HAS_D2D_SURFACE
+HRESULT
+gfxWindowsPlatform::CreateDevice(nsRefPtr<IDXGIAdapter1> &adapter1,
+                                 int featureLevelIndex)
+{
+  nsModuleHandle d3d10module(LoadLibrarySystem32(L"d3d10_1.dll"));
+  if (!d3d10module)
+    return E_FAIL;
+  D3D10CreateDevice1Func createD3DDevice =
+    (D3D10CreateDevice1Func)GetProcAddress(d3d10module, "D3D10CreateDevice1");
+  if (!createD3DDevice)
+    return E_FAIL;
+
+  nsRefPtr<ID3D10Device1> device;
+  HRESULT hr =
+    createD3DDevice(adapter1, D3D10_DRIVER_TYPE_HARDWARE, NULL,
+                    D3D10_CREATE_DEVICE_BGRA_SUPPORT |
+                    D3D10_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS,
+                    static_cast<D3D10_FEATURE_LEVEL1>(kSupportedFeatureLevels[featureLevelIndex]),
+                    D3D10_1_SDK_VERSION, getter_AddRefs(device));
+
+  // If we fail here, the DirectX version or video card probably
+  // changed.  We previously could use 10.1 but now we can't
+  // anymore.  Revert back to doing a 10.0 check first before
+  // the 10.1 check.
+  if (device) {
+    mD2DDevice = cairo_d2d_create_device_from_d3d10device(device);
+
+    // Setup a pref for future launch optimizaitons
+    Preferences::SetInt(kFeatureLevelPref, featureLevelIndex);
+  }
+
+  return device ? S_OK : hr;
+}
+#endif
+
 void
 gfxWindowsPlatform::VerifyD2DDevice(bool aAttemptForce)
 {
 #ifdef CAIRO_HAS_D2D_SURFACE
     if (mD2DDevice) {
         ID3D10Device1 *device = cairo_d2d_device_get_device(mD2DDevice);
 
         if (SUCCEEDED(device->GetDeviceRemovedReason())) {
             return;
         }
         mD2DDevice = nullptr;
     }
 
     mozilla::ScopedGfxFeatureReporter reporter("D2D", aAttemptForce);
 
-    HMODULE d3d10module = LoadLibraryA("d3d10_1.dll");
-    D3D10CreateDevice1Func createD3DDevice = (D3D10CreateDevice1Func)
-        GetProcAddress(d3d10module, "D3D10CreateDevice1");
     nsRefPtr<ID3D10Device1> device;
 
-    if (createD3DDevice) {
-        HMODULE dxgiModule = LoadLibraryA("dxgi.dll");
-        CreateDXGIFactory1Func createDXGIFactory1 = (CreateDXGIFactory1Func)
-            GetProcAddress(dxgiModule, "CreateDXGIFactory1");
+    nsModuleHandle dxgiModule(LoadLibrarySystem32(L"dxgi.dll"));
+    CreateDXGIFactory1Func createDXGIFactory1 = (CreateDXGIFactory1Func)
+        GetProcAddress(dxgiModule, "CreateDXGIFactory1");
 
-        // Try to use a DXGI 1.1 adapter in order to share resources
-        // across processes.
-        nsRefPtr<IDXGIAdapter1> adapter1;
-        if (createDXGIFactory1) {
-            nsRefPtr<IDXGIFactory1> factory1;
-            HRESULT hr = createDXGIFactory1(__uuidof(IDXGIFactory1),
-                                            getter_AddRefs(factory1));
-
-            if (FAILED(hr) || !factory1) {
-              // This seems to happen with some people running the iZ3D driver.
-              // They won't get acceleration.
-              return;
-            }
+    int supportedFeatureLevelsCount = ArrayLength(kSupportedFeatureLevels);
+    // If we're not running in Metro don't allow DX9.3
+    if (!IsRunningInWindowsMetro()) {
+      supportedFeatureLevelsCount--;
+    }
 
-            bool checkDX10 =
-              Preferences::GetBool("gfx.direct3d.checkDX10", true);
-            hr = factory1->EnumAdapters1(0, getter_AddRefs(adapter1));
-            if (SUCCEEDED(hr) && adapter1) {
-              // We have an adapter, check if we've ever found that
-              // createD3DDevice fails for both DX10 and DX10.1.
-              if (!checkDX10) {
-                // Even if the CheckInterfaceSupport call fails, the
-                // createD3DDevice call may still succeed.
-                // We only check it here to reset the pref which is used to skip
-                // the createD3DDevice calls.  This is done in case hardware
-                // changes.
-                hr = adapter1->CheckInterfaceSupport(__uuidof(ID3D10Device),
-                                                     nullptr);
-                if (SUCCEEDED(hr)) {
-                  checkDX10 = true;
-                  Preferences::SetBool("gfx.direct3d.checkDX10", true);
-                }
-              }
-            } else {
-              // We should return and not accelerate if we can't obtain
-              // an adapter.
-              return;
-            }
+    // It takes a lot of time (5-10% of startup time or ~100ms) to do both
+    // a createD3DDevice on D3D10_FEATURE_LEVEL_10_0.  We therefore store
+    // the last used feature level to go direct to that.
+    int featureLevelIndex = Preferences::GetInt(kFeatureLevelPref, 0);
+    if (featureLevelIndex >= supportedFeatureLevelsCount || featureLevelIndex < 0)
+      featureLevelIndex = 0;
 
-            // If we know that the DX10 calls have failed in the past, just
-            // bail out early.  This value will be reset if the adapter's
-            // CheckInterfaceSupport call ever succeeds with ID3D10Device
-            if (!checkDX10) {
-              return;
-            }
+    // Try to use a DXGI 1.1 adapter in order to share resources
+    // across processes.
+    nsRefPtr<IDXGIAdapter1> adapter1;
+    if (createDXGIFactory1) {
+        nsRefPtr<IDXGIFactory1> factory1;
+        HRESULT hr = createDXGIFactory1(__uuidof(IDXGIFactory1),
+                                        getter_AddRefs(factory1));
+
+        if (FAILED(hr) || !factory1) {
+          // This seems to happen with some people running the iZ3D driver.
+          // They won't get acceleration.
+          return;
         }
 
-        // It takes a lot of time (5-10% of startup time or ~100ms) to do both
-        // a createD3DDevice on D3D10_FEATURE_LEVEL_10_0 and 
-        // D3D10_FEATURE_LEVEL_10_1.  Therefore we set a pref if we ever get
-        // 10.1 to work and we use that first if the pref is set.
-        // Going direct to a 10.1 check only takes 20-30ms.
-        // The initialization of hr doesn't matter here because it will get
-        // overwritten whether or not the preference is set.
-        //   - If the preferD3D10_1 pref is set it gets overwritten immediately.
-        //   - If the preferD3D10_1 pref is not set, the if condition after
-        //     the one that follows us immediately will short circuit before 
-        //     checking FAILED(hr) and will again get overwritten immediately.
-        // We initialize it here just so it does not appear to be an
-        // uninitialized value.
-        HRESULT hr = E_FAIL;
-        bool preferD3D10_1 = 
-          Preferences::GetBool("gfx.direct3d.prefer_10_1", false);
-        if (preferD3D10_1) {
-            hr = createD3DDevice(
-                  adapter1, 
-                  D3D10_DRIVER_TYPE_HARDWARE,
-                  NULL,
-                  D3D10_CREATE_DEVICE_BGRA_SUPPORT |
-                  D3D10_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS,
-                  D3D10_FEATURE_LEVEL_10_1,
-                  D3D10_1_SDK_VERSION,
-                  getter_AddRefs(device));
-
-            // If we fail here, the DirectX version or video card probably
-            // changed.  We previously could use 10.1 but now we can't
-            // anymore.  Revert back to doing a 10.0 check first before
-            // the 10.1 check.
-            if (FAILED(hr)) {
-                Preferences::SetBool("gfx.direct3d.prefer_10_1", false);
-            } else {
-                mD2DDevice = cairo_d2d_create_device_from_d3d10device(device);
-            }
+        hr = factory1->EnumAdapters1(0, getter_AddRefs(adapter1));
+        if (FAILED(hr) || !adapter1) {
+          // We should return and not accelerate if we can't obtain
+          // an adapter.
+          return;
         }
+    }
 
-        if (!preferD3D10_1 || FAILED(hr)) {
-            // If preferD3D10_1 is set and 10.1 failed, fall back to 10.0.
-            // if preferD3D10_1 is not yet set, then first try to create
-            // a 10.0 D3D device, then try to see if 10.1 works.
-            nsRefPtr<ID3D10Device1> device1;
-            hr = createD3DDevice(
-                  adapter1, 
-                  D3D10_DRIVER_TYPE_HARDWARE,
-                  NULL,
-                  D3D10_CREATE_DEVICE_BGRA_SUPPORT |
-                  D3D10_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS,
-                  D3D10_FEATURE_LEVEL_10_0,
-                  D3D10_1_SDK_VERSION,
-                  getter_AddRefs(device1));
-
-            if (SUCCEEDED(hr)) {
-                device = device1;
-                if (preferD3D10_1) {
-                  mD2DDevice = 
-                    cairo_d2d_create_device_from_d3d10device(device);
-                }
-            }
-        }
+    // Start with the last used feature level, and move to lower DX versions
+    // until we find one that works.
+    HRESULT hr = E_FAIL;
+    for (int i = featureLevelIndex; i < supportedFeatureLevelsCount; i++) {
+      hr = CreateDevice(adapter1, i);
+      // If it succeeded we found the first available feature level
+      if (SUCCEEDED(hr))
+        break;
+    }
 
-        // If preferD3D10_1 is not yet set and 10.0 succeeded
-        if (!preferD3D10_1 && SUCCEEDED(hr)) {
-            // We have 10.0, let's try 10.1.  This second check will only
-            // ever be done once if it succeeds.  After that an option
-            // will be set to prefer using 10.1 before trying 10.0.
-            // In the case that 10.1 fails, it won't be a long operation
-            // like it is when 10.1 succeeds, so we don't need to optimize
-            // the case where 10.1 is not supported, but 10.0 is supported.
-            nsRefPtr<ID3D10Device1> device1;
-            hr = createD3DDevice(
-                  adapter1, 
-                  D3D10_DRIVER_TYPE_HARDWARE,
-                  NULL,
-                  D3D10_CREATE_DEVICE_BGRA_SUPPORT |
-                  D3D10_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS,
-                  D3D10_FEATURE_LEVEL_10_1,
-                  D3D10_1_SDK_VERSION,
-                  getter_AddRefs(device1));
-
-            if (SUCCEEDED(hr)) {
-                device = device1;
-                Preferences::SetBool("gfx.direct3d.prefer_10_1", true);
-            }
-            mD2DDevice = cairo_d2d_create_device_from_d3d10device(device);
+    // If we succeeded in creating a device, try for a newer device
+    // that we haven't tried yet.
+    if (SUCCEEDED(hr)) {
+      for (int i = featureLevelIndex - 1; i >= 0; i--) {
+        hr = CreateDevice(adapter1, i);
+        // If it failed then we don't have new hardware
+        if (FAILED(hr)) {
+          break;
         }
-
-        if (FAILED(hr) || !device) {
-          Preferences::SetBool("gfx.direct3d.checkDX10", false);
-        }
+      }
     }
 
     if (!mD2DDevice && aAttemptForce) {
         mD2DDevice = cairo_d2d_create_device();
     }
 
     if (mD2DDevice) {
         reporter.SetSuccessful();
--- a/gfx/thebes/gfxWindowsPlatform.h
+++ b/gfx/thebes/gfxWindowsPlatform.h
@@ -23,16 +23,20 @@
 #include "gfxContext.h"
 
 #include "nsTArray.h"
 #include "nsDataHashtable.h"
 
 #include <windows.h>
 #include <objbase.h>
 
+#ifdef CAIRO_HAS_D2D_SURFACE
+#include <dxgi.h>
+#endif
+
 class nsIMemoryMultiReporter;
 
 // Utility to get a Windows HDC from a thebes context,
 // used by both GDI and Uniscribe font shapers
 struct DCFromContext {
     DCFromContext(gfxContext *aContext) {
         dc = NULL;
         nsRefPtr<gfxASurface> aSurface = aContext->CurrentSurface();
@@ -142,16 +146,20 @@ public:
      * Verifies a D2D device is present and working, will attempt to create one
      * it is non-functional or non-existant.
      *
      * \param aAttemptForce Attempt to force D2D cairo device creation by using
      * cairo device creation routines.
      */
     void VerifyD2DDevice(bool aAttemptForce);
 
+#ifdef CAIRO_HAS_D2D_SURFACE
+    HRESULT CreateDevice(nsRefPtr<IDXGIAdapter1> &adapter1, int featureLevelIndex);
+#endif
+
     HDC GetScreenDC() { return mScreenDC; }
 
     nsresult GetFontList(nsIAtom *aLangGroup,
                          const nsACString& aGenericFamily,
                          nsTArray<nsString>& aListOfFonts);
 
     nsresult UpdateFontList();