Commit e1e982cf authored by Christopher Cameron's avatar Christopher Cameron Committed by Commit Bot

Trigger HDR mode when viewing HDR content on macOS

Documentation of -[CAMetalLayer setWantsExtendedDynamicRangeContent:]
reads as follows.

  If any rendering context on the screen has this enabled, all rendered
  content is clamped to the screen’s
  maximumExtendedDynamicRangeColorComponentValue value rather than 1.0.

This indeed does what it says. The exact incantation under the covers
is unknown. I had previously guessed that setting IOSurface's color
space to an HDR color space would do the same trigger, but it does not.

This method privately exists on the CALayer base class.

To ensure that this is triggered appropriately, track whether or not
any IOSurfaces that we are using as CALayer contents are tagged with
an HDR color space. If so, set setWantsExtendedDynamicRangeContent
to YES on the root layer.

Add tests verifying this behavior.

Of note is that if the HDR layer is not promoted to an overlay, then
the buffer that we render it in to will not be appropriately allocated
as HDR-capable, and so the HDR content will end up being clipped.

Bug: 976426
Change-Id: Ib389f1c36d39950ddd06beea5320e5f1890b73a9
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1863689
Commit-Queue: ccameron <ccameron@chromium.org>
Reviewed-by: default avatarSidney San Martín <sdy@chromium.org>
Cr-Commit-Position: refs/heads/master@{#707119}
parent b462cc17
...@@ -16,6 +16,10 @@ ...@@ -16,6 +16,10 @@
#include "ui/gl/ca_renderer_layer_params.h" #include "ui/gl/ca_renderer_layer_params.h"
#include "ui/gl/gl_image_io_surface.h" #include "ui/gl/gl_image_io_surface.h"
@interface CALayer (Private)
@property BOOL wantsExtendedDynamicRangeContent;
@end
namespace gpu { namespace gpu {
namespace { namespace {
...@@ -1102,4 +1106,70 @@ TEST_F(CALayerTreeTest, FullscreenLowPower) { ...@@ -1102,4 +1106,70 @@ TEST_F(CALayerTreeTest, FullscreenLowPower) {
} }
} }
// Verify that sorting context zero is split at non-flat transforms.
TEST_F(CALayerTreeTest, HDRTrigger) {
std::unique_ptr<ui::CARendererLayerTree> ca_layer_trees[3]{
std::make_unique<ui::CARendererLayerTree>(true, true),
std::make_unique<ui::CARendererLayerTree>(true, true),
std::make_unique<ui::CARendererLayerTree>(true, true),
};
CALayerProperties properties;
properties.is_clipped = false;
properties.clip_rect = gfx::Rect();
properties.rect = gfx::Rect(0, 0, 256, 256);
bool result = false;
// We'll use the IOSurface contents to identify the content layers.
scoped_refptr<gl::GLImageIOSurface> sdr_image =
CreateGLImage(gfx::Size(256, 256), gfx::BufferFormat::BGRA_8888, false);
scoped_refptr<gl::GLImageIOSurface> hdr_image =
CreateGLImage(gfx::Size(256, 256), gfx::BufferFormat::BGRA_8888, false);
sdr_image->SetColorSpace(gfx::ColorSpace::CreateSRGB());
hdr_image->SetColorSpace(gfx::ColorSpace::CreateExtendedSRGB());
// Schedule and commit the HDR layer.
properties.gl_image = hdr_image;
result = ScheduleCALayer(ca_layer_trees[0].get(), &properties);
EXPECT_TRUE(result);
ca_layer_trees[0]->CommitScheduledCALayers(
superlayer_, nullptr, properties.rect.size(), properties.scale_factor);
// Validate that the root layer has is triggering HDR.
CALayer* content_layer = nil;
if (@available(macos 10.15, *)) {
CALayer* root_layer = [[superlayer_ sublayers] objectAtIndex:0];
CALayer* clip_and_sorting_layer = [[root_layer sublayers] objectAtIndex:0];
CALayer* clip_and_sorting_rounded_layer =
[[clip_and_sorting_layer sublayers] objectAtIndex:0];
CALayer* transform_layer =
[[clip_and_sorting_rounded_layer sublayers] objectAtIndex:0];
content_layer = [[transform_layer sublayers] objectAtIndex:0];
EXPECT_TRUE([content_layer wantsExtendedDynamicRangeContent]);
}
// Commit the SDR layer.
properties.gl_image = sdr_image;
result = ScheduleCALayer(ca_layer_trees[1].get(), &properties);
EXPECT_TRUE(result);
ca_layer_trees[1]->CommitScheduledCALayers(
superlayer_, std::move(ca_layer_trees[0]), properties.rect.size(),
properties.scale_factor);
// Validate that HDR is off.
if (@available(macos 10.15, *))
EXPECT_FALSE([content_layer wantsExtendedDynamicRangeContent]);
// Commit the HDR layer.
properties.gl_image = hdr_image;
result = ScheduleCALayer(ca_layer_trees[2].get(), &properties);
EXPECT_TRUE(result);
ca_layer_trees[2]->CommitScheduledCALayers(
superlayer_, std::move(ca_layer_trees[1]), properties.rect.size(),
properties.scale_factor);
// Validate that HDR is back on.
if (@available(macos 10.15, *))
EXPECT_TRUE([content_layer wantsExtendedDynamicRangeContent]);
}
} // namespace gpu } // namespace gpu
...@@ -160,6 +160,7 @@ class ACCELERATED_WIDGET_MAC_EXPORT CARendererLayerTree { ...@@ -160,6 +160,7 @@ class ACCELERATED_WIDGET_MAC_EXPORT CARendererLayerTree {
const gfx::RectF& contents_rect, const gfx::RectF& contents_rect,
const gfx::Rect& rect, const gfx::Rect& rect,
unsigned background_color, unsigned background_color,
bool triggers_hdr,
unsigned edge_aa_mask, unsigned edge_aa_mask,
float opacity, float opacity,
unsigned filter); unsigned filter);
...@@ -181,6 +182,7 @@ class ACCELERATED_WIDGET_MAC_EXPORT CARendererLayerTree { ...@@ -181,6 +182,7 @@ class ACCELERATED_WIDGET_MAC_EXPORT CARendererLayerTree {
gfx::RectF contents_rect; gfx::RectF contents_rect;
gfx::RectF rect; gfx::RectF rect;
unsigned background_color = 0; unsigned background_color = 0;
const bool triggers_hdr;
// Note that the CoreAnimation edge antialiasing mask is not the same as // Note that the CoreAnimation edge antialiasing mask is not the same as
// the edge antialiasing mask passed to the constructor. // the edge antialiasing mask passed to the constructor.
CAEdgeAntialiasingMask ca_edge_aa_mask = 0; CAEdgeAntialiasingMask ca_edge_aa_mask = 0;
......
...@@ -21,6 +21,10 @@ ...@@ -21,6 +21,10 @@
#include "ui/gl/ca_renderer_layer_params.h" #include "ui/gl/ca_renderer_layer_params.h"
#include "ui/gl/gl_image_io_surface.h" #include "ui/gl/gl_image_io_surface.h"
@interface CALayer (Private)
@property BOOL wantsExtendedDynamicRangeContent;
@end
namespace ui { namespace ui {
namespace { namespace {
...@@ -384,6 +388,7 @@ CARendererLayerTree::ContentLayer::ContentLayer( ...@@ -384,6 +388,7 @@ CARendererLayerTree::ContentLayer::ContentLayer(
const gfx::RectF& contents_rect, const gfx::RectF& contents_rect,
const gfx::Rect& rect_in, const gfx::Rect& rect_in,
unsigned background_color, unsigned background_color,
bool triggers_hdr,
unsigned edge_aa_mask, unsigned edge_aa_mask,
float opacity, float opacity,
unsigned filter) unsigned filter)
...@@ -392,6 +397,7 @@ CARendererLayerTree::ContentLayer::ContentLayer( ...@@ -392,6 +397,7 @@ CARendererLayerTree::ContentLayer::ContentLayer(
contents_rect(contents_rect), contents_rect(contents_rect),
rect(rect_in), rect(rect_in),
background_color(background_color), background_color(background_color),
triggers_hdr(triggers_hdr),
ca_edge_aa_mask(0), ca_edge_aa_mask(0),
opacity(opacity), opacity(opacity),
ca_filter(filter == GL_LINEAR ? kCAFilterLinear : kCAFilterNearest) { ca_filter(filter == GL_LINEAR ? kCAFilterLinear : kCAFilterNearest) {
...@@ -478,6 +484,7 @@ CARendererLayerTree::ContentLayer::ContentLayer(ContentLayer&& layer) ...@@ -478,6 +484,7 @@ CARendererLayerTree::ContentLayer::ContentLayer(ContentLayer&& layer)
contents_rect(layer.contents_rect), contents_rect(layer.contents_rect),
rect(layer.rect), rect(layer.rect),
background_color(layer.background_color), background_color(layer.background_color),
triggers_hdr(layer.triggers_hdr),
ca_edge_aa_mask(layer.ca_edge_aa_mask), ca_edge_aa_mask(layer.ca_edge_aa_mask),
opacity(layer.opacity), opacity(layer.opacity),
ca_filter(layer.ca_filter), ca_filter(layer.ca_filter),
...@@ -553,6 +560,7 @@ void CARendererLayerTree::TransformLayer::AddContentLayer( ...@@ -553,6 +560,7 @@ void CARendererLayerTree::TransformLayer::AddContentLayer(
const CARendererLayerParams& params) { const CARendererLayerParams& params) {
base::ScopedCFTypeRef<IOSurfaceRef> io_surface; base::ScopedCFTypeRef<IOSurfaceRef> io_surface;
base::ScopedCFTypeRef<CVPixelBufferRef> cv_pixel_buffer; base::ScopedCFTypeRef<CVPixelBufferRef> cv_pixel_buffer;
bool triggers_hdr = false;
if (params.image) { if (params.image) {
gl::GLImageIOSurface* io_surface_image = gl::GLImageIOSurface* io_surface_image =
gl::GLImageIOSurface::FromGLImage(params.image); gl::GLImageIOSurface::FromGLImage(params.image);
...@@ -566,12 +574,12 @@ void CARendererLayerTree::TransformLayer::AddContentLayer( ...@@ -566,12 +574,12 @@ void CARendererLayerTree::TransformLayer::AddContentLayer(
// TODO(ccameron): If this indeed causes the bug to disappear, then // TODO(ccameron): If this indeed causes the bug to disappear, then
// extirpate the CVPixelBufferRef path. // extirpate the CVPixelBufferRef path.
// cv_pixel_buffer = io_surface_image->cv_pixel_buffer(); // cv_pixel_buffer = io_surface_image->cv_pixel_buffer();
triggers_hdr = params.image->color_space().IsHDR();
} }
content_layers.push_back( content_layers.push_back(
ContentLayer(tree, io_surface, cv_pixel_buffer, params.contents_rect, ContentLayer(tree, io_surface, cv_pixel_buffer, params.contents_rect,
params.rect, params.background_color, params.edge_aa_mask, params.rect, params.background_color, triggers_hdr,
params.opacity, params.filter)); params.edge_aa_mask, params.opacity, params.filter));
} }
void CARendererLayerTree::RootLayer::CommitToCA(CALayer* superlayer, void CARendererLayerTree::RootLayer::CommitToCA(CALayer* superlayer,
...@@ -747,6 +755,7 @@ void CARendererLayerTree::ContentLayer::CommitToCA(CALayer* superlayer, ...@@ -747,6 +755,7 @@ void CARendererLayerTree::ContentLayer::CommitToCA(CALayer* superlayer,
bool update_contents_rect = true; bool update_contents_rect = true;
bool update_rect = true; bool update_rect = true;
bool update_background_color = true; bool update_background_color = true;
bool update_triggers_hdr = true;
bool update_ca_edge_aa_mask = true; bool update_ca_edge_aa_mask = true;
bool update_opacity = true; bool update_opacity = true;
bool update_ca_filter = true; bool update_ca_filter = true;
...@@ -760,6 +769,7 @@ void CARendererLayerTree::ContentLayer::CommitToCA(CALayer* superlayer, ...@@ -760,6 +769,7 @@ void CARendererLayerTree::ContentLayer::CommitToCA(CALayer* superlayer,
update_contents_rect = old_layer->contents_rect != contents_rect; update_contents_rect = old_layer->contents_rect != contents_rect;
update_rect = old_layer->rect != rect; update_rect = old_layer->rect != rect;
update_background_color = old_layer->background_color != background_color; update_background_color = old_layer->background_color != background_color;
update_triggers_hdr = old_layer->triggers_hdr != triggers_hdr;
update_ca_edge_aa_mask = old_layer->ca_edge_aa_mask != ca_edge_aa_mask; update_ca_edge_aa_mask = old_layer->ca_edge_aa_mask != ca_edge_aa_mask;
update_opacity = old_layer->opacity != opacity; update_opacity = old_layer->opacity != opacity;
update_ca_filter = old_layer->ca_filter != ca_filter; update_ca_filter = old_layer->ca_filter != ca_filter;
...@@ -780,8 +790,8 @@ void CARendererLayerTree::ContentLayer::CommitToCA(CALayer* superlayer, ...@@ -780,8 +790,8 @@ void CARendererLayerTree::ContentLayer::CommitToCA(CALayer* superlayer,
DCHECK_EQ([ca_layer superlayer], superlayer); DCHECK_EQ([ca_layer superlayer], superlayer);
bool update_anything = update_contents || update_contents_rect || bool update_anything = update_contents || update_contents_rect ||
update_rect || update_background_color || update_rect || update_background_color ||
update_ca_edge_aa_mask || update_opacity || update_triggers_hdr || update_ca_edge_aa_mask ||
update_ca_filter; update_opacity || update_ca_filter;
if (use_av_layer) { if (use_av_layer) {
if (update_contents) { if (update_contents) {
bool result = false; bool result = false;
...@@ -835,6 +845,15 @@ void CARendererLayerTree::ContentLayer::CommitToCA(CALayer* superlayer, ...@@ -835,6 +845,15 @@ void CARendererLayerTree::ContentLayer::CommitToCA(CALayer* superlayer,
CGColorSpaceCreateWithName(kCGColorSpaceSRGB), rgba_color_components)); CGColorSpaceCreateWithName(kCGColorSpaceSRGB), rgba_color_components));
[ca_layer setBackgroundColor:srgb_background_color]; [ca_layer setBackgroundColor:srgb_background_color];
} }
if (update_triggers_hdr) {
if (@available(macos 10.15, *)) {
if ([ca_layer
respondsToSelector:(@selector
(setWantsExtendedDynamicRangeContent:))]) {
[ca_layer setWantsExtendedDynamicRangeContent:triggers_hdr];
}
}
}
if (update_ca_edge_aa_mask) if (update_ca_edge_aa_mask)
[ca_layer setEdgeAntialiasingMask:ca_edge_aa_mask]; [ca_layer setEdgeAntialiasingMask:ca_edge_aa_mask];
if (update_opacity) if (update_opacity)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment