Commit 788edb67 authored by Gil Dekel's avatar Gil Dekel Committed by Commit Bot

ozone/drm: Expose path topology of a display via DisplaySnapshot

This CL exposes the path topology of a display via its DisplaySnapshot
representation. This will later be used during display configuration
fallback strategies.

In addition, this CL adds three UMAs to track (two of them use the
new exposed path topology property):
1) the number of configured external displays
2) the number of configured external displays that are connected via
   an MST.
3) the percentage of configured displays connected via MST (i.e.
   num of external MST displays over total num of external displays).

Bug: b:170745518, 1105919
Test: display_unittests && ozone_unittests
Change-Id: Ida7270e60ff8d5969e1664788e2dcf7831d52c8b
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2527955
Commit-Queue: Gil Dekel <gildekel@chromium.org>
Reviewed-by: default avatarDaniel Nicoara <dnicoara@chromium.org>
Reviewed-by: default avatarSteven Holte <holte@chromium.org>
Reviewed-by: default avatarRobert Sesek <rsesek@chromium.org>
Reviewed-by: default avatarAndres Calderon Jaramillo <andrescj@chromium.org>
Cr-Commit-Position: refs/heads/master@{#831170}
parent b3163817
......@@ -2712,6 +2712,42 @@ reviews. Googlers can read more about this at go/gwsq-gerrit.
</summary>
</histogram>
<histogram name="ConfigureDisplays.Modeset.MstExternalDisplaysCount"
units="count" expires_after="2021-11-20">
<owner>gildekel@chromium.org</owner>
<owner>dcastagna@chromium.org</owner>
<owner>chromeos-gfx@chromium.org</owner>
<summary>
Total number of external displays connected via MST and being configured.
This is recorded every time an external display is connected/disconnected,
or when the display mode of a screen is about to change.
</summary>
</histogram>
<histogram name="ConfigureDisplays.Modeset.MstExternalDisplaysPercentage"
units="%" expires_after="2021-11-20">
<owner>gildekel@chromium.org</owner>
<owner>dcastagna@chromium.org</owner>
<owner>chromeos-gfx@chromium.org</owner>
<summary>
Percentage of external displays connected via MST and being configured. This
is recorded every time an external display is connected/disconnected, or
when the display mode of a screen is about to change.
</summary>
</histogram>
<histogram name="ConfigureDisplays.Modeset.TotalExternalDisplaysCount"
units="count" expires_after="2021-11-20">
<owner>gildekel@chromium.org</owner>
<owner>dcastagna@chromium.org</owner>
<owner>chromeos-gfx@chromium.org</owner>
<summary>
Total number of external displays being configured. This is recorded every
time an external display is connected/disconnected, or when the display mode
of a screen is about to change.
</summary>
</histogram>
<histogram name="Conflicts.ConfirmedBadModules" units="modules"
expires_after="2018-08-30">
<owner>chrisha@chromium.org</owner>
......
......@@ -5,6 +5,7 @@
#include "ui/display/fake/fake_display_snapshot.h"
#include <inttypes.h>
#include <stdint.h>
#include <utility>
#include <vector>
......@@ -162,11 +163,11 @@ std::unique_ptr<FakeDisplaySnapshot> Builder::Build() {
gfx::ScaleToRoundedSize(native_mode_->size(), PixelPitchMmFromDPI(dpi_));
return std::make_unique<FakeDisplaySnapshot>(
id_, origin_, physical_size, type_, is_aspect_preserving_scaling_,
has_overscan_, privacy_screen_state_, has_color_correction_matrix_,
color_correction_in_linear_space_, name_, std::move(modes_),
current_mode_, native_mode_, product_code_, maximum_cursor_size_,
color_space_, bits_per_channel_);
id_, origin_, physical_size, type_, base_connector_id_, path_topology_,
is_aspect_preserving_scaling_, has_overscan_, privacy_screen_state_,
has_color_correction_matrix_, color_correction_in_linear_space_, name_,
std::move(modes_), current_mode_, native_mode_, product_code_,
maximum_cursor_size_, color_space_, bits_per_channel_);
}
Builder& Builder::SetId(int64_t id) {
......@@ -214,6 +215,16 @@ Builder& Builder::SetType(DisplayConnectionType type) {
return *this;
}
Builder& Builder::SetBaseConnectorId(uint64_t base_connector_id) {
base_connector_id_ = base_connector_id;
return *this;
}
Builder& Builder::SetPathTopology(const std::vector<uint64_t>& path_topology) {
path_topology_ = path_topology;
return *this;
}
Builder& Builder::SetIsAspectPerservingScaling(bool val) {
is_aspect_preserving_scaling_ = val;
return *this;
......@@ -308,6 +319,8 @@ FakeDisplaySnapshot::FakeDisplaySnapshot(
const gfx::Point& origin,
const gfx::Size& physical_size,
DisplayConnectionType type,
uint64_t base_connector_id,
const std::vector<uint64_t>& path_topology,
bool is_aspect_preserving_scaling,
bool has_overscan,
PrivacyScreenState privacy_screen_state,
......@@ -325,6 +338,8 @@ FakeDisplaySnapshot::FakeDisplaySnapshot(
origin,
physical_size,
type,
base_connector_id,
path_topology,
is_aspect_preserving_scaling,
has_overscan,
privacy_screen_state,
......
......@@ -55,6 +55,8 @@ class FAKE_DISPLAY_EXPORT FakeDisplaySnapshot : public DisplaySnapshot {
Builder& AddMode(std::unique_ptr<DisplayMode> mode);
Builder& SetOrigin(const gfx::Point& origin);
Builder& SetType(DisplayConnectionType type);
Builder& SetBaseConnectorId(uint64_t base_connector_id);
Builder& SetPathTopology(const std::vector<uint64_t>& path_topology);
Builder& SetIsAspectPerservingScaling(bool is_aspect_preserving_scaling);
Builder& SetHasOverscan(bool has_overscan);
Builder& SetHasColorCorrectionMatrix(bool val);
......@@ -85,6 +87,8 @@ class FAKE_DISPLAY_EXPORT FakeDisplaySnapshot : public DisplaySnapshot {
gfx::Point origin_;
float dpi_ = 96.0;
DisplayConnectionType type_ = DISPLAY_CONNECTION_TYPE_UNKNOWN;
uint64_t base_connector_id_ = 1u;
std::vector<uint64_t> path_topology_ = {};
bool is_aspect_preserving_scaling_ = false;
bool has_overscan_ = false;
PrivacyScreenState privacy_screen_state_ = kNotSupported;
......@@ -106,6 +110,8 @@ class FAKE_DISPLAY_EXPORT FakeDisplaySnapshot : public DisplaySnapshot {
const gfx::Point& origin,
const gfx::Size& physical_size,
DisplayConnectionType type,
uint64_t base_connector_id,
const std::vector<uint64_t>& path_topology,
bool is_aspect_preserving_scaling,
bool has_overscan,
PrivacyScreenState privacy_screen_state,
......
......@@ -4,11 +4,15 @@
#include "ui/display/manager/configure_displays_task.h"
#include <cstddef>
#include "base/auto_reset.h"
#include "base/bind.h"
#include "base/containers/queue.h"
#include "base/logging.h"
#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.h"
#include "base/numerics/safe_conversions.h"
#include "base/stl_util.h"
#include "ui/display/manager/display_util.h"
#include "ui/display/types/display_configuration_params.h"
......@@ -19,6 +23,13 @@ namespace display {
namespace {
// Because we do not offer hardware mirroring, the maximal number of external
// displays that can be configured is limited by the number of available CRTCs,
// which is usually three. Since the lifetime of the UMA using this value is one
// year (exp. Nov. 2021), five buckets are more than enough for
// its histogram (between 0 to 4 external monitors).
constexpr int kMaxDisplaysCount = 5;
// Find the next best mode after |display_mode|. If none can be found return
// nullptr.
const DisplayMode* FindNextMode(const DisplaySnapshot& display_state,
......@@ -199,14 +210,41 @@ void ConfigureDisplaysTask::OnConfigured(
}
// Update the final state.
int mst_external_displays = 0;
size_t total_external_displays = requests_.size();
for (auto& request : requests_) {
// Is this display SST (single-stream vs. MST multi-stream).
bool sst_display = request.display->base_connector_id() &&
request.display->path_topology().empty();
if (!sst_display)
mst_external_displays++;
bool internal = request.display->type() == DISPLAY_CONNECTION_TYPE_INTERNAL;
if (internal)
total_external_displays--;
base::UmaHistogramBoolean(
internal ? "ConfigureDisplays.Internal.Modeset.FinalStatus"
: "ConfigureDisplays.External.Modeset.FinalStatus",
config_success);
}
base::UmaHistogramExactLinear(
"ConfigureDisplays.Modeset.TotalExternalDisplaysCount",
base::checked_cast<int>(total_external_displays), kMaxDisplaysCount);
base::UmaHistogramExactLinear(
"ConfigureDisplays.Modeset.MstExternalDisplaysCount",
mst_external_displays, kMaxDisplaysCount);
if (total_external_displays > 0) {
const int mst_displays_percentage =
100.0 * mst_external_displays / total_external_displays;
UMA_HISTOGRAM_PERCENTAGE(
"ConfigureDisplays.Modeset.MstExternalDisplaysPercentage",
mst_displays_percentage);
}
if (!config_success)
task_status_ = ERROR;
std::move(callback_).Run(task_status_);
......
......@@ -191,8 +191,9 @@ TEST_P(DisplayChangeObserverTest, GetExternalManagedDisplayModeList) {
TEST_P(DisplayChangeObserverTest, GetEmptyExternalManagedDisplayModeList) {
FakeDisplaySnapshot display_snapshot(
123, gfx::Point(), gfx::Size(), DISPLAY_CONNECTION_TYPE_UNKNOWN, false,
false, PrivacyScreenState::kNotSupported, false, false, std::string(), {},
123, gfx::Point(), gfx::Size(), DISPLAY_CONNECTION_TYPE_UNKNOWN,
/*base_connector_id=*/1u, /*path_topology=*/{}, false, false,
PrivacyScreenState::kNotSupported, false, false, std::string(), {},
nullptr, nullptr, 0, gfx::Size(), gfx::ColorSpace(),
/*bits_per_channel=*/8u);
......
......@@ -80,6 +80,8 @@ void CheckDisplaySnapShotMojoEqual(const DisplaySnapshot& input,
EXPECT_EQ(input.origin(), output.origin());
EXPECT_EQ(input.physical_size(), output.physical_size());
EXPECT_EQ(input.type(), output.type());
EXPECT_EQ(input.base_connector_id(), output.base_connector_id());
EXPECT_EQ(input.path_topology(), output.path_topology());
EXPECT_EQ(input.is_aspect_preserving_scaling(),
output.is_aspect_preserving_scaling());
EXPECT_EQ(input.has_overscan(), output.has_overscan());
......@@ -255,6 +257,8 @@ TEST(DisplayStructTraitsTest, DisplaySnapshotCurrentAndNativeModesNull) {
const gfx::Size physical_size(5, 9);
const gfx::Size maximum_cursor_size(3, 5);
const DisplayConnectionType type = DISPLAY_CONNECTION_TYPE_DISPLAYPORT;
const uint64_t base_connector_id = 1u;
const std::vector<uint64_t> path_topology{};
const bool is_aspect_preserving_scaling = true;
const bool has_overscan = true;
const PrivacyScreenState privacy_screen_state = kEnabled;
......@@ -277,12 +281,12 @@ TEST(DisplayStructTraitsTest, DisplaySnapshotCurrentAndNativeModesNull) {
const std::vector<uint8_t> edid = {1};
std::unique_ptr<DisplaySnapshot> input = std::make_unique<DisplaySnapshot>(
display_id, origin, physical_size, type, is_aspect_preserving_scaling,
has_overscan, privacy_screen_state, has_color_correction_matrix,
color_correction_in_linear_space, display_color_space, bits_per_channel,
display_name, sys_path, std::move(modes), PanelOrientation::kNormal, edid,
current_mode, native_mode, product_code, year_of_manufacture,
maximum_cursor_size);
display_id, origin, physical_size, type, base_connector_id, path_topology,
is_aspect_preserving_scaling, has_overscan, privacy_screen_state,
has_color_correction_matrix, color_correction_in_linear_space,
display_color_space, bits_per_channel, display_name, sys_path,
std::move(modes), PanelOrientation::kNormal, edid, current_mode,
native_mode, product_code, year_of_manufacture, maximum_cursor_size);
std::unique_ptr<DisplaySnapshot> output;
SerializeAndDeserialize<mojom::DisplaySnapshot>(input->Clone(), &output);
......@@ -298,6 +302,8 @@ TEST(DisplayStructTraitsTest, DisplaySnapshotCurrentModeNull) {
const gfx::Size physical_size(55, 49);
const gfx::Size maximum_cursor_size(13, 95);
const DisplayConnectionType type = DISPLAY_CONNECTION_TYPE_VGA;
const uint64_t base_connector_id = 1u;
const std::vector<uint64_t> path_topology{};
const bool is_aspect_preserving_scaling = true;
const bool has_overscan = true;
const PrivacyScreenState privacy_screen_state = kEnabled;
......@@ -320,12 +326,12 @@ TEST(DisplayStructTraitsTest, DisplaySnapshotCurrentModeNull) {
const std::vector<uint8_t> edid = {1};
std::unique_ptr<DisplaySnapshot> input = std::make_unique<DisplaySnapshot>(
display_id, origin, physical_size, type, is_aspect_preserving_scaling,
has_overscan, privacy_screen_state, has_color_correction_matrix,
color_correction_in_linear_space, display_color_space, bits_per_channel,
display_name, sys_path, std::move(modes), PanelOrientation::kNormal, edid,
current_mode, native_mode, product_code, year_of_manufacture,
maximum_cursor_size);
display_id, origin, physical_size, type, base_connector_id, path_topology,
is_aspect_preserving_scaling, has_overscan, privacy_screen_state,
has_color_correction_matrix, color_correction_in_linear_space,
display_color_space, bits_per_channel, display_name, sys_path,
std::move(modes), PanelOrientation::kNormal, edid, current_mode,
native_mode, product_code, year_of_manufacture, maximum_cursor_size);
std::unique_ptr<DisplaySnapshot> output;
SerializeAndDeserialize<mojom::DisplaySnapshot>(input->Clone(), &output);
......@@ -341,6 +347,8 @@ TEST(DisplayStructTraitsTest, DisplaySnapshotExternal) {
const gfx::Size physical_size(520, 320);
const gfx::Size maximum_cursor_size(4, 5);
const DisplayConnectionType type = DISPLAY_CONNECTION_TYPE_HDMI;
const uint64_t base_connector_id = 1u;
const std::vector<uint64_t> path_topology{};
const bool is_aspect_preserving_scaling = false;
const bool has_overscan = false;
const PrivacyScreenState privacy_screen_state = kDisabled;
......@@ -367,12 +375,12 @@ TEST(DisplayStructTraitsTest, DisplaySnapshotExternal) {
const std::vector<uint8_t> edid = {2, 3, 4, 5};
std::unique_ptr<DisplaySnapshot> input = std::make_unique<DisplaySnapshot>(
display_id, origin, physical_size, type, is_aspect_preserving_scaling,
has_overscan, privacy_screen_state, has_color_correction_matrix,
color_correction_in_linear_space, display_color_space, bits_per_channel,
display_name, sys_path, std::move(modes), PanelOrientation::kLeftUp, edid,
current_mode, native_mode, product_code, year_of_manufacture,
maximum_cursor_size);
display_id, origin, physical_size, type, base_connector_id, path_topology,
is_aspect_preserving_scaling, has_overscan, privacy_screen_state,
has_color_correction_matrix, color_correction_in_linear_space,
display_color_space, bits_per_channel, display_name, sys_path,
std::move(modes), PanelOrientation::kLeftUp, edid, current_mode,
native_mode, product_code, year_of_manufacture, maximum_cursor_size);
std::unique_ptr<DisplaySnapshot> output;
SerializeAndDeserialize<mojom::DisplaySnapshot>(input->Clone(), &output);
......@@ -387,6 +395,8 @@ TEST(DisplayStructTraitsTest, DisplaySnapshotInternal) {
const gfx::Size physical_size(270, 180);
const gfx::Size maximum_cursor_size(64, 64);
const DisplayConnectionType type = DISPLAY_CONNECTION_TYPE_INTERNAL;
const uint64_t base_connector_id = 1u;
const std::vector<uint64_t> path_topology{};
const bool is_aspect_preserving_scaling = true;
const bool has_overscan = false;
const PrivacyScreenState privacy_screen_state = kNotSupported;
......@@ -410,12 +420,12 @@ TEST(DisplayStructTraitsTest, DisplaySnapshotInternal) {
const std::vector<uint8_t> edid = {2, 3};
std::unique_ptr<DisplaySnapshot> input = std::make_unique<DisplaySnapshot>(
display_id, origin, physical_size, type, is_aspect_preserving_scaling,
has_overscan, privacy_screen_state, has_color_correction_matrix,
color_correction_in_linear_space, display_color_space, bits_per_channel,
display_name, sys_path, std::move(modes), PanelOrientation::kRightUp,
edid, current_mode, native_mode, product_code, year_of_manufacture,
maximum_cursor_size);
display_id, origin, physical_size, type, base_connector_id, path_topology,
is_aspect_preserving_scaling, has_overscan, privacy_screen_state,
has_color_correction_matrix, color_correction_in_linear_space,
display_color_space, bits_per_channel, display_name, sys_path,
std::move(modes), PanelOrientation::kRightUp, edid, current_mode,
native_mode, product_code, year_of_manufacture, maximum_cursor_size);
std::unique_ptr<DisplaySnapshot> output;
SerializeAndDeserialize<mojom::DisplaySnapshot>(input->Clone(), &output);
......
......@@ -16,6 +16,8 @@ struct DisplaySnapshot {
gfx.mojom.Point origin;
gfx.mojom.Size physical_size;
display.mojom.DisplayConnectionType type;
uint64 base_connector_id;
array<uint64> path_topology;
bool is_aspect_preserving_scaling;
bool has_overscan;
display.mojom.PrivacyScreenState privacy_screen_state;
......
......@@ -4,6 +4,8 @@
#include "ui/display/mojom/display_snapshot_mojom_traits.h"
#include <cstdint>
#include "mojo/public/cpp/base/file_path_mojom_traits.h"
#include "ui/display/types/display_constants.h"
#include "ui/gfx/color_space.h"
......@@ -81,6 +83,10 @@ bool StructTraits<display::mojom::DisplaySnapshotDataView,
if (!data.ReadType(&type))
return false;
std::vector<uint64_t> path_topology;
if (!data.ReadPathTopology(&path_topology))
return false;
display::PrivacyScreenState privacy_screen_state;
if (!data.ReadPrivacyScreenState(&privacy_screen_state))
return false;
......@@ -138,8 +144,8 @@ bool StructTraits<display::mojom::DisplaySnapshotDataView,
return false;
*out = std::make_unique<display::DisplaySnapshot>(
data.display_id(), origin, physical_size, type,
data.is_aspect_preserving_scaling(), data.has_overscan(),
data.display_id(), origin, physical_size, type, data.base_connector_id(),
path_topology, data.is_aspect_preserving_scaling(), data.has_overscan(),
privacy_screen_state, data.has_color_correction_matrix(),
data.color_correction_in_linear_space(), color_space,
data.bits_per_channel(), display_name, file_path, std::move(modes),
......
......@@ -39,6 +39,16 @@ struct StructTraits<display::mojom::DisplaySnapshotDataView,
return snapshot->type();
}
static uint64_t base_connector_id(
const std::unique_ptr<display::DisplaySnapshot>& snapshot) {
return snapshot->base_connector_id();
}
static const std::vector<uint64_t>& path_topology(
const std::unique_ptr<display::DisplaySnapshot>& snapshot) {
return snapshot->path_topology();
}
static display::PanelOrientation panel_orientation(
const std::unique_ptr<display::DisplaySnapshot>& snapshot) {
return snapshot->panel_orientation();
......
......@@ -65,6 +65,8 @@ DisplaySnapshot::DisplaySnapshot(int64_t display_id,
const gfx::Point& origin,
const gfx::Size& physical_size,
DisplayConnectionType type,
uint64_t base_connector_id,
const std::vector<uint64_t>& path_topology,
bool is_aspect_preserving_scaling,
bool has_overscan,
PrivacyScreenState privacy_screen_state,
......@@ -86,6 +88,8 @@ DisplaySnapshot::DisplaySnapshot(int64_t display_id,
origin_(origin),
physical_size_(physical_size),
type_(type),
base_connector_id_(base_connector_id),
path_topology_(path_topology),
is_aspect_preserving_scaling_(is_aspect_preserving_scaling),
has_overscan_(has_overscan),
privacy_screen_state_(privacy_screen_state),
......@@ -129,29 +133,37 @@ std::unique_ptr<DisplaySnapshot> DisplaySnapshot::Clone() {
}
return std::make_unique<DisplaySnapshot>(
display_id_, origin_, physical_size_, type_,
is_aspect_preserving_scaling_, has_overscan_, privacy_screen_state_,
has_color_correction_matrix_, color_correction_in_linear_space_,
color_space_, bits_per_channel_, display_name_, sys_path_,
std::move(clone_modes), panel_orientation_, edid_, cloned_current_mode,
cloned_native_mode, product_code_, year_of_manufacture_,
maximum_cursor_size_);
display_id_, origin_, physical_size_, type_, base_connector_id_,
path_topology_, is_aspect_preserving_scaling_, has_overscan_,
privacy_screen_state_, has_color_correction_matrix_,
color_correction_in_linear_space_, color_space_, bits_per_channel_,
display_name_, sys_path_, std::move(clone_modes), panel_orientation_,
edid_, cloned_current_mode, cloned_native_mode, product_code_,
year_of_manufacture_, maximum_cursor_size_);
}
std::string DisplaySnapshot::ToString() const {
std::string sharing_connector;
if (base_connector_id_) {
sharing_connector = path_topology_.empty() ? "NO" : "YES";
} else {
sharing_connector = "parsing_error";
}
return base::StringPrintf(
"id=%" PRId64
" current_mode=%s native_mode=%s origin=%s"
" panel_orientation=%d"
" physical_size=%s, type=%s name=\"%s\" (year:%d) "
"modes=(%s)",
" physical_size=%s, type=%s sharing_base_connector=%s name=\"%s\" "
"(year:%d) modes=(%s)",
display_id_,
current_mode_ ? current_mode_->ToString().c_str() : "nullptr",
native_mode_ ? native_mode_->ToString().c_str() : "nullptr",
origin_.ToString().c_str(), panel_orientation_,
physical_size_.ToString().c_str(),
DisplayConnectionTypeString(type_).c_str(), display_name_.c_str(),
year_of_manufacture_, ModeListString(modes_).c_str());
DisplayConnectionTypeString(type_).c_str(), sharing_connector.c_str(),
display_name_.c_str(), year_of_manufacture_,
ModeListString(modes_).c_str());
}
// static
......
......@@ -33,6 +33,8 @@ class DISPLAY_TYPES_EXPORT DisplaySnapshot {
const gfx::Point& origin,
const gfx::Size& physical_size,
DisplayConnectionType type,
uint64_t base_connector_id,
const std::vector<uint64_t>& path_topology,
bool is_aspect_preserving_scaling,
bool has_overscan,
PrivacyScreenState privacy_screen_state,
......@@ -57,6 +59,8 @@ class DISPLAY_TYPES_EXPORT DisplaySnapshot {
void set_origin(const gfx::Point& origin) { origin_ = origin; }
const gfx::Size& physical_size() const { return physical_size_; }
DisplayConnectionType type() const { return type_; }
uint64_t base_connector_id() const { return base_connector_id_; }
const std::vector<uint64_t>& path_topology() const { return path_topology_; }
bool is_aspect_preserving_scaling() const {
return is_aspect_preserving_scaling_;
}
......@@ -109,6 +113,53 @@ class DISPLAY_TYPES_EXPORT DisplaySnapshot {
const DisplayConnectionType type_;
// The next two private members represent the connection path between the
// source device and this display. Consider the following three-display setup:
// +-------------+
// | Source | +-------------+
// | (Device) | | BranchX |
// | | | (MST) |
// | [conn6]--->| [port1]--->DisplayA
// +-------------+ | |
// | | +-------------+
// | | | BranchY |
// | | | (MST) |
// | [port2]--->| [port1]----->DisplayB
// +-------------+ | |
// | [port2]----->DisplayC
// +-------------+
// [conn6]: is the root of the topology tree (a.k.a. the base connector),
// which maps to a physical connector on the device. This value can be used to
// determine if two or more external displays are sharing the same physical
// port.
// Important: Do not confuse this value with a display's connector ID!
// The base connector will be listed as disconnected when a branch device is
// attached to it to signal that it is not available for use, while new
// connector IDs are spawned for connected monitors down the path. A display's
// connector ID will be equal to the base connector ID only when the display
// is connected directly to the source device.
// [BranchX|port1]: is an output port to which DisplayA is connected.
// [BranchX|port2]: is an output port to which BranchY is connected.
// The ports on BranchY follow the same logic. Notice that port numbers across
// branch devices are NOT unique.
//
// Example 1: if |this| represents DisplayB:
// |base_connector_id_| == 6
// |path_topology_| == {2, 1}
// |base_connector_id_| != |this| connector id.
//
// Example 2: if |this| represents a display that is connected directly to the
// source device above:
// |base_connector_id_| == 6
// |path_topology_| == {}
// |base_connector_id_| == |this| connector id.
//
// The path is in a failed/error state if |base_connector_id_| == 0. This
// indicates that the display is connected to one or more branch devices, but
// the path could not be parsed.
const uint64_t base_connector_id_;
const std::vector<uint64_t> path_topology_;
const bool is_aspect_preserving_scaling_;
const bool has_overscan_;
......
......@@ -13,16 +13,23 @@
#include <algorithm>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "base/containers/flat_map.h"
#include "base/logging.h"
#include "base/metrics/histogram_functions.h"
#include "base/notreached.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_piece.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "ui/display/types/display_constants.h"
#include "ui/display/types/display_mode.h"
#include "ui/display/util/display_util.h"
#include "ui/display/util/edid_parser.h"
#include "ui/ozone/platform/drm/common/scoped_drm_types.h"
namespace ui {
......@@ -195,6 +202,20 @@ display::PrivacyScreenState GetPrivacyScreenState(int fd,
connector->prop_values[index]);
}
std::vector<uint64_t> GetPathTopology(int fd, drmModeConnector* connector) {
ScopedDrmPropertyBlobPtr path_blob =
GetDrmPropertyBlob(fd, connector, "PATH");
if (!path_blob) {
DCHECK_GT(connector->connector_id, 0u);
// The topology is consisted solely of the connector id.
return {base::strict_cast<uint64_t>(connector->connector_id)};
}
return ParsePathBlob(*path_blob);
}
bool IsAspectPreserving(int fd, drmModeConnector* connector) {
ScopedDrmPropertyPtr property;
int index = GetDrmProperty(fd, connector, "scaling mode", &property);
......@@ -437,6 +458,12 @@ std::unique_ptr<display::DisplaySnapshot> CreateDisplaySnapshot(
const gfx::Size physical_size =
gfx::Size(info->connector()->mmWidth, info->connector()->mmHeight);
const display::DisplayConnectionType type = GetDisplayType(info->connector());
uint64_t base_connector_id = 0u;
std::vector<uint64_t> path_topology = GetPathTopology(fd, info->connector());
if (!path_topology.empty()) {
base_connector_id = path_topology.front();
path_topology.erase(path_topology.begin());
}
const bool is_aspect_preserving_scaling =
IsAspectPreserving(fd, info->connector());
const display::PanelOrientation panel_orientation =
......@@ -498,12 +525,12 @@ std::unique_ptr<display::DisplaySnapshot> CreateDisplaySnapshot(
ExtractDisplayModes(info, active_pixel_size, &current_mode, &native_mode);
return std::make_unique<display::DisplaySnapshot>(
display_id, origin, physical_size, type, is_aspect_preserving_scaling,
has_overscan, privacy_screen_state, has_color_correction_matrix,
color_correction_in_linear_space, display_color_space, bits_per_channel,
display_name, sys_path, std::move(modes), panel_orientation, edid,
current_mode, native_mode, product_code, year_of_manufacture,
maximum_cursor_size);
display_id, origin, physical_size, type, base_connector_id, path_topology,
is_aspect_preserving_scaling, has_overscan, privacy_screen_state,
has_color_correction_matrix, color_correction_in_linear_space,
display_color_space, bits_per_channel, display_name, sys_path,
std::move(modes), panel_orientation, edid, current_mode, native_mode,
product_code, year_of_manufacture, maximum_cursor_size);
}
int GetFourCCFormatForOpaqueFramebuffer(gfx::BufferFormat format) {
......@@ -544,4 +571,64 @@ uint64_t GetEnumValueForName(int fd, int property_id, const char* str) {
return 0;
}
// Returns a vector that holds the path topology of the display. Returns an
// empty vector upon failure.
//
// A path topology c-string is of the format:
// mst:{DRM_BASE_CONNECTOR_ID#}-{BRANCH_1_PORT#}-...-{BRANCH_N_PORT#}\0
//
// For example, the display configuration:
// Device <--conn6-- MST1 <--port2-- MST2 <--port1-- Display
// may produce the following topology c-string:
// "mst:6-2-1"
//
// To see how this string is constructed in the DRM:
// https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/gpu/drm/drm_dp_mst_topology.c?h=v5.10-rc3#n2229
std::vector<uint64_t> ParsePathBlob(const drmModePropertyBlobRes& path_blob) {
if (!path_blob.length) {
LOG(ERROR) << "PATH property blob is empty.";
return {};
}
std::string path_str(
static_cast<char*>(path_blob.data),
base::strict_cast<std::string::size_type>(path_blob.length));
base::StringPiece path_string_piece(path_str);
path_string_piece = base::TrimString(path_string_piece, std::string("\0", 1u),
base::TRIM_TRAILING);
const std::string prefix("mst:");
if (!base::StartsWith(path_string_piece, prefix,
base::CompareCase::SENSITIVE)) {
LOG(ERROR) << "Invalid PATH string prefix. Does not contain '" << prefix
<< "'. Input: '" << path_str << "'";
return {};
}
path_string_piece.remove_prefix(prefix.length());
std::vector<uint64_t> path;
for (const auto& string_port :
base::SplitStringPiece(path_string_piece, "-", base::KEEP_WHITESPACE,
base::SPLIT_WANT_ALL)) {
uint64_t int_port = 0;
if (base::StringToUint64(string_port, &int_port) && int_port > 0) {
path.push_back(int_port);
} else {
LOG(ERROR)
<< "One or more port values in the PATH string are invalid. Input: '"
<< path_str << "'";
return {};
}
}
if (path.size() < 2) {
LOG(ERROR)
<< "Insufficient number of ports (should be at least 2 but found "
<< path.size() << "). Input: '" << path_str << "'";
return {};
}
return path;
}
} // namespace ui
......@@ -100,6 +100,8 @@ bool ModeIsInterlaced(const drmModeModeInfo& mode);
uint64_t GetEnumValueForName(int fd, int property_id, const char* str);
std::vector<uint64_t> ParsePathBlob(const drmModePropertyBlobRes& path_blob);
} // namespace ui
#endif // UI_OZONE_PLATFORM_DRM_COMMON_DRM_UTIL_H_
......@@ -16,6 +16,7 @@
#include "ui/display/types/display_snapshot.h"
#include "ui/display/util/edid_parser.h"
#include "ui/gfx/geometry/size.h"
#include "ui/ozone/platform/drm/common/scoped_drm_types.h"
namespace ui {
......@@ -180,4 +181,176 @@ TEST_F(DrmUtilTest, TestDisplayModesExtraction) {
EXPECT_EQ(extracted_modes[1].get(), native_mode);
}
TEST(PathBlobParser, InvalidBlob) {
char data[] = "this doesn't matter";
drmModePropertyBlobRes blob{1, 0, data};
EXPECT_TRUE(ParsePathBlob(blob).empty());
}
TEST(PathBlobParser, EmptyOrNullString) {
{
char empty[] = "";
drmModePropertyBlobRes blob{1, sizeof(empty), empty};
EXPECT_TRUE(ParsePathBlob(blob).empty());
}
{
char null[] = "\0";
drmModePropertyBlobRes blob{1, sizeof(null), null};
EXPECT_TRUE(ParsePathBlob(blob).empty());
}
}
TEST(PathBlobParser, InvalidPathFormat) {
// Space(s)
{
char s[] = " ";
drmModePropertyBlobRes blob{1, sizeof(s), s};
EXPECT_TRUE(ParsePathBlob(blob).empty());
}
{
char s[] = " ";
drmModePropertyBlobRes blob{1, sizeof(s), s};
EXPECT_TRUE(ParsePathBlob(blob).empty());
}
// Missing colon
{
char s[] = "mst6-2-1";
drmModePropertyBlobRes blob{1, sizeof(s), s};
EXPECT_TRUE(ParsePathBlob(blob).empty());
}
// Caps 'mst:'
{
char s[] = "MST:6-2-1";
drmModePropertyBlobRes blob{1, sizeof(s), s};
EXPECT_TRUE(ParsePathBlob(blob).empty());
}
// Subset of "mst:"
{
char s[] = "ms";
drmModePropertyBlobRes blob{1, sizeof(s), s};
EXPECT_TRUE(ParsePathBlob(blob).empty());
}
// No 'mst:'
{
char s[] = "6-2-1";
drmModePropertyBlobRes blob{1, sizeof(s), s};
EXPECT_TRUE(ParsePathBlob(blob).empty());
}
// Other colon-delimited prefix
{
char s[] = "path:6-2-1";
drmModePropertyBlobRes blob{1, sizeof(s), s};
EXPECT_TRUE(ParsePathBlob(blob).empty());
}
// Invalid port number or format
{
char s[] = "mst:";
drmModePropertyBlobRes blob{1, sizeof(s), s};
EXPECT_TRUE(ParsePathBlob(blob).empty());
}
{
char s[] = "mst:6";
drmModePropertyBlobRes blob{1, sizeof(s), s};
EXPECT_TRUE(ParsePathBlob(blob).empty());
}
{
char s[] = "mst::6-2-1";
drmModePropertyBlobRes blob{1, sizeof(s), s};
EXPECT_TRUE(ParsePathBlob(blob).empty());
}
{
char s[] = "mst:-6-2-1";
drmModePropertyBlobRes blob{1, sizeof(s), s};
EXPECT_TRUE(ParsePathBlob(blob).empty());
}
{
char s[] = "mst:6-2-1-";
drmModePropertyBlobRes blob{1, sizeof(s), s};
EXPECT_TRUE(ParsePathBlob(blob).empty());
}
{
char s[] = "mst:c7";
drmModePropertyBlobRes blob{1, sizeof(s), s};
EXPECT_TRUE(ParsePathBlob(blob).empty());
}
{
char s[] = "mst:6-b-1";
drmModePropertyBlobRes blob{1, sizeof(s), s};
EXPECT_TRUE(ParsePathBlob(blob).empty());
}
{
char s[] = "mst:6--2";
drmModePropertyBlobRes blob{1, sizeof(s), s};
EXPECT_TRUE(ParsePathBlob(blob).empty());
}
{
char s[] = "mst:---";
drmModePropertyBlobRes blob{1, sizeof(s), s};
EXPECT_TRUE(ParsePathBlob(blob).empty());
}
{
char s[] = "mst:6- -2- -1";
drmModePropertyBlobRes blob{1, sizeof(s), s};
EXPECT_TRUE(ParsePathBlob(blob).empty());
}
{
char s[] = "mst:6 -2-1";
drmModePropertyBlobRes blob{1, sizeof(s), s};
EXPECT_TRUE(ParsePathBlob(blob).empty());
}
{
char s[] = "mst:6-'2'-1";
drmModePropertyBlobRes blob{1, sizeof(s), s};
EXPECT_TRUE(ParsePathBlob(blob).empty());
}
// Null character
{
char s[] = "mst:6-\0-2-1";
drmModePropertyBlobRes blob{1, sizeof(s), s};
EXPECT_TRUE(ParsePathBlob(blob).empty());
}
}
TEST(PathBlobParser, ValidPathFormat) {
std::vector<uint64_t> expected = {6u, 2u, 1u};
{
char valid[] = "mst:6-2-1";
drmModePropertyBlobRes blob{1, sizeof(valid), valid};
EXPECT_EQ(expected, ParsePathBlob(blob));
}
{
char valid[] = "mst:6-2-1\0";
drmModePropertyBlobRes blob{1, sizeof(valid), valid};
EXPECT_EQ(expected, ParsePathBlob(blob));
}
{
char valid[] = {'m', 's', 't', ':', '6', '-', '2', '-', '1'};
drmModePropertyBlobRes blob{1, sizeof(valid), valid};
EXPECT_EQ(expected, ParsePathBlob(blob));
}
}
} // namespace ui
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