chromium/ui/gl/hdr_metadata_helper_win.cc

// Copyright 2019 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/hdr_metadata_helper_win.h"

#include "base/compiler_specific.h"
#include "ui/gl/gpu_switching_manager.h"

namespace {

// Magic constants to convert to fixed point.
// https://docs.microsoft.com/en-us/windows/win32/api/dxgi1_5/ns-dxgi1_5-dxgi_hdr_metadata_hdr10
static constexpr int kPrimariesFixedPoint = 50000;
static constexpr int kMinLuminanceFixedPoint = 10000;

}  // namespace

namespace gl {

HDRMetadataHelperWin::HDRMetadataHelperWin(
    Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device)
    : d3d11_device_(std::move(d3d11_device)) {
  UpdateDisplayMetadata();
  ui::GpuSwitchingManager::GetInstance()->AddObserver(this);
}

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

std::optional<DXGI_HDR_METADATA_HDR10>
HDRMetadataHelperWin::GetDisplayMetadata() {
  if (!brightest_monitor_) {
    return std::nullopt;
  }
  auto it = hdr_metadatas_.find(brightest_monitor_);
  if (it == hdr_metadatas_.end()) {
    return std::nullopt;
  }
  return it->second;
}

std::optional<DXGI_HDR_METADATA_HDR10> HDRMetadataHelperWin::GetDisplayMetadata(
    HWND window) {
  auto it =
      hdr_metadatas_.find(MonitorFromWindow(window, MONITOR_DEFAULTTONEAREST));
  if (it == hdr_metadatas_.end()) {
    return std::nullopt;
  }
  return it->second;
}

void HDRMetadataHelperWin::UpdateDisplayMetadata() {
  brightest_monitor_ = nullptr;
  hdr_metadatas_.clear();

  if (!d3d11_device_)
    return;

  Microsoft::WRL::ComPtr<IDXGIDevice> dxgi_device;
  if (FAILED(d3d11_device_.As(&dxgi_device)))
    return;

  Microsoft::WRL::ComPtr<IDXGIAdapter> dxgi_adapter;
  if (FAILED(dxgi_device->GetAdapter(&dxgi_adapter)))
    return;

  Microsoft::WRL::ComPtr<IDXGIFactory> dxgi_factory;
  if (FAILED(dxgi_adapter->GetParent(IID_PPV_ARGS(&dxgi_factory))))
    return;

  FLOAT max_luminance = 0;
  HMONITOR brightest_monitor = nullptr;
  std::unordered_map<HMONITOR, DXGI_HDR_METADATA_HDR10> hdr_metadatas;

  // Enumerate all the monitors attached to all the adapters.  Pick the
  // brightest monitor as the one we want as default.
  Microsoft::WRL::ComPtr<IDXGIAdapter> adapter;
  for (unsigned int i = 0;
       dxgi_factory->EnumAdapters(i, &adapter) != DXGI_ERROR_NOT_FOUND; i++) {
    Microsoft::WRL::ComPtr<IDXGIOutput> output;
    for (unsigned int u = 0;
         adapter->EnumOutputs(u, &output) != DXGI_ERROR_NOT_FOUND; u++) {
      Microsoft::WRL::ComPtr<IDXGIOutput6> output6;
      if (FAILED(output.As(&output6)))
        continue;

      DXGI_OUTPUT_DESC1 desc1{};
      if (FAILED(output6->GetDesc1(&desc1)))
        continue;

      if (max_luminance < desc1.MaxLuminance) {
        max_luminance = desc1.MaxLuminance;
        brightest_monitor = desc1.Monitor;
      }

      hdr_metadatas[desc1.Monitor] = OutputDESC1ToDXGI(desc1);
    }
  }

  if (!brightest_monitor) {
    return;
  }

  brightest_monitor_ = brightest_monitor;
  hdr_metadatas_ = std::move(hdr_metadatas);
}

// static
DXGI_HDR_METADATA_HDR10 HDRMetadataHelperWin::HDRMetadataToDXGI(
    const gfx::HDRMetadata& hdr_metadata) {
  DXGI_HDR_METADATA_HDR10 metadata{};

  const auto smpte_st_2086 =
      hdr_metadata.smpte_st_2086.value_or(gfx::HdrMetadataSmpteSt2086());
  const auto& primaries = smpte_st_2086.primaries;
  metadata.RedPrimary[0] = primaries.fRX * kPrimariesFixedPoint;
  // SAFETY: required from Windows API.
  UNSAFE_BUFFERS(metadata.RedPrimary[1]) = primaries.fRY * kPrimariesFixedPoint;
  metadata.GreenPrimary[0] = primaries.fGX * kPrimariesFixedPoint;
  UNSAFE_BUFFERS(metadata.GreenPrimary[1]) =
      primaries.fGY * kPrimariesFixedPoint;
  metadata.BluePrimary[0] = primaries.fBX * kPrimariesFixedPoint;
  UNSAFE_BUFFERS(metadata.BluePrimary[1]) =
      primaries.fBY * kPrimariesFixedPoint;
  metadata.WhitePoint[0] = primaries.fWX * kPrimariesFixedPoint;
  UNSAFE_BUFFERS(metadata.WhitePoint[1]) = primaries.fWY * kPrimariesFixedPoint;
  metadata.MaxMasteringLuminance = smpte_st_2086.luminance_max;
  metadata.MinMasteringLuminance =
      smpte_st_2086.luminance_min * kMinLuminanceFixedPoint;

  const auto cta_861_3 =
      hdr_metadata.cta_861_3.value_or(gfx::HdrMetadataCta861_3());
  metadata.MaxContentLightLevel = cta_861_3.max_content_light_level;
  metadata.MaxFrameAverageLightLevel = cta_861_3.max_frame_average_light_level;

  return metadata;
}

DXGI_HDR_METADATA_HDR10 HDRMetadataHelperWin::OutputDESC1ToDXGI(
    const DXGI_OUTPUT_DESC1& desc1) {
  DXGI_HDR_METADATA_HDR10 metadata{};

  auto& primary_r = desc1.RedPrimary;
  metadata.RedPrimary[0] = primary_r[0] * kPrimariesFixedPoint;
  // SAFETY: required from Windows API.
  UNSAFE_BUFFERS(metadata.RedPrimary[1]) =
      UNSAFE_BUFFERS(primary_r[1]) * kPrimariesFixedPoint;
  auto& primary_g = desc1.GreenPrimary;
  metadata.GreenPrimary[0] = primary_g[0] * kPrimariesFixedPoint;
  UNSAFE_BUFFERS(metadata.GreenPrimary[1]) =
      UNSAFE_BUFFERS(primary_g[1]) * kPrimariesFixedPoint;
  auto& primary_b = desc1.BluePrimary;
  metadata.BluePrimary[0] = primary_b[0] * kPrimariesFixedPoint;
  UNSAFE_BUFFERS(metadata.BluePrimary[1]) =
      UNSAFE_BUFFERS(primary_b[1]) * kPrimariesFixedPoint;
  auto& white_point = desc1.WhitePoint;
  metadata.WhitePoint[0] = white_point[0] * kPrimariesFixedPoint;
  UNSAFE_BUFFERS(metadata.WhitePoint[1]) =
      UNSAFE_BUFFERS(white_point[1]) * kPrimariesFixedPoint;
  metadata.MaxMasteringLuminance = desc1.MaxLuminance;
  metadata.MinMasteringLuminance = desc1.MinLuminance * kMinLuminanceFixedPoint;
  // It's unclear how to set these properly, so this is a guess.
  // Also note that these are not fixed-point.
  metadata.MaxContentLightLevel = desc1.MaxFullFrameLuminance;
  metadata.MaxFrameAverageLightLevel = desc1.MaxFullFrameLuminance;

  return metadata;
}

void HDRMetadataHelperWin::OnDisplayAdded() {
  UpdateDisplayMetadata();
}

void HDRMetadataHelperWin::OnDisplayRemoved() {
  UpdateDisplayMetadata();
}
}  // namespace gl