chromium/ui/gl/direct_composition_support.cc

// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ui/gl/direct_composition_support.h"

#include <dcomp.h>
#include <dxgi1_6.h>

#include <set>

#include "base/command_line.h"
#include "base/compiler_specific.h"
#include "base/logging.h"
#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.h"
#include "base/synchronization/lock.h"
#include "base/win/windows_version.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gl/gl_features.h"
#include "ui/gl/gl_switches.h"
#include "ui/gl/gl_utils.h"
#include "ui/gl/gpu_switching_manager.h"

namespace gl {
namespace {
// Whether the overlay caps are valid or not. GUARDED_BY GetOverlayLock().
bool g_overlay_caps_valid = false;
// Indicates support for either NV12 or YUY2 overlays. GUARDED_BY
// GetOverlayLock().
bool g_supports_overlays = false;
// Whether the GPU can support hardware overlays or not.
bool g_supports_hardware_overlays = false;
// Whether video processor auto HDR is supported.
bool g_supports_vp_auto_hdr = false;
// Whether the DecodeSwapChain is disabled or not.
bool g_disable_decode_swap_chain = false;
// Whether to force the nv12 overlay support.
bool g_force_nv12_overlay_support = false;
// Whether software overlays have been disabled.
bool g_disable_sw_overlays = false;

// The lock to guard g_overlay_caps_valid and g_supports_overlays.
base::Lock& GetOverlayLock() {
  static base::NoDestructor<base::Lock> overlay_lock;
  return *overlay_lock;
}

bool SupportsOverlays() {
  base::AutoLock auto_lock(GetOverlayLock());
  return g_supports_overlays;
}

bool SupportsHardwareOverlays() {
  base::AutoLock auto_lock(GetOverlayLock());
  return g_supports_hardware_overlays;
}

bool SupportsVideoProcessorAutoHDR() {
  base::AutoLock auto_lock(GetOverlayLock());
  return g_supports_vp_auto_hdr;
}

void SetSupportsOverlays(bool support) {
  base::AutoLock auto_lock(GetOverlayLock());
  g_supports_overlays = support;
}

void SetSupportsHardwareOverlays(bool support) {
  base::AutoLock auto_lock(GetOverlayLock());
  g_supports_hardware_overlays = support;
}

void SetSupportsVideoProcessorAutoHDR(bool support) {
  base::AutoLock auto_lock(GetOverlayLock());
  g_supports_vp_auto_hdr = support;
}

bool SupportsSoftwareOverlays() {
  return base::FeatureList::IsEnabled(
             features::kDirectCompositionSoftwareOverlays) &&
         !g_disable_sw_overlays;
}

bool OverlayCapsValid() {
  base::AutoLock auto_lock(GetOverlayLock());
  return g_overlay_caps_valid;
}

void SetOverlayCapsValid(bool valid) {
  base::AutoLock auto_lock(GetOverlayLock());
  g_overlay_caps_valid = valid;
}

// A wrapper of IDXGIOutput4::CheckOverlayColorSpaceSupport()
bool CheckOverlayColorSpaceSupport(
    DXGI_FORMAT dxgi_format,
    DXGI_COLOR_SPACE_TYPE dxgi_color_space,
    Microsoft::WRL::ComPtr<IDXGIOutput> output,
    Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device) {
  UINT color_space_support_flags = 0;
  Microsoft::WRL::ComPtr<IDXGIOutput4> output4;
  if (FAILED(output.As(&output4)) ||
      FAILED(output4->CheckOverlayColorSpaceSupport(
          dxgi_format, dxgi_color_space, d3d11_device.Get(),
          &color_space_support_flags)))
    return false;
  return (color_space_support_flags &
          DXGI_OVERLAY_COLOR_SPACE_SUPPORT_FLAG_PRESENT);
}

// Used for adjusting overlay size to monitor size.
gfx::Size g_primary_monitor_size;

// The number of all visible display monitors on a desktop.
int g_num_monitors = 0;

// Whether there is a HDR capable display monitor being connected.
bool g_system_hdr_enabled = false;

// Per-monitor HDR capability
std::set<HMONITOR>* GetHDRMonitors() {
  static base::NoDestructor<std::set<HMONITOR>> hdr_monitors;
  return hdr_monitors.get();
}

// Global direct composition device.
IDCompositionDevice3* g_dcomp_device = nullptr;
// Global d3d11 device used by direct composition.
ID3D11Device* g_d3d11_device = nullptr;
// Whether swap chain present failed and direct composition should be disabled.
bool g_direct_composition_swap_chain_failed = false;

// Preferred overlay format set when detecting overlay support during
// initialization.  Set to NV12 by default so that it's used when enabling
// overlays using command line flags.
DXGI_FORMAT g_overlay_format_used = DXGI_FORMAT_NV12;
DXGI_FORMAT g_overlay_format_used_hdr = DXGI_FORMAT_UNKNOWN;

// These are the raw support info, which shouldn't depend on field trial state,
// or command line flags. GUARDED_BY GetOverlayLock().
UINT g_nv12_overlay_support_flags = 0;
UINT g_yuy2_overlay_support_flags = 0;
UINT g_bgra8_overlay_support_flags = 0;
UINT g_rgb10a2_overlay_support_flags = 0;
UINT g_p010_overlay_support_flags = 0;

// When this is set, if NV12 or YUY2 overlays are supported, set BGRA8 overlays
// as supported as well.
bool g_enable_bgra8_overlays_with_yuv_overlay_support = false;

// Force enabling DXGI_FORMAT_R10G10B10A2_UNORM format for overlay. Intel
// Icelake and Tigerlake fail to report the cap of this HDR overlay format.
// TODO(magchen@): Remove this workaround when this cap is fixed in the Intel
// drivers.
bool g_force_rgb10a2_overlay_support = false;

// Per Intel's request, only use NV12 for overlay when
// COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P709 is also supported. At least one Intel
// Gen9 SKU does not support NV12 overlays and it cannot be screened by the
// device id.
bool g_check_ycbcr_studio_g22_left_p709_for_nv12_support = false;

void SetOverlaySupportFlagsForFormats(UINT nv12_flags,
                                      UINT yuy2_flags,
                                      UINT bgra8_flags,
                                      UINT rgb10a2_flags,
                                      UINT p010_flags) {
  base::AutoLock auto_lock(GetOverlayLock());
  g_nv12_overlay_support_flags = nv12_flags;
  g_yuy2_overlay_support_flags = yuy2_flags;
  g_bgra8_overlay_support_flags = bgra8_flags;
  g_rgb10a2_overlay_support_flags = rgb10a2_flags;
  g_p010_overlay_support_flags = p010_flags;
}

bool FlagsSupportsOverlays(UINT flags) {
  return (flags & (DXGI_OVERLAY_SUPPORT_FLAG_DIRECT |
                   DXGI_OVERLAY_SUPPORT_FLAG_SCALING));
}

void GetGpuDriverOverlayInfo(bool* supports_overlays,
                             bool* supports_hardware_overlays,
                             DXGI_FORMAT* overlay_format_used,
                             DXGI_FORMAT* overlay_format_used_hdr,
                             UINT* nv12_overlay_support_flags,
                             UINT* yuy2_overlay_support_flags,
                             UINT* bgra8_overlay_support_flags,
                             UINT* rgb10a2_overlay_support_flags,
                             UINT* p010_overlay_support_flags) {
  // Initialization
  *supports_overlays = false;
  *supports_hardware_overlays = false;
  *overlay_format_used = DXGI_FORMAT_NV12;
  *overlay_format_used_hdr = DXGI_FORMAT_R10G10B10A2_UNORM;
  *nv12_overlay_support_flags = 0;
  *yuy2_overlay_support_flags = 0;
  *bgra8_overlay_support_flags = 0;
  *rgb10a2_overlay_support_flags = 0;
  *p010_overlay_support_flags = 0;

  // Check for DirectComposition support first to prevent likely crashes.
  if (!DirectCompositionSupported())
    return;

  // Before Windows 10 Anniversary Update (Redstone 1), overlay planes wouldn't
  // be assigned to non-UWP apps.
  if (base::win::GetVersion() < base::win::Version::WIN10_RS1)
    return;

  Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device = g_d3d11_device;
  if (!d3d11_device) {
    DLOG(ERROR) << "Failed to retrieve D3D11 device";
    return;
  }

  Microsoft::WRL::ComPtr<IDXGIDevice> dxgi_device;
  if (FAILED(d3d11_device.As(&dxgi_device))) {
    DLOG(ERROR) << "Failed to retrieve DXGI device";
    return;
  }

  Microsoft::WRL::ComPtr<IDXGIAdapter> dxgi_adapter;
  if (FAILED(dxgi_device->GetAdapter(&dxgi_adapter))) {
    DLOG(ERROR) << "Failed to retrieve DXGI adapter";
    return;
  }

  // This will fail if the D3D device is "Microsoft Basic Display Adapter".
  Microsoft::WRL::ComPtr<ID3D11VideoDevice> video_device;
  if (FAILED(d3d11_device.As(&video_device))) {
    DLOG(ERROR) << "Failed to retrieve video device";
    return;
  }

  unsigned int i = 0;
  while (true) {
    Microsoft::WRL::ComPtr<IDXGIOutput> output;
    if (FAILED(dxgi_adapter->EnumOutputs(i++, &output)))
      break;
    DCHECK(output);
    Microsoft::WRL::ComPtr<IDXGIOutput3> output3;
    if (FAILED(output.As(&output3)))
      continue;
    DCHECK(output3);
    output3->CheckOverlaySupport(DXGI_FORMAT_NV12, d3d11_device.Get(),
                                 nv12_overlay_support_flags);
    output3->CheckOverlaySupport(DXGI_FORMAT_YUY2, d3d11_device.Get(),
                                 yuy2_overlay_support_flags);
    output3->CheckOverlaySupport(DXGI_FORMAT_B8G8R8A8_UNORM, d3d11_device.Get(),
                                 bgra8_overlay_support_flags);
    // Today it still returns false, which blocks Chrome from using HDR
    // overlays.
    output3->CheckOverlaySupport(DXGI_FORMAT_R10G10B10A2_UNORM,
                                 d3d11_device.Get(),
                                 rgb10a2_overlay_support_flags);
    output3->CheckOverlaySupport(DXGI_FORMAT_P010, d3d11_device.Get(),
                                 p010_overlay_support_flags);
    if (FlagsSupportsOverlays(*nv12_overlay_support_flags)) {
      // NV12 format is preferred if it's supported.
      *overlay_format_used = DXGI_FORMAT_NV12;
      *supports_hardware_overlays = true;

      if (g_check_ycbcr_studio_g22_left_p709_for_nv12_support &&
          !CheckOverlayColorSpaceSupport(
              DXGI_FORMAT_NV12, DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P709,
              output, d3d11_device)) {
        // Some new Intel drivers only claim to support unscaled overlays, but
        // scaled overlays still work. It's possible DWM works around it by
        // performing an extra scaling Blt before calling the driver. Even when
        // scaled overlays aren't actually supported, presentation using the
        // overlay path should be relatively efficient.
        *supports_hardware_overlays = false;
      }
    }
    if (!*supports_hardware_overlays &&
        FlagsSupportsOverlays(*yuy2_overlay_support_flags)) {
      // If NV12 isn't supported, fallback to YUY2 if it's supported.
      *overlay_format_used = DXGI_FORMAT_YUY2;
      *supports_hardware_overlays = true;
    }
    if (g_enable_bgra8_overlays_with_yuv_overlay_support) {
      if (FlagsSupportsOverlays(*nv12_overlay_support_flags))
        *bgra8_overlay_support_flags = *nv12_overlay_support_flags;
      else if (FlagsSupportsOverlays(*yuy2_overlay_support_flags))
        *bgra8_overlay_support_flags = *yuy2_overlay_support_flags;
    }

    // RGB10A2 overlay is used for displaying HDR content. In Intel's
    // platform, RGB10A2 overlay is enabled only when
    // DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020 is supported.
    if (FlagsSupportsOverlays(*rgb10a2_overlay_support_flags)) {
      if (!CheckOverlayColorSpaceSupport(
              DXGI_FORMAT_R10G10B10A2_UNORM,
              DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020, output, d3d11_device))
        *rgb10a2_overlay_support_flags = 0;
    }
    if (g_force_rgb10a2_overlay_support) {
      *rgb10a2_overlay_support_flags = DXGI_OVERLAY_SUPPORT_FLAG_SCALING;
    }

    // Early out after the first output that reports overlay support. All
    // outputs are expected to report the same overlay support according to
    // Microsoft's WDDM documentation:
    // https://docs.microsoft.com/en-us/windows-hardware/drivers/display/multiplane-overlay-hardware-requirements
    // TODO(sunnyps): If the above is true, then we can only look at first
    // output instead of iterating over all outputs.
    if (*supports_hardware_overlays)
      break;
  }

  *supports_overlays = *supports_hardware_overlays;
  if (*supports_hardware_overlays || !SupportsSoftwareOverlays()) {
    return;
  }

  // If no devices with hardware overlay support were found use software ones.
  *supports_overlays = true;
  *nv12_overlay_support_flags = 0;
  *yuy2_overlay_support_flags = 0;
  *bgra8_overlay_support_flags = 0;
  *rgb10a2_overlay_support_flags = 0;
  *p010_overlay_support_flags = 0;

  // Software overlays always use NV12 because it's slightly more efficient and
  // YUY2 was only used because Skylake doesn't support NV12 hardware overlays.
  *overlay_format_used = DXGI_FORMAT_NV12;
}

void UpdateOverlaySupport() {
  if (OverlayCapsValid())
    return;
  SetOverlayCapsValid(true);

  bool supports_overlays = false;
  bool supports_hardware_overlays = false;
  DXGI_FORMAT overlay_format_used = DXGI_FORMAT_NV12;
  DXGI_FORMAT overlay_format_used_hdr = DXGI_FORMAT_R10G10B10A2_UNORM;
  UINT nv12_overlay_support_flags = 0;
  UINT yuy2_overlay_support_flags = 0;
  UINT bgra8_overlay_support_flags = 0;
  UINT rgb10a2_overlay_support_flags = 0;
  UINT p010_overlay_support_flags = 0;

  GetGpuDriverOverlayInfo(
      &supports_overlays, &supports_hardware_overlays, &overlay_format_used,
      &overlay_format_used_hdr, &nv12_overlay_support_flags,
      &yuy2_overlay_support_flags, &bgra8_overlay_support_flags,
      &rgb10a2_overlay_support_flags, &p010_overlay_support_flags);

  if (g_force_nv12_overlay_support) {
    supports_overlays = true;
    nv12_overlay_support_flags = DXGI_OVERLAY_SUPPORT_FLAG_SCALING;
    overlay_format_used = DXGI_FORMAT_NV12;
  }

  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
          switches::kDirectCompositionVideoSwapChainFormat)) {
    std::string override_format =
        base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
            switches::kDirectCompositionVideoSwapChainFormat);
    if (override_format == kSwapChainFormatNV12) {
      overlay_format_used = DXGI_FORMAT_NV12;
    } else if (override_format == kSwapChainFormatYUY2) {
      overlay_format_used = DXGI_FORMAT_YUY2;
    } else if (override_format == kSwapChainFormatBGRA) {
      overlay_format_used = DXGI_FORMAT_B8G8R8A8_UNORM;
    } else {
      DLOG(ERROR) << "Invalid value for switch "
                  << switches::kDirectCompositionVideoSwapChainFormat;
    }
  }

  // Record histograms.
  if (supports_overlays) {
    base::UmaHistogramSparse("GPU.DirectComposition.OverlayFormatUsed3",
                             overlay_format_used);
  }
  base::UmaHistogramBoolean("GPU.DirectComposition.OverlaysSupported",
                            supports_overlays);
  base::UmaHistogramBoolean("GPU.DirectComposition.HardwareOverlaysSupported",
                            supports_hardware_overlays);

  // Update global caps.
  SetSupportsOverlays(supports_overlays);
  SetSupportsHardwareOverlays(supports_hardware_overlays);
  SetOverlaySupportFlagsForFormats(
      nv12_overlay_support_flags, yuy2_overlay_support_flags,
      bgra8_overlay_support_flags, rgb10a2_overlay_support_flags,
      p010_overlay_support_flags);
  g_overlay_format_used = overlay_format_used;
  g_overlay_format_used_hdr = overlay_format_used_hdr;
}

