Support HDR Swap Chains without HDR Metadata in Chromium on Windows

Currently in Chromium on Windows swap chains with the
DXGI_FORMAT_R10G10B10A2_UNORM for HDR 10 content are only created
when there is accompanying HDR metadata. For WebRTC video streams,
HDR metadata is an optional extension. In addition the gfx
HDRMetadata structure
https://source.chromium.org/chromium/chromium/src/+/main:ui/gfx/hdr_metadata.h;l=132?q=gfx::HDRMetadata&ss=chromium
already has reasonable defaults (the gamut is set to rec2020,
minimum luminance to 0 nits, and maximum luminance to 10,000 nits)

Without metadata the DXGI swap chain defaults to
DXGI_FORMAT_B8G8R8A8_UNORM which is basically downgrading the
stream to SDR.

This patch provides functionality to allow hdr10 swap chains to
be created on Windows if there is no accompanying metadata if
the input color space is BT.2020 with the transfer function PQ.

We also ensure that the DC overlay processor allows the video
overlay to be created if the video format is BT.2020 with transfer
function PQ (HDR 10).

BUG=376304009

Change-Id: I15edb5ecb0d99359c9038c9505579ab6905753a2
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5976756
Reviewed-by: Sida Zhu <zhusida@bytedance.com>
Reviewed-by: Markus Handell <handellm@google.com>
Reviewed-by: Kenneth Russell <kbr@chromium.org>
Reviewed-by: Alexei Svitkine <asvitkine@chromium.org>
Reviewed-by: Maggie Chen <magchen@chromium.org>
Commit-Queue: Anantanarayanan Iyengar US <aiyengar@nvidia.com>
Cr-Commit-Position: refs/heads/main@{#1377106}
diff --git a/components/viz/service/display/dc_layer_overlay.cc b/components/viz/service/display/dc_layer_overlay.cc
index 8e09084..7d06734 100644
--- a/components/viz/service/display/dc_layer_overlay.cc
+++ b/components/viz/service/display/dc_layer_overlay.cc
@@ -59,23 +59,16 @@
   DC_LAYER_FAILED_NOT_DAMAGED = 15,
   DC_LAYER_FAILED_YUV_VIDEO_QUAD_MOVED = 16,
   DC_LAYER_FAILED_YUV_VIDEO_QUAD_HDR_TONE_MAPPING = 17,
-  DC_LAYER_FAILED_YUV_VIDEO_QUAD_NO_HDR_METADATA = 18,
+  DC_LAYER_FAILED_YUV_VIDEO_QUAD_NO_HDR_METADATA [[deprecated]] = 18,
   DC_LAYER_FAILED_YUV_VIDEO_QUAD_HLG = 19,
   DC_LAYER_FAILED_YUV_VIDEO_QUAD_NO_P010_VIDEO_PROCESSOR_SUPPORT = 20,
   DC_LAYER_FAILED_YUV_VIDEO_QUAD_HDR_NON_FULLSCREEN [[deprecated]] = 21,
   DC_LAYER_FAILED_YUV_VIDEO_QUAD_HDR_NON_P010 = 22,
   DC_LAYER_FAILED_YUV_VIDEO_QUAD_UNSUPPORTED_COLORSPACE = 23,
-  kMaxValue = DC_LAYER_FAILED_YUV_VIDEO_QUAD_UNSUPPORTED_COLORSPACE,
+  DC_LAYER_FAILED_YUV_VIDEO_QUAD_HDR_NON_PQ10 = 24,
+  kMaxValue = DC_LAYER_FAILED_YUV_VIDEO_QUAD_HDR_NON_PQ10,
 };
 
