// 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