std::vector<DXGI_OUTPUT_DESC1> GetDirectCompositionOutputDescs() {
  std::vector<DXGI_OUTPUT_DESC1> output_descs;
  // HDR support was introduced in Windows 10 Creators Update.
  if (base::win::GetVersion() < base::win::Version::WIN10_RS2) {
    return output_descs;
  }

  // Only direct composition surface can allocate HDR swap chains.
  if (!DirectCompositionSupported()) {
    return output_descs;
  }

  HRESULT hr = S_OK;
  Microsoft::WRL::ComPtr<IDXGIFactory1> factory;
  hr = CreateDXGIFactory1(IID_PPV_ARGS(&factory));
  if (FAILED(hr)) {
    DLOG(ERROR) << "Failed to create DXGI factory.";
    return output_descs;
  }

  for (UINT adapter_index = 0;; ++adapter_index) {
    Microsoft::WRL::ComPtr<IDXGIAdapter> adapter;
    hr = factory->EnumAdapters(adapter_index, &adapter);
    if (hr == DXGI_ERROR_NOT_FOUND) {
      break;
    }
    if (FAILED(hr)) {
      DLOG(ERROR) << "Unexpected error creating DXGI adapter.";
      break;
    }

    for (UINT output_index = 0;; ++output_index) {
      Microsoft::WRL::ComPtr<IDXGIOutput> output;
      hr = adapter->EnumOutputs(output_index, &output);
      if (hr == DXGI_ERROR_NOT_FOUND) {
        break;
      }
      if (FAILED(hr)) {
        DLOG(ERROR) << "Unexpected error creating DXGI adapter.";
        break;
      }

      Microsoft::WRL::ComPtr<IDXGIOutput6> output6;
      hr = output->QueryInterface(IID_PPV_ARGS(&output6));
      if (FAILED(hr)) {
        DLOG(WARNING) << "IDXGIOutput6 is required for HDR detection.";
        continue;
      }

      DXGI_OUTPUT_DESC1 desc;
      if (FAILED(output6->GetDesc1(&desc))) {
        DLOG(ERROR) << "Unexpected error getting output descriptor.";
        continue;
      }

      output_descs.push_back(std::move(desc));
    }
  }

  return output_descs;
}