-bool IsCompatibleHDRMetadata(
-    const std::optional<gfx::HDRMetadata>& hdr_metadata) {
-  return hdr_metadata &&
-         ((hdr_metadata->smpte_st_2086 &&
-           hdr_metadata->smpte_st_2086->IsValid()) ||
-          (hdr_metadata->cta_861_3 && hdr_metadata->cta_861_3->IsValid()));
-}
-
 DCLayerResult ValidateYUVOverlay(
     const gfx::ProtectedVideoType& protected_video_type,
     const gfx::ColorSpace& video_color_space,
@@ -115,13 +108,17 @@
   }
 
   if (video_color_space.IsHDR()) {
-    // Otherwise, it could be a parser bug like https://crbug.com/1362288 if the
-    // hdr metadata is still missing. Missing `smpte_st_2086` or `cta_861_3`
-    // could always causes intel driver crash when in HDR overlay mode, and
-    // technically as long as one of the `smpte_st_2086` or `cta_861_3` exists
-    // could solve the crash issue.
-    if (!IsCompatibleHDRMetadata(hdr_metadata)) {
-      return DC_LAYER_FAILED_YUV_VIDEO_QUAD_NO_HDR_METADATA;
+    // We allow HDR10 overlays to be created without metadata if the input
+    // stream is BT.2020 and the transfer function is PQ (Perceptual
+    // Quantizer). For this combination, the corresponding DXGI color space is
+    // DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020 (full range RGB),
+    // DXGI_COLOR_SPACE_RGB_STUDIO_G2084_NONE_P2020 (studio range RGB)
+    // DXGI_COLOR_SPACE_YCBCR_STUDIO_G2084_TOPLEFT_P2020 (studio range YUV)
+    if ((video_color_space.GetPrimaryID() !=
+         gfx::ColorSpace::PrimaryID::BT2020) ||
+        (video_color_space.GetTransferID() !=
+         gfx::ColorSpace::TransferID::PQ)) {
+      return DC_LAYER_FAILED_YUV_VIDEO_QUAD_HDR_NON_PQ10;
     }
 
     // Do not promote hdr overlay if buffer is not in 10bit P010 format. as this
diff --git a/components/viz/service/display/overlay_dc_unittest.cc b/components/viz/service/display/overlay_dc_unittest.cc
index cc1a4aea..6d514d9 100644
--- a/components/viz/service/display/overlay_dc_unittest.cc
+++ b/components/viz/service/display/overlay_dc_unittest.cc
@@ -1699,7 +1699,7 @@
     EXPECT_EQ(0U, overlay_data.promoted_overlays.size());
   }
 
-  // Frame 3 should skip overlay as hdr metadata is invalid.
+  // Frame 3 should promote overlay even if hdr metadata is invalid.
   {
     auto pass = CreateRenderPass();
     pass->content_color_usage = gfx::ContentColorUsage::kHDR;
@@ -1722,8 +1722,9 @@
         &pass_list, render_pass_filters, render_pass_backdrop_filters,
         std::move(surface_damage_rect_list));
 
-    // Should skip overlay.
-    EXPECT_EQ(0U, overlay_data.promoted_overlays.size());
+    // Should promote overlay as we allow HDR 10 overlays with BT.2020 and
+    // transfer function PQ without hdr_metadata.
+    EXPECT_EQ(1U, overlay_data.promoted_overlays.size());
   }
 
   // Frame 4 should promote overlay as hdr metadata contains cta_861_3.
diff --git a/tools/metrics/histograms/metadata/gpu/enums.xml b/tools/metrics/histograms/metadata/gpu/enums.xml
index f48ff51..29d49f9 100644
--- a/tools/metrics/histograms/metadata/gpu/enums.xml
+++ b/tools/metrics/histograms/metadata/gpu/enums.xml
@@ -248,6 +248,7 @@
   <int value="21" label="Failed YUV video quad hdr non fullscreen"/>
   <int value="22" label="Failed YUV video quad hdr non p010"/>
   <int value="23" label="Failed YUV video quad unsupported colorspace"/>
+  <int value="24" label="Failed YUV video quad hdr non pq10"/>
 </enum>
 
 <enum name="DelegatedStatus">
diff --git a/ui/gl/swap_chain_presenter.cc b/ui/gl/swap_chain_presenter.cc
index 809de8aa..630cc3732 100644
--- a/ui/gl/swap_chain_presenter.cc
+++ b/ui/gl/swap_chain_presenter.cc
@@ -445,6 +445,12 @@
   return true;
 }
 
