0

Add video processor based Auto HDR support on Windows

(This CL is copied from https://crrev.com/c/4535570.
Author: Romain Pokrzywka.)

This adds vp-based SDR-to-HDR conversion on Windows for
supported NVIDIA GPUs via the ID3D11VideoProcessor interface.

The feature is gated by a new disable_vp_auto_hdr flag
to limit it to supported vendor/gpu/driver combinations.

Currently only enabled for NVIDIA GPUs with a 545+ driver,
using the vendor-specific extension.

Similar to RTX Super Resolution, the feature will be off
by default, requiring manual opt-in by users via a toggle
in the nvidia control panel application.

Bug: 1445741
Change-Id: Ib0068306168588bcb61b8196c509497083005588
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4652913
Reviewed-by: Jonathan Ross <jonross@chromium.org>
Auto-Submit: Maggie Chen <magchen@chromium.org>
Commit-Queue: Maggie Chen <magchen@chromium.org>
Reviewed-by: Dale Curtis <dalecurtis@chromium.org>
Reviewed-by: Vasiliy Telezhnikov <vasilyt@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1164814}
This commit is contained in:
Maggie Chen
2023-06-30 18:34:54 +00:00
committed by Chromium LUCI CQ
parent 29eaa29e6b
commit b2fae4c984
15 changed files with 412 additions and 132 deletions

@ -4178,6 +4178,32 @@
"features": [
"force_dcomp_triple_buffer_video_swap_chain"
]
},
{
"id": 414,
"cr_bugs": [
1318380
],
"description": "Only enable video processor auto HDR on NVIDIA GPUs with 545+ driver",
"os": {
"type": "win"
},
"exceptions": [
{
"vendor_id": "0x10de",
"os": {
"type": "win"
},
"driver_version": {
"schema": "nvidia_driver",
"op": ">",
"value": "545.00"
}
}
],
"features": [
"disable_vp_auto_hdr"
]
}
]

@ -63,6 +63,7 @@ disable_skia_reduce_ops_task_splitting
disable_software_to_accelerated_canvas_upgrade
disable_texture_storage
disable_timestamp_queries
disable_vp_auto_hdr
disable_vp_scaling
disable_vp_super_resolution
disable_webgl_rgb_multisampling_usage