void UpdateMonitorInfo() {
  g_num_monitors = GetSystemMetrics(SM_CMONITORS);

  MONITORINFO monitor_info;
  monitor_info.cbSize = sizeof(monitor_info);
  if (GetMonitorInfo(MonitorFromWindow(nullptr, MONITOR_DEFAULTTOPRIMARY),
                     &monitor_info)) {
    g_primary_monitor_size = gfx::Rect(monitor_info.rcMonitor).size();
  } else {
    g_primary_monitor_size = gfx::Size();
  }

  GetHDRMonitors()->clear();
  g_system_hdr_enabled = false;
  for (const auto& desc : GetDirectCompositionOutputDescs()) {
    if (desc.ColorSpace == DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020) {
      GetHDRMonitors()->insert(desc.Monitor);
      g_system_hdr_enabled = true;
    }
  }
  UMA_HISTOGRAM_BOOLEAN("GPU.Output.HDR", g_system_hdr_enabled);
}

// Update video processor auto HDR feature support status.
// Must be called on GpuMain thread.
void UpdateVideoProcessorAutoHDRSupport() {
  if (GetGlWorkarounds().disable_vp_auto_hdr) {
    SetSupportsVideoProcessorAutoHDR(false);
    return;
  }

  if (!base::FeatureList::IsEnabled(features::kNvidiaVpTrueHDR)) {
    SetSupportsVideoProcessorAutoHDR(false);
    return;
  }

  Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device = g_d3d11_device;
  if (!d3d11_device) {
    DLOG(ERROR) << "Failed to get device";
    SetSupportsVideoProcessorAutoHDR(false);
    return;
  }

  Microsoft::WRL::ComPtr<ID3D11DeviceContext> d3d11_context;
  // D3D11 immediate context isn't allowed to be accessed simultaneously on two
  // threads, and all other callers are using this on the GpuMain thread, so
  // this function must be called on GpuMain thread.
  d3d11_device->GetImmediateContext(&d3d11_context);
  if (!d3d11_context) {
    DLOG(ERROR) << "Failed to get context";
    SetSupportsVideoProcessorAutoHDR(false);
    return;
  }

  Microsoft::WRL::ComPtr<ID3D11VideoContext> d3d11_video_context;
  if (FAILED(d3d11_context.As(&d3d11_video_context))) {
    DLOG(ERROR) << "Failed to retrieve video context";
    SetSupportsVideoProcessorAutoHDR(false);
    return;
  }

  Microsoft::WRL::ComPtr<ID3D11VideoDevice> d3d11_video_device;
  if (FAILED(d3d11_device.As(&d3d11_video_device))) {
    DLOG(ERROR) << "Failed to retrieve video device";
    SetSupportsVideoProcessorAutoHDR(false);
    return;
  }

  D3D11_VIDEO_PROCESSOR_CONTENT_DESC desc;
  desc.InputFrameFormat = D3D11_VIDEO_FRAME_FORMAT_PROGRESSIVE;
  desc.InputFrameRate.Numerator = 60;
  desc.InputFrameRate.Denominator = 1;
  desc.InputWidth = 1920;
  desc.InputHeight = 1080;
  desc.OutputFrameRate.Numerator = 60;
  desc.OutputFrameRate.Denominator = 1;
  desc.OutputWidth = 1920;
  desc.OutputHeight = 1080;
  desc.Usage = D3D11_VIDEO_USAGE_PLAYBACK_NORMAL;

  Microsoft::WRL::ComPtr<ID3D11VideoProcessorEnumerator> d3d11_video_enumerator;
  if (FAILED(d3d11_video_device->CreateVideoProcessorEnumerator(
          &desc, &d3d11_video_enumerator))) {
    DLOG(ERROR) << "Failed to create video processor enumerator";
    SetSupportsVideoProcessorAutoHDR(false);
    return;
  }

  Microsoft::WRL::ComPtr<ID3D11VideoProcessor> d3d11_video_processor;
  if (FAILED(d3d11_video_device->CreateVideoProcessor(
          d3d11_video_enumerator.Get(), 0, &d3d11_video_processor))) {
    DLOG(ERROR) << "Failed to create video processor";
    SetSupportsVideoProcessorAutoHDR(false);
    return;
  }

  constexpr GUID kNvidiaTrueHDRInterfaceGUID = {
      0xfdd62bb4,
      0x620b,
      0x4fd7,
      {0x9a, 0xb3, 0x1e, 0x59, 0xd0, 0xd5, 0x44, 0xb3}};

  UINT driver_supports_true_hdr = 0;
  HRESULT hr = d3d11_video_context->VideoProcessorGetStreamExtension(
      d3d11_video_processor.Get(), 0, &kNvidiaTrueHDRInterfaceGUID,
      sizeof(driver_supports_true_hdr), &driver_supports_true_hdr);
  if (FAILED(hr)) {
    DLOG(ERROR) << "Failed to get stream extension with error 0x" << std::hex
                << hr;
    SetSupportsVideoProcessorAutoHDR(false);
    return;
  }

  d3d11_video_processor.Reset();
  d3d11_video_enumerator.Reset();
  d3d11_video_context.Reset();
  d3d11_video_device.Reset();
  d3d11_context.Reset();
  d3d11_device.Reset();

  SetSupportsVideoProcessorAutoHDR(driver_supports_true_hdr == 1);
}

}  // namespace

