Skip to content

Commit

Permalink
CanvasHDR: Explicitly plumb HDR mode
Browse files Browse the repository at this point in the history
The CanvasHDR API has two modes:
- In "default" HDR is only enabled for color spaces that would be
  HDR if used by images or videos (namely, HLG and PQ)
- In "extended", floating-point buffers can use an extended range,
  which will not be tone mapped.

Plumb this information from where it originates in
HTMLCanvasElement::configureHighDynamicRange through the compositor.
This path is identical to the path taken by HDRMetadata.

There are two important places where this path lands:
- TextureLayerImpl::GetContentColorUsage will inform the compositor
  that it needs to upgrade its buffer to HDR (in non-delegated
  compositing on macOS, and always on Windows).
- metal::HDRCopierLayer will enable HDR mode in delegated compositing
  on macOS.

Bug: 1274220
Change-Id: Ie4a90c6c755d454eb5e6a692ab65d813a5f088d4
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4004741
Commit-Queue: ccameron chromium <ccameron@chromium.org>
Reviewed-by: Juanmi Huertas <juanmihd@chromium.org>
Reviewed-by: Jeremy Roman <jbroman@chromium.org>
Commit-Queue: Juanmi Huertas <juanmihd@chromium.org>
Reviewed-by: Dominick Ng <dominickn@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1067511}
  • Loading branch information
ccameron-chromium authored and Chromium LUCI CQ committed Nov 4, 2022
1 parent d0aeb74 commit fba44b9
Show file tree
Hide file tree
Showing 29 changed files with 169 additions and 55 deletions.
11 changes: 8 additions & 3 deletions cc/layers/texture_layer.cc
Original file line number Diff line number Diff line change
Expand Up @@ -78,10 +78,14 @@ void TextureLayer::SetUV(const gfx::PointF& top_left,
SetNeedsCommit();
}