@ -33,6 +33,7 @@ CreateDirectCompositionSurfaceSettings(
workarounds.no_downscaled_overlay_promotion;
settings.disable_nv12_dynamic_textures =
workarounds.disable_nv12_dynamic_textures;
settings.disable_vp_auto_hdr = workarounds.disable_vp_auto_hdr;
settings.disable_vp_scaling = workarounds.disable_vp_scaling;
settings.disable_vp_super_resolution =
workarounds.disable_vp_super_resolution;

@ -1107,15 +1107,20 @@ chromium-metrics-reviews@google.com.
</summary>
</histogram>
<histogram name="GPU.NvidiaVpSuperResolution.{State}.SetStreamExt"
enum="Hresult" expires_after="2023-06-30">
<histogram name="GPU.Nvidia{Extension}.{State}.SetStreamExt" enum="Hresult"
expires_after="2023-12-31">
<owner>magchen@chromium.org</owner>
<owner>zmo@chromium.org</owner>
<owner>graphics-dev@chromium.org</owner>
<summary>
Records HRESULT in NvidiaVpSuperResolution. This metric is recorded every
frame when processing video streams in DirectComposition.
Records HRESULT when setting the video processor stream extension for Nvidia
{Extension} to {State}. This metric is recorded every frame when processing
video streams in DirectComposition.
</summary>
<token key="Extension">
<variant name="VpSuperResolution" summary="RTX SuperResolution"/>
<variant name="VpTrueHDR" summary="RTX TrueHDR"/>
</token>
<token key="State">
<variant name="Off" summary="off"/>
<variant name="On" summary="on"/>
@ -1466,16 +1471,20 @@ chromium-metrics-reviews@google.com.
</summary>
</histogram>
<histogram name="GPU.VideoProcessorBlt.VpSuperResolution.{State}"
enum="Hresult" expires_after="2023-06-30">
<histogram name="GPU.VideoProcessorBlt.{Extension}.{State}" enum="Hresult"
expires_after="2023-12-31">
<owner>magchen@chromium.org</owner>
<owner>zmo@chromium.org</owner>
<owner>graphics-dev@chromium.org</owner>
<summary>
Records HRESULT for VideoProcessorBlt. This metric is recorded when
processing a video steam in DirectComposition with Video Processor Super
Rsolution {State}.
processing a video stream in DirectComposition with {Extension} {State}.
</summary>
<token key="Extension">
<variant name="VpAutoHDR" summary="Video Processor Auto HDR"/>
<variant name="VpSuperResolution"
summary="Video Processor Super Resolution"/>
</token>
<token key="State">
<variant name="Off" summary="off"/>
<variant name="On" summary="on"/>

@ -49,11 +49,13 @@ VideoProcessorWrapper& VideoProcessorWrapper::operator=(
VideoProcessorWrapper&& other) = default;
DCLayerTree::DCLayerTree(bool disable_nv12_dynamic_textures,
bool disable_vp_auto_hdr,
bool disable_vp_scaling,
bool disable_vp_super_resolution,
bool force_dcomp_triple_buffer_video_swap_chain,
bool no_downscaled_overlay_promotion)
: disable_nv12_dynamic_textures_(disable_nv12_dynamic_textures),
disable_vp_auto_hdr_(disable_vp_auto_hdr),
disable_vp_scaling_(disable_vp_scaling),
disable_vp_super_resolution_(disable_vp_super_resolution),
force_dcomp_triple_buffer_video_swap_chain_(

@ -70,6 +70,7 @@ class DCLayerTree {
DCompositionInkTrailPoint>;
DCLayerTree(bool disable_nv12_dynamic_textures,
bool disable_vp_auto_hdr,
bool disable_vp_scaling,
bool disable_vp_super_resolution,
bool force_dcomp_triple_buffer_video_swap_chain,
@ -103,6 +104,8 @@ class DCLayerTree {
return disable_nv12_dynamic_textures_;
}
bool disable_vp_auto_hdr() const { return disable_vp_auto_hdr_; }
bool disable_vp_scaling() const { return disable_vp_scaling_; }
bool disable_vp_super_resolution() const {
@ -340,6 +343,7 @@ class DCLayerTree {
gfx::Size& resource_size_in_pixels);
const bool disable_nv12_dynamic_textures_;
const bool disable_vp_auto_hdr_;
const bool disable_vp_scaling_;
const bool disable_vp_super_resolution_;
const bool force_dcomp_triple_buffer_video_swap_chain_;

@ -51,6 +51,7 @@ DCompPresenter::DCompPresenter(
max_pending_frames_(settings.max_pending_frames),
layer_tree_(std::make_unique<DCLayerTree>(
settings.disable_nv12_dynamic_textures,
settings.disable_vp_auto_hdr,
settings.disable_vp_scaling,
settings.disable_vp_super_resolution,
settings.force_dcomp_triple_buffer_video_swap_chain,

@ -5,6 +5,7 @@
#include "ui/gl/direct_composition_support.h"
#include <dxgi1_6.h>
#include <set>
#include "base/command_line.h"
#include "base/metrics/histogram_functions.h"
@ -104,6 +105,9 @@ 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> g_hdr_monitors = {};
// Global direct composition device.
IDCompositionDevice3* g_dcomp_device = nullptr;
// Whether swap chain present failed and direct composition should be disabled.
@ -361,6 +365,68 @@ void UpdateOverlaySupport() {
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);
@ -372,10 +438,16 @@ void UpdateMonitorInfo() {
} else {
g_primary_monitor_size = gfx::Size();
}
g_hdr_monitors.clear();
g_system_hdr_enabled = false;
auto dxgi_info = gl::GetDirectCompositionHDRMonitorDXGIInfo();
for (const auto& output_desc : dxgi_info->output_descs)
g_system_hdr_enabled |= output_desc->hdr_enabled;
for (const auto& desc : GetDirectCompositionOutputDescs()) {
if (desc.ColorSpace == DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020) {
g_hdr_monitors.insert(desc.Monitor);
g_system_hdr_enabled = true;
}
}
UMA_HISTOGRAM_BOOLEAN("GPU.Output.HDR", g_system_hdr_enabled);
}
} // namespace
@ -563,6 +635,15 @@ bool DirectCompositionSystemHDREnabled() {
return g_system_hdr_enabled;
}
bool DirectCompositionMonitorHDREnabled(HWND window) {
if (g_num_monitors == 0) {
UpdateMonitorInfo();
}
return g_hdr_monitors.find(MonitorFromWindow(
window, MONITOR_DEFAULTTONEAREST)) != g_hdr_monitors.end();
}
DXGI_FORMAT GetDirectCompositionSDROverlayFormat() {
return g_overlay_format_used;
}
@ -593,77 +674,26 @@ void SetDirectCompositionOverlayFormatUsedForTesting(DXGI_FORMAT format) {
gfx::mojom::DXGIInfoPtr GetDirectCompositionHDRMonitorDXGIInfo() {
auto result_info = gfx::mojom::DXGIInfo::New();
// HDR support was introduced in Windows 10 Creators Update.
if (base::win::GetVersion() < base::win::Version::WIN10_RS2)
return result_info;
// Only direct composition surface can allocate HDR swap chains.
if (!DirectCompositionSupported())
return result_info;
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 result_info;
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];
result_output->primaries.fRY = desc.RedPrimary[1];
result_output->primaries.fGX = desc.GreenPrimary[0];
result_output->primaries.fGY = desc.GreenPrimary[1];
result_output->primaries.fBX = desc.BluePrimary[0];
result_output->primaries.fBY = desc.BluePrimary[1];
result_output->primaries.fWX = desc.WhitePoint[0];
result_output->primaries.fWY = 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));
}
bool hdr_monitor_found = false;
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;
}
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];
result_output->primaries.fRY = desc.RedPrimary[1];
result_output->primaries.fGX = desc.GreenPrimary[0];
result_output->primaries.fGY = desc.GreenPrimary[1];
result_output->primaries.fBX = desc.BluePrimary[0];
result_output->primaries.fBY = desc.BluePrimary[1];
result_output->primaries.fWX = desc.WhitePoint[0];
result_output->primaries.fWY = desc.WhitePoint[1];
result_output->min_luminance = desc.MinLuminance;
result_output->max_luminance = desc.MaxLuminance;
result_output->max_full_frame_luminance = desc.MaxFullFrameLuminance;
hdr_monitor_found |= result_output->hdr_enabled;
result_info->output_descs.push_back(std::move(result_output));
}
}
UMA_HISTOGRAM_BOOLEAN("GPU.Output.HDR", hdr_monitor_found);
return result_info;
}