void InitializeDirectComposition(
    Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device) {
  DCHECK(!g_dcomp_device);
  if (GetGlWorkarounds().disable_direct_composition) {
    return;
  }

  // Blocklist direct composition if MCTU.dll or MCTUX.dll are injected. These
  // are user mode drivers for display adapters from Magic Control Technology
  // Corporation.
  if (GetModuleHandle(TEXT("MCTU.dll")) || GetModuleHandle(TEXT("MCTUX.dll"))) {
    DLOG(ERROR) << "Blocklisted due to third party modules";
    return;
  }

  // Load DLL at runtime since older Windows versions don't have dcomp.
  HMODULE dcomp_module = ::GetModuleHandle(L"dcomp.dll");
  if (!dcomp_module) {
    DLOG(ERROR) << "Failed to load dcomp.dll";
    return;
  }

  using PFN_DCOMPOSITION_CREATE_DEVICE3 = HRESULT(WINAPI*)(
      IUnknown * renderingDevice, REFIID iid, void** dcompositionDevice);
  PFN_DCOMPOSITION_CREATE_DEVICE3 create_device3_function =
      reinterpret_cast<PFN_DCOMPOSITION_CREATE_DEVICE3>(
          ::GetProcAddress(dcomp_module, "DCompositionCreateDevice3"));
  if (!create_device3_function) {
    DLOG(ERROR) << "GetProcAddress failed for DCompositionCreateDevice3";
    return;
  }

  Microsoft::WRL::ComPtr<IDXGIDevice> dxgi_device;
  d3d11_device.As(&dxgi_device);

  Microsoft::WRL::ComPtr<IDCompositionDesktopDevice> desktop_device;
  HRESULT hr =
      create_device3_function(dxgi_device.Get(), IID_PPV_ARGS(&desktop_device));
  if (FAILED(hr)) {
    DLOG(ERROR) << "DCompositionCreateDevice3 failed with error 0x" << std::hex
                << hr;
    return;
  }

  Microsoft::WRL::ComPtr<IDCompositionDevice3> dcomp_device;
  hr = desktop_device.As(&dcomp_device);
  if (FAILED(hr)) {
    DLOG(ERROR) << "Failed to retrieve IDCompositionDevice3 with error 0x"
                << std::hex << hr;
    return;
  }

  g_dcomp_device = dcomp_device.Detach();
  DCHECK(g_dcomp_device);

  g_d3d11_device = d3d11_device.Detach();

  UpdateVideoProcessorAutoHDRSupport();
}

