Skip to content

Commit

Permalink
ozone/drm: wire and react to new GLSurface ColorSpaces
Browse files Browse the repository at this point in the history
When there are HDR quads to be presented, BufferQueue calls
Reshape() which in turn calls its GLSurfaces' Resize(). This
CL extends the implementation of GLSurface, GbmSurfaceless,
to catch the ColorSpace and send it to its DrmWindowProxy.

Said Proxy then tells DrmThread which sends the ColorSpace
to the DrmGpuDisplayManager that tells all its DrmDisplays.
If the DrmDisplay supports HDR, configures the linear gamma
ramp to reach full scale (full brightness) or just a fraction
of it, depending on the ColorSpace.

A diagram of the classes and their relationships can be found
in https://i.imgur.com/MqW3Gb8.png (https://imgur.com/a/KSxSln7).

Bug: 958166
Change-Id: I095ac9804bb6499b03f6b1179635827fcff07d06
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2210637
Commit-Queue: Miguel Casas <mcasas@chromium.org>
Reviewed-by: Daniel Nicoara <dnicoara@chromium.org>
Cr-Commit-Position: refs/heads/master@{#773248}
  • Loading branch information
yellowdoge authored and Commit Bot committed May 29, 2020
1 parent ce80258 commit 6a65733
Show file tree
Hide file tree
Showing 15 changed files with 326 additions and 10 deletions.
6 changes: 4 additions & 2 deletions ui/display/manager/display_change_observer.cc
Original file line number Diff line number Diff line change
Expand Up @@ -105,15 +105,17 @@ gfx::DisplayColorSpaces FillDisplayColorSpaces(
sdr_color_space, DisplaySnapshot::PrimaryFormat());

if (allow_high_bit_depth) {
constexpr float kSDRJoint = 0.55;
constexpr float kHDRLevel = 3.0;
gfx::ColorSpace hdr_color_space;
if (primary_id == gfx::ColorSpace::PrimaryID::CUSTOM) {
skcms_Matrix3x3 primary_matrix{};
snapshot_color_space.GetPrimaryMatrix(&primary_matrix);
hdr_color_space = gfx::ColorSpace::CreatePiecewiseHDR(
primary_id, 0.99, 2.0, &primary_matrix);
primary_id, kSDRJoint, kHDRLevel, &primary_matrix);
} else {
hdr_color_space =
gfx::ColorSpace::CreatePiecewiseHDR(primary_id, 0.99, 2.0);
gfx::ColorSpace::CreatePiecewiseHDR(primary_id, kSDRJoint, kHDRLevel);
}

display_color_spaces.SetOutputColorSpaceAndBufferFormat(
Expand Down
1 change: 1 addition & 0 deletions ui/ozone/platform/drm/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,7 @@ source_set("gbm_unittests") {
testonly = true
sources = [
"common/drm_util_unittest.cc",
"gpu/drm_display_unittest.cc",
"gpu/drm_overlay_manager_unittest.cc",
"gpu/drm_overlay_validator_unittest.cc",
"gpu/drm_thread_unittest.cc",
Expand Down
1 change: 1 addition & 0 deletions ui/ozone/platform/drm/gpu/drm_device.h
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,7 @@ class DrmDevice : public base::RefCountedThreadSafe<DrmDevice> {

protected:
friend class base::RefCountedThreadSafe<DrmDevice>;
friend class DrmDisplayTest;

virtual ~DrmDevice();

Expand Down
62 changes: 56 additions & 6 deletions ui/ozone/platform/drm/gpu/drm_display.cc
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "base/trace_event/trace_event.h"
#include "ui/display/types/display_snapshot.h"
#include "ui/display/types/gamma_ramp_rgb_entry.h"
#include "ui/gfx/color_space.h"
#include "ui/ozone/platform/drm/common/drm_util.h"
#include "ui/ozone/platform/drm/gpu/drm_device.h"
#include "ui/ozone/platform/drm/gpu/screen_manager.h"
Expand Down Expand Up @@ -82,12 +83,24 @@ std::vector<drmModeModeInfo> GetDrmModeVector(drmModeConnector* connector) {
return modes;
}

void FillLinearValues(std::vector<display::GammaRampRGBEntry>* table,
size_t table_size,
float max_value) {
for (size_t i = 0; i < table_size; i++) {
const uint16_t v =
max_value * std::numeric_limits<uint16_t>::max() * i / (table_size - 1);
struct display::GammaRampRGBEntry gamma_entry = {v, v, v};
table->push_back(gamma_entry);
}
}

} // namespace

DrmDisplay::DrmDisplay(ScreenManager* screen_manager,
const scoped_refptr<DrmDevice>& drm)
: screen_manager_(screen_manager), drm_(drm) {
}
: screen_manager_(screen_manager),
drm_(drm),
current_color_space_(gfx::ColorSpace::CreateSRGB()) {}

DrmDisplay::~DrmDisplay() {
}
Expand All @@ -112,6 +125,8 @@ std::unique_ptr<display::DisplaySnapshot> DrmDisplay::Update(

display_id_ = params->display_id();
modes_ = GetDrmModeVector(info->connector());
is_hdr_capable_ =
params->bits_per_channel() > 8 && params->color_space().IsHDR();
return params;
}

Expand Down Expand Up @@ -202,10 +217,14 @@ void DrmDisplay::SetBackgroundColor(const uint64_t background_color) {
void DrmDisplay::SetGammaCorrection(
const std::vector<display::GammaRampRGBEntry>& degamma_lut,
const std::vector<display::GammaRampRGBEntry>& gamma_lut) {
if (!drm_->plane_manager()->SetGammaCorrection(crtc_, degamma_lut,
gamma_lut)) {
LOG(ERROR) << "Failed to set gamma tables for display: crtc_id = " << crtc_;
}
// When both |degamma_lut| and |gamma_lut| are empty they are interpreted as
// "linear/pass-thru" [1]. If the display |is_hdr_capable_| we have to make
// sure the |current_color_space_| is considered properly.
// [1] https://www.kernel.org/doc/html/v4.19/gpu/drm-kms.html#color-management-properties
if (degamma_lut.empty() && gamma_lut.empty() && is_hdr_capable_)
SetColorSpace(current_color_space_);
else
CommitGammaCorrection(degamma_lut, gamma_lut);
}

// TODO(gildekel): consider reformatting this to use the new DRM API or cache
Expand All @@ -229,4 +248,35 @@ void DrmDisplay::SetPrivacyScreen(bool enabled) {
}
}

void DrmDisplay::SetColorSpace(const gfx::ColorSpace& color_space) {
// There's only something to do if the display supports HDR.
if (!is_hdr_capable_)
return;
current_color_space_ = color_space;

// When |color_space| is HDR we can simply leave the gamma tables empty, which
// is interpreted as "linear/pass-thru", see [1]. However when we have an SDR
// |color_space|, we need to write a scaled down |gamma| function to prevent
// the mode change brightness to be visible.
std::vector<display::GammaRampRGBEntry> degamma;
std::vector<display::GammaRampRGBEntry> gamma;
if (current_color_space_.IsHDR())
return CommitGammaCorrection(degamma, gamma);

// TODO(mcasas) This should be the same value as in DisplayChangeObservers's
// FillDisplayColorSpaces, move to a common place.
constexpr float kSDRJoint = 0.55;
// TODO(mcasas): Retrieve this from the |drm_| HardwareDisplayPlaneManager.
constexpr size_t kNumGammaSamples = 16ul;
FillLinearValues(&gamma, kNumGammaSamples, kSDRJoint);
CommitGammaCorrection(degamma, gamma);
}

void DrmDisplay::CommitGammaCorrection(
const std::vector<display::GammaRampRGBEntry>& degamma_lut,
const std::vector<display::GammaRampRGBEntry>& gamma_lut) {
if (!drm_->plane_manager()->SetGammaCorrection(crtc_, degamma_lut, gamma_lut))
LOG(ERROR) << "Failed to set gamma tables for display: crtc_id = " << crtc_;
}

} // namespace ui
11 changes: 10 additions & 1 deletion ui/ozone/platform/drm/gpu/drm_display.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "ui/display/types/display_constants.h"
#include "ui/gfx/color_space.h"
#include "ui/gfx/geometry/point.h"
#include "ui/ozone/platform/drm/common/scoped_drm_types.h"

Expand All @@ -24,7 +25,6 @@ struct GammaRampRGBEntry;
}

namespace ui {

class DrmDevice;
class HardwareDisplayControllerInfo;
class ScreenManager;
Expand Down Expand Up @@ -54,8 +54,15 @@ class DrmDisplay {
const std::vector<display::GammaRampRGBEntry>& degamma_lut,
const std::vector<display::GammaRampRGBEntry>& gamma_lut);
void SetPrivacyScreen(bool enabled);
void SetColorSpace(const gfx::ColorSpace& color_space);

void set_is_hdr_capable_for_testing(bool value) { is_hdr_capable_ = value; }

private:
void CommitGammaCorrection(
const std::vector<display::GammaRampRGBEntry>& degamma_lut,
const std::vector<display::GammaRampRGBEntry>& gamma_lut);

ScreenManager* screen_manager_; // Not owned.

int64_t display_id_ = -1;
Expand All @@ -64,6 +71,8 @@ class DrmDisplay {
ScopedDrmConnectorPtr connector_;
std::vector<drmModeModeInfo> modes_;
gfx::Point origin_;
bool is_hdr_capable_ = false;
gfx::ColorSpace current_color_space_;

DISALLOW_COPY_AND_ASSIGN(DrmDisplay);
};
Expand Down
192 changes: 192 additions & 0 deletions ui/ozone/platform/drm/gpu/drm_display_unittest.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
// Copyright 2020 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/ozone/platform/drm/gpu/drm_display.h"

#include <utility>

#include "base/test/task_environment.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/gfx/color_space.h"
#include "ui/gfx/linux/test/mock_gbm_device.h"
#include "ui/ozone/platform/drm/gpu/hardware_display_plane.h"
#include "ui/ozone/platform/drm/gpu/hardware_display_plane_manager.h"
#include "ui/ozone/platform/drm/gpu/mock_drm_device.h"
#include "ui/ozone/platform/drm/gpu/screen_manager.h"

using ::testing::_;
using ::testing::SizeIs;

// Verifies that the argument goes from 0 to the maximum uint16_t times |scale|.
MATCHER_P(MatchesLinearRamp, scale, "") {
EXPECT_FALSE(arg.empty());

EXPECT_EQ(arg.front().r, 0);
EXPECT_EQ(arg.front().g, 0);
EXPECT_EQ(arg.front().b, 0);

const uint16_t max_value = std::numeric_limits<uint16_t>::max() * scale;

const auto middle_element = arg[arg.size() / 2];
const uint16_t middle_value = max_value * (arg.size() / 2) / (arg.size() - 1);
EXPECT_EQ(middle_element.r, middle_value);
EXPECT_EQ(middle_element.g, middle_value);
EXPECT_EQ(middle_element.b, middle_value);

const uint16_t last_value = max_value;
EXPECT_EQ(arg.back().r, last_value);
EXPECT_EQ(arg.back().g, last_value);
EXPECT_EQ(arg.back().b, last_value);

return true;
}

namespace ui {

namespace {

class MockHardwareDisplayPlaneManager : public HardwareDisplayPlaneManager {
public:
explicit MockHardwareDisplayPlaneManager(DrmDevice* drm)
: HardwareDisplayPlaneManager(drm) {}
~MockHardwareDisplayPlaneManager() override = default;

MOCK_METHOD3(SetGammaCorrection,
bool(uint32_t crtc_id,
const std::vector<display::GammaRampRGBEntry>& degamma_lut,
const std::vector<display::GammaRampRGBEntry>& gamma_lut));

bool Modeset(uint32_t crtc_id,
uint32_t framebuffer_id,
uint32_t connector_id,
const drmModeModeInfo& mode,
const HardwareDisplayPlaneList& plane_list) override {
return false;
}
bool DisableModeset(uint32_t crtc_id, uint32_t connector) override {
return false;
}
bool Commit(HardwareDisplayPlaneList* plane_list,
scoped_refptr<PageFlipRequest> page_flip_request,
std::unique_ptr<gfx::GpuFence>* out_fence) override {
return false;
}
bool DisableOverlayPlanes(HardwareDisplayPlaneList* plane_list) override {
return false;
}
bool SetColorCorrectionOnAllCrtcPlanes(
uint32_t crtc_id,
ScopedDrmColorCtmPtr ctm_blob_data) override {
return false;
}
bool ValidatePrimarySize(const DrmOverlayPlane& primary,
const drmModeModeInfo& mode) override {
return false;
}
void RequestPlanesReadyCallback(
DrmOverlayPlaneList planes,
base::OnceCallback<void(DrmOverlayPlaneList planes)> callback) override {
return;
}
bool InitializePlanes() override { return false; }
bool SetPlaneData(HardwareDisplayPlaneList* plane_list,
HardwareDisplayPlane* hw_plane,
const DrmOverlayPlane& overlay,
uint32_t crtc_id,
const gfx::Rect& src_rect) override {
return false;
}
std::unique_ptr<HardwareDisplayPlane> CreatePlane(
uint32_t plane_id) override {
return nullptr;
}
bool IsCompatible(HardwareDisplayPlane* plane,
const DrmOverlayPlane& overlay,
uint32_t crtc_index) const override {
return false;
}
bool CommitColorMatrix(const CrtcProperties& crtc_props) override {
return false;
}
bool CommitGammaCorrection(const CrtcProperties& crtc_props) override {
return false;
}
};

} // namespace

class DrmDisplayTest : public testing::Test {
protected:
DrmDisplayTest()
: mock_drm_device_(base::MakeRefCounted<MockDrmDevice>(
std::make_unique<MockGbmDevice>())),
drm_display_(&screen_manager_, mock_drm_device_) {}

MockHardwareDisplayPlaneManager* AddMockHardwareDisplayPlaneManager() {
auto mock_hardware_display_plane_manager =
std::make_unique<MockHardwareDisplayPlaneManager>(
mock_drm_device_.get());
MockHardwareDisplayPlaneManager* pointer =
mock_hardware_display_plane_manager.get();
mock_drm_device_->plane_manager_ =
std::move(mock_hardware_display_plane_manager);
return pointer;
}

base::test::TaskEnvironment env_;
scoped_refptr<DrmDevice> mock_drm_device_;
ScreenManager screen_manager_;
DrmDisplay drm_display_;
};

TEST_F(DrmDisplayTest, SetColorSpace) {
drm_display_.set_is_hdr_capable_for_testing(true);
MockHardwareDisplayPlaneManager* plane_manager =
AddMockHardwareDisplayPlaneManager();

ON_CALL(*plane_manager, SetGammaCorrection(_, SizeIs(0), _))
.WillByDefault(::testing::Return(true));

const auto kHDRColorSpace = gfx::ColorSpace::CreateHDR10();
EXPECT_CALL(*plane_manager, SetGammaCorrection(_, SizeIs(0), SizeIs(0)));
drm_display_.SetColorSpace(kHDRColorSpace);

const auto kSDRColorSpace = gfx::ColorSpace::CreateREC709();
constexpr float kSDRReducedScale = 0.55;
EXPECT_CALL(
*plane_manager,
SetGammaCorrection(_, SizeIs(0), MatchesLinearRamp(kSDRReducedScale)));
drm_display_.SetColorSpace(kSDRColorSpace);
}

TEST_F(DrmDisplayTest, SetEmptyGammaCorrectionNonHDRDisplay) {
MockHardwareDisplayPlaneManager* plane_manager =
AddMockHardwareDisplayPlaneManager();

ON_CALL(*plane_manager, SetGammaCorrection(_, _, _))
.WillByDefault(::testing::Return(true));

EXPECT_CALL(*plane_manager, SetGammaCorrection(_, SizeIs(0), SizeIs(0)));
drm_display_.SetGammaCorrection(std::vector<display::GammaRampRGBEntry>(),
std::vector<display::GammaRampRGBEntry>());
}

TEST_F(DrmDisplayTest, SetEmptyGammaCorrectionHDRDisplay) {
drm_display_.set_is_hdr_capable_for_testing(true);
MockHardwareDisplayPlaneManager* plane_manager =
AddMockHardwareDisplayPlaneManager();

ON_CALL(*plane_manager, SetGammaCorrection(_, _, _))
.WillByDefault(::testing::Return(true));

constexpr float kSDRReducedScale = 0.55;
EXPECT_CALL(
*plane_manager,
SetGammaCorrection(_, SizeIs(0), MatchesLinearRamp(kSDRReducedScale)));
drm_display_.SetGammaCorrection(std::vector<display::GammaRampRGBEntry>(),
std::vector<display::GammaRampRGBEntry>());
}

} // namespace ui
11 changes: 11 additions & 0 deletions ui/ozone/platform/drm/gpu/drm_gpu_display_manager.cc
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,17 @@ void DrmGpuDisplayManager::SetPrivacyScreen(int64_t display_id, bool enabled) {
display->SetPrivacyScreen(enabled);
}

void DrmGpuDisplayManager::SetColorSpace(int64_t crtc_id,
const gfx::ColorSpace& color_space) {
for (const auto& display : displays_) {
if (display->crtc() == crtc_id) {
display->SetColorSpace(color_space);
return;
}
}
LOG(ERROR) << __func__ << " there is no display with CRTC ID " << crtc_id;
}

DrmDisplay* DrmGpuDisplayManager::FindDisplay(int64_t display_id) {
for (const auto& display : displays_) {
if (display->display_id() == display_id)
Expand Down
Loading

0 comments on commit 6a65733

Please sign in to comment.