@ -78,6 +78,9 @@ GL_EXPORT UINT GetDXGIWaitableSwapChainMaxQueuedFrames();
// Returns true if there is an HDR capable display connected.
GL_EXPORT bool DirectCompositionSystemHDREnabled();
// Returns true if the window is displayed on an HDR capable display.
GL_EXPORT bool DirectCompositionMonitorHDREnabled(HWND window);
// Returns the collected DXGI information.
GL_EXPORT gfx::mojom::DXGIInfoPtr GetDirectCompositionHDRMonitorDXGIInfo();

@ -56,6 +56,7 @@ DirectCompositionSurfaceWin::DirectCompositionSurfaceWin(
settings.use_angle_texture_offset)),
layer_tree_(std::make_unique<DCLayerTree>(
settings.disable_nv12_dynamic_textures,
settings.disable_vp_auto_hdr,
settings.disable_vp_scaling,
settings.disable_vp_super_resolution,
settings.force_dcomp_triple_buffer_video_swap_chain,

@ -48,6 +48,7 @@ class GL_EXPORT DirectCompositionSurfaceWin : public GLSurfaceEGL,
struct Settings {
bool disable_nv12_dynamic_textures = false;
bool disable_vp_auto_hdr = false;
bool disable_vp_scaling = false;
bool disable_vp_super_resolution = false;
bool force_dcomp_triple_buffer_video_swap_chain = false;

@ -232,6 +232,11 @@ BASE_FEATURE(kNvidiaVpSuperResolution,
"NvidiaVpSuperResolution",
base::FEATURE_ENABLED_BY_DEFAULT);
// Allow overlay swapchain to use NVIDIA video processor for trueHDR.
BASE_FEATURE(kNvidiaVpTrueHDR,
"NvidiaVpTrueHDR",
base::FEATURE_ENABLED_BY_DEFAULT);
// Default to using ANGLE's OpenGL backend
BASE_FEATURE(kDefaultANGLEOpenGL,
"DefaultANGLEOpenGL",

@ -98,6 +98,7 @@ GL_EXPORT BASE_DECLARE_FEATURE(kDirectCompositionLetterboxVideoOptimization);
GL_EXPORT BASE_DECLARE_FEATURE(kEGLDualGPURendering);
GL_EXPORT BASE_DECLARE_FEATURE(kIntelVpSuperResolution);
GL_EXPORT BASE_DECLARE_FEATURE(kNvidiaVpSuperResolution);
GL_EXPORT BASE_DECLARE_FEATURE(kNvidiaVpTrueHDR);
GL_EXPORT BASE_DECLARE_FEATURE(kDefaultANGLEOpenGL);
GL_EXPORT BASE_DECLARE_FEATURE(kDefaultANGLEMetal);
GL_EXPORT BASE_DECLARE_FEATURE(kDefaultANGLEVulkan);

@ -41,6 +41,21 @@ BASE_FEATURE(kFallbackBT709VideoToBT601,
"FallbackBT709VideoToBT601",
base::FEATURE_DISABLED_BY_DEFAULT);
gfx::ColorSpace GetOutputColorSpace(const gfx::ColorSpace& input_color_space,
bool is_yuv_swapchain) {
gfx::ColorSpace output_color_space =
is_yuv_swapchain ? input_color_space : gfx::ColorSpace::CreateSRGB();
if (base::FeatureList::IsEnabled(kFallbackBT709VideoToBT601) &&
(output_color_space == gfx::ColorSpace::CreateREC709())) {
output_color_space = gfx::ColorSpace::CreateREC601();
}
if (input_color_space.IsHDR()) {
output_color_space = gfx::ColorSpace::CreateHDR10();
}
return output_color_space;
}
bool IsProtectedVideo(gfx::ProtectedVideoType protected_video_type) {
return protected_video_type != gfx::ProtectedVideoType::kClear;
}
@ -316,6 +331,62 @@ HRESULT ToggleVpSuperResolution(UINT gpu_vendor_id,
return E_NOTIMPL;
}
HRESULT ToggleNvidiaVpTrueHDR(ID3D11VideoContext* video_context,
ID3D11VideoProcessor* video_processor,
bool enable) {
TRACE_EVENT1("gpu", "ToggleNvidiaVpTrueHDR", "on", enable);
constexpr GUID kNvidiaTrueHDRInterfaceGUID = {
0xfdd62bb4,
0x620b,
0x4fd7,
{0x9a, 0xb3, 0x1e, 0x59, 0xd0, 0xd5, 0x44, 0xb3}};
constexpr UINT kStreamExtensionVersionV4 = 0x4;
constexpr UINT kStreamExtensionMethodTrueHDR = 0x3;
struct {
UINT version;
UINT method;
UINT enable : 1;
UINT reserved : 31;
} stream_extension_info = {kStreamExtensionVersionV4,
kStreamExtensionMethodTrueHDR, enable ? 1u : 0u,
0u};
HRESULT hr = video_context->VideoProcessorSetStreamExtension(
video_processor, 0, &kNvidiaTrueHDRInterfaceGUID,
sizeof(stream_extension_info), &stream_extension_info);
base::UmaHistogramSparse(enable ? "GPU.NvidiaVpTrueHDR.On.SetStreamExt"
: "GPU.NvidiaVpTrueHDR.Off.SetStreamExt",
hr);
if (FAILED(hr)) {
DLOG(ERROR) << "VideoProcessorSetStreamExtension failed with error 0x"
<< std::hex << hr;
}
return hr;
}
HRESULT ToggleVpAutoHDR(UINT gpu_vendor_id,
ID3D11VideoContext* video_context,
ID3D11VideoProcessor* video_processor,
bool enable) {
if (gpu_vendor_id == 0x10de) {
return ToggleNvidiaVpTrueHDR(video_context, video_processor, enable);
}
return E_NOTIMPL;
}
bool IsVpAutoHDREnabled(UINT gpu_vendor_id) {
if (gpu_vendor_id == 0x10de &&
base::FeatureList::IsEnabled(features::kNvidiaVpTrueHDR)) {
return true;
}
return false;
}
bool IsWithinMargin(int i, int j) {
constexpr int kFullScreenMargin = 10;
return (std::abs(i - j) < kFullScreenMargin);
@ -1343,10 +1414,19 @@ bool SwapChainPresenter::PresentToSwapChain(DCLayerOverlayParams& params,
}
bool content_is_hdr = input_color_space.IsHDR();
bool use_hdr_swap_chain = content_is_hdr && params.hdr_metadata.IsValid();
// Enable VideoProcessor-HDR for SDR content if the monitor and
// GPU driver support it
bool use_vp_auto_hdr = !content_is_hdr &&
DirectCompositionMonitorHDREnabled(window_) &&
enable_vp_auto_hdr_ && !is_on_battery_power_;
bool use_hdr_swap_chain =
((content_is_hdr && params.hdr_metadata.IsValid()) || use_vp_auto_hdr);
DXGI_FORMAT swap_chain_format =
GetSwapChainFormat(params.protected_video_type, use_hdr_swap_chain);
bool swap_chain_format_changed = swap_chain_format != swap_chain_format_;
bool toggle_protected_video =
swap_chain_protected_video_type_ != params.protected_video_type;
@ -1414,7 +1494,7 @@ bool SwapChainPresenter::PresentToSwapChain(DCLayerOverlayParams& params,
if (!VideoProcessorBlt(std::move(input_texture), input_level,
std::move(keyed_mutex), params.content_rect,
input_color_space, content_is_hdr, stream_metadata)) {
input_color_space, stream_metadata, use_vp_auto_hdr)) {
return false;
}
@ -1671,8 +1751,8 @@ bool SwapChainPresenter::VideoProcessorBlt(
Microsoft::WRL::ComPtr<IDXGIKeyedMutex> keyed_mutex,
const gfx::Rect& content_rect,
const gfx::ColorSpace& src_color_space,
bool content_is_hdr,
absl::optional<DXGI_HDR_METADATA_HDR10> stream_hdr_metadata) {
absl::optional<DXGI_HDR_METADATA_HDR10> stream_hdr_metadata,
bool use_vp_auto_hdr) {
TRACE_EVENT2("gpu", "SwapChainPresenter::VideoProcessorBlt", "content_rect",
content_rect.ToString(), "swap_chain_size",
swap_chain_size_.ToString());
@ -1682,15 +1762,7 @@ bool SwapChainPresenter::VideoProcessorBlt(
// doesn't need a |force_yuv| parameter (and the associated plumbing).
bool is_yuv_swapchain = IsYUVSwapChainFormat(swap_chain_format_);
gfx::ColorSpace output_color_space =
is_yuv_swapchain ? src_color_space : gfx::ColorSpace::CreateSRGB();
if (base::FeatureList::IsEnabled(kFallbackBT709VideoToBT601) &&
(output_color_space == gfx::ColorSpace::CreateREC709())) {
output_color_space = gfx::ColorSpace::CreateREC601();
}
if (content_is_hdr) {
output_color_space = gfx::ColorSpace::CreateHDR10();
}
GetOutputColorSpace(src_color_space, is_yuv_swapchain);
VideoProcessorWrapper* video_processor_wrapper =
layer_tree_->InitializeVideoProcessor(
content_rect.size(), swap_chain_size_, output_color_space.IsHDR());
@ -1716,8 +1788,12 @@ bool SwapChainPresenter::VideoProcessorBlt(
DXGI_COLOR_SPACE_TYPE output_dxgi_color_space =
gfx::ColorSpaceWin::GetDXGIColorSpace(output_color_space,
/*force_yuv=*/is_yuv_swapchain);
DXGI_COLOR_SPACE_TYPE swap_dxgi_color_space =
use_vp_auto_hdr ? gfx::ColorSpaceWin::GetDXGIColorSpace(
gfx::ColorSpace::CreateHDR10())
: output_dxgi_color_space;
if (SUCCEEDED(swap_chain3->SetColorSpace1(output_dxgi_color_space))) {
if (SUCCEEDED(swap_chain3->SetColorSpace1(swap_dxgi_color_space))) {
context1->VideoProcessorSetOutputColorSpace1(video_processor.Get(),
output_dxgi_color_space);
}
@ -1821,16 +1897,34 @@ bool SwapChainPresenter::VideoProcessorBlt(
DCHECK(output_view_);
}
bool use_vp_super_resolution = false;
if (!layer_tree_->disable_vp_super_resolution() &&
!force_vp_super_resolution_off_) {
hr =
ToggleVpSuperResolution(gpu_vendor_id_, video_context.Get(),
video_processor.Get(), !is_on_battery_power_);
if (enable_vp_auto_hdr_) {
hr = ToggleVpAutoHDR(gpu_vendor_id_, video_context.Get(),
video_processor.Get(), use_vp_auto_hdr);
if (FAILED(hr)) {
force_vp_super_resolution_off_ = true;
enable_vp_auto_hdr_ = false;
if (use_vp_auto_hdr) {
if (!RevertSwapChainToSDR(video_device, video_processor,
video_processor_enumerator, swap_chain3,
context1, src_color_space)) {
return false;
}
use_vp_auto_hdr = false;
}
}
}
bool use_vp_super_resolution =
enable_vp_super_resolution_ && !is_on_battery_power_;
if (enable_vp_super_resolution_) {
hr = ToggleVpSuperResolution(gpu_vendor_id_, video_context.Get(),
video_processor.Get(),
use_vp_super_resolution);
if (FAILED(hr)) {
enable_vp_super_resolution_ = false;
use_vp_super_resolution = false;
}
use_vp_super_resolution = !is_on_battery_power_ && SUCCEEDED(hr);
}
{
@ -1838,46 +1932,77 @@ bool SwapChainPresenter::VideoProcessorBlt(
hr = video_context->VideoProcessorBlt(video_processor.Get(),
output_view_.Get(), 0, 1, &stream);
}
base::UmaHistogramSparse(
(use_vp_auto_hdr ? "GPU.VideoProcessorBlt.VpAutoHDR.On"
: "GPU.VideoProcessorBlt.VpAutoHDR.Off"),
hr);
base::UmaHistogramSparse(
(use_vp_super_resolution
? "GPU.VideoProcessorBlt.VpSuperResolution.On"
: "GPU.VideoProcessorBlt.VpSuperResolution.Off"),
hr);
if (FAILED(hr)) {
// Retry VideoProcessorBlt with vp super resolution off if it was on.
if (use_vp_super_resolution) {
DLOG(ERROR) << "Retry VideoProcessorBlt with VpSuperResolution off "
"after it failed with error 0x"
<< std::hex << hr;
// Retry VideoProcessorBlt with VpSuperResolution off if it was on.
if (FAILED(hr) && use_vp_super_resolution) {
DLOG(ERROR) << "Retry VideoProcessorBlt with VpSuperResolution off "
"after it failed with error 0x"
<< std::hex << hr;
ToggleVpSuperResolution(gpu_vendor_id_, video_context.Get(),
video_processor.Get(), false);
{
TRACE_EVENT0("gpu", "ID3D11VideoContext::VideoProcessorBlt");
hr = video_context->VideoProcessorBlt(
video_processor.Get(), output_view_.Get(), 0, 1, &stream);
}
base::UmaHistogramSparse(
"GPU.VideoProcessorBlt.VpSuperResolution.RetryOffAfterError", hr);
// We shouldn't use VpSuperResolution if it was the reason that caused
// the VideoProcessorBlt failure.
force_vp_super_resolution_off_ = SUCCEEDED(hr);
ToggleVpSuperResolution(gpu_vendor_id_, video_context.Get(),
video_processor.Get(), false);
{
TRACE_EVENT0("gpu", "ID3D11VideoContext::VideoProcessorBlt");
hr = video_context->VideoProcessorBlt(
video_processor.Get(), output_view_.Get(), 0, 1, &stream);
}
base::UmaHistogramSparse(
"GPU.VideoProcessorBlt.VpSuperResolution.RetryOffAfterError", hr);
if (FAILED(hr)) {
DLOG(ERROR) << "VideoProcessorBlt failed with error 0x" << std::hex
<< hr;
// We shouldn't use VpSuperResolution if it was the reason that caused
// the VideoProcessorBlt failure.
if (SUCCEEDED(hr)) {
enable_vp_super_resolution_ = false;
}
}
// To prevent it from failing in all coming frames, disable overlay if
// VideoProcessorBlt is not implemented in the GPU driver.
if (hr == E_NOTIMPL) {
DisableDirectCompositionOverlays();
}
if (FAILED(hr) && use_vp_auto_hdr) {
DLOG(ERROR) << "Retry VideoProcessorBlt with VpAutoHDR off "
"after it failed with error 0x"
<< std::hex << hr;
ToggleVpAutoHDR(gpu_vendor_id_, video_context.Get(),
video_processor.Get(), false);
if (!RevertSwapChainToSDR(video_device, video_processor,
video_processor_enumerator, swap_chain3,
context1, src_color_space)) {
return false;
}
{
TRACE_EVENT0("gpu", "ID3D11VideoContext::VideoProcessorBlt");
hr = video_context->VideoProcessorBlt(
video_processor.Get(), output_view_.Get(), 0, 1, &stream);
}
base::UmaHistogramSparse(
"GPU.VideoProcessorBlt.VpAutoHDR.RetryOffAfterError", hr);
// We shouldn't use VpAutoHDR if it was the reason that caused
// the VideoProcessorBlt failure.
if (SUCCEEDED(hr)) {
enable_vp_auto_hdr_ = false;
}
}
if (FAILED(hr)) {
DLOG(ERROR) << "VideoProcessorBlt failed with error 0x" << std::hex << hr;
// To prevent it from failing in all coming frames, disable overlay if
// VideoProcessorBlt is not implemented in the GPU driver.
if (hr == E_NOTIMPL) {
DisableDirectCompositionOverlays();
}
return false;
}
}
@ -2056,6 +2181,10 @@ bool SwapChainPresenter::ReallocateSwapChain(
DLOG(ERROR) << "Failed to get adapter desc with error 0x" << std::hex << hr;
}
enable_vp_auto_hdr_ =
!layer_tree_->disable_vp_auto_hdr() && IsVpAutoHDREnabled(gpu_vendor_id_);
enable_vp_super_resolution_ = !layer_tree_->disable_vp_super_resolution();
return true;
}
@ -2116,4 +2245,60 @@ SwapChainPresenter::GetSwapChainMedia() const {
return nullptr;
}
bool SwapChainPresenter::RevertSwapChainToSDR(
Microsoft::WRL::ComPtr<ID3D11VideoDevice> video_device,
Microsoft::WRL::ComPtr<ID3D11VideoProcessor> video_processor,
Microsoft::WRL::ComPtr<ID3D11VideoProcessorEnumerator>
video_processor_enumerator,
Microsoft::WRL::ComPtr<IDXGISwapChain3> swap_chain3,
Microsoft::WRL::ComPtr<ID3D11VideoContext1> context1,
const gfx::ColorSpace& input_color_space) {
if (!video_device || !video_processor || !video_processor_enumerator ||
!swap_chain3 || !context1) {
return false;
}
// Restore the SDR swap chain and output view
if (!ReallocateSwapChain(
gfx::Size(swap_chain_size_),
GetSwapChainFormat(swap_chain_protected_video_type_, /*hdr=*/false),
swap_chain_protected_video_type_)) {
ReleaseSwapChainResources();
return false;
}
content_ = swap_chain_.Get();
Microsoft::WRL::ComPtr<ID3D11Texture2D> swap_chain_buffer;
swap_chain_->GetBuffer(0, IID_PPV_ARGS(&swap_chain_buffer));
D3D11_VIDEO_PROCESSOR_OUTPUT_VIEW_DESC output_desc = {};
output_desc.ViewDimension = D3D11_VPOV_DIMENSION_TEXTURE2D;
output_desc.Texture2D.MipSlice = 0;
HRESULT hr = video_device->CreateVideoProcessorOutputView(
swap_chain_buffer.Get(), video_processor_enumerator.Get(), &output_desc,
&output_view_);
if (FAILED(hr)) {
DLOG(ERROR) << "CreateVideoProcessorOutputView failed with error 0x"
<< std::hex << hr;
return false;
}
DCHECK(output_view_);
// Reset the output color space for the swap chain and video processor
bool is_yuv_swapchain = IsYUVSwapChainFormat(swap_chain_format_);
gfx::ColorSpace output_color_space =
GetOutputColorSpace(input_color_space, is_yuv_swapchain);
DXGI_COLOR_SPACE_TYPE output_dxgi_color_space =
gfx::ColorSpaceWin::GetDXGIColorSpace(output_color_space,
is_yuv_swapchain);
context1->VideoProcessorSetOutputColorSpace1(video_processor.Get(),
output_dxgi_color_space);
hr = swap_chain3->SetColorSpace1(output_dxgi_color_space);
if (FAILED(hr)) {
DLOG(ERROR) << "SetColorSpace1 failed with error 0x" << std::hex << hr;
return false;
}
return true;
}
} // namespace gl

@ -123,8 +123,8 @@ class SwapChainPresenter : public base::PowerStateObserver {
Microsoft::WRL::ComPtr<IDXGIKeyedMutex> keyed_mutex,
const gfx::Rect& content_rect,
const gfx::ColorSpace& src_color_space,
bool content_is_hdr,
absl::optional<DXGI_HDR_METADATA_HDR10> stream_hdr_metadata);
absl::optional<DXGI_HDR_METADATA_HDR10> stream_hdr_metadata,
bool use_vp_auto_hdr);
// Get the size of the monitor on which the window handle is displayed.
gfx::Size GetMonitorSize() const;
@ -245,6 +245,15 @@ class SwapChainPresenter : public base::PowerStateObserver {
// Release resources related to `PresentDCOMPSurface()`.
void ReleaseDCOMPSurfaceResourcesIfNeeded();
bool RevertSwapChainToSDR(
Microsoft::WRL::ComPtr<ID3D11VideoDevice> video_device,
Microsoft::WRL::ComPtr<ID3D11VideoProcessor> video_processor,
Microsoft::WRL::ComPtr<ID3D11VideoProcessorEnumerator>
video_processor_enumerator,
Microsoft::WRL::ComPtr<IDXGISwapChain3> swap_chain3,
Microsoft::WRL::ComPtr<ID3D11VideoContext1> context1,
const gfx::ColorSpace& input_color_space);
// The Direct Composition surface handle from MediaFoundationRenderer.
HANDLE dcomp_surface_handle_ = INVALID_HANDLE_VALUE;
@ -315,7 +324,8 @@ class SwapChainPresenter : public base::PowerStateObserver {
Microsoft::WRL::ComPtr<IDXGIDecodeSwapChain> decode_swap_chain_;
Microsoft::WRL::ComPtr<IUnknown> decode_surface_;
bool is_on_battery_power_;
bool force_vp_super_resolution_off_ = false;
bool enable_vp_auto_hdr_ = false;
bool enable_vp_super_resolution_ = false;
UINT gpu_vendor_id_ = 0;
// Number of frames per second.