void ShutdownDirectComposition() {
  if (g_dcomp_device) {
    g_dcomp_device->Release();
    g_dcomp_device = nullptr;
    g_d3d11_device->Release();
    g_d3d11_device = nullptr;
  }
}

IDCompositionDevice3* GetDirectCompositionDevice() {
  return g_dcomp_device;
}

ID3D11Device* GetDirectCompositionD3D11Device() {
  return g_d3d11_device;
}

bool DirectCompositionSupported() {
  return g_dcomp_device && !g_direct_composition_swap_chain_failed;
}

bool DirectCompositionOverlaysSupported() {
  // Always initialize and record overlay support information irrespective of
  // command line flags.
  UpdateOverlaySupport();

  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
  // Enable flag should be checked before the disable workaround, so we could
  // overwrite GPU driver bug workarounds in testing.
  if (command_line->HasSwitch(
          switches::kEnableDirectCompositionVideoOverlays)) {
    return true;
  }
  if (GetGlWorkarounds().disable_direct_composition_video_overlays) {
    return false;
  }

  return SupportsOverlays();
}

bool DirectCompositionHardwareOverlaysSupported() {
  UpdateOverlaySupport();
  return SupportsHardwareOverlays();
}

bool DirectCompositionDecodeSwapChainSupported() {
  if (!g_disable_decode_swap_chain) {
    UpdateOverlaySupport();
    return GetDirectCompositionSDROverlayFormat() == DXGI_FORMAT_NV12;
  }
  return false;
}

