Commit 8768493d authored by Christopher Cameron's avatar Christopher Cameron Committed by Commit Bot

Mac: Cache monitor ICC profiles in the GPU process

When we draw IOSurfaces using CoreAnimation, there is a power "discount"
if the IOSurface has the exact same byte-for-byte ICC profile as the
monitor it is displayed on.

The browser retrieves these ICC profiles via ScreenMac, and the renderer
process is sent them via ScreenInfo, but the GPU process does not get
them. As a consequence, color spaces assigned to GLImages via
SetColorSpaceMetadataCHROMIUM do not match monitors' ICC profiles, and
power suffers.

Solve this by creating a DisplayICCProfiles class to provide a map from
ColorSpace objects to the ICC profile data that created them.
Instantiate this in the GPU process for use with by
GLImageIOSurface::SetColorSpace.

As a follow on to this patch (but not for merge):
- Change the zero copy path to use SetColorSpaceMetadataCHROMIUM
- Remove ICCProfile::FromCacheMac
- Remove sending ICCProfiles via IPC to the renderer (they are currently
  only used for this purpose)
- Make SetColorSpaceMetadataCHROMIUM be the only path that will produce
  this byte-for-byte ICC profile match (it will be the only one that
  matters)

Bug: 869570
Cq-Include-Trybots: luci.chromium.try:android_optional_gpu_tests_rel;luci.chromium.try:linux_optional_gpu_tests_rel;luci.chromium.try:mac_optional_gpu_tests_rel;luci.chromium.try:win_optional_gpu_tests_rel;master.tryserver.blink:linux_trusty_blink_rel
Change-Id: Ifb0981a97ccbb249b1d0dab1c0aa57e48931ebdb
Reviewed-on: https://chromium-review.googlesource.com/1157288
Commit-Queue: ccameron <ccameron@chromium.org>
Reviewed-by: default avatarKenneth Russell <kbr@chromium.org>
Reviewed-by: default avatarSunny Sachanandani <sunnyps@chromium.org>
Cr-Commit-Position: refs/heads/master@{#579946}
parent d0f379fd
...@@ -383,6 +383,10 @@ jumbo_component("color_space") { ...@@ -383,6 +383,10 @@ jumbo_component("color_space") {
"//skia", "//skia",
] ]
if (is_mac) { if (is_mac) {
sources += [
"mac/display_icc_profiles.cc",
"mac/display_icc_profiles.h",
]
libs = [ libs = [
"CoreFoundation.framework", "CoreFoundation.framework",
"CoreGraphics.framework", "CoreGraphics.framework",
......
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ui/gfx/mac/display_icc_profiles.h"
#include "ui/gfx/icc_profile.h"
namespace gfx {
DisplayICCProfiles* DisplayICCProfiles::GetInstance() {
static base::NoDestructor<DisplayICCProfiles> profiles;
return profiles.get();
}
base::ScopedCFTypeRef<CFDataRef> DisplayICCProfiles::GetDataForColorSpace(
const ColorSpace& color_space) {
UpdateIfNeeded();
base::ScopedCFTypeRef<CFDataRef> result;
auto found = map_.find(color_space);
if (found != map_.end())
result = found->second;
return result;
}
DisplayICCProfiles::DisplayICCProfiles() {
CGDisplayRegisterReconfigurationCallback(
DisplayICCProfiles::DisplayReconfigurationCallBack, this);
}
DisplayICCProfiles::~DisplayICCProfiles() {
NOTREACHED();
}
void DisplayICCProfiles::UpdateIfNeeded() {
if (!needs_update_)
return;
map_.clear();
// Always add Apple's sRGB profile.
base::ScopedCFTypeRef<CFDataRef> srgb_icc(CGColorSpaceCopyICCProfile(
CGColorSpaceCreateWithName(kCGColorSpaceSRGB)));
map_[ColorSpace::CreateSRGB()] = srgb_icc;
// Add the profiles for all active displays.
uint32_t display_count = 0;
CGError error = kCGErrorSuccess;
error = CGGetActiveDisplayList(0, nullptr, &display_count);
if (error != kCGErrorSuccess)
return;
std::vector<CGDirectDisplayID> displays(display_count);
error = CGGetActiveDisplayList(displays.size(), &displays[0], &display_count);
if (error != kCGErrorSuccess)
return;
for (uint32_t i = 0; i < display_count; ++i) {
base::ScopedCFTypeRef<CGColorSpaceRef> cg_color_space(
CGDisplayCopyColorSpace(displays[i]));
if (!cg_color_space)
continue;
base::ScopedCFTypeRef<CFDataRef> icc_data(
CGColorSpaceCopyICCProfile(cg_color_space));
if (!icc_data)
continue;
ICCProfile icc_profile = ICCProfile::FromData(CFDataGetBytePtr(icc_data),
CFDataGetLength(icc_data));
ColorSpace color_space = icc_profile.GetColorSpace();
// If the ICC profile isn't accurately parametrically approximated, then
// don't store its data (we will assign the best parametric fit to
// IOSurfaces, and rely on the system compositor to do conversion to the
// display profile).
if (color_space.IsParametricAccurate())
map_[color_space] = icc_data;
}
}
// static
void DisplayICCProfiles::DisplayReconfigurationCallBack(
CGDirectDisplayID display,
CGDisplayChangeSummaryFlags flags,
void* user_info) {
DisplayICCProfiles* profiles =
reinterpret_cast<DisplayICCProfiles*>(user_info);
profiles->needs_update_ = true;
}
} // namespace gfx
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef UI_GFX_MAC_DISPLAY_ICC_PROFILES_H_
#define UI_GFX_MAC_DISPLAY_ICC_PROFILES_H_
#include <CoreGraphics/CoreGraphics.h>
#include "base/containers/flat_map.h"
#include "base/mac/scoped_cftyperef.h"
#include "base/macros.h"
#include "base/no_destructor.h"
#include "ui/gfx/color_space.h"
#include "ui/gfx/color_space_export.h"
namespace gfx {
// A map from ColorSpace objects to the display ICC profile data from which the
// ColorSpace was derived.
// - The color space for an IOSurface, when composited by CoreAnimation, is
// specified via ICC profile metadata.
// - The power cost of compositing an IOSurface that has the same color space
// as the display it is being composited to is substantially less (~0.5 W for
// fullscreen updates at 60fps) than the cost of compositing an IOSurface
// that has a different color space than the display is being composited to.
// - This power savings is realized only if the ICC profile metadata on the
// IOSurface matches, byte-for-byte, the profile of the CGDirectDisplayID it
// is being displayed on.
// - This structure maintains a map from ColorSpace objects to ICC profile data
// for all displays in the system (and auto-updates as displays change).
class COLOR_SPACE_EXPORT DisplayICCProfiles {
public:
static DisplayICCProfiles* GetInstance();
// This will return null if |color_space| does not correspond to a display.
base::ScopedCFTypeRef<CFDataRef> GetDataForColorSpace(
const ColorSpace& color_space);
private:
friend class base::NoDestructor<DisplayICCProfiles>;
static void DisplayReconfigurationCallBack(CGDirectDisplayID display,
CGDisplayChangeSummaryFlags flags,
void* user_info);
DisplayICCProfiles();
~DisplayICCProfiles();
void UpdateIfNeeded();
base::flat_map<ColorSpace, base::ScopedCFTypeRef<CFDataRef>> map_;
bool needs_update_ = true;
DISALLOW_COPY_AND_ASSIGN(DisplayICCProfiles);
};
} // namespace gfx
#endif // UI_GFX_MAC_DISPLAY_ICC_PROFILES_H_
...@@ -215,6 +215,12 @@ void IOSurfaceSetColorSpace(IOSurfaceRef io_surface, ...@@ -215,6 +215,12 @@ void IOSurfaceSetColorSpace(IOSurfaceRef io_surface,
// If that fails, generate parametric data. // If that fails, generate parametric data.
if (!icc_profile.IsValid()) { if (!icc_profile.IsValid()) {
if (color_space == ColorSpace::CreateSRGB()) {
base::ScopedCFTypeRef<CFDataRef> srgb_icc(
CGColorSpaceCopyICCProfile(base::mac::GetSRGBColorSpace()));
IOSurfaceSetValue(io_surface, CFSTR("IOSurfaceColorSpace"), srgb_icc);
return;
}
icc_profile = icc_profile =
ICCProfile::FromParametricColorSpace(color_space.GetAsFullRangeRGB()); ICCProfile::FromParametricColorSpace(color_space.GetAsFullRangeRGB());
} }
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#include "base/trace_event/process_memory_dump.h" #include "base/trace_event/process_memory_dump.h"
#include "base/trace_event/trace_event.h" #include "base/trace_event/trace_event.h"
#include "ui/gfx/buffer_format_util.h" #include "ui/gfx/buffer_format_util.h"
#include "ui/gfx/mac/display_icc_profiles.h"
#include "ui/gfx/mac/io_surface.h" #include "ui/gfx/mac/io_surface.h"
#include "ui/gl/gl_bindings.h" #include "ui/gl/gl_bindings.h"
#include "ui/gl/gl_context.h" #include "ui/gl/gl_context.h"
...@@ -468,6 +469,20 @@ void GLImageIOSurface::SetColorSpace(const gfx::ColorSpace& color_space) { ...@@ -468,6 +469,20 @@ void GLImageIOSurface::SetColorSpace(const gfx::ColorSpace& color_space) {
if (color_space_ == color_space) if (color_space_ == color_space)
return; return;
color_space_ = color_space; color_space_ = color_space;
// Prefer to use data from DisplayICCProfiles, which will give a byte-for-byte
// match for color spaces of the system displays. Note that DisplayICCProfiles
// is not used in IOSurfaceSetColorSpace because that call may be made in the
// renderer process (e.g, for video frames).
base::ScopedCFTypeRef<CFDataRef> cf_data =
gfx::DisplayICCProfiles::GetInstance()->GetDataForColorSpace(color_space);
if (cf_data) {
IOSurfaceSetValue(io_surface_, CFSTR("IOSurfaceColorSpace"), cf_data);
return;
}
// Only if that fails, fall back to IOSurfaceSetColorSpace, which will
// generate a profile.
IOSurfaceSetColorSpace(io_surface_, color_space); IOSurfaceSetColorSpace(io_surface_, color_space);
} }
......
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