+bool IsCompatibleHDRMetadata(const gfx::HDRMetadata& hdr_metadata) {
+  return (
+      (hdr_metadata.smpte_st_2086 && hdr_metadata.smpte_st_2086->IsValid()) ||
+      (hdr_metadata.cta_861_3 && hdr_metadata.cta_861_3->IsValid()));
+}
+
 }  // namespace
 
 SwapChainPresenter::PresentationHistory::PresentationHistory() = default;
@@ -1543,19 +1549,29 @@
 
   bool content_is_hdr = input_color_space.IsHDR();
 
-  // Enable VideoProcessor-HDR for SDR content if the monitor supports it and
-  // the GPU driver version is not blocked (enable_vp_auto_hdr_). The actual GPU
-  // driver support will be queried right after InitializeVideoProcessor() and
-  // is checked in ToggleVpAutoHDR().
+  // Enable VideoProcessor-HDR for SDR content if the monitor supports it
+  // and the GPU driver version is not blocked (enable_vp_auto_hdr_). The
+  // actual GPU driver support will be queried right after
+  // InitializeVideoProcessor() and is checked in ToggleVpAutoHDR().
   bool use_vp_auto_hdr =
       !content_is_hdr &&
       DirectCompositionMonitorHDREnabled(layer_tree_->window()) &&
       enable_vp_auto_hdr_ && !is_on_battery_power_;
 
+  // We allow HDR10 swap chains to be created without metadata if the input
+  // stream is BT.2020 and the transfer function is PQ (Perceptual Quantizer).
+  // For this combination, the corresponding DXGI color space is
+  // DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020 (full range RGB),
+  // DXGI_COLOR_SPACE_RGB_STUDIO_G2084_NONE_P2020 (studio range RGB)
+  // DXGI_COLOR_SPACE_YCBCR_STUDIO_G2084_TOPLEFT_P2020 (studio range YUV)
+  bool content_is_pq10 =
+      (input_color_space.GetPrimaryID() ==
+       gfx::ColorSpace::PrimaryID::BT2020) &&
+      (input_color_space.GetTransferID() == gfx::ColorSpace::TransferID::PQ);
+
   bool use_hdr_swap_chain =
       DirectCompositionMonitorHDREnabled(layer_tree_->window()) &&
-      ((content_is_hdr && params.video_params.hdr_metadata.IsValid()) ||
-       use_vp_auto_hdr);
+      (content_is_pq10 || use_vp_auto_hdr);
 
   // Try to use P010 swapchain when playing 10-bit content on SDR monitor where
   // P010 pixel format is also detected as displayable surface, due to the
@@ -1634,9 +1650,19 @@
   }
 
   std::optional<DXGI_HDR_METADATA_HDR10> stream_metadata;
-  if (params.video_params.hdr_metadata.IsValid()) {
-    stream_metadata = HDRMetadataHelperWin::HDRMetadataToDXGI(
-        params.video_params.hdr_metadata);
+  if (content_is_pq10) {
+    gfx::HDRMetadata hdr_metadata = params.video_params.hdr_metadata;
+    // Potential parser bug (https://crbug.com/1362288) if HDR metadata is
+    // incompatible. Missing `smpte_st_2086` or `cta_861_3` can cause Intel
+    // driver crashes in HDR overlay mode. Having at least one of
+    // `smpte_st_2086` or `cta_861_3` can prevent crashes. If HDR metadata is
+    // invalid, set up default metadata (HdrMetadataSmpteSt2086) to avoid
+    // crashes.
+    if (!IsCompatibleHDRMetadata(hdr_metadata)) {
+      hdr_metadata = gfx::HDRMetadata::PopulateUnspecifiedWithDefaults(
+          std::make_optional(params.video_params.hdr_metadata));
+    }
+    stream_metadata = HDRMetadataHelperWin::HDRMetadataToDXGI(hdr_metadata);
   }
 
   if (!VideoProcessorBlt(std::move(input_texture), input_level,