Desktop Capture Performance Improvement via HMONITOR Caching

The GetHmonitorFromDeviceIndex API previously made
repeated calls to the EnumDisplayDevicesW OS API,
which is an expensive kernel operation.
Specifically, EnumDisplayDevicesW was invoked twice—once in
IsScreenValid and again in GetScreenRect.

GetHmonitorFromDeviceIndex alone cost is 1000 counts meanwhile
its parent ProcessFrame() itself is around 4000 counts, which
includes memcpy (2000 counts) and other d3d11 operation (1000).

This change removes the redundant call to IsScreenValid, no
calling the expensive EnumDisplayDevicesW, as GetScreenRect
already performs the necessary validation.

Additionally, the HMONITOR value is now cached for each
WgcCaptureSession used in screen capture.

After this CL, GetHmonitorFromDeviceIndex cost is almost 0
as it isn't shown on the traces anymore.

Bug: chromium:416088620
Change-Id: I1a9076ac9c797c3cb12f420dc60c73f174fe4f9f
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/390180
Reviewed-by: Ilya Nikolaevskiy <ilnik@webrtc.org>
Reviewed-by: Alexander Cooper <alcooper@chromium.org>
Commit-Queue: Sunggook Chue <sunggch@microsoft.com>
Cr-Commit-Position: refs/heads/main@{#44599}
diff --git a/modules/desktop_capture/win/screen_capture_utils.cc b/modules/desktop_capture/win/screen_capture_utils.cc
index 30120db..8ac8bfc 100644
--- a/modules/desktop_capture/win/screen_capture_utils.cc
+++ b/modules/desktop_capture/win/screen_capture_utils.cc
@@ -69,12 +69,7 @@
     return true;
   }
 
-  std::wstring device_key;
-  if (!IsScreenValid(device_index, &device_key)) {
-    return false;
-  }
-
-  DesktopRect screen_rect = GetScreenRect(device_index, device_key);
+  DesktopRect screen_rect = GetScreenRect(device_index, std::nullopt);
   if (screen_rect.is_empty()) {
     return false;
   }
@@ -169,7 +164,7 @@
 }
 
 DesktopRect GetScreenRect(const DesktopCapturer::SourceId screen,
-                          const std::wstring& device_key) {
+                          const std::optional<std::wstring>& device_key) {
   if (screen == kFullDesktopScreenId) {
     return GetFullscreenRect();
   }
@@ -185,7 +180,7 @@
   // sure we are capturing the same device when devices are added or removed.
   // DeviceKey is documented as reserved, but it actually contains the registry
   // key for the device and is unique for each monitor, while DeviceID is not.
-  if (device_key != device.DeviceKey) {
+  if (device_key.has_value() && *device_key != device.DeviceKey) {
     return DesktopRect();
   }
 
diff --git a/modules/desktop_capture/win/screen_capture_utils.h b/modules/desktop_capture/win/screen_capture_utils.h
index 71c79b9..68407f5 100644
--- a/modules/desktop_capture/win/screen_capture_utils.h
+++ b/modules/desktop_capture/win/screen_capture_utils.h
@@ -61,7 +61,9 @@
 // Returns true if `screen` is a valid screen. The screen device key is
 // returned through `device_key` if the screen is valid. The device key can be
 // used in GetScreenRect to verify the screen matches the previously obtained
-// id.
+// id. It calls the EnumDisplayDevices API to check if the screen is valid but
+// EnumDisplayDevices is quite slow so the caller of this function should
+// be aware of the performance impact.
 bool IsScreenValid(DesktopCapturer::SourceId screen, std::wstring* device_key);
 
 // Get the rect of the entire system in system coordinate system. I.e. the
@@ -69,10 +71,12 @@
 DesktopRect GetFullscreenRect();
 
 // Get the rect of the screen identified by `screen`, relative to the primary
-// display's top-left. If the screen device key does not match `device_key`, or
-// the screen does not exist, or any error happens, an empty rect is returned.
-RTC_EXPORT DesktopRect GetScreenRect(DesktopCapturer::SourceId screen,
-                                     const std::wstring& device_key);
+// display's top-left. If the optional screen device key exists, and does not
+// match `device_key`, or the screen does not exist, or any error happens,
+// an empty rect is returned.
+RTC_EXPORT DesktopRect
+GetScreenRect(DesktopCapturer::SourceId screen,
+              const std::optional<std::wstring>& device_key);
 
 }  // namespace webrtc
 
diff --git a/modules/desktop_capture/win/screen_capturer_win_gdi.cc b/modules/desktop_capture/win/screen_capturer_win_gdi.cc
index 24716813..ae6fb1d 100644
--- a/modules/desktop_capture/win/screen_capturer_win_gdi.cc
+++ b/modules/desktop_capture/win/screen_capturer_win_gdi.cc
@@ -112,9 +112,12 @@
 }
 
 bool ScreenCapturerWinGdi::SelectSource(SourceId id) {
-  bool valid = IsScreenValid(id, &current_device_key_);
-  if (valid)
+  std::wstring device_key;
+  bool valid = IsScreenValid(id, &device_key);
+  if (valid) {
     current_screen_id_ = id;
+    current_device_key_ = device_key;
+  }
   return valid;
 }
 
