Allow specifying GPU adapter LUID for WGC capturer

Add d3d_device_luid option to DesktopCaptureOptions so that the WGC
capturer can create its D3D11 device on a specific GPU adapter. This
is needed for texture sharing with the GPU process when the system has
multiple adapters.

Bug: chromium:40929600
Change-Id: Ib04e5eb88a0c57f7cf13e984c6f3a96296928d25
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/461040
Reviewed-by: Harald Alvestrand <hta@webrtc.org>
Commit-Queue: Wang, Zhibo1 <zhibo1.wang@intel.com>
Reviewed-by: Ilya Nikolaevskiy <ilnik@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#47340}
diff --git a/modules/desktop_capture/desktop_capture_options.h b/modules/desktop_capture/desktop_capture_options.h
index 3ce4678..9cba039 100644
--- a/modules/desktop_capture/desktop_capture_options.h
+++ b/modules/desktop_capture/desktop_capture_options.h
@@ -27,6 +27,10 @@
 #include "modules/desktop_capture/mac/desktop_configuration_monitor.h"
 #endif
 
+#if defined(WEBRTC_WIN)
+#include <windows.h>
+#endif
+
 #include "modules/desktop_capture/full_screen_window_detector.h"
 
 namespace webrtc {
@@ -234,6 +238,11 @@
   void set_allow_wgc_using_texture(bool allow) {
     allow_wgc_using_texture_ = allow;
   }
+
+  // The LUID of the GPU adapter to use for D3D11 device creation in the WGC
+  // capturer. A zero LUID means use the system default adapter.
+  LUID d3d_device_luid() const { return d3d_device_luid_; }
+  void set_d3d_device_luid(LUID luid) { d3d_device_luid_ = luid; }
 #endif  // defined(RTC_ENABLE_WIN_WGC)
 #endif  // defined(WEBRTC_WIN)
 
@@ -293,6 +302,7 @@
   bool wgc_require_border_ = false;
   bool wgc_include_secondary_windows_ = false;
   bool allow_wgc_using_texture_ = false;
+  LUID d3d_device_luid_ = {};
 #endif
 #endif
 #if defined(WEBRTC_USE_X11)
diff --git a/modules/desktop_capture/win/wgc_capturer_win.cc b/modules/desktop_capture/win/wgc_capturer_win.cc
index cc9ea52..bdbcda6 100644
--- a/modules/desktop_capture/win/wgc_capturer_win.cc
+++ b/modules/desktop_capture/win/wgc_capturer_win.cc
@@ -11,6 +11,7 @@
 #include "modules/desktop_capture/win/wgc_capturer_win.h"
 
 #include <DispatcherQueue.h>
+#include <dxgi1_4.h>
 #include <windows.foundation.metadata.h>
 #include <windows.graphics.capture.h>
 
@@ -284,11 +285,28 @@
 
   callback_ = callback;
 
-  // Create a Direct3D11 device to share amongst the WgcCaptureSessions. Many
-  // parameters are nullptr as the implemention uses defaults that work well for
-  // us.
-  HRESULT hr = D3D11CreateDevice(
-      /*adapter=*/nullptr, D3D_DRIVER_TYPE_HARDWARE,
+  // Create a Direct3D11 device to share amongst the WgcCaptureSessions.
+  // When a specific GPU adapter LUID is provided (e.g. for texture sharing with
+  // the GPU process), create the device on that adapter. Otherwise, fall back
+  // to the system default adapter.
+  HRESULT hr;
+  ComPtr<IDXGIAdapter> adapter;
+  const LUID luid = options_.d3d_device_luid();
+  if (luid.LowPart != 0 || luid.HighPart != 0) {
+    ComPtr<IDXGIFactory4> factory4;
+    hr = CreateDXGIFactory1(IID_PPV_ARGS(&factory4));
+    if (SUCCEEDED(hr)) {
+      hr = factory4->EnumAdapterByLuid(luid, IID_PPV_ARGS(&adapter));
+    }
+    if (FAILED(hr)) {
+      RTC_LOG(LS_WARNING) << "Failed to get adapter by LUID, "
+                          << "falling back to default adapter: " << hr;
+    }
+  }
+
+  hr = D3D11CreateDevice(
+      adapter.Get(),
+      adapter ? D3D_DRIVER_TYPE_UNKNOWN : D3D_DRIVER_TYPE_HARDWARE,
       /*software_rasterizer=*/nullptr, D3D11_CREATE_DEVICE_BGRA_SUPPORT,
       /*feature_levels=*/nullptr, /*feature_levels_size=*/0, D3D11_SDK_VERSION,
       &d3d11_device_, /*feature_level=*/nullptr, /*device_context=*/nullptr);
diff --git a/modules/desktop_capture/win/wgc_capturer_win_unittest.cc b/modules/desktop_capture/win/wgc_capturer_win_unittest.cc
index 8119c70..2048eb2 100644
--- a/modules/desktop_capture/win/wgc_capturer_win_unittest.cc
+++ b/modules/desktop_capture/win/wgc_capturer_win_unittest.cc
@@ -10,6 +10,9 @@
 
 #include "modules/desktop_capture/win/wgc_capturer_win.h"
 
+#include <dxgi.h>
+#include <wrl/client.h>
+
 #include <algorithm>
 #include <cstddef>
 #include <cstdint>
@@ -100,6 +103,22 @@
 
 }  // namespace
 
+class LogMessageMatcher : public LogSink {
+ public:
+  explicit LogMessageMatcher(const std::string& substring)
+      : substring_(substring) {}
+  bool matched() const { return matched_; }
+
+ private:
+  void OnLogMessage(const std::string& message) override {
+    if (message.find(substring_) != std::string::npos) {
+      matched_ = true;
+    }
+  }
+  std::string substring_;
+  bool matched_ = false;
+};
+
 class WgcCapturerWinTest : public ::testing::TestWithParam<CaptureType>,
                            public DesktopCapturer::Callback {
  public:
@@ -124,9 +143,9 @@
     source_id_ = GetTestWindowIdFromSourceList();
   }
 
-  void SetUpForScreenCapture() {
-    capturer_ = WgcCapturerWin::CreateRawScreenCapturer(
-        DesktopCaptureOptions::CreateDefault());
+  void SetUpForScreenCapture(const DesktopCaptureOptions& options =
+                                 DesktopCaptureOptions::CreateDefault()) {
+    capturer_ = WgcCapturerWin::CreateRawScreenCapturer(options);
     source_id_ = GetScreenIdFromSourceList();
   }
 
@@ -419,6 +438,56 @@
   EXPECT_GT(frame_->size().height(), 0);
 }
 
+TEST_F(WgcCapturerMonitorTest, CaptureWithValidAdapterLuid) {
+  // Get the LUID of the first DXGI adapter on the system.
+  Microsoft::WRL::ComPtr<IDXGIFactory1> factory;
+  ASSERT_TRUE(SUCCEEDED(CreateDXGIFactory1(IID_PPV_ARGS(&factory))));
+  Microsoft::WRL::ComPtr<IDXGIAdapter> adapter;
+  ASSERT_TRUE(SUCCEEDED(factory->EnumAdapters(0, &adapter)));
+  DXGI_ADAPTER_DESC desc;
+  ASSERT_TRUE(SUCCEEDED(adapter->GetDesc(&desc)));
+
+  LogMessageMatcher fallback_matcher("Failed to get adapter by LUID");
+  LogMessage::AddLogToStream(&fallback_matcher, LS_WARNING);
+
+  DesktopCaptureOptions options = DesktopCaptureOptions::CreateDefault();
+  options.set_d3d_device_luid(desc.AdapterLuid);
+  SetUpForScreenCapture(options);
+  EXPECT_TRUE(capturer_->SelectSource(kFullDesktopScreenId));
+
+  capturer_->Start(this);
+
+  LogMessage::RemoveLogToStream(&fallback_matcher);
+  EXPECT_FALSE(fallback_matcher.matched());
+
+  DoCapture();
+  EXPECT_GT(frame_->size().width(), 0);
+  EXPECT_GT(frame_->size().height(), 0);
+}
+
+TEST_F(WgcCapturerMonitorTest, CaptureWithInvalidAdapterLuid) {
+  // Use a non-zero but invalid LUID. The capturer should fall back to the
+  // default adapter and still capture successfully.
+  LUID invalid_luid = {0xFFFF, 0x7FFF};
+
+  LogMessageMatcher fallback_matcher("falling back to default adapter");
+  LogMessage::AddLogToStream(&fallback_matcher, LS_WARNING);
+
+  DesktopCaptureOptions options = DesktopCaptureOptions::CreateDefault();
+  options.set_d3d_device_luid(invalid_luid);
+  SetUpForScreenCapture(options);
+  EXPECT_TRUE(capturer_->SelectSource(kFullDesktopScreenId));
+
+  capturer_->Start(this);
+
+  LogMessage::RemoveLogToStream(&fallback_matcher);
+  EXPECT_TRUE(fallback_matcher.matched());
+
+  DoCapture();
+  EXPECT_GT(frame_->size().width(), 0);
+  EXPECT_GT(frame_->size().height(), 0);
+}
+
 class WgcCapturerWindowTest : public WgcCapturerWinTest {
  public:
   void SetUp() {