void DisableDirectCompositionOverlays() {
  SetSupportsOverlays(false);
  DirectCompositionOverlayCapsMonitor::GetInstance()
      ->NotifyOverlayCapsChanged();
}

bool DirectCompositionScaledOverlaysSupported() {
  UpdateOverlaySupport();
  if (g_overlay_format_used == DXGI_FORMAT_NV12) {
    return (g_nv12_overlay_support_flags & DXGI_OVERLAY_SUPPORT_FLAG_SCALING) ||
           (SupportsOverlays() && SupportsSoftwareOverlays());
  } else if (g_overlay_format_used == DXGI_FORMAT_YUY2) {
    return !!(g_yuy2_overlay_support_flags & DXGI_OVERLAY_SUPPORT_FLAG_SCALING);
  } else {
    DCHECK_EQ(g_overlay_format_used, DXGI_FORMAT_B8G8R8A8_UNORM);
    // Assume scaling is supported for BGRA overlays.
    return true;
  }
}

bool VideoProcessorAutoHDRSupported() {
  return SupportsVideoProcessorAutoHDR();
}

bool CheckVideoProcessorFormatSupport(DXGI_FORMAT dxgi_format) {
  Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device = g_d3d11_device;
  if (!d3d11_device) {
    DLOG(ERROR) << "Failed to retrieve D3D11 device";
    return false;
  }

  Microsoft::WRL::ComPtr<ID3D11VideoDevice> video_device;
  if (FAILED(d3d11_device.As(&video_device))) {
    DLOG(ERROR) << "Failed to retrieve video device";
    return false;
  }

  UINT device = 0;
  if (!SUCCEEDED(d3d11_device->CheckFormatSupport(dxgi_format, &device))) {
    DLOG(ERROR) << "Failed to check supported format";
    return false;
  }

  D3D11_VIDEO_PROCESSOR_CONTENT_DESC desc;
  desc.InputFrameFormat = D3D11_VIDEO_FRAME_FORMAT_PROGRESSIVE;
  desc.InputFrameRate.Numerator = 60;
  desc.InputFrameRate.Denominator = 1;
  desc.InputWidth = 1920;
  desc.InputHeight = 1080;
  desc.OutputFrameRate.Numerator = 60;
  desc.OutputFrameRate.Denominator = 1;
  desc.OutputWidth = 1920;
  desc.OutputHeight = 1080;
  desc.Usage = D3D11_VIDEO_USAGE_PLAYBACK_NORMAL;

  Microsoft::WRL::ComPtr<ID3D11VideoProcessorEnumerator> video_enumerator;
  if (!SUCCEEDED(video_device->CreateVideoProcessorEnumerator(
          &desc, &video_enumerator))) {
    DLOG(ERROR) << "Failed to create video processor enumerator";
    return false;
  }

  if (!video_enumerator) {
    DLOG(ERROR) << "Failed to locate video enumerator";
    return false;
  }

  UINT enumerator = 0;
  if (!SUCCEEDED(video_enumerator->CheckVideoProcessorFormat(dxgi_format,
                                                             &enumerator))) {
    DLOG(ERROR) << "Failed to check video processor format";
    video_enumerator.Reset();
    return false;
  }

  video_enumerator.Reset();
  return (enumerator & D3D11_VIDEO_PROCESSOR_FORMAT_SUPPORT_OUTPUT) &&
         (device & D3D11_FORMAT_SUPPORT_VIDEO_PROCESSOR_OUTPUT);
}

UINT GetDirectCompositionOverlaySupportFlags(DXGI_FORMAT format) {
  UpdateOverlaySupport();
  base::AutoLock auto_lock(GetOverlayLock());
  UINT support_flag = 0;
  switch (format) {
    case DXGI_FORMAT_NV12:
      support_flag = g_nv12_overlay_support_flags;
      break;
    case DXGI_FORMAT_YUY2:
      support_flag = g_yuy2_overlay_support_flags;
      break;
    case DXGI_FORMAT_B8G8R8A8_UNORM:
      support_flag = g_bgra8_overlay_support_flags;
      break;
    case DXGI_FORMAT_R10G10B10A2_UNORM:
      support_flag = g_rgb10a2_overlay_support_flags;
      break;
    case DXGI_FORMAT_P010:
      support_flag = g_p010_overlay_support_flags;
      break;
    default:
      NOTREACHED_IN_MIGRATION();
      break;
  }
  return support_flag;
}

gfx::Size GetDirectCompositionPrimaryMonitorSize() {
  if (g_primary_monitor_size.IsEmpty())
    UpdateMonitorInfo();
  return g_primary_monitor_size;
}

int GetDirectCompositionNumMonitors() {
  if (g_num_monitors == 0)
    UpdateMonitorInfo();
  return g_num_monitors;
}

bool DirectCompositionSystemHDREnabled() {
  if (g_num_monitors == 0)
    UpdateMonitorInfo();
  return g_system_hdr_enabled;
}

bool DirectCompositionMonitorHDREnabled(HWND window) {
  if (g_num_monitors == 0) {
    UpdateMonitorInfo();
  }

  return GetHDRMonitors()->find(MonitorFromWindow(
             window, MONITOR_DEFAULTTONEAREST)) != GetHDRMonitors()->end();
}