void TextureLayer::SetHDRMetadata(
void TextureLayer::SetHDRConfiguration(
gfx::HDRMode hdr_mode,
absl::optional<gfx::HDRMetadata> hdr_metadata) {
if (hdr_metadata_.Read(*this) == hdr_metadata)
if (hdr_mode_.Read(*this) == hdr_mode &&
hdr_metadata_.Read(*this) == hdr_metadata) {
return;
}
hdr_mode_.Write(*this) = hdr_mode;
hdr_metadata_.Write(*this) = hdr_metadata;
SetNeedsCommit();
}
Expand Down Expand Up @@ -215,7 +219,8 @@ void TextureLayer::PushPropertiesTo(
texture_layer->SetPremultipliedAlpha(premultiplied_alpha_.Read(*this));
texture_layer->SetBlendBackgroundColor(blend_background_color_.Read(*this));
texture_layer->SetForceTextureToOpaque(force_texture_to_opaque_.Read(*this));
texture_layer->SetHDRMetadata(hdr_metadata_.Read(*this));
texture_layer->SetHDRConfiguration(hdr_mode_.Read(*this),
hdr_metadata_.Read(*this));
if (needs_set_resource_.Read(*this)) {
viz::TransferableResource resource;
viz::ReleaseCallback release_callback;
Expand Down
4 changes: 3 additions & 1 deletion cc/layers/texture_layer.h
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,8 @@ class CC_EXPORT TextureLayer : public Layer, SharedBitmapIdRegistrar {
viz::ReleaseCallback release_callback);

// Set or unset HDR metadata.
void SetHDRMetadata(absl::optional<gfx::HDRMetadata> hdr_metadata);
void SetHDRConfiguration(gfx::HDRMode mode,
absl::optional<gfx::HDRMetadata> hdr_metadata);

void SetLayerTreeHost(LayerTreeHost* layer_tree_host) override;
bool Update() override;
Expand Down Expand Up @@ -179,6 +180,7 @@ class CC_EXPORT TextureLayer : public Layer, SharedBitmapIdRegistrar {
ProtectedSequenceReadable<bool> premultiplied_alpha_;
ProtectedSequenceReadable<bool> blend_background_color_;
ProtectedSequenceReadable<bool> force_texture_to_opaque_;
ProtectedSequenceReadable<gfx::HDRMode> hdr_mode_;
ProtectedSequenceWritable<absl::optional<gfx::HDRMetadata>> hdr_metadata_;

ProtectedSequenceWritable<scoped_refptr<TransferableResourceHolder>>
Expand Down
9 changes: 7 additions & 2 deletions cc/layers/texture_layer_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ void TextureLayerImpl::PushPropertiesTo(LayerImpl* layer) {
texture_layer->SetBlendBackgroundColor(blend_background_color_);
texture_layer->SetForceTextureToOpaque(force_texture_to_opaque_);
texture_layer->SetNearestNeighbor(nearest_neighbor_);
texture_layer->SetHDRMetadata(hdr_metadata_);
texture_layer->SetHDRConfiguration(hdr_mode_, hdr_metadata_);
if (own_resource_) {
texture_layer->SetTransferableResource(transferable_resource_,
std::move(release_callback_));
Expand Down Expand Up @@ -155,6 +155,7 @@ void TextureLayerImpl::AppendQuads(viz::CompositorRenderPass* render_pass,
nearest_neighbor_, /*secure_output=*/false,
gfx::ProtectedVideoType::kClear);
quad->set_resource_size_in_pixels(transferable_resource_.size);
quad->hdr_mode = hdr_mode_;
quad->hdr_metadata = hdr_metadata_;
ValidateQuadResources(quad);
}
Expand Down Expand Up @@ -200,6 +201,8 @@ void TextureLayerImpl::ReleaseResources() {
}

gfx::ContentColorUsage TextureLayerImpl::GetContentColorUsage() const {
if (hdr_mode_ == gfx::HDRMode::kExtended)
return gfx::ContentColorUsage::kHDR;
return transferable_resource_.color_space.GetContentColorUsage();
}

Expand Down Expand Up @@ -231,8 +234,10 @@ void TextureLayerImpl::SetUVBottomRight(const gfx::PointF& bottom_right) {
uv_bottom_right_ = bottom_right;
}

void TextureLayerImpl::SetHDRMetadata(
void TextureLayerImpl::SetHDRConfiguration(
gfx::HDRMode hdr_mode,
absl::optional<gfx::HDRMetadata> hdr_metadata) {
hdr_mode_ = hdr_mode;
hdr_metadata_ = hdr_metadata;
}

Expand Down
4 changes: 3 additions & 1 deletion cc/layers/texture_layer_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@ class CC_EXPORT TextureLayerImpl : public LayerImpl {
void SetNearestNeighbor(bool nearest_neighbor);
void SetUVTopLeft(const gfx::PointF& top_left);
void SetUVBottomRight(const gfx::PointF& bottom_right);
void SetHDRMetadata(absl::optional<gfx::HDRMetadata> hdr_metadata);
void SetHDRConfiguration(gfx::HDRMode mode,
absl::optional<gfx::HDRMetadata> hdr_metadata);

void SetTransferableResource(const viz::TransferableResource& resource,
viz::ReleaseCallback release_callback);
Expand Down Expand Up @@ -90,6 +91,7 @@ class CC_EXPORT TextureLayerImpl : public LayerImpl {
bool nearest_neighbor_ = false;
gfx::PointF uv_top_left_ = gfx::PointF();
gfx::PointF uv_bottom_right_ = gfx::PointF(1.f, 1.f);
gfx::HDRMode hdr_mode_ = gfx::HDRMode::kDefault;
absl::optional<gfx::HDRMetadata> hdr_metadata_;

// True while the |transferable_resource_| is owned by this layer, and
Expand Down
6 changes: 4 additions & 2 deletions cc/layers/texture_layer_unittest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -220,8 +220,10 @@ TEST_F(TextureLayerTest, CheckPropertyChangeCausesCorrectBehavior) {
gfx::PointF(0.25f, 0.25f), gfx::PointF(0.75f, 0.75f)));
EXPECT_SET_NEEDS_COMMIT(1, test_layer->SetPremultipliedAlpha(false));
EXPECT_SET_NEEDS_COMMIT(1, test_layer->SetBlendBackgroundColor(true));
EXPECT_SET_NEEDS_COMMIT(0, test_layer->SetHDRMetadata(absl::nullopt));
EXPECT_SET_NEEDS_COMMIT(1, test_layer->SetHDRMetadata(gfx::HDRMetadata()));
EXPECT_SET_NEEDS_COMMIT(0, test_layer->SetHDRConfiguration(
gfx::HDRMode::kDefault, absl::nullopt));
EXPECT_SET_NEEDS_COMMIT(1, test_layer->SetHDRConfiguration(
gfx::HDRMode::kDefault, gfx::HDRMetadata()));
}

class RunOnCommitLayerTreeHostClient : public FakeLayerTreeHostClient {
Expand Down
2 changes: 2 additions & 0 deletions components/metal_util/hdr_copier_layer.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

#include "components/metal_util/metal_util_export.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "ui/gfx/hdr_metadata.h"

#include <IOSurface/IOSurface.h>

Expand All @@ -21,6 +22,7 @@ namespace metal {

// Return true if we should use the HDRCopier for the specified content.
bool METAL_UTIL_EXPORT ShouldUseHDRCopier(IOSurfaceRef buffer,
gfx::HDRMode hdr_mode,
const gfx::ColorSpace& color_space);

// Create a layer which may have its contents set an HDR IOSurface via
Expand Down
70 changes: 39 additions & 31 deletions components/metal_util/hdr_copier_layer.mm
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
namespace {

// Source of the shader to perform tonemapping. Note that the functions
// ToLinearSRGB, ToLinearPQ, and ToLinearHLG are copy-pasted from the GLSL
// ToLinearSRGBIsh, ToLinearPQ, and ToLinearHLG are copy-pasted from the GLSL
// shader source in gfx::ColorTransform.
// TODO(https://crbug.com/1101041): Add non-identity tonemapping to the shader.
const char* tonemapping_shader_source =
Expand All @@ -45,13 +45,20 @@
" float2 texcoord;\n"
"} RasterizerData;\n"
"\n"
"float ToLinearSRGB(float v) {\n"
"float ToLinearSRGBIsh(float v, constant float* gabcdef) {\n"
" float g = gabcdef[0];\n"
" float a = gabcdef[1];\n"
" float b = gabcdef[2];\n"
" float c = gabcdef[3];\n"
" float d = gabcdef[4];\n"
" float e = gabcdef[5];\n"
" float f = gabcdef[6];\n"
" float abs_v = abs(v);\n"
" float sgn_v = sign(v);\n"
" if (abs_v < 0.0404482362771082f)\n"
" return v/12.92f;\n"
" if (abs_v < d)\n"
" return sgn_v*(c*abs_v + f);\n"
" else\n"
" return sgn_v*pow((abs_v+0.055f)/1.055f, 2.4f);\n"
" return sgn_v*(pow(a*abs_v+b, g) + e);\n"
"}\n"
"\n"
"float ToLinearPQ(float v) {\n"
Expand Down Expand Up @@ -96,15 +103,16 @@
"fragment float4 fragmentShader(RasterizerData in [[stage_in]],\n"
" texture2d<float> t [[texture(0)]],\n"
" constant float3x3& m [[buffer(0)]],\n"
" constant uint32_t& f [[buffer(1)]]) {\n"
" constant uint32_t& f [[buffer(1)]],\n"
" constant float* gabcdef [[buffer(2)]]) {\n"
" constexpr sampler s(metal::mag_filter::nearest,\n"
" metal::min_filter::nearest);\n"
" float4 color = t.sample(s, in.texcoord);\n"
" switch (f) {\n"
" case 1:\n"
" color.x = ToLinearSRGB(color.x);\n"
" color.y = ToLinearSRGB(color.y);\n"
" color.z = ToLinearSRGB(color.z);\n"
" color.x = ToLinearSRGBIsh(color.x, gabcdef);\n"
" color.y = ToLinearSRGBIsh(color.y, gabcdef);\n"
" color.z = ToLinearSRGBIsh(color.z, gabcdef);\n"
" break;\n"
" case 2:\n"
" color.x = ToLinearPQ(color.x);\n"
Expand All @@ -127,9 +135,11 @@
// defined in the above source. Return 0 if the transfer function is
// unsupported.
uint32_t GetTransferFunctionIndex(const gfx::ColorSpace& color_space) {
skcms_TransferFunction fn;
if (color_space.GetTransferFunction(&fn))
return 1;

switch (color_space.GetTransferID()) {
case gfx::ColorSpace::TransferID::SRGB_HDR:
return 1;
case gfx::ColorSpace::TransferID::PQ:
return 2;
case gfx::ColorSpace::TransferID::HLG:
Expand All @@ -139,20 +149,6 @@ uint32_t GetTransferFunctionIndex(const gfx::ColorSpace& color_space) {
}
}

bool MapsUnitIntervalToUnitInterval(const gfx::ColorSpace& color_space) {
switch (color_space.GetTransferID()) {
case gfx::ColorSpace::TransferID::SRGB_HDR:
return true;
case gfx::ColorSpace::TransferID::PQ:
case gfx::ColorSpace::TransferID::HLG:
return false;
default:
break;
}
NOTREACHED();
return false;
}

// Convert from an IOSurface's pixel format to a MTLPixelFormat. Crash on any
// unsupported formats. Return true in `is_unorm` if the format, when sampled,
// can produce values outside of [0, 1].
Expand Down Expand Up @@ -381,6 +377,9 @@ - (void)setHDRContents:(IOSurfaceRef)buffer
uint32_t transfer_function_index = GetTransferFunctionIndex(color_space);
DCHECK(transfer_function_index);

skcms_TransferFunction fn;
color_space.GetTransferFunction(&fn);

// Matrix is the primary transform matrix from |color_space| to sRGB.
simd::float3x3 matrix;
{
Expand All @@ -397,11 +396,12 @@ - (void)setHDRContents:(IOSurfaceRef)buffer
simd::make_float3(m.vals[0][2], m.vals[1][2], m.vals[2][2]));
}

[encoder setVertexBytes:positions length:sizeof(positions) atIndex:0];
[encoder setFragmentBytes:&matrix length:sizeof(matrix) atIndex:0];
[encoder setFragmentBytes:&transfer_function_index
length:sizeof(transfer_function_index)
atIndex:1];
[encoder setVertexBytes:positions length:sizeof(positions) atIndex:0];
[encoder setFragmentBytes:&matrix length:sizeof(matrix) atIndex:0];
[encoder setFragmentBytes:&fn length:sizeof(fn) atIndex:2];
[encoder drawPrimitives:MTLPrimitiveTypeTriangle
vertexStart:0
vertexCount:6];
Expand Down Expand Up @@ -446,6 +446,7 @@ void UpdateHDRCopierLayer(
}

bool ShouldUseHDRCopier(IOSurfaceRef buffer,
gfx::HDRMode hdr_mode,
const gfx::ColorSpace& color_space) {
if (@available(macos 10.15, *)) {
// Only some transfer functions are supported.
Expand All @@ -457,11 +458,18 @@ bool ShouldUseHDRCopier(IOSurfaceRef buffer,
if (IOSurfaceGetMTLPixelFormat(buffer, &is_unorm) == MTLPixelFormatInvalid)
return false;

// Unorm formats will only be in the [0, 1] range, so only copy them if
// their transfer function will actually go outside of [0, 1].
if (is_unorm && MapsUnitIntervalToUnitInterval(color_space))
return false;
if (color_space.IsToneMappedByDefault())
return true;

if (hdr_mode == gfx::HDRMode::kDefault) {
if (color_space.GetTransferID() ==
gfx::ColorSpace::TransferID::SRGB_HDR) {
// Rasterized tiles and the primary plane specify a color space of
// SRGB_HDR with gfx::HDRMode::kDefault.
return !is_unorm;
}
return false;
}
return true;
}
return false;
Expand Down
1 change: 1 addition & 0 deletions components/viz/common/quads/texture_draw_quad.h
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ class VIZ_COMMON_EXPORT TextureDrawQuad : public DrawQuad {
// creation (e.g. color space, protection type).
bool is_stream_video : 1;

gfx::HDRMode hdr_mode = gfx::HDRMode::kDefault;
absl::optional<gfx::HDRMetadata> hdr_metadata;

// kClear if the contents do not require any special protection. See enum of a
Expand Down
1 change: 1 addition & 0 deletions components/viz/service/display/ca_layer_overlay.cc
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ gfx::CALayerResult FromTextureQuad(DisplayResourceProvider* resource_provider,
}
ca_layer_overlay->opacity *= quad->vertex_opacity[0];
ca_layer_overlay->filter = quad->nearest_neighbor ? GL_NEAREST : GL_LINEAR;
ca_layer_overlay->hdr_mode = quad->hdr_mode;
ca_layer_overlay->hdr_metadata = quad->hdr_metadata;
if (quad->is_video_frame)
ca_layer_overlay->protected_video_type = quad->protected_video_type;
Expand Down
2 changes: 2 additions & 0 deletions components/viz/service/display/ca_layer_overlay.h
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@ class VIZ_SERVICE_EXPORT CALayerOverlay {
unsigned edge_aa_mask = 0;
// The minification and magnification filters for the CALayer.
unsigned filter = 0;
// The HDR mode for this quad.
gfx::HDRMode hdr_mode = gfx::HDRMode::kDefault;
// The HDR metadata for this quad.
absl::optional<gfx::HDRMetadata> hdr_metadata;
// The protected video status of the AVSampleBufferDisplayLayer.
Expand Down
2 changes: 2 additions & 0 deletions components/viz/service/display/overlay_candidate.h
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,8 @@ class VIZ_SERVICE_EXPORT OverlayCandidate {
gfx::BufferFormat format = gfx::BufferFormat::RGBA_8888;
// ColorSpace of the buffer for scanout.
gfx::ColorSpace color_space;
// HDR mode for the buffer.
gfx::HDRMode hdr_mode = gfx::HDRMode::kDefault;
// Optional HDR Metadata for the buffer.
absl::optional<gfx::HDRMetadata> hdr_metadata;
// Size of the resource, in pixels.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -441,7 +441,8 @@ void OutputPresenterGL::ScheduleOverlayPlane(
gfx::ToEnclosingRect(overlay_plane_candidate.bounds_rect),
overlay_plane_candidate.background_color.toSkColor(),
overlay_plane_candidate.edge_aa_mask, overlay_plane_candidate.opacity,
overlay_plane_candidate.filter, overlay_plane_candidate.hdr_metadata,
overlay_plane_candidate.filter, overlay_plane_candidate.hdr_mode,
overlay_plane_candidate.hdr_metadata,
overlay_plane_candidate.protected_video_type));
#endif
}
Expand Down
9 changes: 5 additions & 4 deletions gpu/ipc/service/image_transport_surface_overlay_mac.mm
Original file line number Diff line number Diff line change
Expand Up @@ -313,10 +313,11 @@
gfx::Transform(), image, overlay_plane_data.color_space,
overlay_plane_data.crop_rect, // contents_rect
gfx::ToNearestRect(overlay_plane_data.display_bounds), // rect
SK_ColorTRANSPARENT, // background_color
0, // edge_aa_mask
1.f, // opacity
GL_LINEAR, // filter
SK_ColorTRANSPARENT, // background_color
0, // edge_aa_mask
1.f, // opacity
GL_LINEAR, // filter
gfx::HDRMode::kDefault,
absl::nullopt, // hdr_metadata
gfx::ProtectedVideoType::kClear); // protected_video_type
return ca_layer_tree_coordinator_->GetPendingCARendererLayerTree()
Expand Down
1 change: 1 addition & 0 deletions services/viz/public/cpp/compositing/quads_mojom_traits.cc
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ bool StructTraits<viz::mojom::TextureQuadStateDataView, viz::DrawQuad>::Read(
if (!data.ReadUvTopLeft(&quad->uv_top_left) ||
!data.ReadUvBottomRight(&quad->uv_bottom_right) ||
!data.ReadProtectedVideoType(&protected_video_type) ||
!data.ReadHdrMode(&quad->hdr_mode) ||
!data.ReadHdrMetadata(&quad->hdr_metadata) ||
!data.ReadOverlayPriorityHint(&overlay_priority_hint)) {
return false;
Expand Down
6 changes: 6 additions & 0 deletions services/viz/public/cpp/compositing/quads_mojom_traits.h
Original file line number Diff line number Diff line change
Expand Up @@ -456,6 +456,12 @@ struct StructTraits<viz::mojom::TextureQuadStateDataView, viz::DrawQuad> {
return quad->is_video_frame;
}

static gfx::HDRMode hdr_mode(const viz::DrawQuad& input) {
const viz::TextureDrawQuad* quad =
viz::TextureDrawQuad::MaterialCast(&input);
return quad->hdr_mode;
}

static const absl::optional<gfx::HDRMetadata> hdr_metadata(
const viz::DrawQuad& input) {
const viz::TextureDrawQuad* quad =
Expand Down
1 change: 1 addition & 0 deletions services/viz/public/mojom/compositing/quads.mojom
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ struct TextureQuadState {
bool is_stream_video;
bool is_video_frame;
ProtectedVideoState protected_video_type;
gfx.mojom.HDRMode hdr_mode;
gfx.mojom.HDRMetadata? hdr_metadata;
gfx.mojom.Rect? damage_rect;
OverlayPriority overlay_priority_hint;
Expand Down
15 changes: 13 additions & 2 deletions third_party/blink/renderer/core/html/canvas/html_canvas_element.cc
Original file line number Diff line number Diff line change
Expand Up @@ -442,6 +442,17 @@ CanvasRenderingContext* HTMLCanvasElement::GetCanvasRenderingContextInternal(
void HTMLCanvasElement::configureHighDynamicRange(
const CanvasHighDynamicRangeOptions* options,
ExceptionState& exception_state) {
gfx::HDRMode hdr_mode = gfx::HDRMode::kDefault;
if (options->hasMode()) {
switch (options->mode().AsEnum()) {
case V8CanvasHighDynamicRangeMode::Enum::kDefault:
hdr_mode = gfx::HDRMode::kDefault;
break;
case V8CanvasHighDynamicRangeMode::Enum::kExtended:
hdr_mode = gfx::HDRMode::kExtended;
break;
}
}
absl::optional<gfx::HDRMetadata> hdr_metadata;
if (options->hasSmpteSt2086Metadata()) {
hdr_metadata = gfx::HDRMetadata();
Expand All @@ -465,13 +476,13 @@ void HTMLCanvasElement::configureHighDynamicRange(
NOTIMPLEMENTED();
}

CanvasResourceHost::SetHDRMetadata(hdr_metadata);
CanvasResourceHost::SetHDRConfiguration(hdr_mode, hdr_metadata);
if (context_ && (IsWebGL() || IsWebGPU())) {
// TODO(https://crbug.com/1274220): Implement HDR support for WebGL and
// WebGPU.
NOTIMPLEMENTED();
} else if (canvas2d_bridge_) {
canvas2d_bridge_->SetHDRMetadata(hdr_metadata);
canvas2d_bridge_->SetHDRConfiguration(hdr_mode, hdr_metadata);
}
}

Expand Down

0 comments on commit fba44b9

Please sign in to comment.