d3d: Use EnumAdapterByGpuPreference when available

Dawn currently uses EnumAdapters1 to enumerate IDXGIAdapters, and then
uses various properties of the adapter to infer the power preference.
This change uses the IDXGIFactory6::EnumAdapterByGpuPreference to
correctly return the most accurate power preference order, in
IDXGIFactory6 is available. EnumAdapters1 is used as a fallback. The
order of adapters returned by either API should take priority, so the
manual sorting is Dawn is bypassed for D3D backends.

Bug: 342299155

Change-Id: I5e27fbe6bb301cb393aafbc9468015818407044c
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/189641
Commit-Queue: Patrick To <patrto@microsoft.com>
Reviewed-by: Austin Eng <enga@chromium.org>
Reviewed-by: Rafael Cintron <rafael.cintron@microsoft.com>
diff --git a/src/dawn/native/Instance.cpp b/src/dawn/native/Instance.cpp
index efd7dfd..58d4fad 100644
--- a/src/dawn/native/Instance.cpp
+++ b/src/dawn/native/Instance.cpp
@@ -377,6 +377,14 @@
         adapters.push_back(
             CreateAdapter(physicalDevice, featureLevel, togglesDesc, unpacked->powerPreference));
     }
+
+    if (options->backendType == wgpu::BackendType::D3D11 ||
+        options->backendType == wgpu::BackendType::D3D12) {
+        // If a D3D backend was requested, the order of the adapters returned by DXGI should be
+        // preserved instead of sorting by whether they are integrated vs. discrete. DXGI
+        // returns the correct order based on system settings and configuration.
+        return adapters;
+    }
     return SortAdapters(std::move(adapters), options);
 }
 
diff --git a/src/dawn/native/d3d/BackendD3D.cpp b/src/dawn/native/d3d/BackendD3D.cpp
index 478d60f..9b323af 100644
--- a/src/dawn/native/d3d/BackendD3D.cpp
+++ b/src/dawn/native/d3d/BackendD3D.cpp
@@ -61,6 +61,17 @@
     return std::move(factory);
 }
 
+DXGI_GPU_PREFERENCE ToDXGIPowerPreference(wgpu::PowerPreference powerPreference) {
+    switch (powerPreference) {
+        case wgpu::PowerPreference::Undefined:
+            return DXGI_GPU_PREFERENCE_UNSPECIFIED;
+        case wgpu::PowerPreference::LowPower:
+            return DXGI_GPU_PREFERENCE_MINIMUM_POWER;
+        case wgpu::PowerPreference::HighPerformance:
+            return DXGI_GPU_PREFERENCE_HIGH_PERFORMANCE;
+    }
+}
+
 }  // anonymous namespace
 
 Backend::Backend(InstanceBase* instance, wgpu::BackendType type)
@@ -261,7 +272,7 @@
         return it->second;
     }
 
-    ComPtr<IDXGIAdapter1> dxgiAdapter = nullptr;
+    ComPtr<IDXGIAdapter1> dxgiAdapter;
     DAWN_TRY(CheckHRESULT(GetFactory()->EnumAdapterByLuid(luid, IID_PPV_ARGS(&dxgiAdapter)),
                           "EnumAdapterByLuid"));
 
@@ -308,11 +319,29 @@
         return {std::move(physicalDevice)};
     }
 
+    std::function<HRESULT(uint32_t, ComPtr<IDXGIAdapter1>&)> enumAdapters;
+    DXGI_GPU_PREFERENCE gpuPreference = ToDXGIPowerPreference(options->powerPreference);
+
+    ComPtr<IDXGIFactory6> factory6;
+    HRESULT hr = GetFactory()->QueryInterface(IID_PPV_ARGS(&factory6));
+    if (SUCCEEDED(hr)) {
+        // IDXGIFactory6 is not available on all versions of Windows 10. If it is available, use it
+        // to enumerate the adapters based on the desired power preference.
+        enumAdapters = [&](uint32_t adapterIndex, ComPtr<IDXGIAdapter1>& dxgiAdapter) -> HRESULT {
+            return factory6->EnumAdapterByGpuPreference(adapterIndex, gpuPreference,
+                                                        IID_PPV_ARGS(&dxgiAdapter));
+        };
+    } else {
+        enumAdapters = [&](uint32_t adapterIndex, ComPtr<IDXGIAdapter1>& dxgiAdapter) -> HRESULT {
+            return GetFactory()->EnumAdapters1(adapterIndex, &dxgiAdapter);
+        };
+    }
+
     // Enumerate and discover all available physicalDevices.
     std::vector<Ref<PhysicalDeviceBase>> physicalDevices;
     for (uint32_t adapterIndex = 0;; ++adapterIndex) {
-        ComPtr<IDXGIAdapter1> dxgiAdapter = nullptr;
-        if (GetFactory()->EnumAdapters1(adapterIndex, &dxgiAdapter) == DXGI_ERROR_NOT_FOUND) {
+        ComPtr<IDXGIAdapter1> dxgiAdapter;
+        if (enumAdapters(adapterIndex, dxgiAdapter) == DXGI_ERROR_NOT_FOUND) {
             break;  // No more physicalDevices to enumerate.
         }