DXGI_FORMAT GetDirectCompositionSDROverlayFormat() {
  return g_overlay_format_used;
}

void SetDirectCompositionScaledOverlaysSupportedForTesting(bool supported) {
  UpdateOverlaySupport();
  if (supported) {
    g_nv12_overlay_support_flags |= DXGI_OVERLAY_SUPPORT_FLAG_SCALING;
    g_yuy2_overlay_support_flags |= DXGI_OVERLAY_SUPPORT_FLAG_SCALING;
    g_rgb10a2_overlay_support_flags |= DXGI_OVERLAY_SUPPORT_FLAG_SCALING;
    g_p010_overlay_support_flags |= DXGI_OVERLAY_SUPPORT_FLAG_SCALING;
  } else {
    g_nv12_overlay_support_flags &= ~DXGI_OVERLAY_SUPPORT_FLAG_SCALING;
    g_yuy2_overlay_support_flags &= ~DXGI_OVERLAY_SUPPORT_FLAG_SCALING;
    g_rgb10a2_overlay_support_flags &= ~DXGI_OVERLAY_SUPPORT_FLAG_SCALING;
    g_p010_overlay_support_flags &= ~DXGI_OVERLAY_SUPPORT_FLAG_SCALING;
  }
  g_disable_sw_overlays = !supported;
  SetSupportsHardwareOverlays(supported);
  DCHECK_EQ(supported, DirectCompositionScaledOverlaysSupported());
}

void SetDirectCompositionOverlayFormatUsedForTesting(DXGI_FORMAT format) {
  DCHECK(format == DXGI_FORMAT_NV12 || format == DXGI_FORMAT_YUY2 ||
         format == DXGI_FORMAT_B8G8R8A8_UNORM);
  UpdateOverlaySupport();
  g_overlay_format_used = format;
  DCHECK_EQ(format, GetDirectCompositionSDROverlayFormat());
}

gfx::mojom::DXGIInfoPtr GetDirectCompositionHDRMonitorDXGIInfo() {
  auto result_info = gfx::mojom::DXGIInfo::New();

  for (const auto& desc : GetDirectCompositionOutputDescs()) {
    auto result_output = gfx::mojom::DXGIOutputDesc::New();
    result_output->device_name = desc.DeviceName;
    result_output->hdr_enabled =
        desc.ColorSpace == DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020;
    result_output->primaries.fRX = desc.RedPrimary[0];
    // SAFETY: required from Windows API.
    result_output->primaries.fRY = UNSAFE_BUFFERS(desc.RedPrimary[1]);
    result_output->primaries.fGX = desc.GreenPrimary[0];
    result_output->primaries.fGY = UNSAFE_BUFFERS(desc.GreenPrimary[1]);
    result_output->primaries.fBX = desc.BluePrimary[0];
    result_output->primaries.fBY = UNSAFE_BUFFERS(desc.BluePrimary[1]);
    result_output->primaries.fWX = desc.WhitePoint[0];
    result_output->primaries.fWY = UNSAFE_BUFFERS(desc.WhitePoint[1]);
    result_output->min_luminance = desc.MinLuminance;
    result_output->max_luminance = desc.MaxLuminance;
    result_output->max_full_frame_luminance = desc.MaxFullFrameLuminance;
    result_info->output_descs.push_back(std::move(result_output));
  }

  return result_info;
}

bool DXGISwapChainTearingSupported() {
  static const bool supported = [] {
    Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device = g_d3d11_device;
    if (!d3d11_device) {
      DLOG(ERROR) << "Not using swap chain tearing because failed to retrieve "
                     "D3D11 device from ANGLE";
      return false;
    }
    Microsoft::WRL::ComPtr<IDXGIDevice> dxgi_device;
    d3d11_device.As(&dxgi_device);
    DCHECK(dxgi_device);
    Microsoft::WRL::ComPtr<IDXGIAdapter> dxgi_adapter;
    dxgi_device->GetAdapter(&dxgi_adapter);
    DCHECK(dxgi_adapter);
    Microsoft::WRL::ComPtr<IDXGIFactory5> dxgi_factory;
    if (FAILED(dxgi_adapter->GetParent(IID_PPV_ARGS(&dxgi_factory)))) {
      DLOG(ERROR) << "Not using swap chain tearing because failed to retrieve "
                     "IDXGIFactory5 interface";
      return false;
    }

    BOOL present_allow_tearing = FALSE;
    DCHECK(dxgi_factory);
    if (FAILED(dxgi_factory->CheckFeatureSupport(
            DXGI_FEATURE_PRESENT_ALLOW_TEARING, &present_allow_tearing,
            sizeof(present_allow_tearing)))) {
      DLOG(ERROR)
          << "Not using swap chain tearing because CheckFeatureSupport failed";
      return false;
    }
    return !!present_allow_tearing;
  }();
  return supported;
}

bool DirectCompositionSwapChainTearingEnabled() {
  return DXGISwapChainTearingSupported() && !features::UseGpuVsync();
}

bool DXGIWaitableSwapChainEnabled() {
  return base::FeatureList::IsEnabled(features::kDXGIWaitableSwapChain);
}

UINT GetDXGIWaitableSwapChainMaxQueuedFrames() {
  return static_cast<UINT>(
      features::kDXGIWaitableSwapChainMaxQueuedFrames.Get());
}

