Commit 36b941dd authored by Gil Dekel's avatar Gil Dekel Committed by Chromium LUCI CQ

ui/display: Submit modeset requests individually during fallback

This CL changes the fallback strategy in ConfigureDisplaysTask to submit
requests individually when an atomic modeset test fails. More
particularly, it groups displays by the physical connectors they are
attached to and submit these request groups for atomic modesetting.

For example, if the request {internal display, MST1-1, MST1-2, HDMI}
fails as a whole, the next configuration attempt would repeatedly try
each of the following groups until a modeset succeeds: {internal
display}, {MST1-1, MST1-2} and {HDMI}. When a request group fails
modeset, the request with the highest bandwidth requirement is
downgraded first.

Finally, if any of the request groups exhausts all its alternative
modes, the entire configuration attempt terminates and signals back an
ERROR status.

In addition, this CL adds extensive testing for multiple display
configurations, including multiple MST hubs, nested MST structures,
MST and non-MST (i.e. independent) external displays, bad MST hubs, and
configurations with no internal displays, such as a Chromebox.

Bug: b:176819129, b:177356832
Test: display_unittests && ozone_unittests
Change-Id: Iac2b7f0330a74396c72f8d3867552dde593800e6
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2621674
Commit-Queue: Gil Dekel <gildekel@chromium.org>
Reviewed-by: default avatarDaniel Nicoara <dnicoara@chromium.org>
Reviewed-by: default avatarGil Dekel <gildekel@chromium.org>
Cr-Commit-Position: refs/heads/master@{#846418}
parent 2fdb6229
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include "base/auto_reset.h" #include "base/auto_reset.h"
#include "base/bind.h" #include "base/bind.h"
#include "base/containers/flat_set.h"
#include "base/containers/queue.h" #include "base/containers/queue.h"
#include "base/logging.h" #include "base/logging.h"
#include "base/metrics/histogram_functions.h" #include "base/metrics/histogram_functions.h"
...@@ -17,6 +18,7 @@ ...@@ -17,6 +18,7 @@
#include "ui/display/manager/display_util.h" #include "ui/display/manager/display_util.h"
#include "ui/display/types/display_configuration_params.h" #include "ui/display/types/display_configuration_params.h"
#include "ui/display/types/display_constants.h" #include "ui/display/types/display_constants.h"
#include "ui/display/types/display_mode.h"
#include "ui/display/types/display_snapshot.h" #include "ui/display/types/display_snapshot.h"
#include "ui/display/types/native_display_delegate.h" #include "ui/display/types/native_display_delegate.h"
...@@ -132,19 +134,31 @@ void UpdateResolutionAndRefreshRateUma(const DisplayConfigureRequest& request) { ...@@ -132,19 +134,31 @@ void UpdateResolutionAndRefreshRateUma(const DisplayConfigureRequest& request) {
histogram->Add(request.mode ? std::round(request.mode->refresh_rate()) : 0); histogram->Add(request.mode ? std::round(request.mode->refresh_rate()) : 0);
} }
void UpdateAttemptSucceededUma(DisplaySnapshot* display, bool display_success) { void UpdateAttemptSucceededUma(
const bool internal = display->type() == DISPLAY_CONNECTION_TYPE_INTERNAL; const std::vector<DisplayConfigureRequest>& requests,
base::UmaHistogramBoolean( bool display_success) {
internal ? "ConfigureDisplays.Internal.Modeset.AttemptSucceeded" for (const auto& request : requests) {
: "ConfigureDisplays.External.Modeset.AttemptSucceeded", const bool internal =
display_success); request.display->type() == DISPLAY_CONNECTION_TYPE_INTERNAL;
base::UmaHistogramBoolean(
internal ? "ConfigureDisplays.Internal.Modeset.AttemptSucceeded"
: "ConfigureDisplays.External.Modeset.AttemptSucceeded",
display_success);
VLOG(2) << "Configured status=" << display_success
<< " display=" << request.display->display_id()
<< " origin=" << request.origin.ToString()
<< " mode=" << (request.mode ? request.mode->ToString() : "null");
}
} }
void UpdateFinalStatusUma(const std::vector<DisplayConfigureRequest>& requests, void UpdateFinalStatusUma(
bool config_success) { const std::vector<RequestAndStatusList>& requests_and_statuses) {
int mst_external_displays = 0; int mst_external_displays = 0;
size_t total_external_displays = requests.size(); size_t total_external_displays = requests_and_statuses.size();
for (auto& request : requests) { for (auto& request_and_status : requests_and_statuses) {
const DisplayConfigureRequest& request = request_and_status.first;
// Is this display SST (single-stream vs. MST multi-stream). // Is this display SST (single-stream vs. MST multi-stream).
bool sst_display = request.display->base_connector_id() && bool sst_display = request.display->base_connector_id() &&
request.display->path_topology().empty(); request.display->path_topology().empty();
...@@ -158,7 +172,7 @@ void UpdateFinalStatusUma(const std::vector<DisplayConfigureRequest>& requests, ...@@ -158,7 +172,7 @@ void UpdateFinalStatusUma(const std::vector<DisplayConfigureRequest>& requests,
base::UmaHistogramBoolean( base::UmaHistogramBoolean(
internal ? "ConfigureDisplays.Internal.Modeset.FinalStatus" internal ? "ConfigureDisplays.Internal.Modeset.FinalStatus"
: "ConfigureDisplays.External.Modeset.FinalStatus", : "ConfigureDisplays.External.Modeset.FinalStatus",
config_success); request_and_status.second);
} }
base::UmaHistogramExactLinear( base::UmaHistogramExactLinear(
...@@ -213,9 +227,14 @@ void ConfigureDisplaysTask::Run() { ...@@ -213,9 +227,14 @@ void ConfigureDisplaysTask::Run() {
UpdateResolutionAndRefreshRateUma(request); UpdateResolutionAndRefreshRateUma(request);
} }
delegate_->Configure(config_requests, const auto& on_configured =
base::BindOnce(&ConfigureDisplaysTask::OnConfigured, pending_display_group_requests_.empty()
weak_ptr_factory_.GetWeakPtr())); ? &ConfigureDisplaysTask::OnFirstAttemptConfigured
: &ConfigureDisplaysTask::OnRetryConfigured;
delegate_->Configure(
config_requests,
base::BindOnce(on_configured, weak_ptr_factory_.GetWeakPtr()));
} }
void ConfigureDisplaysTask::OnConfigurationChanged() {} void ConfigureDisplaysTask::OnConfigurationChanged() {}
...@@ -227,45 +246,128 @@ void ConfigureDisplaysTask::OnDisplaySnapshotsInvalidated() { ...@@ -227,45 +246,128 @@ void ConfigureDisplaysTask::OnDisplaySnapshotsInvalidated() {
std::move(callback_).Run(task_status_); std::move(callback_).Run(task_status_);
} }
void ConfigureDisplaysTask::OnConfigured(bool config_success) { void ConfigureDisplaysTask::OnFirstAttemptConfigured(bool config_success) {
bool should_reconfigure = false; UpdateAttemptSucceededUma(requests_, config_success);
for (auto& request : requests_) { if (!config_success) {
// Update displays upon success or prep |requests_| for reconfiguration. // Partition |requests_| into smaller groups, update the task's state, and
// initiate the retry logic. The next time |delegate_|->Configure()
// terminates OnRetryConfigured() will be executed instead.
PartitionRequests();
DCHECK(!pending_display_group_requests_.empty());
requests_ = pending_display_group_requests_.front();
task_status_ = PARTIAL_SUCCESS;
Run();
return;
}
// This code execute only when the first modeset attempt fully succeeds.
// Update the displays' status and report success.
for (const auto& request : requests_) {
request.display->set_current_mode(request.mode);
request.display->set_origin(request.origin);
final_requests_status_.emplace_back(std::make_pair(request, true));
}
UpdateFinalStatusUma(final_requests_status_);
std::move(callback_).Run(task_status_);
}
void ConfigureDisplaysTask::OnRetryConfigured(bool config_success) {
UpdateAttemptSucceededUma(requests_, config_success);
if (!config_success) {
// If one of the largest display request can be downgraded, try again.
// Otherwise this configuration task is a failure.
if (DowngradeLargestRequestWithAlternativeModes()) {
Run();
return;
} else {
task_status_ = ERROR;
}
}
// This code executes only when this display group request fully succeeds or
// fails to modeset. Update the final status of this group.
for (const auto& request : requests_) {
final_requests_status_.emplace_back(
std::make_pair(request, config_success));
if (config_success) { if (config_success) {
request.display->set_current_mode(request.mode); request.display->set_current_mode(request.mode);
request.display->set_origin(request.origin); request.display->set_origin(request.origin);
} else {
// For the failing config, check if there is another mode to be requested.
// If there is one, attempt to reconfigure everything again.
const DisplayMode* next_mode =
FindNextMode(*request.display, request.mode);
if (next_mode) {
request.mode = next_mode;
should_reconfigure = true;
}
} }
VLOG(2) << "Configured status=" << config_success
<< " display=" << request.display->display_id()
<< " origin=" << request.origin.ToString()
<< " mode=" << (request.mode ? request.mode->ToString() : "null");
UpdateAttemptSucceededUma(request.display, config_success);
} }
if (should_reconfigure) { // Subsequent modeset attempts will be done on the next pending display group,
task_status_ = PARTIAL_SUCCESS; // if one exists.
pending_display_group_requests_.pop();
requests_.clear();
if (!pending_display_group_requests_.empty()) {
requests_ = pending_display_group_requests_.front();
Run(); Run();
return; return;
} }
// Update the final state. // No more display groups to retry.
UpdateFinalStatusUma(requests_, config_success); UpdateFinalStatusUma(final_requests_status_);
if (!config_success)
task_status_ = ERROR;
std::move(callback_).Run(task_status_); std::move(callback_).Run(task_status_);
} }
void ConfigureDisplaysTask::PartitionRequests() {
pending_display_group_requests_ = PartitionedRequestsQueue();
base::flat_set<uint64_t> handled_connectors;
for (size_t i = 0; i < requests_.size(); ++i) {
uint64_t connector_id = requests_[i].display->base_connector_id();
if (handled_connectors.find(connector_id) != handled_connectors.end())
continue;
std::vector<DisplayConfigureRequest> request_group;
for (size_t j = i; j < requests_.size(); ++j) {
if (connector_id == requests_[j].display->base_connector_id())
request_group.push_back(requests_[j]);
}
pending_display_group_requests_.push(request_group);
handled_connectors.insert(connector_id);
}
}
bool ConfigureDisplaysTask::DowngradeLargestRequestWithAlternativeModes() {
auto cmp = [](DisplayConfigureRequest* lhs, DisplayConfigureRequest* rhs) {
return *lhs->mode < *rhs->mode;
};
std::priority_queue<DisplayConfigureRequest*,
std::vector<DisplayConfigureRequest*>, decltype(cmp)>
sorted_requests(cmp);
for (auto& request : requests_) {
if (request.display->type() == DISPLAY_CONNECTION_TYPE_INTERNAL)
continue;
if (!request.mode)
continue;
sorted_requests.push(&request);
}
// Fail if there are no viable candidates to downgrade
if (sorted_requests.empty())
return false;
while (!sorted_requests.empty()) {
DisplayConfigureRequest* next_request = sorted_requests.top();
sorted_requests.pop();
const DisplayMode* next_mode =
FindNextMode(*next_request->display, next_request->mode);
if (next_mode) {
next_request->mode = next_mode;
return true;
}
}
return false;
}
} // namespace display } // namespace display
...@@ -11,7 +11,6 @@ ...@@ -11,7 +11,6 @@
#include <vector> #include <vector>
#include "base/callback.h" #include "base/callback.h"
#include "base/containers/flat_map.h"
#include "base/containers/queue.h" #include "base/containers/queue.h"
#include "base/macros.h" #include "base/macros.h"
#include "base/memory/weak_ptr.h" #include "base/memory/weak_ptr.h"
...@@ -35,7 +34,25 @@ struct DISPLAY_MANAGER_EXPORT DisplayConfigureRequest { ...@@ -35,7 +34,25 @@ struct DISPLAY_MANAGER_EXPORT DisplayConfigureRequest {
gfx::Point origin; gfx::Point origin;
}; };
// Applies the display configuration asynchronously. using RequestAndStatusList = std::pair<DisplayConfigureRequest, bool>;
// ConfigureDisplaysTask is in charge of applying the display configuration as
// requested by Ash. If the original request fails, the task will attempt to
// modify the request by downgrading the resolution of one or more of the
// displays and try again until it either succeeds a modeset or exhaust all
// available options.
//
// Displays are bandwidth constrained in 2 ways: (1) system memory bandwidth
// (ie: scanning pixels from memory), and (2) link bandwidth (ie: scanning
// pixels from the SoC to the display). Naturally all displays share (1),
// however with DisplayPort Multi-stream Transport (DP MST), displays may also
// share (2). The introduction of MST support drastically increases the
// likelihood of modeset failures due to (2) since multiple displays will all be
// sharing the same physical connection.
//
// If we're constrained by (1), reducing the resolution of any display will
// relieve pressure. However if we're constrained by (2), only those displays on
// the saturated link can relieve pressure.
class DISPLAY_MANAGER_EXPORT ConfigureDisplaysTask class DISPLAY_MANAGER_EXPORT ConfigureDisplaysTask
: public NativeDisplayObserver { : public NativeDisplayObserver {
public: public:
...@@ -52,6 +69,8 @@ class DISPLAY_MANAGER_EXPORT ConfigureDisplaysTask ...@@ -52,6 +69,8 @@ class DISPLAY_MANAGER_EXPORT ConfigureDisplaysTask
}; };
using ResponseCallback = base::OnceCallback<void(Status)>; using ResponseCallback = base::OnceCallback<void(Status)>;
using PartitionedRequestsQueue =
std::queue<std::vector<DisplayConfigureRequest>>;
ConfigureDisplaysTask(NativeDisplayDelegate* delegate, ConfigureDisplaysTask(NativeDisplayDelegate* delegate,
const std::vector<DisplayConfigureRequest>& requests, const std::vector<DisplayConfigureRequest>& requests,
...@@ -66,12 +85,49 @@ class DISPLAY_MANAGER_EXPORT ConfigureDisplaysTask ...@@ -66,12 +85,49 @@ class DISPLAY_MANAGER_EXPORT ConfigureDisplaysTask
void OnDisplaySnapshotsInvalidated() override; void OnDisplaySnapshotsInvalidated() override;
private: private:
void OnConfigured(bool config_status); // Deals with the aftermath of the initial configuration, which attempts to
// configure all displays together.
// Upon failure, partitions the original request from Ash into smaller
// requests where the displays are grouped by the physical connector they
// connect to and initiates the retry sequence.
void OnFirstAttemptConfigured(bool config_status);
// Deals with the aftermath of a configuration retry, which attempts to
// configure a subset of the displays grouped together by the physical
// connector they connect to.
// Upon success, initiates the retry sequence on the next group of displays.
// Otherwise, downgrades the display with the largest bandwidth requirement
// and tries again.
// If any of the display groups entirely fail to modeset (i.e. exhaust all
// available modes during retry), the configuration will fail as a whole, but
// will continue to try to modeset the remaining display groups.
void OnRetryConfigured(bool config_status);
// Partition |requests_| by their base connector id (i.e. the physical
// connector the displays are connected to) and populate the result in
// |pending_display_group_requests_|. We assume the order of requests
// submitted by Ash is important, so the partitioning is done in order.
void PartitionRequests();
// Downgrade the request with the highest bandwidth requirement AND
// alternative modes in |requests_| (excluding internal displays and disable
// requests). Return false if no request was downgraded.
bool DowngradeLargestRequestWithAlternativeModes();
NativeDisplayDelegate* delegate_; // Not owned. NativeDisplayDelegate* delegate_; // Not owned.
// Initially, |requests_| holds the configuration request submitted by Ash.
// During retry, |requests_| will represent a group of displays that are
// currently attempting configuration.
std::vector<DisplayConfigureRequest> requests_; std::vector<DisplayConfigureRequest> requests_;
// A queue of display requests grouped by their
// |requests_[index]->display->base_connector_id()|.
PartitionedRequestsQueue pending_display_group_requests_;
// The final requests and their configuration status for UMA.
std::vector<RequestAndStatusList> final_requests_status_;
// When the task finishes executing it runs the callback to notify that the // When the task finishes executing it runs the callback to notify that the
// task is done and the task status. // task is done and the task status.
ResponseCallback callback_; ResponseCallback callback_;
......
...@@ -21,6 +21,14 @@ namespace test { ...@@ -21,6 +21,14 @@ namespace test {
namespace { namespace {
// Non-zero generic connector IDs.
constexpr uint64_t kEdpConnectorId = 71u;
constexpr uint64_t kSecondConnectorId = kEdpConnectorId + 10u;
constexpr uint64_t kThirdConnectorId = kEdpConnectorId + 20u;
// Invalid PATH topology parse connector ID.
constexpr uint64_t kInvalidConnectorId = 0u;
class ConfigureDisplaysTaskTest : public testing::Test { class ConfigureDisplaysTaskTest : public testing::Test {
public: public:
ConfigureDisplaysTaskTest() ConfigureDisplaysTaskTest()
...@@ -37,6 +45,7 @@ class ConfigureDisplaysTaskTest : public testing::Test { ...@@ -37,6 +45,7 @@ class ConfigureDisplaysTaskTest : public testing::Test {
.SetCurrentMode(medium_mode_.Clone()) .SetCurrentMode(medium_mode_.Clone())
.AddMode(small_mode_.Clone()) .AddMode(small_mode_.Clone())
.SetType(DISPLAY_CONNECTION_TYPE_INTERNAL) .SetType(DISPLAY_CONNECTION_TYPE_INTERNAL)
.SetBaseConnectorId(kEdpConnectorId)
.Build()); .Build());
displays_.push_back(FakeDisplaySnapshot::Builder() displays_.push_back(FakeDisplaySnapshot::Builder()
.SetId(456) .SetId(456)
...@@ -44,6 +53,7 @@ class ConfigureDisplaysTaskTest : public testing::Test { ...@@ -44,6 +53,7 @@ class ConfigureDisplaysTaskTest : public testing::Test {
.SetCurrentMode(big_mode_.Clone()) .SetCurrentMode(big_mode_.Clone())
.AddMode(small_mode_.Clone()) .AddMode(small_mode_.Clone())
.SetType(DISPLAY_CONNECTION_TYPE_DISPLAYPORT) .SetType(DISPLAY_CONNECTION_TYPE_DISPLAYPORT)
.SetBaseConnectorId(kSecondConnectorId)
.Build()); .Build());
} }
...@@ -72,7 +82,11 @@ class ConfigureDisplaysTaskTest : public testing::Test { ...@@ -72,7 +82,11 @@ class ConfigureDisplaysTaskTest : public testing::Test {
} // namespace } // namespace
TEST_F(ConfigureDisplaysTaskTest, ConfigureWithOneDisplay) { /**************************************************
* Cases that report ConfigureDisplaysTask::SUCCESS
**************************************************/
TEST_F(ConfigureDisplaysTaskTest, ConfigureInternalDisplay) {
ConfigureDisplaysTask::ResponseCallback callback = base::BindOnce( ConfigureDisplaysTask::ResponseCallback callback = base::BindOnce(
&ConfigureDisplaysTaskTest::ConfigureCallback, base::Unretained(this)); &ConfigureDisplaysTaskTest::ConfigureCallback, base::Unretained(this));
...@@ -89,7 +103,9 @@ TEST_F(ConfigureDisplaysTaskTest, ConfigureWithOneDisplay) { ...@@ -89,7 +103,9 @@ TEST_F(ConfigureDisplaysTaskTest, ConfigureWithOneDisplay) {
log_.GetActionsAndClear()); log_.GetActionsAndClear());
} }
TEST_F(ConfigureDisplaysTaskTest, ConfigureWithTwoDisplay) { // Tests that and an internal + one external display pass modeset. Note that
// this case covers an external display connected via MST as well.
TEST_F(ConfigureDisplaysTaskTest, ConfigureInternalAndOneExternalDisplays) {
ConfigureDisplaysTask::ResponseCallback callback = base::BindOnce( ConfigureDisplaysTask::ResponseCallback callback = base::BindOnce(
&ConfigureDisplaysTaskTest::ConfigureCallback, base::Unretained(this)); &ConfigureDisplaysTaskTest::ConfigureCallback, base::Unretained(this));
...@@ -113,7 +129,128 @@ TEST_F(ConfigureDisplaysTaskTest, ConfigureWithTwoDisplay) { ...@@ -113,7 +129,128 @@ TEST_F(ConfigureDisplaysTaskTest, ConfigureWithTwoDisplay) {
log_.GetActionsAndClear()); log_.GetActionsAndClear());
} }
TEST_F(ConfigureDisplaysTaskTest, DisableDisplayFails) { // Tests that one external display (with no internal display present;
// e.g. chromebox) pass modeset. Note that this case covers an external display
// connected via MST as well.
TEST_F(ConfigureDisplaysTaskTest, ConfigureOneExternalDisplay) {
ConfigureDisplaysTask::ResponseCallback callback = base::BindOnce(
&ConfigureDisplaysTaskTest::ConfigureCallback, base::Unretained(this));
std::vector<DisplayConfigureRequest> requests(
1, DisplayConfigureRequest(displays_[1].get(),
displays_[1]->native_mode(), gfx::Point()));
ConfigureDisplaysTask task(&delegate_, requests, std::move(callback));
task.Run();
EXPECT_TRUE(callback_called_);
EXPECT_EQ(ConfigureDisplaysTask::SUCCESS, status_);
EXPECT_EQ(GetCrtcAction({displays_[1]->display_id(), gfx::Point(),
displays_[1]->native_mode()}),
log_.GetActionsAndClear());
}
// Tests that two external MST displays (with no internal display present; e.g.
// chromebox) pass modeset.
TEST_F(ConfigureDisplaysTaskTest, ConfigureTwoMstDisplays) {
ConfigureDisplaysTask::ResponseCallback callback = base::BindOnce(
&ConfigureDisplaysTaskTest::ConfigureCallback, base::Unretained(this));
// Two displays sharing the same base connector via MST.
displays_[0] = FakeDisplaySnapshot::Builder()
.SetId(456)
.SetNativeMode(big_mode_.Clone())
.SetCurrentMode(big_mode_.Clone())
.AddMode(small_mode_.Clone())
.SetType(DISPLAY_CONNECTION_TYPE_DISPLAYPORT)
.SetBaseConnectorId(kSecondConnectorId)
.Build();
displays_[1] = FakeDisplaySnapshot::Builder()
.SetId(789)
.SetNativeMode(big_mode_.Clone())
.SetCurrentMode(big_mode_.Clone())
.AddMode(small_mode_.Clone())
.SetType(DISPLAY_CONNECTION_TYPE_DISPLAYPORT)
.SetBaseConnectorId(kSecondConnectorId)
.Build();
std::vector<DisplayConfigureRequest> requests;
for (const auto& display : displays_) {
requests.emplace_back(display.get(), display->native_mode(), gfx::Point());
}
ConfigureDisplaysTask task(&delegate_, requests, std::move(callback));
task.Run();
EXPECT_TRUE(callback_called_);
EXPECT_EQ(ConfigureDisplaysTask::SUCCESS, status_);
EXPECT_EQ(
JoinActions(
GetCrtcAction({displays_[0]->display_id(), gfx::Point(), &big_mode_})
.c_str(),
GetCrtcAction({displays_[1]->display_id(), gfx::Point(), &big_mode_})
.c_str(),
nullptr),
log_.GetActionsAndClear());
}
// Although most devices do not support more than three displays in total
// (including the internal display), this tests that this configuration can pass
// all displays in a single request.
TEST_F(ConfigureDisplaysTaskTest, ConfigureInternalAndTwoMstAndHdmiDisplays) {
ConfigureDisplaysTask::ResponseCallback callback = base::BindOnce(
&ConfigureDisplaysTaskTest::ConfigureCallback, base::Unretained(this));
// Add an additional display to base connector kSecondConnectorId via MST.
displays_.push_back(FakeDisplaySnapshot::Builder()
.SetId(789)
.SetNativeMode(big_mode_.Clone())
.SetCurrentMode(big_mode_.Clone())
.AddMode(small_mode_.Clone())
.SetType(DISPLAY_CONNECTION_TYPE_DISPLAYPORT)
.SetBaseConnectorId(kSecondConnectorId)
.Build());
// Additional independent HDMI display (has its own connector).
displays_.push_back(FakeDisplaySnapshot::Builder()
.SetId(101112)
.SetNativeMode(big_mode_.Clone())
.SetCurrentMode(big_mode_.Clone())
.AddMode(medium_mode_.Clone())
.AddMode(small_mode_.Clone())
.SetType(DISPLAY_CONNECTION_TYPE_HDMI)
.SetBaseConnectorId(kThirdConnectorId)
.Build());
std::vector<DisplayConfigureRequest> requests;
for (const auto& display : displays_) {
requests.emplace_back(display.get(), display->native_mode(), gfx::Point());
}
ConfigureDisplaysTask task(&delegate_, requests, std::move(callback));
task.Run();
EXPECT_TRUE(callback_called_);
EXPECT_EQ(ConfigureDisplaysTask::SUCCESS, status_);
EXPECT_EQ(
JoinActions(
GetCrtcAction({displays_[0]->display_id(), gfx::Point(),
displays_[0]->native_mode()})
.c_str(),
GetCrtcAction({displays_[1]->display_id(), gfx::Point(), &big_mode_})
.c_str(),
GetCrtcAction({displays_[2]->display_id(), gfx::Point(), &big_mode_})
.c_str(),
GetCrtcAction({displays_[3]->display_id(), gfx::Point(), &big_mode_})
.c_str(),
nullptr),
log_.GetActionsAndClear());
}
/************************************************
* Cases that report ConfigureDisplaysTask::ERROR
************************************************/
TEST_F(ConfigureDisplaysTaskTest, DisableInternalDisplayFails) {
ConfigureDisplaysTask::ResponseCallback callback = base::BindOnce( ConfigureDisplaysTask::ResponseCallback callback = base::BindOnce(
&ConfigureDisplaysTaskTest::ConfigureCallback, base::Unretained(this)); &ConfigureDisplaysTaskTest::ConfigureCallback, base::Unretained(this));
...@@ -126,10 +263,21 @@ TEST_F(ConfigureDisplaysTaskTest, DisableDisplayFails) { ...@@ -126,10 +263,21 @@ TEST_F(ConfigureDisplaysTaskTest, DisableDisplayFails) {
EXPECT_TRUE(callback_called_); EXPECT_TRUE(callback_called_);
EXPECT_EQ(ConfigureDisplaysTask::ERROR, status_); EXPECT_EQ(ConfigureDisplaysTask::ERROR, status_);
EXPECT_EQ(GetCrtcAction({displays_[0]->display_id(), gfx::Point(), nullptr}), EXPECT_EQ(
log_.GetActionsAndClear()); JoinActions(
// Initial modeset fails. Initiate retry logic.
GetCrtcAction({displays_[0]->display_id(), gfx::Point(), nullptr})
.c_str(),
// There is no way to downgrade a disable request. Configuration
// fails.
GetCrtcAction({displays_[0]->display_id(), gfx::Point(), nullptr})
.c_str(),
nullptr),
log_.GetActionsAndClear());
} }
// Tests that the internal display does not attempt to fallback to alternative
// modes upon failure to modeset with preferred mode.
TEST_F(ConfigureDisplaysTaskTest, NoModeChangeAttemptWhenInternalDisplayFails) { TEST_F(ConfigureDisplaysTaskTest, NoModeChangeAttemptWhenInternalDisplayFails) {
ConfigureDisplaysTask::ResponseCallback callback = base::BindOnce( ConfigureDisplaysTask::ResponseCallback callback = base::BindOnce(
&ConfigureDisplaysTaskTest::ConfigureCallback, base::Unretained(this)); &ConfigureDisplaysTaskTest::ConfigureCallback, base::Unretained(this));
...@@ -144,12 +292,26 @@ TEST_F(ConfigureDisplaysTaskTest, NoModeChangeAttemptWhenInternalDisplayFails) { ...@@ -144,12 +292,26 @@ TEST_F(ConfigureDisplaysTaskTest, NoModeChangeAttemptWhenInternalDisplayFails) {
EXPECT_TRUE(callback_called_); EXPECT_TRUE(callback_called_);
EXPECT_EQ(ConfigureDisplaysTask::ERROR, status_); EXPECT_EQ(ConfigureDisplaysTask::ERROR, status_);
EXPECT_EQ(GetCrtcAction({displays_[0]->display_id(), gfx::Point(), EXPECT_EQ(JoinActions(
displays_[0]->native_mode()}), // Initial modeset fails. Initiate retry logic.
GetCrtcAction({displays_[0]->display_id(), gfx::Point(),
displays_[0]->native_mode()})
.c_str(),
// Retry logic fails to modeset internal display. Since internal
// displays are restricted to their preferred mode, there are no
// other modes to try. The configuration fails completely.
GetCrtcAction({displays_[0]->display_id(), gfx::Point(),
displays_[0]->native_mode()})
.c_str(),
nullptr),
log_.GetActionsAndClear()); log_.GetActionsAndClear());
} }
TEST_F(ConfigureDisplaysTaskTest, ConfigureWithOneDisplayFails) { // Tests that an external display (with no internal display present; e.g.
// chromebox) attempts to fallback to alternative modes upon failure to modeset
// to the original request before completely failing. Note that this case
// applies to a single external display over MST as well.
TEST_F(ConfigureDisplaysTaskTest, ConfigureOneExternalNoInternalDisplayFails) {
ConfigureDisplaysTask::ResponseCallback callback = base::BindOnce( ConfigureDisplaysTask::ResponseCallback callback = base::BindOnce(
&ConfigureDisplaysTaskTest::ConfigureCallback, base::Unretained(this)); &ConfigureDisplaysTaskTest::ConfigureCallback, base::Unretained(this));
...@@ -162,20 +324,106 @@ TEST_F(ConfigureDisplaysTaskTest, ConfigureWithOneDisplayFails) { ...@@ -162,20 +324,106 @@ TEST_F(ConfigureDisplaysTaskTest, ConfigureWithOneDisplayFails) {
EXPECT_TRUE(callback_called_); EXPECT_TRUE(callback_called_);
EXPECT_EQ(ConfigureDisplaysTask::ERROR, status_); EXPECT_EQ(ConfigureDisplaysTask::ERROR, status_);
EXPECT_EQ(JoinActions(GetCrtcAction({displays_[1]->display_id(), gfx::Point(), EXPECT_EQ(
&big_mode_}) JoinActions(
.c_str(), // Initial modeset fails. Initiate retry logic.
GetCrtcAction({displays_[1]->display_id(), gfx::Point(), GetCrtcAction({displays_[1]->display_id(), gfx::Point(), &big_mode_})
&small_mode_}) .c_str(),
.c_str(), // External display will fail, downgrade once, and fail completely.
nullptr), GetCrtcAction({displays_[1]->display_id(), gfx::Point(), &big_mode_})
log_.GetActionsAndClear()); .c_str(),
GetCrtcAction(
{displays_[1]->display_id(), gfx::Point(), &small_mode_})
.c_str(),
nullptr),
log_.GetActionsAndClear());
} }
TEST_F(ConfigureDisplaysTaskTest, ConfigureWithTwoDisplayFails) { // Tests that two external non-MST displays (with no internal display present;
// e.g. chromebox) attempt to fallback to alternative modes upon failure to
// modeset to the original request before completely failing.
TEST_F(ConfigureDisplaysTaskTest, ConfigureTwoNoneMstDisplaysNoInternalFail) {
ConfigureDisplaysTask::ResponseCallback callback = base::BindOnce( ConfigureDisplaysTask::ResponseCallback callback = base::BindOnce(
&ConfigureDisplaysTaskTest::ConfigureCallback, base::Unretained(this)); &ConfigureDisplaysTaskTest::ConfigureCallback, base::Unretained(this));
displays_[0] = FakeDisplaySnapshot::Builder()
.SetId(456)
.SetNativeMode(big_mode_.Clone())
.SetCurrentMode(big_mode_.Clone())
.AddMode(small_mode_.Clone())
.SetType(DISPLAY_CONNECTION_TYPE_DISPLAYPORT)
.SetBaseConnectorId(kSecondConnectorId)
.Build();
displays_[1] = FakeDisplaySnapshot::Builder()
.SetId(789)
.SetNativeMode(big_mode_.Clone())
.SetCurrentMode(big_mode_.Clone())
.AddMode(medium_mode_.Clone())
.SetType(DISPLAY_CONNECTION_TYPE_DISPLAYPORT)
.SetBaseConnectorId(kThirdConnectorId)
.Build();
delegate_.set_max_configurable_pixels(small_mode_.size().GetArea());
std::vector<DisplayConfigureRequest> requests;
for (const auto& display : displays_) {
requests.emplace_back(display.get(), display->native_mode(), gfx::Point());
}
ConfigureDisplaysTask task(&delegate_, requests, std::move(callback));
task.Run();
EXPECT_TRUE(callback_called_);
EXPECT_EQ(ConfigureDisplaysTask::ERROR, status_);
EXPECT_EQ(
JoinActions(
// All displays will fail to modeset together. Initiate retry logic.
GetCrtcAction({displays_[0]->display_id(), gfx::Point(), &big_mode_})
.c_str(),
GetCrtcAction({displays_[1]->display_id(), gfx::Point(), &big_mode_})
.c_str(),
// |displays_[0]| will fail, downgrade once, and pass.
GetCrtcAction({displays_[0]->display_id(), gfx::Point(), &big_mode_})
.c_str(),
GetCrtcAction(
{displays_[0]->display_id(), gfx::Point(), &small_mode_})
.c_str(),
// |displays_[1]| will fail, downgrade once, and fail completely.
GetCrtcAction({displays_[1]->display_id(), gfx::Point(), &big_mode_})
.c_str(),
GetCrtcAction(
{displays_[1]->display_id(), gfx::Point(), &medium_mode_})
.c_str(),
nullptr),
log_.GetActionsAndClear());
}
// Tests that two external MST displays (with no internal display present; e.g.
// chromebox) attempt to fallback to alternative modes upon failure to modeset
// to the original request before completely failing.
TEST_F(ConfigureDisplaysTaskTest, ConfigureTwoMstDisplaysNoInternalFail) {
ConfigureDisplaysTask::ResponseCallback callback = base::BindOnce(
&ConfigureDisplaysTaskTest::ConfigureCallback, base::Unretained(this));
// Two displays sharing the same base connector.
displays_[0] = FakeDisplaySnapshot::Builder()
.SetId(456)
.SetNativeMode(big_mode_.Clone())
.SetCurrentMode(big_mode_.Clone())
.AddMode(small_mode_.Clone())
.SetType(DISPLAY_CONNECTION_TYPE_DISPLAYPORT)
.SetBaseConnectorId(kSecondConnectorId)
.Build();
displays_[1] = FakeDisplaySnapshot::Builder()
.SetId(789)
.SetNativeMode(big_mode_.Clone())
.SetCurrentMode(big_mode_.Clone())
.AddMode(medium_mode_.Clone())
.AddMode(small_mode_.Clone())
.SetType(DISPLAY_CONNECTION_TYPE_DISPLAYPORT)
.SetBaseConnectorId(kSecondConnectorId)
.Build();
delegate_.set_max_configurable_pixels(1); delegate_.set_max_configurable_pixels(1);
std::vector<DisplayConfigureRequest> requests; std::vector<DisplayConfigureRequest> requests;
...@@ -188,23 +436,755 @@ TEST_F(ConfigureDisplaysTaskTest, ConfigureWithTwoDisplayFails) { ...@@ -188,23 +436,755 @@ TEST_F(ConfigureDisplaysTaskTest, ConfigureWithTwoDisplayFails) {
EXPECT_TRUE(callback_called_); EXPECT_TRUE(callback_called_);
EXPECT_EQ(ConfigureDisplaysTask::ERROR, status_); EXPECT_EQ(ConfigureDisplaysTask::ERROR, status_);
EXPECT_EQ(JoinActions(GetCrtcAction({displays_[0]->display_id(), gfx::Point(), EXPECT_EQ(
displays_[0]->native_mode()}) JoinActions(
.c_str(), // All displays will fail to modeset together. Initiate retry logic.
GetCrtcAction({displays_[1]->display_id(), gfx::Point(), GetCrtcAction({displays_[0]->display_id(), gfx::Point(), &big_mode_})
&big_mode_}) .c_str(),
.c_str(), GetCrtcAction({displays_[1]->display_id(), gfx::Point(), &big_mode_})
GetCrtcAction({displays_[0]->display_id(), gfx::Point(), .c_str(),
displays_[0]->native_mode()}) // MST displays will be tested (and fail) together.
.c_str(), GetCrtcAction({displays_[0]->display_id(), gfx::Point(), &big_mode_})
GetCrtcAction({displays_[1]->display_id(), gfx::Point(), .c_str(),
&small_mode_}) GetCrtcAction({displays_[1]->display_id(), gfx::Point(), &big_mode_})
.c_str(), .c_str(),
nullptr), // |displays_[0]| will downgrade first. Configuration will fail.
log_.GetActionsAndClear()); GetCrtcAction(
{displays_[0]->display_id(), gfx::Point(), &small_mode_})
.c_str(),
GetCrtcAction({displays_[1]->display_id(), gfx::Point(), &big_mode_})
.c_str(),
// |displays_[1] will downgrade next. Configuration still fails.
GetCrtcAction(
{displays_[0]->display_id(), gfx::Point(), &small_mode_})
.c_str(),
GetCrtcAction(
{displays_[1]->display_id(), gfx::Point(), &medium_mode_})
.c_str(),
// Since |displays_[1]| is still the largest and has one more mode, it
// downgrades again. Configuration fails completely.
GetCrtcAction(
{displays_[0]->display_id(), gfx::Point(), &small_mode_})
.c_str(),
GetCrtcAction(
{displays_[1]->display_id(), gfx::Point(), &small_mode_})
.c_str(),
nullptr),
log_.GetActionsAndClear());
}
// Tests that the internal display does not attempt to fallback to alternative
// modes upon failure to modeset with preferred mode while an external display
// is present. Note that this case applies for an internal + a single external
// display over MST as well.
TEST_F(ConfigureDisplaysTaskTest,
ConfigureInternalAndOneExternalDisplaysFailsDueToInternal) {
ConfigureDisplaysTask::ResponseCallback callback = base::BindOnce(
&ConfigureDisplaysTaskTest::ConfigureCallback, base::Unretained(this));
delegate_.set_max_configurable_pixels(1);
std::vector<DisplayConfigureRequest> requests;
for (const auto& display : displays_) {
requests.emplace_back(display.get(), display->native_mode(), gfx::Point());
}
ConfigureDisplaysTask task(&delegate_, requests, std::move(callback));
task.Run();
EXPECT_TRUE(callback_called_);
EXPECT_EQ(ConfigureDisplaysTask::ERROR, status_);
EXPECT_EQ(
JoinActions(
// All displays will fail to modeset together. Initiate retry logic.
GetCrtcAction({displays_[0]->display_id(), gfx::Point(),
displays_[0]->native_mode()})
.c_str(),
GetCrtcAction({displays_[1]->display_id(), gfx::Point(), &big_mode_})
.c_str(),
// Retry logic fails to modeset internal display. Since internal
// displays are restricted to their preferred mode, there are no other
// modes to try. The configuration will fail completely, but the
// external display will attempt to modeset as well.
GetCrtcAction({displays_[0]->display_id(), gfx::Point(),
displays_[0]->native_mode()})
.c_str(),
// External display will fail, downgrade once, and fail again.
GetCrtcAction({displays_[1]->display_id(), gfx::Point(), &big_mode_})
.c_str(),
GetCrtcAction(
{displays_[1]->display_id(), gfx::Point(), &small_mode_})
.c_str(),
nullptr),
log_.GetActionsAndClear());
}
// Tests that an external display attempts to fallback to alternative modes upon
// failure to modeset to the original request after the internal display modeset
// successfully. Note that this case applies for an internal + a single MST
// display as well.
TEST_F(ConfigureDisplaysTaskTest,
ConfigureInternalAndOneExternalDisplaysFailsDueToExternal) {
ConfigureDisplaysTask::ResponseCallback callback = base::BindOnce(
&ConfigureDisplaysTaskTest::ConfigureCallback, base::Unretained(this));
displays_[0] = FakeDisplaySnapshot::Builder()
.SetId(123)
.SetNativeMode(small_mode_.Clone())
.SetCurrentMode(small_mode_.Clone())
.SetType(DISPLAY_CONNECTION_TYPE_INTERNAL)
.SetBaseConnectorId(kEdpConnectorId)
.Build();
displays_[1] = FakeDisplaySnapshot::Builder()
.SetId(456)
.SetNativeMode(big_mode_.Clone())
.SetCurrentMode(big_mode_.Clone())
.AddMode(medium_mode_.Clone())
.SetType(DISPLAY_CONNECTION_TYPE_DISPLAYPORT)
.SetBaseConnectorId(kSecondConnectorId)
.Build();
delegate_.set_max_configurable_pixels(small_mode_.size().GetArea());
std::vector<DisplayConfigureRequest> requests;
for (const auto& display : displays_) {
requests.emplace_back(display.get(), display->native_mode(), gfx::Point());
}
ConfigureDisplaysTask task(&delegate_, requests, std::move(callback));
task.Run();
EXPECT_TRUE(callback_called_);
EXPECT_EQ(ConfigureDisplaysTask::ERROR, status_);
EXPECT_EQ(
JoinActions(
// All displays will fail to modeset together. Initiate retry logic.
GetCrtcAction({displays_[0]->display_id(), gfx::Point(),
displays_[0]->native_mode()})
.c_str(),
GetCrtcAction({displays_[1]->display_id(), gfx::Point(), &big_mode_})
.c_str(),
// Internal display will succeed to modeset.
GetCrtcAction({displays_[0]->display_id(), gfx::Point(),
displays_[0]->native_mode()})
.c_str(),
// External display fails, downgrades once, and fails completely.
GetCrtcAction({displays_[1]->display_id(), gfx::Point(), &big_mode_})
.c_str(),
GetCrtcAction(
{displays_[1]->display_id(), gfx::Point(), &medium_mode_})
.c_str(),
nullptr),
log_.GetActionsAndClear());
}
// Tests that the internal display does not attempt to fallback to alternative
// modes upon failure to modeset with preferred mode while two external MST
// displays are present.
TEST_F(ConfigureDisplaysTaskTest,
ConfigureInternalAndTwoMstExternalDisplaysFailsDueToInternal) {
ConfigureDisplaysTask::ResponseCallback callback = base::BindOnce(
&ConfigureDisplaysTaskTest::ConfigureCallback, base::Unretained(this));
// Add an additional display to base connector kSecondConnectorId via MST.
displays_.push_back(FakeDisplaySnapshot::Builder()
.SetId(789)
.SetNativeMode(big_mode_.Clone())
.SetCurrentMode(big_mode_.Clone())
.AddMode(small_mode_.Clone())
.SetType(DISPLAY_CONNECTION_TYPE_DISPLAYPORT)
.SetBaseConnectorId(kSecondConnectorId)
.Build());
delegate_.set_max_configurable_pixels(1);
std::vector<DisplayConfigureRequest> requests;
for (const auto& display : displays_) {
requests.emplace_back(display.get(), display->native_mode(), gfx::Point());
}
ConfigureDisplaysTask task(&delegate_, requests, std::move(callback));
task.Run();
EXPECT_TRUE(callback_called_);
EXPECT_EQ(ConfigureDisplaysTask::ERROR, status_);
EXPECT_EQ(
JoinActions(
// All displays will fail to modeset together. Initiate retry logic.
GetCrtcAction({displays_[0]->display_id(), gfx::Point(),
displays_[0]->native_mode()})
.c_str(),
GetCrtcAction({displays_[1]->display_id(), gfx::Point(), &big_mode_})
.c_str(),
GetCrtcAction({displays_[2]->display_id(), gfx::Point(), &big_mode_})
.c_str(),
// Retry logic fails to modeset internal display. Since internal
// displays are restricted to their preferred mode, there are no other
// modes to try. The configuration will fail completely. The external
// displays will attempt to modeset next.
GetCrtcAction({displays_[0]->display_id(), gfx::Point(),
displays_[0]->native_mode()})
.c_str(),
// MST displays will be tested (and fail) together.
GetCrtcAction({displays_[1]->display_id(), gfx::Point(), &big_mode_})
.c_str(),
GetCrtcAction({displays_[2]->display_id(), gfx::Point(), &big_mode_})
.c_str(),
// |displays_[1]| will downgrade first. Configuration will fail.
GetCrtcAction(
{displays_[1]->display_id(), gfx::Point(), &small_mode_})
.c_str(),
GetCrtcAction({displays_[2]->display_id(), gfx::Point(), &big_mode_})
.c_str(),
// |displays_[2] will downgrade next and configuration fails
// completely.
GetCrtcAction(
{displays_[1]->display_id(), gfx::Point(), &small_mode_})
.c_str(),
GetCrtcAction(
{displays_[2]->display_id(), gfx::Point(), &small_mode_})
.c_str(),
nullptr),
log_.GetActionsAndClear());
}
// Tests that two external MST displays attempt to fallback to alternative modes
// upon failure to modeset to the original request after the internal display
// succeeded to modeset
TEST_F(ConfigureDisplaysTaskTest,
ConfigureInternalAndTwoMstExternalDisplaysFailsDueToExternals) {
ConfigureDisplaysTask::ResponseCallback callback = base::BindOnce(
&ConfigureDisplaysTaskTest::ConfigureCallback, base::Unretained(this));
displays_[0] = FakeDisplaySnapshot::Builder()
.SetId(123)
.SetNativeMode(small_mode_.Clone())
.SetCurrentMode(small_mode_.Clone())
.SetType(DISPLAY_CONNECTION_TYPE_INTERNAL)
.SetBaseConnectorId(kEdpConnectorId)
.Build();
// Two MST displays sharing the same base connector.
displays_[1] = FakeDisplaySnapshot::Builder()
.SetId(456)
.SetNativeMode(big_mode_.Clone())
.SetCurrentMode(big_mode_.Clone())
.AddMode(small_mode_.Clone())
.SetType(DISPLAY_CONNECTION_TYPE_DISPLAYPORT)
.SetBaseConnectorId(kSecondConnectorId)
.Build();
displays_.push_back(FakeDisplaySnapshot::Builder()
.SetId(789)
.SetNativeMode(big_mode_.Clone())
.SetCurrentMode(big_mode_.Clone())
.AddMode(medium_mode_.Clone())
.SetType(DISPLAY_CONNECTION_TYPE_DISPLAYPORT)
.SetBaseConnectorId(kSecondConnectorId)
.Build());
delegate_.set_max_configurable_pixels(small_mode_.size().GetArea());
std::vector<DisplayConfigureRequest> requests;
for (const auto& display : displays_) {
requests.emplace_back(display.get(), display->native_mode(), gfx::Point());
}
ConfigureDisplaysTask task(&delegate_, requests, std::move(callback));
task.Run();
EXPECT_TRUE(callback_called_);
EXPECT_EQ(ConfigureDisplaysTask::ERROR, status_);
EXPECT_EQ(
JoinActions(
// All displays will fail to modeset together. Initiate retry logic.
GetCrtcAction({displays_[0]->display_id(), gfx::Point(),
displays_[0]->native_mode()})
.c_str(),
GetCrtcAction({displays_[1]->display_id(), gfx::Point(), &big_mode_})
.c_str(),
GetCrtcAction({displays_[2]->display_id(), gfx::Point(), &big_mode_})
.c_str(),
// Internal display will succeed to modeset.
GetCrtcAction({displays_[0]->display_id(), gfx::Point(),
displays_[0]->native_mode()})
.c_str(),
// MST displays will be tested (and fail) together.
GetCrtcAction({displays_[1]->display_id(), gfx::Point(), &big_mode_})
.c_str(),
GetCrtcAction({displays_[2]->display_id(), gfx::Point(), &big_mode_})
.c_str(),
// |displays_[1]| will downgrade first. Configuration will fail.
GetCrtcAction(
{displays_[1]->display_id(), gfx::Point(), &small_mode_})
.c_str(),
GetCrtcAction({displays_[2]->display_id(), gfx::Point(), &big_mode_})
.c_str(),
// |displays_[2] will downgrade next and configuration fails
// completely.
GetCrtcAction(
{displays_[1]->display_id(), gfx::Point(), &small_mode_})
.c_str(),
GetCrtcAction(
{displays_[2]->display_id(), gfx::Point(), &medium_mode_})
.c_str(),
nullptr),
log_.GetActionsAndClear());
}
// Tests that the internal display does not attempt to fallback to alternative
// modes upon failure to modeset with preferred mode while two MST and one HDMI
// displays are present.
TEST_F(ConfigureDisplaysTaskTest,
ConfigureInternalAndTwoMstAndHdmiDisplaysFailsDueToInternal) {
ConfigureDisplaysTask::ResponseCallback callback = base::BindOnce(
&ConfigureDisplaysTaskTest::ConfigureCallback, base::Unretained(this));
// Add an additional display to kSecondConnectorId (via MST).
displays_.push_back(FakeDisplaySnapshot::Builder()
.SetId(789)
.SetNativeMode(big_mode_.Clone())
.SetCurrentMode(big_mode_.Clone())
.AddMode(small_mode_.Clone())
.SetType(DISPLAY_CONNECTION_TYPE_DISPLAYPORT)
.SetBaseConnectorId(kSecondConnectorId)
.Build());
// Additional independent HDMI display (has its own connector).
displays_.push_back(FakeDisplaySnapshot::Builder()
.SetId(101112)
.SetNativeMode(big_mode_.Clone())
.SetCurrentMode(big_mode_.Clone())
.AddMode(medium_mode_.Clone())
.AddMode(small_mode_.Clone())
.SetType(DISPLAY_CONNECTION_TYPE_HDMI)
.SetBaseConnectorId(kThirdConnectorId)
.Build());
delegate_.set_max_configurable_pixels(1);
std::vector<DisplayConfigureRequest> requests;
for (const auto& display : displays_) {
requests.emplace_back(display.get(), display->native_mode(), gfx::Point());
}
ConfigureDisplaysTask task(&delegate_, requests, std::move(callback));
task.Run();
EXPECT_TRUE(callback_called_);
EXPECT_EQ(ConfigureDisplaysTask::ERROR, status_);
EXPECT_EQ(
JoinActions(
// All displays will fail to modeset together. Initiate retry logic.
GetCrtcAction({displays_[0]->display_id(), gfx::Point(),
displays_[0]->native_mode()})
.c_str(),
GetCrtcAction({displays_[1]->display_id(), gfx::Point(), &big_mode_})
.c_str(),
GetCrtcAction({displays_[2]->display_id(), gfx::Point(), &big_mode_})
.c_str(),
GetCrtcAction({displays_[3]->display_id(), gfx::Point(), &big_mode_})
.c_str(),
// Retry logic fails to modeset internal display. Since internal
// displays are restricted to their preferred mode, there are no other
// modes to try. The configuration will fail completely, but the
// external displays will still attempt to configure.
GetCrtcAction({displays_[0]->display_id(), gfx::Point(),
displays_[0]->native_mode()})
.c_str(),
// MST displays_[1,2] will be tested (and fail) together. displays_[1]
// downgrades first and fails, Then displays_[2], and the process will
// repeat once more before the group fails to modeset.
GetCrtcAction({displays_[1]->display_id(), gfx::Point(), &big_mode_})
.c_str(),
GetCrtcAction({displays_[2]->display_id(), gfx::Point(), &big_mode_})
.c_str(),
GetCrtcAction(
{displays_[1]->display_id(), gfx::Point(), &small_mode_})
.c_str(),
GetCrtcAction({displays_[2]->display_id(), gfx::Point(), &big_mode_})
.c_str(),
GetCrtcAction(
{displays_[1]->display_id(), gfx::Point(), &small_mode_})
.c_str(),
GetCrtcAction(
{displays_[2]->display_id(), gfx::Point(), &small_mode_})
.c_str(),
// Finally, HDMI display will attempt to modeset and cycle through its
// three available modes.
GetCrtcAction({displays_[3]->display_id(), gfx::Point(), &big_mode_})
.c_str(),
GetCrtcAction(
{displays_[3]->display_id(), gfx::Point(), &medium_mode_})
.c_str(),
GetCrtcAction(
{displays_[3]->display_id(), gfx::Point(), &small_mode_})
.c_str(),
nullptr),
log_.GetActionsAndClear());
}
// Tests that two external MST displays attempt to fallback to alternative modes
// upon failure to modeset to the original request after the internal display
// succeeded to modeset.
TEST_F(ConfigureDisplaysTaskTest,
ConfigureInternalAndTwoMstAndHdmiDisplaysFailsDueToMst) {
ConfigureDisplaysTask::ResponseCallback callback = base::BindOnce(
&ConfigureDisplaysTaskTest::ConfigureCallback, base::Unretained(this));
// Add an additional display to kSecondConnectorId (via MST).
displays_.push_back(FakeDisplaySnapshot::Builder()
.SetId(789)
.SetNativeMode(big_mode_.Clone())
.SetCurrentMode(big_mode_.Clone())
.SetType(DISPLAY_CONNECTION_TYPE_DISPLAYPORT)
.SetBaseConnectorId(kSecondConnectorId)
.Build());
// Additional independent HDMI display (has its own connector).
displays_.push_back(FakeDisplaySnapshot::Builder()
.SetId(101112)
.SetNativeMode(big_mode_.Clone())
.SetCurrentMode(big_mode_.Clone())
.AddMode(medium_mode_.Clone())
.AddMode(small_mode_.Clone())
.SetType(DISPLAY_CONNECTION_TYPE_HDMI)
.SetBaseConnectorId(kThirdConnectorId)
.Build());
delegate_.set_max_configurable_pixels(medium_mode_.size().GetArea());
std::vector<DisplayConfigureRequest> requests;
for (const auto& display : displays_) {
requests.emplace_back(display.get(), display->native_mode(), gfx::Point());
}
ConfigureDisplaysTask task(&delegate_, requests, std::move(callback));
task.Run();
EXPECT_TRUE(callback_called_);
EXPECT_EQ(ConfigureDisplaysTask::ERROR, status_);
EXPECT_EQ(
JoinActions(
// All displays will fail to modeset together. Initiate retry logic.
GetCrtcAction({displays_[0]->display_id(), gfx::Point(),
displays_[0]->native_mode()})
.c_str(),
GetCrtcAction({displays_[1]->display_id(), gfx::Point(), &big_mode_})
.c_str(),
GetCrtcAction({displays_[2]->display_id(), gfx::Point(), &big_mode_})
.c_str(),
GetCrtcAction({displays_[3]->display_id(), gfx::Point(), &big_mode_})
.c_str(),
// Internal display will succeed to modeset.
GetCrtcAction({displays_[0]->display_id(), gfx::Point(),
displays_[0]->native_mode()})
.c_str(),
// MST displays will be tested (and fail) together. displays_[1]
// downgrade first. Modeset fails.
GetCrtcAction({displays_[1]->display_id(), gfx::Point(), &big_mode_})
.c_str(),
GetCrtcAction({displays_[2]->display_id(), gfx::Point(), &big_mode_})
.c_str(),
// displays_[2] downgrade next, but there are no other modes available
// for displays_[2], so configuration fails completely for the MST
// group. The HDMI display will attempt to modeset nest.
GetCrtcAction(
{displays_[1]->display_id(), gfx::Point(), &small_mode_})
.c_str(),
GetCrtcAction({displays_[2]->display_id(), gfx::Point(), &big_mode_})
.c_str(),
// HDMI display attempts to modeset, fails, downgrades once, and
// passes modeset.
GetCrtcAction({displays_[3]->display_id(), gfx::Point(), &big_mode_})
.c_str(),
GetCrtcAction(
{displays_[3]->display_id(), gfx::Point(), &medium_mode_})
.c_str(),
nullptr),
log_.GetActionsAndClear());
}
// Tests that the HDMI display attempts to fallback to alternative modes upon
// failure to modeset to the original request after the internal and two MST
// displays succeeded to modeset.
TEST_F(ConfigureDisplaysTaskTest,
ConfigureInternalAndTwoMstAndHdmiDisplaysFailsDueToHDMI) {
ConfigureDisplaysTask::ResponseCallback callback = base::BindOnce(
&ConfigureDisplaysTaskTest::ConfigureCallback, base::Unretained(this));
// Two displays sharing the same base connector.
displays_[1] = FakeDisplaySnapshot::Builder()
.SetId(456)
.SetNativeMode(medium_mode_.Clone())
.SetCurrentMode(medium_mode_.Clone())
.SetType(DISPLAY_CONNECTION_TYPE_DISPLAYPORT)
.SetBaseConnectorId(kSecondConnectorId)
.Build();
displays_.push_back(FakeDisplaySnapshot::Builder()
.SetId(789)
.SetNativeMode(medium_mode_.Clone())
.SetCurrentMode(medium_mode_.Clone())
.SetType(DISPLAY_CONNECTION_TYPE_DISPLAYPORT)
.SetBaseConnectorId(kSecondConnectorId)
.Build());
// Additional independent HDMI display (has its own connector).
displays_.push_back(FakeDisplaySnapshot::Builder()
.SetId(101112)
.SetNativeMode(big_mode_.Clone())
.SetCurrentMode(big_mode_.Clone())
.SetType(DISPLAY_CONNECTION_TYPE_HDMI)
.SetBaseConnectorId(kThirdConnectorId)
.Build());
delegate_.set_max_configurable_pixels(medium_mode_.size().GetArea());
std::vector<DisplayConfigureRequest> requests;
for (const auto& display : displays_) {
requests.emplace_back(display.get(), display->native_mode(), gfx::Point());
}
ConfigureDisplaysTask task(&delegate_, requests, std::move(callback));
task.Run();
EXPECT_TRUE(callback_called_);
EXPECT_EQ(ConfigureDisplaysTask::ERROR, status_);
EXPECT_EQ(
JoinActions(
// All displays will fail to modeset together. Initiate retry logic.
GetCrtcAction({displays_[0]->display_id(), gfx::Point(),
displays_[0]->native_mode()})
.c_str(),
GetCrtcAction(
{displays_[1]->display_id(), gfx::Point(), &medium_mode_})
.c_str(),
GetCrtcAction(
{displays_[2]->display_id(), gfx::Point(), &medium_mode_})
.c_str(),
GetCrtcAction({displays_[3]->display_id(), gfx::Point(), &big_mode_})
.c_str(),
// Internal display will succeed modeset.
GetCrtcAction({displays_[0]->display_id(), gfx::Point(),
displays_[0]->native_mode()})
.c_str(),
// MST displays will be tested and pass together.
GetCrtcAction(
{displays_[1]->display_id(), gfx::Point(), &medium_mode_})
.c_str(),
GetCrtcAction(
{displays_[2]->display_id(), gfx::Point(), &medium_mode_})
.c_str(),
// HDMI display will fail modeset, but since there are no other modes
// available for fallback configuration fails completely.
GetCrtcAction({displays_[3]->display_id(), gfx::Point(), &big_mode_})
.c_str(),
nullptr),
log_.GetActionsAndClear());
} }
TEST_F(ConfigureDisplaysTaskTest, ReconfigureLastDisplayPartialSuccess) { // Tests that two external displays that share a bad MST hub are tested and fail
// together, since they are grouped under kInvalidConnectorId. Also test that
// this does not affect the internal display's ability configured separately
// during retry and passes modeset.
TEST_F(ConfigureDisplaysTaskTest,
ConfigureInternalAndOneBadMstHubWithTwoDisplaysFails) {
ConfigureDisplaysTask::ResponseCallback callback = base::BindOnce(
&ConfigureDisplaysTaskTest::ConfigureCallback, base::Unretained(this));
// Two displays sharing a bad MST Hub that did not report its PATH topology
// correctly.
displays_[1] = FakeDisplaySnapshot::Builder()
.SetId(456)
.SetNativeMode(big_mode_.Clone())
.SetCurrentMode(big_mode_.Clone())
.SetType(DISPLAY_CONNECTION_TYPE_DISPLAYPORT)
.SetBaseConnectorId(kInvalidConnectorId)
.Build();
displays_.push_back(FakeDisplaySnapshot::Builder()
.SetId(789)
.SetNativeMode(big_mode_.Clone())
.SetCurrentMode(big_mode_.Clone())
.SetType(DISPLAY_CONNECTION_TYPE_DISPLAYPORT)
.SetBaseConnectorId(kInvalidConnectorId)
.Build());
delegate_.set_max_configurable_pixels(medium_mode_.size().GetArea());
std::vector<DisplayConfigureRequest> requests;
for (const auto& display : displays_) {
requests.emplace_back(display.get(), display->native_mode(), gfx::Point());
}
ConfigureDisplaysTask task(&delegate_, requests, std::move(callback));
task.Run();
EXPECT_TRUE(callback_called_);
EXPECT_EQ(ConfigureDisplaysTask::ERROR, status_);
EXPECT_EQ(
JoinActions(
// All displays will fail to modeset together. Initiate retry logic.
GetCrtcAction({displays_[0]->display_id(), gfx::Point(),
displays_[0]->native_mode()})
.c_str(),
GetCrtcAction({displays_[1]->display_id(), gfx::Point(), &big_mode_})
.c_str(),
GetCrtcAction({displays_[2]->display_id(), gfx::Point(), &big_mode_})
.c_str(),
// Internal display will succeed modeset.
GetCrtcAction({displays_[0]->display_id(), gfx::Point(),
displays_[0]->native_mode()})
.c_str(),
// displays_[1] and displays_[2] will be tested and fail together
// under connector kInvalidConnectorId. Since neither expose any
// alternative modes to try, configuration completely fails.
GetCrtcAction({displays_[1]->display_id(), gfx::Point(), &big_mode_})
.c_str(),
GetCrtcAction({displays_[2]->display_id(), gfx::Point(), &big_mode_})
.c_str(),
nullptr),
log_.GetActionsAndClear());
}
// Tests that four external displays that share two separate bad MST hubs are
// tested and fail together, since they are grouped under kInvalidConnectorId.
// Also test that this does not affect the internal display's ability configured
// separately during retry and passes modeset.
TEST_F(ConfigureDisplaysTaskTest,
ConfigureInternalAndTwoBadMstHubsWithFourDisplaysFails) {
ConfigureDisplaysTask::ResponseCallback callback = base::BindOnce(
&ConfigureDisplaysTaskTest::ConfigureCallback, base::Unretained(this));
// Four displays sharing two bad MST Hubs that did not report their PATH
// topology correctly. First two:
displays_[1] = FakeDisplaySnapshot::Builder()
.SetId(456)
.SetNativeMode(big_mode_.Clone())
.SetCurrentMode(big_mode_.Clone())
.SetType(DISPLAY_CONNECTION_TYPE_DISPLAYPORT)
.SetBaseConnectorId(kInvalidConnectorId)
.Build();
displays_.push_back(FakeDisplaySnapshot::Builder()
.SetId(789)
.SetNativeMode(big_mode_.Clone())
.SetCurrentMode(big_mode_.Clone())
.AddMode(medium_mode_.Clone())
.AddMode(small_mode_.Clone())
.SetType(DISPLAY_CONNECTION_TYPE_DISPLAYPORT)
.SetBaseConnectorId(kInvalidConnectorId)
.Build());
// Last two:
displays_.push_back(FakeDisplaySnapshot::Builder()
.SetId(101112)
.SetNativeMode(big_mode_.Clone())
.SetCurrentMode(big_mode_.Clone())
.AddMode(medium_mode_.Clone())
.AddMode(small_mode_.Clone())
.SetType(DISPLAY_CONNECTION_TYPE_DISPLAYPORT)
.SetBaseConnectorId(kInvalidConnectorId)
.Build());
displays_.push_back(FakeDisplaySnapshot::Builder()
.SetId(131415)
.SetNativeMode(big_mode_.Clone())
.SetCurrentMode(big_mode_.Clone())
.SetType(DISPLAY_CONNECTION_TYPE_DISPLAYPORT)
.SetBaseConnectorId(kInvalidConnectorId)
.Build());
delegate_.set_max_configurable_pixels(medium_mode_.size().GetArea());
std::vector<DisplayConfigureRequest> requests;
for (const auto& display : displays_) {
requests.emplace_back(display.get(), display->native_mode(), gfx::Point());
}
ConfigureDisplaysTask task(&delegate_, requests, std::move(callback));
task.Run();
EXPECT_TRUE(callback_called_);
EXPECT_EQ(ConfigureDisplaysTask::ERROR, status_);
EXPECT_EQ(
JoinActions(
// All displays will fail to modeset together. Initiate retry logic.
GetCrtcAction({displays_[0]->display_id(), gfx::Point(),
displays_[0]->native_mode()})
.c_str(),
GetCrtcAction({displays_[1]->display_id(), gfx::Point(), &big_mode_})
.c_str(),
GetCrtcAction({displays_[2]->display_id(), gfx::Point(), &big_mode_})
.c_str(),
GetCrtcAction({displays_[3]->display_id(), gfx::Point(), &big_mode_})
.c_str(),
GetCrtcAction({displays_[4]->display_id(), gfx::Point(), &big_mode_})
.c_str(),
// Internal display will succeed modeset.
GetCrtcAction({displays_[0]->display_id(), gfx::Point(),
displays_[0]->native_mode()})
.c_str(),
// displays_[1-4] will be tested and downgraded as a group, since they
// share kInvalidConnectorId due to bad MST hubs.
GetCrtcAction({displays_[1]->display_id(), gfx::Point(), &big_mode_})
.c_str(),
GetCrtcAction({displays_[2]->display_id(), gfx::Point(), &big_mode_})
.c_str(),
GetCrtcAction({displays_[3]->display_id(), gfx::Point(), &big_mode_})
.c_str(),
GetCrtcAction({displays_[4]->display_id(), gfx::Point(), &big_mode_})
.c_str(),
// displays_[2] will downgrade first, since it is the next largest
// display with available alternative modes.
GetCrtcAction({displays_[1]->display_id(), gfx::Point(), &big_mode_})
.c_str(),
GetCrtcAction(
{displays_[2]->display_id(), gfx::Point(), &medium_mode_})
.c_str(),
GetCrtcAction({displays_[3]->display_id(), gfx::Point(), &big_mode_})
.c_str(),
GetCrtcAction({displays_[4]->display_id(), gfx::Point(), &big_mode_})
.c_str(),
// displays_[3] will downgrade next, and fail.
GetCrtcAction({displays_[1]->display_id(), gfx::Point(), &big_mode_})
.c_str(),
GetCrtcAction(
{displays_[2]->display_id(), gfx::Point(), &medium_mode_})
.c_str(),
GetCrtcAction(
{displays_[3]->display_id(), gfx::Point(), &medium_mode_})
.c_str(),
GetCrtcAction({displays_[4]->display_id(), gfx::Point(), &big_mode_})
.c_str(),
// Same downgrade process as above will repeat for displays_[2] and
// displays_[3] before failing completely.
GetCrtcAction({displays_[1]->display_id(), gfx::Point(), &big_mode_})
.c_str(),
GetCrtcAction(
{displays_[2]->display_id(), gfx::Point(), &small_mode_})
.c_str(),
GetCrtcAction(
{displays_[3]->display_id(), gfx::Point(), &medium_mode_})
.c_str(),
GetCrtcAction({displays_[4]->display_id(), gfx::Point(), &big_mode_})
.c_str(),
GetCrtcAction({displays_[1]->display_id(), gfx::Point(), &big_mode_})
.c_str(),
GetCrtcAction(
{displays_[2]->display_id(), gfx::Point(), &small_mode_})
.c_str(),
GetCrtcAction(
{displays_[3]->display_id(), gfx::Point(), &small_mode_})
.c_str(),
GetCrtcAction({displays_[4]->display_id(), gfx::Point(), &big_mode_})
.c_str(),
nullptr),
log_.GetActionsAndClear());
}
/**********************************************************
* Cases that report ConfigureDisplaysTask::PARTIAL_SUCCESS
**********************************************************/
// Tests that the last display (in order of available displays) attempts and
// succeeds to fallback after it fails to modeset the initial request.
TEST_F(ConfigureDisplaysTaskTest, ConfigureLastDisplayPartialSuccess) {
ConfigureDisplaysTask::ResponseCallback callback = base::BindOnce( ConfigureDisplaysTask::ResponseCallback callback = base::BindOnce(
&ConfigureDisplaysTaskTest::ConfigureCallback, base::Unretained(this)); &ConfigureDisplaysTaskTest::ConfigureCallback, base::Unretained(this));
...@@ -220,23 +1200,31 @@ TEST_F(ConfigureDisplaysTaskTest, ReconfigureLastDisplayPartialSuccess) { ...@@ -220,23 +1200,31 @@ TEST_F(ConfigureDisplaysTaskTest, ReconfigureLastDisplayPartialSuccess) {
EXPECT_TRUE(callback_called_); EXPECT_TRUE(callback_called_);
EXPECT_EQ(ConfigureDisplaysTask::PARTIAL_SUCCESS, status_); EXPECT_EQ(ConfigureDisplaysTask::PARTIAL_SUCCESS, status_);
EXPECT_EQ(JoinActions(GetCrtcAction({displays_[0]->display_id(), gfx::Point(), EXPECT_EQ(
displays_[0]->native_mode()}) JoinActions(
.c_str(), // All displays will fail to modeset together. Initiate retry logic.
GetCrtcAction({displays_[1]->display_id(), gfx::Point(), GetCrtcAction({displays_[0]->display_id(), gfx::Point(),
&big_mode_}) displays_[0]->native_mode()})
.c_str(), .c_str(),
GetCrtcAction({displays_[0]->display_id(), gfx::Point(), GetCrtcAction({displays_[1]->display_id(), gfx::Point(), &big_mode_})
displays_[0]->native_mode()}) .c_str(),
.c_str(), // Internal display will succeed to modeset.
GetCrtcAction({displays_[1]->display_id(), gfx::Point(), GetCrtcAction({displays_[0]->display_id(), gfx::Point(),
&small_mode_}) displays_[0]->native_mode()})
.c_str(), .c_str(),
nullptr), // Last display will fail once, downgrade, and pass.
log_.GetActionsAndClear()); GetCrtcAction({displays_[1]->display_id(), gfx::Point(), &big_mode_})
.c_str(),
GetCrtcAction(
{displays_[1]->display_id(), gfx::Point(), &small_mode_})
.c_str(),
nullptr),
log_.GetActionsAndClear());
} }
TEST_F(ConfigureDisplaysTaskTest, ReconfigureMiddleDisplayPartialSuccess) { // Tests that the second display (in order of available displays) attempts and
// succeeds to fallback after it fails to modeset the initial request.
TEST_F(ConfigureDisplaysTaskTest, ConfigureMiddleDisplayPartialSuccess) {
ConfigureDisplaysTask::ResponseCallback callback = base::BindOnce( ConfigureDisplaysTask::ResponseCallback callback = base::BindOnce(
&ConfigureDisplaysTaskTest::ConfigureCallback, base::Unretained(this)); &ConfigureDisplaysTaskTest::ConfigureCallback, base::Unretained(this));
...@@ -245,6 +1233,7 @@ TEST_F(ConfigureDisplaysTaskTest, ReconfigureMiddleDisplayPartialSuccess) { ...@@ -245,6 +1233,7 @@ TEST_F(ConfigureDisplaysTaskTest, ReconfigureMiddleDisplayPartialSuccess) {
.SetNativeMode(small_mode_.Clone()) .SetNativeMode(small_mode_.Clone())
.SetCurrentMode(small_mode_.Clone()) .SetCurrentMode(small_mode_.Clone())
.SetType(DISPLAY_CONNECTION_TYPE_HDMI) .SetType(DISPLAY_CONNECTION_TYPE_HDMI)
.SetBaseConnectorId(kThirdConnectorId)
.Build()); .Build());
delegate_.set_max_configurable_pixels(medium_mode_.size().GetArea()); delegate_.set_max_configurable_pixels(medium_mode_.size().GetArea());
...@@ -259,28 +1248,332 @@ TEST_F(ConfigureDisplaysTaskTest, ReconfigureMiddleDisplayPartialSuccess) { ...@@ -259,28 +1248,332 @@ TEST_F(ConfigureDisplaysTaskTest, ReconfigureMiddleDisplayPartialSuccess) {
EXPECT_TRUE(callback_called_); EXPECT_TRUE(callback_called_);
EXPECT_EQ(ConfigureDisplaysTask::PARTIAL_SUCCESS, status_); EXPECT_EQ(ConfigureDisplaysTask::PARTIAL_SUCCESS, status_);
EXPECT_EQ(JoinActions(GetCrtcAction({displays_[0]->display_id(), gfx::Point(), EXPECT_EQ(
displays_[0]->native_mode()}) JoinActions(
.c_str(), // All displays will fail to modeset together. Initiate retry logic.
GetCrtcAction({displays_[1]->display_id(), gfx::Point(), GetCrtcAction({displays_[0]->display_id(), gfx::Point(),
&big_mode_}) displays_[0]->native_mode()})
.c_str(), .c_str(),
GetCrtcAction({displays_[2]->display_id(), gfx::Point(), GetCrtcAction({displays_[1]->display_id(), gfx::Point(), &big_mode_})
&small_mode_}) .c_str(),
.c_str(), GetCrtcAction(
GetCrtcAction({displays_[0]->display_id(), gfx::Point(), {displays_[2]->display_id(), gfx::Point(), &small_mode_})
displays_[0]->native_mode()}) .c_str(),
.c_str(), // Internal display will succeed to modeset.
GetCrtcAction({displays_[1]->display_id(), gfx::Point(), GetCrtcAction({displays_[0]->display_id(), gfx::Point(),
&small_mode_}) displays_[0]->native_mode()})
.c_str(), .c_str(),
GetCrtcAction({displays_[2]->display_id(), gfx::Point(), // Second display will fail once, downgrade, and pass.
&small_mode_}) GetCrtcAction({displays_[1]->display_id(), gfx::Point(), &big_mode_})
.c_str(), .c_str(),
nullptr), GetCrtcAction(
log_.GetActionsAndClear()); {displays_[1]->display_id(), gfx::Point(), &small_mode_})
.c_str(),
// Third external display will succeed to modeset on first attempt.
GetCrtcAction(
{displays_[2]->display_id(), gfx::Point(), &small_mode_})
.c_str(),
nullptr),
log_.GetActionsAndClear());
}
// Tests that both MST displays fail initial configuration and are tested,
// downgraded, and eventually pass modeset as a group and separately from the
// internal display.
TEST_F(ConfigureDisplaysTaskTest, ConfigureTwoMstDisplaysPartialSuccess) {
ConfigureDisplaysTask::ResponseCallback callback = base::BindOnce(
&ConfigureDisplaysTaskTest::ConfigureCallback, base::Unretained(this));
// Add an additional display to the base connector kSecondConnectorId via MST.
displays_.push_back(FakeDisplaySnapshot::Builder()
.SetId(789)
.SetNativeMode(big_mode_.Clone())
.SetCurrentMode(big_mode_.Clone())
.AddMode(small_mode_.Clone())
.SetType(DISPLAY_CONNECTION_TYPE_DISPLAYPORT)
.SetBaseConnectorId(kSecondConnectorId)
.Build());
delegate_.set_max_configurable_pixels(medium_mode_.size().GetArea());
std::vector<DisplayConfigureRequest> requests;
for (const auto& display : displays_) {
requests.emplace_back(display.get(), display->native_mode(), gfx::Point());
}
ConfigureDisplaysTask task(&delegate_, requests, std::move(callback));
task.Run();
EXPECT_TRUE(callback_called_);
EXPECT_EQ(ConfigureDisplaysTask::PARTIAL_SUCCESS, status_);
EXPECT_EQ(
JoinActions(
// All displays will fail to modeset together. Initiate retry logic.
GetCrtcAction({displays_[0]->display_id(), gfx::Point(),
displays_[0]->native_mode()})
.c_str(),
GetCrtcAction({displays_[1]->display_id(), gfx::Point(), &big_mode_})
.c_str(),
GetCrtcAction({displays_[2]->display_id(), gfx::Point(), &big_mode_})
.c_str(),
// Internal display will succeed modeset.
GetCrtcAction({displays_[0]->display_id(), gfx::Point(),
displays_[0]->native_mode()})
.c_str(),
// MST displays will be tested (and fail) together.
GetCrtcAction({displays_[1]->display_id(), gfx::Point(), &big_mode_})
.c_str(),
GetCrtcAction({displays_[2]->display_id(), gfx::Point(), &big_mode_})
.c_str(),
// |displays_[1]| will downgrade first. Configuration will fail.
GetCrtcAction(
{displays_[1]->display_id(), gfx::Point(), &small_mode_})
.c_str(),
GetCrtcAction({displays_[2]->display_id(), gfx::Point(), &big_mode_})
.c_str(),
// |displays_[2] will downgrade next. Configuration succeeds.
GetCrtcAction(
{displays_[1]->display_id(), gfx::Point(), &small_mode_})
.c_str(),
GetCrtcAction(
{displays_[2]->display_id(), gfx::Point(), &small_mode_})
.c_str(),
nullptr),
log_.GetActionsAndClear());
} }
// Tests that the two MST displays, and then the HDMI display fail initial
// configuration, are tested, downgraded, and eventually pass modeset as
// separate groups.
TEST_F(ConfigureDisplaysTaskTest,
ConfigureInternalAndTwoMstAndHdmiDisplaysPartialSuccess) {
ConfigureDisplaysTask::ResponseCallback callback = base::BindOnce(
&ConfigureDisplaysTaskTest::ConfigureCallback, base::Unretained(this));
// Add an additional display to the base connector kSecondConnectorId via MST.
displays_.push_back(FakeDisplaySnapshot::Builder()
.SetId(789)
.SetNativeMode(big_mode_.Clone())
.SetCurrentMode(big_mode_.Clone())
.AddMode(small_mode_.Clone())
.SetType(DISPLAY_CONNECTION_TYPE_DISPLAYPORT)
.SetBaseConnectorId(kSecondConnectorId)
.Build());
// Additional independent HDMI display (has its own connector).
displays_.push_back(FakeDisplaySnapshot::Builder()
.SetId(101112)
.SetNativeMode(big_mode_.Clone())
.SetCurrentMode(big_mode_.Clone())
.AddMode(medium_mode_.Clone())
.AddMode(small_mode_.Clone())
.SetType(DISPLAY_CONNECTION_TYPE_HDMI)
.SetBaseConnectorId(kThirdConnectorId)
.Build());
delegate_.set_max_configurable_pixels(medium_mode_.size().GetArea());
std::vector<DisplayConfigureRequest> requests;
for (const auto& display : displays_) {
requests.emplace_back(display.get(), display->native_mode(), gfx::Point());
}
ConfigureDisplaysTask task(&delegate_, requests, std::move(callback));
task.Run();
EXPECT_TRUE(callback_called_);
EXPECT_EQ(ConfigureDisplaysTask::PARTIAL_SUCCESS, status_);
EXPECT_EQ(
JoinActions(
// All displays will fail to modeset together. Initiate retry logic.
GetCrtcAction({displays_[0]->display_id(), gfx::Point(),
displays_[0]->native_mode()})
.c_str(),
GetCrtcAction({displays_[1]->display_id(), gfx::Point(), &big_mode_})
.c_str(),
GetCrtcAction({displays_[2]->display_id(), gfx::Point(), &big_mode_})
.c_str(),
GetCrtcAction({displays_[3]->display_id(), gfx::Point(), &big_mode_})
.c_str(),
// Internal display will succeed modeset.
GetCrtcAction({displays_[0]->display_id(), gfx::Point(),
displays_[0]->native_mode()})
.c_str(),
// Both MST displays will be tested (and fail) together.
GetCrtcAction({displays_[1]->display_id(), gfx::Point(), &big_mode_})
.c_str(),
GetCrtcAction({displays_[2]->display_id(), gfx::Point(), &big_mode_})
.c_str(),
// |displays_[1]| will downgrade first. Configuration will fail.
GetCrtcAction(
{displays_[1]->display_id(), gfx::Point(), &small_mode_})
.c_str(),
GetCrtcAction({displays_[2]->display_id(), gfx::Point(), &big_mode_})
.c_str(),
// |displays_[2] will downgrade next. Configuration succeeds.
GetCrtcAction(
{displays_[1]->display_id(), gfx::Point(), &small_mode_})
.c_str(),
GetCrtcAction(
{displays_[2]->display_id(), gfx::Point(), &small_mode_})
.c_str(),
// HDMI display will fail modeset and downgrade once. Configuration
// will then succeed.
GetCrtcAction({displays_[3]->display_id(), gfx::Point(), &big_mode_})
.c_str(),
GetCrtcAction(
{displays_[3]->display_id(), gfx::Point(), &medium_mode_})
.c_str(),
nullptr),
log_.GetActionsAndClear());
}
// Tests a nested MST configuration in which after a successful modset on the
// root branch device (i.e. two external displays connected to a single MST hub)
// one display is removed from the original MST hub, connected to a second MST
// hub together with a third display, and then the second MST hub is connected
// to the first. The tests ensures that the three MST displays are grouped,
// tested, and fallback together appropriately before passing modeset.
TEST_F(ConfigureDisplaysTaskTest,
ConfigureInternalAndMstThenNestAnotherMstForThreeExternalDisplays) {
// We now have one internal display + two external displays connected via MST.
displays_.push_back(FakeDisplaySnapshot::Builder()
.SetId(789)
.SetNativeMode(big_mode_.Clone())
.SetCurrentMode(big_mode_.Clone())
.AddMode(small_mode_.Clone())
.SetType(DISPLAY_CONNECTION_TYPE_DISPLAYPORT)
.SetBaseConnectorId(kSecondConnectorId)
.Build());
// Initial configuration succeeds modeset.
{
ConfigureDisplaysTask::ResponseCallback callback = base::BindOnce(
&ConfigureDisplaysTaskTest::ConfigureCallback, base::Unretained(this));
std::vector<DisplayConfigureRequest> requests;
for (const auto& display : displays_) {
requests.emplace_back(display.get(), display->native_mode(),
gfx::Point());
}
ConfigureDisplaysTask task(&delegate_, requests, std::move(callback));
task.Run();
EXPECT_TRUE(callback_called_);
EXPECT_EQ(ConfigureDisplaysTask::SUCCESS, status_);
EXPECT_EQ(
JoinActions(GetCrtcAction({displays_[0]->display_id(), gfx::Point(),
displays_[0]->native_mode()})
.c_str(),
GetCrtcAction(
{displays_[1]->display_id(), gfx::Point(), &big_mode_})
.c_str(),
GetCrtcAction(
{displays_[2]->display_id(), gfx::Point(), &big_mode_})
.c_str(),
nullptr),
log_.GetActionsAndClear());
}
// Add an additional display to kSecondConnectorId. This is akin to unplugging
// One display from the first MST hub, attaching it to a second one, together
// with a third display, and plugging the second MST hub to the first.
displays_.push_back(FakeDisplaySnapshot::Builder()
.SetId(101112)
.SetNativeMode(big_mode_.Clone())
.SetCurrentMode(big_mode_.Clone())
.AddMode(small_mode_.Clone())
.SetType(DISPLAY_CONNECTION_TYPE_DISPLAYPORT)
.SetBaseConnectorId(kSecondConnectorId)
.Build());
// Simulate bandwidth pressure by reducing configurable pixels.
delegate_.set_max_configurable_pixels(medium_mode_.size().GetArea());
// This configuration requires that all displays connected via the nested
// MST setup downgrade, so we test that all three displays are grouped,
// fallback appropriately, and eventually succeed modeset.
{
ConfigureDisplaysTask::ResponseCallback callback = base::BindOnce(
&ConfigureDisplaysTaskTest::ConfigureCallback, base::Unretained(this));
std::vector<DisplayConfigureRequest> requests;
for (const auto& display : displays_) {
requests.emplace_back(display.get(), display->native_mode(),
gfx::Point());
}
ConfigureDisplaysTask task(&delegate_, requests, std::move(callback));
task.Run();
EXPECT_TRUE(callback_called_);
EXPECT_EQ(ConfigureDisplaysTask::PARTIAL_SUCCESS, status_);
EXPECT_EQ(
JoinActions(
// All displays will fail to modeset together. Initiate retry logic.
GetCrtcAction({displays_[0]->display_id(), gfx::Point(),
displays_[0]->native_mode()})
.c_str(),
GetCrtcAction(
{displays_[1]->display_id(), gfx::Point(), &big_mode_})
.c_str(),
GetCrtcAction(
{displays_[2]->display_id(), gfx::Point(), &big_mode_})
.c_str(),
GetCrtcAction(
{displays_[3]->display_id(), gfx::Point(), &big_mode_})
.c_str(),
// Internal display will succeed to modeset.
GetCrtcAction({displays_[0]->display_id(), gfx::Point(),
displays_[0]->native_mode()})
.c_str(),
// All MST displays will fail modeset together.
GetCrtcAction(
{displays_[1]->display_id(), gfx::Point(), &big_mode_})
.c_str(),
GetCrtcAction(
{displays_[2]->display_id(), gfx::Point(), &big_mode_})
.c_str(),
GetCrtcAction(
{displays_[3]->display_id(), gfx::Point(), &big_mode_})
.c_str(),
// displays_[1] will downgrade first, then displays_[2], followed by
// displays_[3]. Then the configuration will pass modeset.
GetCrtcAction(
{displays_[1]->display_id(), gfx::Point(), &small_mode_})
.c_str(),
GetCrtcAction(
{displays_[2]->display_id(), gfx::Point(), &big_mode_})
.c_str(),
GetCrtcAction(
{displays_[3]->display_id(), gfx::Point(), &big_mode_})
.c_str(),
GetCrtcAction(
{displays_[1]->display_id(), gfx::Point(), &small_mode_})
.c_str(),
GetCrtcAction(
{displays_[2]->display_id(), gfx::Point(), &small_mode_})
.c_str(),
GetCrtcAction(
{displays_[3]->display_id(), gfx::Point(), &big_mode_})
.c_str(),
GetCrtcAction(
{displays_[1]->display_id(), gfx::Point(), &small_mode_})
.c_str(),
GetCrtcAction(
{displays_[2]->display_id(), gfx::Point(), &small_mode_})
.c_str(),
GetCrtcAction(
{displays_[3]->display_id(), gfx::Point(), &small_mode_})
.c_str(),
nullptr),
log_.GetActionsAndClear());
}
}
// Tests that an internal display with one external display pass modeset
// asynchronously after the external display fallback once.
TEST_F(ConfigureDisplaysTaskTest, AsyncConfigureWithTwoDisplaysPartialSuccess) { TEST_F(ConfigureDisplaysTaskTest, AsyncConfigureWithTwoDisplaysPartialSuccess) {
ConfigureDisplaysTask::ResponseCallback callback = base::BindOnce( ConfigureDisplaysTask::ResponseCallback callback = base::BindOnce(
&ConfigureDisplaysTaskTest::ConfigureCallback, base::Unretained(this)); &ConfigureDisplaysTaskTest::ConfigureCallback, base::Unretained(this));
...@@ -301,20 +1594,26 @@ TEST_F(ConfigureDisplaysTaskTest, AsyncConfigureWithTwoDisplaysPartialSuccess) { ...@@ -301,20 +1594,26 @@ TEST_F(ConfigureDisplaysTaskTest, AsyncConfigureWithTwoDisplaysPartialSuccess) {
EXPECT_TRUE(callback_called_); EXPECT_TRUE(callback_called_);
EXPECT_EQ(ConfigureDisplaysTask::PARTIAL_SUCCESS, status_); EXPECT_EQ(ConfigureDisplaysTask::PARTIAL_SUCCESS, status_);
EXPECT_EQ(JoinActions(GetCrtcAction({displays_[0]->display_id(), gfx::Point(), EXPECT_EQ(
displays_[0]->native_mode()}) JoinActions(
.c_str(), // All displays will fail to modeset together.
GetCrtcAction({displays_[1]->display_id(), gfx::Point(), GetCrtcAction({displays_[0]->display_id(), gfx::Point(),
&big_mode_}) displays_[0]->native_mode()})
.c_str(), .c_str(),
GetCrtcAction({displays_[0]->display_id(), gfx::Point(), GetCrtcAction({displays_[1]->display_id(), gfx::Point(), &big_mode_})
displays_[0]->native_mode()}) .c_str(),
.c_str(), // Internal display will succeed to modeset.
GetCrtcAction({displays_[1]->display_id(), gfx::Point(), GetCrtcAction({displays_[0]->display_id(), gfx::Point(),
&small_mode_}) displays_[0]->native_mode()})
.c_str(), .c_str(),
nullptr), // External display will fail once, downgrade, and pass.
log_.GetActionsAndClear()); GetCrtcAction({displays_[1]->display_id(), gfx::Point(), &big_mode_})
.c_str(),
GetCrtcAction(
{displays_[1]->display_id(), gfx::Point(), &small_mode_})
.c_str(),
nullptr),
log_.GetActionsAndClear());
} }
} // namespace test } // namespace test
......
...@@ -29,6 +29,11 @@ namespace { ...@@ -29,6 +29,11 @@ namespace {
constexpr int64_t kDisplayIds[3] = {123, 456, 789}; constexpr int64_t kDisplayIds[3] = {123, 456, 789};
// Non-zero generic connector IDs.
constexpr uint64_t kEdpConnectorId = 71u;
constexpr uint64_t kSecondConnectorId = kEdpConnectorId + 10u;
constexpr uint64_t kThirdConnectorId = kEdpConnectorId + 20u;
std::unique_ptr<DisplayMode> MakeDisplayMode(int width, std::unique_ptr<DisplayMode> MakeDisplayMode(int width,
int height, int height,
bool is_interlaced, bool is_interlaced,
...@@ -243,6 +248,7 @@ class DisplayConfiguratorTest : public testing::Test { ...@@ -243,6 +248,7 @@ class DisplayConfiguratorTest : public testing::Test {
.SetNativeMode(small_mode_.Clone()) .SetNativeMode(small_mode_.Clone())
.SetCurrentMode(small_mode_.Clone()) .SetCurrentMode(small_mode_.Clone())
.SetType(DISPLAY_CONNECTION_TYPE_INTERNAL) .SetType(DISPLAY_CONNECTION_TYPE_INTERNAL)
.SetBaseConnectorId(kEdpConnectorId)
.SetIsAspectPerservingScaling(true) .SetIsAspectPerservingScaling(true)
.Build(); .Build();
...@@ -252,6 +258,7 @@ class DisplayConfiguratorTest : public testing::Test { ...@@ -252,6 +258,7 @@ class DisplayConfiguratorTest : public testing::Test {
.SetCurrentMode(big_mode_.Clone()) .SetCurrentMode(big_mode_.Clone())
.AddMode(small_mode_.Clone()) .AddMode(small_mode_.Clone())
.SetType(DISPLAY_CONNECTION_TYPE_HDMI) .SetType(DISPLAY_CONNECTION_TYPE_HDMI)
.SetBaseConnectorId(kSecondConnectorId)
.SetIsAspectPerservingScaling(true) .SetIsAspectPerservingScaling(true)
.Build(); .Build();
...@@ -260,6 +267,7 @@ class DisplayConfiguratorTest : public testing::Test { ...@@ -260,6 +267,7 @@ class DisplayConfiguratorTest : public testing::Test {
.SetNativeMode(small_mode_.Clone()) .SetNativeMode(small_mode_.Clone())
.SetCurrentMode(small_mode_.Clone()) .SetCurrentMode(small_mode_.Clone())
.SetType(DISPLAY_CONNECTION_TYPE_HDMI) .SetType(DISPLAY_CONNECTION_TYPE_HDMI)
.SetBaseConnectorId(kThirdConnectorId)
.SetIsAspectPerservingScaling(true) .SetIsAspectPerservingScaling(true)
.Build(); .Build();
...@@ -962,6 +970,7 @@ TEST_F(DisplayConfiguratorTest, HandleConfigureCrtcFailure) { ...@@ -962,6 +970,7 @@ TEST_F(DisplayConfiguratorTest, HandleConfigureCrtcFailure) {
.AddMode(modes[3]->Clone()) .AddMode(modes[3]->Clone())
.AddMode(modes[4]->Clone()) .AddMode(modes[4]->Clone())
.SetType(DISPLAY_CONNECTION_TYPE_INTERNAL) .SetType(DISPLAY_CONNECTION_TYPE_INTERNAL)
.SetBaseConnectorId(kEdpConnectorId)
.SetIsAspectPerservingScaling(true) .SetIsAspectPerservingScaling(true)
.Build(); .Build();
...@@ -973,8 +982,16 @@ TEST_F(DisplayConfiguratorTest, HandleConfigureCrtcFailure) { ...@@ -973,8 +982,16 @@ TEST_F(DisplayConfiguratorTest, HandleConfigureCrtcFailure) {
state_controller_.set_state(MULTIPLE_DISPLAY_STATE_SINGLE); state_controller_.set_state(MULTIPLE_DISPLAY_STATE_SINGLE);
UpdateOutputs(1, true); UpdateOutputs(1, true);
EXPECT_EQ(GetCrtcAction({outputs_[0]->display_id(), gfx::Point(0, 0), EXPECT_EQ(JoinActions(
outputs_[0]->native_mode()}), // Initial attempt fails. Initiate retry logic.
GetCrtcAction({outputs_[0]->display_id(), gfx::Point(0, 0),
outputs_[0]->native_mode()})
.c_str(),
// Retry fails since it cannot downgrade the internal display.
GetCrtcAction({outputs_[0]->display_id(), gfx::Point(0, 0),
outputs_[0]->native_mode()})
.c_str(),
nullptr),
log_->GetActionsAndClear()); log_->GetActionsAndClear());
outputs_[0] = FakeDisplaySnapshot::Builder() outputs_[0] = FakeDisplaySnapshot::Builder()
...@@ -986,6 +1003,7 @@ TEST_F(DisplayConfiguratorTest, HandleConfigureCrtcFailure) { ...@@ -986,6 +1003,7 @@ TEST_F(DisplayConfiguratorTest, HandleConfigureCrtcFailure) {
.AddMode(modes[3]->Clone()) .AddMode(modes[3]->Clone())
.AddMode(modes[4]->Clone()) .AddMode(modes[4]->Clone())
.SetType(DISPLAY_CONNECTION_TYPE_DISPLAYPORT) .SetType(DISPLAY_CONNECTION_TYPE_DISPLAYPORT)
.SetBaseConnectorId(kEdpConnectorId)
.SetIsAspectPerservingScaling(true) .SetIsAspectPerservingScaling(true)
.Build(); .Build();
...@@ -994,16 +1012,22 @@ TEST_F(DisplayConfiguratorTest, HandleConfigureCrtcFailure) { ...@@ -994,16 +1012,22 @@ TEST_F(DisplayConfiguratorTest, HandleConfigureCrtcFailure) {
// is closed). // is closed).
UpdateOutputs(1, true); UpdateOutputs(1, true);
EXPECT_EQ(JoinActions(GetCrtcAction({outputs_[0]->display_id(), EXPECT_EQ(JoinActions(
gfx::Point(0, 0), modes[0].get()}) // Initial attempt fails. Initiate retry logic.
.c_str(), GetCrtcAction({outputs_[0]->display_id(), gfx::Point(0, 0),
GetCrtcAction({outputs_[0]->display_id(), modes[0].get()})
gfx::Point(0, 0), modes[3].get()}) .c_str(),
.c_str(), // Retry attempts trying all available modes.
GetCrtcAction({outputs_[0]->display_id(), GetCrtcAction({outputs_[0]->display_id(), gfx::Point(0, 0),
gfx::Point(0, 0), modes[2].get()}) modes[0].get()})
.c_str(), .c_str(),
nullptr), GetCrtcAction({outputs_[0]->display_id(), gfx::Point(0, 0),
modes[3].get()})
.c_str(),
GetCrtcAction({outputs_[0]->display_id(), gfx::Point(0, 0),
modes[2].get()})
.c_str(),
nullptr),
log_->GetActionsAndClear()); log_->GetActionsAndClear());
outputs_[0] = FakeDisplaySnapshot::Builder() outputs_[0] = FakeDisplaySnapshot::Builder()
...@@ -1015,6 +1039,7 @@ TEST_F(DisplayConfiguratorTest, HandleConfigureCrtcFailure) { ...@@ -1015,6 +1039,7 @@ TEST_F(DisplayConfiguratorTest, HandleConfigureCrtcFailure) {
.AddMode(modes[3]->Clone()) .AddMode(modes[3]->Clone())
.AddMode(modes[4]->Clone()) .AddMode(modes[4]->Clone())
.SetType(DISPLAY_CONNECTION_TYPE_INTERNAL) .SetType(DISPLAY_CONNECTION_TYPE_INTERNAL)
.SetBaseConnectorId(kEdpConnectorId)
.SetIsAspectPerservingScaling(true) .SetIsAspectPerservingScaling(true)
.Build(); .Build();
...@@ -1027,6 +1052,7 @@ TEST_F(DisplayConfiguratorTest, HandleConfigureCrtcFailure) { ...@@ -1027,6 +1052,7 @@ TEST_F(DisplayConfiguratorTest, HandleConfigureCrtcFailure) {
.AddMode(modes[3]->Clone()) .AddMode(modes[3]->Clone())
.AddMode(modes[4]->Clone()) .AddMode(modes[4]->Clone())
.SetType(DISPLAY_CONNECTION_TYPE_HDMI) .SetType(DISPLAY_CONNECTION_TYPE_HDMI)
.SetBaseConnectorId(kSecondConnectorId)
.SetIsAspectPerservingScaling(true) .SetIsAspectPerservingScaling(true)
.Build(); .Build();
...@@ -1039,39 +1065,37 @@ TEST_F(DisplayConfiguratorTest, HandleConfigureCrtcFailure) { ...@@ -1039,39 +1065,37 @@ TEST_F(DisplayConfiguratorTest, HandleConfigureCrtcFailure) {
EXPECT_EQ( EXPECT_EQ(
JoinActions( JoinActions(
// Initial attempt fails. Initiate retry logic.
GetCrtcAction({outputs_[0]->display_id(), gfx::Point(0, 0), GetCrtcAction({outputs_[0]->display_id(), gfx::Point(0, 0),
outputs_[0]->native_mode()}) outputs_[0]->native_mode()})
.c_str(), .c_str(),
// Then attempt to configure crtc1 with the first mode.
GetCrtcAction( GetCrtcAction(
{outputs_[1]->display_id(), gfx::Point(0, 0), modes[0].get()}) {outputs_[1]->display_id(), gfx::Point(0, 0), modes[0].get()})
.c_str(), .c_str(),
// First try is expected to fail and it will retry with the next // Retry logic fails to modeset internal display. Since internal
// largest mode in the list (for non-internal displays).since the // displays are restricted to their preferred mode, there are no other
// internal display will always fail, the display configurator will // modes to try. The configuration fails completely, but the external
// attempt all of the external display's available modes before it // display will still try to modeset.
// gives up.
GetCrtcAction({outputs_[0]->display_id(), gfx::Point(0, 0), GetCrtcAction({outputs_[0]->display_id(), gfx::Point(0, 0),
outputs_[0]->native_mode()}) outputs_[0]->native_mode()})
.c_str(), .c_str(),
// The external display will cycle through all its available modes
// before failing completely.
GetCrtcAction( GetCrtcAction(
{outputs_[1]->display_id(), gfx::Point(0, 0), modes[3].get()}) {outputs_[1]->display_id(), gfx::Point(0, 0), modes[0].get()})
.c_str(), .c_str(),
GetCrtcAction({outputs_[0]->display_id(), gfx::Point(0, 0), GetCrtcAction(
outputs_[0]->native_mode()}) {outputs_[1]->display_id(), gfx::Point(0, 0), modes[3].get()})
.c_str(), .c_str(),
GetCrtcAction( GetCrtcAction(
{outputs_[1]->display_id(), gfx::Point(0, 0), modes[2].get()}) {outputs_[1]->display_id(), gfx::Point(0, 0), modes[2].get()})
.c_str(), .c_str(),
GetCrtcAction({outputs_[0]->display_id(), gfx::Point(0, 0),
outputs_[0]->native_mode()})
.c_str(),
GetCrtcAction( GetCrtcAction(
{outputs_[1]->display_id(), gfx::Point(0, 0), modes[1].get()}) {outputs_[1]->display_id(), gfx::Point(0, 0), modes[1].get()})
.c_str(), .c_str(),
// Since it was requested to go into mirror mode and the configured // Since mirror mode configuration failed it should now attempt to
// modes were different, it should now try and setup a valid // configure in extended mode. However, initial attempt fails.
// configurable extended mode in the same order described above. // Initiate retry logic.
GetCrtcAction({outputs_[0]->display_id(), gfx::Point(0, 0), GetCrtcAction({outputs_[0]->display_id(), gfx::Point(0, 0),
outputs_[0]->native_mode()}) outputs_[0]->native_mode()})
.c_str(), .c_str(),
...@@ -1080,25 +1104,27 @@ TEST_F(DisplayConfiguratorTest, HandleConfigureCrtcFailure) { ...@@ -1080,25 +1104,27 @@ TEST_F(DisplayConfiguratorTest, HandleConfigureCrtcFailure) {
DisplayConfigurator::kVerticalGap), DisplayConfigurator::kVerticalGap),
modes[0].get()}) modes[0].get()})
.c_str(), .c_str(),
// Just as above, retry logic fails to modeset internal display.
GetCrtcAction({outputs_[0]->display_id(), gfx::Point(0, 0), GetCrtcAction({outputs_[0]->display_id(), gfx::Point(0, 0),
outputs_[0]->native_mode()}) outputs_[0]->native_mode()})
.c_str(), .c_str(),
// The configuration fails completely but still attempts to modeset
// the external display.
GetCrtcAction({outputs_[1]->display_id(), GetCrtcAction({outputs_[1]->display_id(),
gfx::Point(0, modes[0]->size().height() + gfx::Point(0, modes[0]->size().height() +
DisplayConfigurator::kVerticalGap), DisplayConfigurator::kVerticalGap),
modes[3].get()}) modes[0].get()})
.c_str(), .c_str(),
GetCrtcAction({outputs_[0]->display_id(), gfx::Point(0, 0), GetCrtcAction({outputs_[1]->display_id(),
outputs_[0]->native_mode()}) gfx::Point(0, modes[0]->size().height() +
DisplayConfigurator::kVerticalGap),
modes[3].get()})
.c_str(), .c_str(),
GetCrtcAction({outputs_[1]->display_id(), GetCrtcAction({outputs_[1]->display_id(),
gfx::Point(0, modes[0]->size().height() + gfx::Point(0, modes[0]->size().height() +
DisplayConfigurator::kVerticalGap), DisplayConfigurator::kVerticalGap),
modes[2].get()}) modes[2].get()})
.c_str(), .c_str(),
GetCrtcAction({outputs_[0]->display_id(), gfx::Point(0, 0),
outputs_[0]->native_mode()})
.c_str(),
GetCrtcAction({outputs_[1]->display_id(), GetCrtcAction({outputs_[1]->display_id(),
gfx::Point(0, modes[0]->size().height() + gfx::Point(0, modes[0]->size().height() +
DisplayConfigurator::kVerticalGap), DisplayConfigurator::kVerticalGap),
...@@ -1304,8 +1330,12 @@ TEST_F(DisplayConfiguratorTest, ...@@ -1304,8 +1330,12 @@ TEST_F(DisplayConfiguratorTest,
EXPECT_EQ(0, observer_.num_changes()); EXPECT_EQ(0, observer_.num_changes());
EXPECT_EQ(1, observer_.num_failures()); EXPECT_EQ(1, observer_.num_failures());
EXPECT_EQ(GetCrtcActions(DisplayConfig::kOff, &small_mode_, &big_mode_), EXPECT_EQ(
log_->GetActionsAndClear()); JoinActions(
GetCrtcActions(DisplayConfig::kOff, &small_mode_, &big_mode_).c_str(),
GetCrtcActions(DisplayConfig::kOff, &small_mode_, &big_mode_).c_str(),
nullptr),
log_->GetActionsAndClear());
// This configuration should trigger a display configuration since the // This configuration should trigger a display configuration since the
// previous configuration failed. // previous configuration failed.
...@@ -1321,6 +1351,11 @@ TEST_F(DisplayConfiguratorTest, ...@@ -1321,6 +1351,11 @@ TEST_F(DisplayConfiguratorTest,
JoinActions( JoinActions(
GetCrtcActions(&small_mode_, &big_mode_).c_str(), GetCrtcActions(&small_mode_, &big_mode_).c_str(),
GetCrtcActions(&small_mode_).c_str(), GetCrtcActions(&small_mode_).c_str(),
GetCrtcAction({outputs_[1]->display_id(),
gfx::Point(0, small_mode_.size().height() +
DisplayConfigurator::kVerticalGap),
&big_mode_})
.c_str(),
GetCrtcAction({outputs_[1]->display_id(), GetCrtcAction({outputs_[1]->display_id(),
gfx::Point(0, small_mode_.size().height() + gfx::Point(0, small_mode_.size().height() +
DisplayConfigurator::kVerticalGap), DisplayConfigurator::kVerticalGap),
......
...@@ -16,12 +16,17 @@ ...@@ -16,12 +16,17 @@
#include "ui/display/manager/display_layout_manager.h" #include "ui/display/manager/display_layout_manager.h"
#include "ui/display/manager/test/action_logger_util.h" #include "ui/display/manager/test/action_logger_util.h"
#include "ui/display/manager/test/test_native_display_delegate.h" #include "ui/display/manager/test/test_native_display_delegate.h"
#include "ui/display/types/display_constants.h"
namespace display { namespace display {
namespace test { namespace test {
namespace { namespace {
// Non-zero generic connector IDs.
constexpr uint64_t kEdpConnectorId = 71u;
constexpr uint64_t kSecondConnectorId = kEdpConnectorId + 10u;
class TestSoftwareMirroringController class TestSoftwareMirroringController
: public DisplayConfigurator::SoftwareMirroringController { : public DisplayConfigurator::SoftwareMirroringController {
public: public:
...@@ -153,13 +158,17 @@ class UpdateDisplayConfigurationTaskTest : public testing::Test { ...@@ -153,13 +158,17 @@ class UpdateDisplayConfigurationTaskTest : public testing::Test {
.SetId(123) .SetId(123)
.SetNativeMode(small_mode_.Clone()) .SetNativeMode(small_mode_.Clone())
.SetCurrentMode(small_mode_.Clone()) .SetCurrentMode(small_mode_.Clone())
.SetType(DISPLAY_CONNECTION_TYPE_INTERNAL)
.SetBaseConnectorId(kEdpConnectorId)
.Build(); .Build();
displays_[1] = FakeDisplaySnapshot::Builder() displays_[1] = FakeDisplaySnapshot::Builder()
.SetId(456) .SetId(456)
.SetNativeMode(big_mode_.Clone()) .SetNativeMode(big_mode_.Clone())
.SetCurrentMode(big_mode_.Clone()) .SetCurrentMode(big_mode_.Clone())
.SetType(DISPLAY_CONNECTION_TYPE_DISPLAYPORT)
.AddMode(small_mode_.Clone()) .AddMode(small_mode_.Clone())
.SetBaseConnectorId(kSecondConnectorId)
.Build(); .Build();
} }
~UpdateDisplayConfigurationTaskTest() override = default; ~UpdateDisplayConfigurationTaskTest() override = default;
...@@ -340,21 +349,33 @@ TEST_F(UpdateDisplayConfigurationTaskTest, FailExtendedConfiguration) { ...@@ -340,21 +349,33 @@ TEST_F(UpdateDisplayConfigurationTaskTest, FailExtendedConfiguration) {
EXPECT_TRUE(configured_); EXPECT_TRUE(configured_);
EXPECT_FALSE(configuration_status_); EXPECT_FALSE(configuration_status_);
EXPECT_EQ( EXPECT_EQ(
JoinActions(GetCrtcAction( JoinActions(
{displays_[0]->display_id(), gfx::Point(), &small_mode_}) // All displays will fail to modeset together. Initiate retry logic.
.c_str(), GetCrtcAction(
GetCrtcAction({displays_[1]->display_id(), {displays_[0]->display_id(), gfx::Point(), &small_mode_})
gfx::Point(0, small_mode_.size().height()), .c_str(),
&big_mode_}) GetCrtcAction({displays_[1]->display_id(),
.c_str(), gfx::Point(0, small_mode_.size().height()),
GetCrtcAction( &big_mode_})
{displays_[0]->display_id(), gfx::Point(), &small_mode_}) .c_str(),
.c_str(), // Retry logic fails to modeset internal display. Since internal
GetCrtcAction({displays_[1]->display_id(), // displays are restricted to their preferred mode, there are no other
gfx::Point(0, small_mode_.size().height()), // modes to try. The configuration will fail, but the external display
&small_mode_}) // will still try to modeset.
.c_str(), GetCrtcAction(
nullptr), {displays_[0]->display_id(), gfx::Point(), &small_mode_})
.c_str(),
// External display fail modeset, downgrade once, and then fail
// completely.
GetCrtcAction({displays_[1]->display_id(),
gfx::Point(0, small_mode_.size().height()),
&big_mode_})
.c_str(),
GetCrtcAction({displays_[1]->display_id(),
gfx::Point(0, small_mode_.size().height()),
&small_mode_})
.c_str(),
nullptr),
log_.GetActionsAndClear()); log_.GetActionsAndClear());
} }
......
...@@ -21,6 +21,18 @@ std::unique_ptr<DisplayMode> DisplayMode::Clone() const { ...@@ -21,6 +21,18 @@ std::unique_ptr<DisplayMode> DisplayMode::Clone() const {
new DisplayMode(size_, is_interlaced_, refresh_rate_)); new DisplayMode(size_, is_interlaced_, refresh_rate_));
} }
bool DisplayMode::operator<(const DisplayMode& other) const {
if (size_.GetArea() < other.size_.GetArea())
return true;
if (size_.GetArea() > other.size_.GetArea())
return false;
if (size_.width() < other.size_.width())
return true;
if (size_.width() > other.size_.width())
return false;
return refresh_rate_ < other.refresh_rate_;
}
std::string DisplayMode::ToString() const { std::string DisplayMode::ToString() const {
return base::StringPrintf("[%s %srate=%f]", size_.ToString().c_str(), return base::StringPrintf("[%s %srate=%f]", size_.ToString().c_str(),
is_interlaced_ ? "interlaced " : "", refresh_rate_); is_interlaced_ ? "interlaced " : "", refresh_rate_);
......
...@@ -27,6 +27,8 @@ class DISPLAY_TYPES_EXPORT DisplayMode { ...@@ -27,6 +27,8 @@ class DISPLAY_TYPES_EXPORT DisplayMode {
bool is_interlaced() const { return is_interlaced_; } bool is_interlaced() const { return is_interlaced_; }
float refresh_rate() const { return refresh_rate_; } float refresh_rate() const { return refresh_rate_; }
bool operator<(const DisplayMode& other) const;
std::string ToString() const; std::string ToString() const;
private: private:
......
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