diff --git a/modules/desktop_capture/win/screen_capturer_win_gdi.h b/modules/desktop_capture/win/screen_capturer_win_gdi.h
index 7c3977e..6e103ae 100644
--- a/modules/desktop_capture/win/screen_capturer_win_gdi.h
+++ b/modules/desktop_capture/win/screen_capturer_win_gdi.h
@@ -61,7 +61,7 @@
   Callback* callback_ = nullptr;
   std::unique_ptr<SharedMemoryFactory> shared_memory_factory_;
   SourceId current_screen_id_ = kFullDesktopScreenId;
-  std::wstring current_device_key_;
+  std::optional<std::wstring> current_device_key_;
 
   ScopedThreadDesktop desktop_;
 
diff --git a/modules/desktop_capture/win/screen_capturer_win_magnifier.cc b/modules/desktop_capture/win/screen_capturer_win_magnifier.cc
index 9a3c9d5..6d2384e 100644
--- a/modules/desktop_capture/win/screen_capturer_win_magnifier.cc
+++ b/modules/desktop_capture/win/screen_capturer_win_magnifier.cc
@@ -135,7 +135,9 @@
 }
 
 bool ScreenCapturerWinMagnifier::SelectSource(SourceId id) {
-  if (IsScreenValid(id, &current_device_key_)) {
+  std::wstring device_key;
+  if (IsScreenValid(id, &device_key)) {
+    current_device_key_ = device_key;
     current_screen_id_ = id;
     return true;
   }
diff --git a/modules/desktop_capture/win/screen_capturer_win_magnifier.h b/modules/desktop_capture/win/screen_capturer_win_magnifier.h
index 07c5b1e..80cf6d4 100644
--- a/modules/desktop_capture/win/screen_capturer_win_magnifier.h
+++ b/modules/desktop_capture/win/screen_capturer_win_magnifier.h
@@ -104,7 +104,7 @@
   Callback* callback_ = nullptr;
   std::unique_ptr<SharedMemoryFactory> shared_memory_factory_;
   ScreenId current_screen_id_ = kFullDesktopScreenId;
-  std::wstring current_device_key_;
+  std::optional<std::wstring> current_device_key_;
   HWND excluded_window_ = NULL;
 
   // Queue of the frames buffers.
diff --git a/modules/desktop_capture/win/wgc_capture_session.cc b/modules/desktop_capture/win/wgc_capture_session.cc
index 100f28a..63a6a15 100644
--- a/modules/desktop_capture/win/wgc_capture_session.cc
+++ b/modules/desktop_capture/win/wgc_capture_session.cc
@@ -13,8 +13,6 @@
 #include <DispatcherQueue.h>
 #include <windows.graphics.capture.interop.h>
 #include <windows.graphics.directX.direct3d11.interop.h>
-#include <windows.graphics.h>
-#include <wrl/client.h>
 #include <wrl/event.h>
 
 #include <algorithm>
@@ -469,18 +467,21 @@
   DesktopFrame* current_frame = queue_.current_frame();
   DesktopFrame* previous_frame = queue_.previous_frame();
 
-  HMONITOR monitor;
   if (is_window_source_) {
     // If the captured window moves to another screen, the HMONITOR associated
     // with the captured window will change. Therefore, we need to get the value
     // of HMONITOR per frame.
-    monitor = MonitorFromWindow(reinterpret_cast<HWND>(source_id_),
-                                /*dwFlags=*/MONITOR_DEFAULTTONEAREST);
+    monitor_ = ::MonitorFromWindow(reinterpret_cast<HWND>(source_id_),
+                                   /*dwFlags=*/MONITOR_DEFAULTTONEAREST);
   } else {
-    if (!GetHmonitorFromDeviceIndex(source_id_, &monitor)) {
-      RTC_LOG(LS_ERROR) << "Failed to get HMONITOR from device index.";
-      d3d_context->Unmap(mapped_texture_.Get(), 0);
-      return E_FAIL;
+    if (!monitor_.has_value()) {
+      HMONITOR monitor;
+      if (!GetHmonitorFromDeviceIndex(source_id_, &monitor)) {
+        RTC_LOG(LS_ERROR) << "Failed to get HMONITOR from device index.";
+        d3d_context->Unmap(mapped_texture_.Get(), 0);
+        return E_FAIL;
+      }
+      monitor_ = monitor;
     }
   }
 
@@ -490,7 +491,7 @@
   // 1, 1.5, 2.5, etc.
   DEVICE_SCALE_FACTOR device_scale_factor = DEVICE_SCALE_FACTOR_INVALID;
   HRESULT scale_factor_hr =
-      GetScaleFactorForMonitor(monitor, &device_scale_factor);
+      GetScaleFactorForMonitor(monitor_.value(), &device_scale_factor);
   RTC_LOG_IF(LS_ERROR, FAILED(scale_factor_hr))
       << "Failed to get scale factor for monitor: " << scale_factor_hr;
   if (device_scale_factor != DEVICE_SCALE_FACTOR_INVALID) {
diff --git a/modules/desktop_capture/win/wgc_capture_session.h b/modules/desktop_capture/win/wgc_capture_session.h
index 5d3f056..ca88b06 100644
--- a/modules/desktop_capture/win/wgc_capture_session.h
+++ b/modules/desktop_capture/win/wgc_capture_session.h
@@ -155,6 +155,11 @@
   // The unique id to represent a Source of current DesktopCapturer.
   intptr_t source_id_;
 
+  // The monitor that is being captured when the target source_id is a
+  // screen. For window sources, it can't be used because the window can move
+  // around around the different monitors.
+  std::optional<HMONITOR> monitor_;
+
   // The source type of the capture session. It can be either a window or a
   // screen.
   bool is_window_source_;