void SetDirectCompositionOverlayWorkarounds(
    const DirectCompositionOverlayWorkarounds& workarounds) {
  // This has to be set before initializing overlay caps.
  CHECK(!OverlayCapsValid());
  g_disable_sw_overlays = workarounds.disable_sw_video_overlays;
  g_disable_decode_swap_chain = workarounds.disable_decode_swap_chain;
  g_enable_bgra8_overlays_with_yuv_overlay_support =
      workarounds.enable_bgra8_overlays_with_yuv_overlay_support;
  g_force_nv12_overlay_support = workarounds.force_nv12_overlay_support;
  g_force_rgb10a2_overlay_support = workarounds.force_rgb10a2_overlay_support;
  g_check_ycbcr_studio_g22_left_p709_for_nv12_support =
      workarounds.check_ycbcr_studio_g22_left_p709_for_nv12_support;
}

void SetDirectCompositionSwapChainFailed() {
  if (!g_direct_composition_swap_chain_failed) {
    g_direct_composition_swap_chain_failed = true;
    DirectCompositionOverlayCapsMonitor::GetInstance()
        ->NotifyOverlayCapsChanged();
  }
}

void SetDirectCompositionMonitorInfoForTesting(
    int num_monitors,
    const gfx::Size& primary_monitor_size) {
  g_num_monitors = num_monitors;
  g_primary_monitor_size = primary_monitor_size;
}

std::optional<bool> g_direct_composition_texture_supported;

bool DirectCompositionTextureSupported() {
  if (g_direct_composition_texture_supported.has_value()) {
    return g_direct_composition_texture_supported.value();
  }

  if (!g_dcomp_device || !g_d3d11_device) {
    // We don't support DComp textures if we haven't initialized Direct
    // Composition. This can happen if Direct Composition is disabled, e.g.
    // during software rendering mode.
    return false;
  }

  Microsoft::WRL::ComPtr<IDCompositionDevice2> dcomp_device = g_dcomp_device;
  CHECK(dcomp_device);

  Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device = g_d3d11_device;
  CHECK(d3d11_device);

  // Set the result to false early in case any of the following conditions fail.
  // We don't set this earlier in case this function is called before
  // |InitializeDirectComposition|.
  g_direct_composition_texture_supported = false;

  Microsoft::WRL::ComPtr<IDCompositionDevice4> dcomp_device4;
  HRESULT hr = dcomp_device.As(&dcomp_device4);
  if (FAILED(hr)) {
    // Not a recent enough Windows system
    DLOG(ERROR) << "QueryInterface to IDCompositionDevice4 failed: "
                << logging::SystemErrorCodeToString(hr);
    return false;
  }

  BOOL supports_composition_textures = FALSE;
  hr = dcomp_device4->CheckCompositionTextureSupport(
      d3d11_device.Get(), &supports_composition_textures);
  if (FAILED(hr)) {
    DLOG(ERROR) << "CheckCompositionTextureSupport failed: "
                << logging::SystemErrorCodeToString(hr);
    return false;
  }

  if (supports_composition_textures == FALSE) {
    DLOG(ERROR) << "CheckCompositionTextureSupport reported unsupported";
    return false;
  }

  g_direct_composition_texture_supported = true;
  return true;
}

// For DirectComposition Display Monitor.
DirectCompositionOverlayCapsMonitor::DirectCompositionOverlayCapsMonitor()
    : observer_list_(new base::ObserverListThreadSafe<
                     DirectCompositionOverlayCapsObserver>()) {
  ui::GpuSwitchingManager::GetInstance()->AddObserver(this);
}

DirectCompositionOverlayCapsMonitor::~DirectCompositionOverlayCapsMonitor() {
  ui::GpuSwitchingManager::GetInstance()->RemoveObserver(this);
}

// static
DirectCompositionOverlayCapsMonitor*
DirectCompositionOverlayCapsMonitor::GetInstance() {
  static base::NoDestructor<DirectCompositionOverlayCapsMonitor>
      direct_compoisition_overlay_cap_monitor;
  return direct_compoisition_overlay_cap_monitor.get();
}

void DirectCompositionOverlayCapsMonitor::AddObserver(
    DirectCompositionOverlayCapsObserver* observer) {
  observer_list_->AddObserver(observer);
}

void DirectCompositionOverlayCapsMonitor::RemoveObserver(
    DirectCompositionOverlayCapsObserver* observer) {
  observer_list_->RemoveObserver(observer);
}

void DirectCompositionOverlayCapsMonitor::NotifyOverlayCapsChanged() {
  observer_list_->Notify(
      FROM_HERE, &DirectCompositionOverlayCapsObserver::OnOverlayCapsChanged);
}

// Called from GpuSwitchingObserver on the GPU main thread.
void DirectCompositionOverlayCapsMonitor::OnGpuSwitched(
    gl::GpuPreference active_gpu_heuristic) {}

// Called from GpuSwitchingObserver on the GPU main thread.
void DirectCompositionOverlayCapsMonitor::OnDisplayAdded() {
  SetOverlayCapsValid(false);
  UpdateOverlaySupport();
  UpdateVideoProcessorAutoHDRSupport();
  UpdateMonitorInfo();

  NotifyOverlayCapsChanged();
}

// Called from GpuSwitchingObserver on the GPU main thread.
void DirectCompositionOverlayCapsMonitor::OnDisplayRemoved() {
  SetOverlayCapsValid(false);
  UpdateOverlaySupport();
  UpdateVideoProcessorAutoHDRSupport();
  UpdateMonitorInfo();

  NotifyOverlayCapsChanged();
}

// Called from GpuSwitchingObserver on the GPU main thread.
void DirectCompositionOverlayCapsMonitor::OnDisplayMetricsChanged() {
  UpdateMonitorInfo();

  NotifyOverlayCapsChanged();
}

}  // namespace gl