// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef UI_DISPLAY_MANAGER_CONFIGURE_DISPLAYS_TASK_H_
#define UI_DISPLAY_MANAGER_CONFIGURE_DISPLAYS_TASK_H_
#include <stddef.h>
#include <queue>
#include <vector>
#include "base/functional/callback.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "ui/display/manager/display_manager_export.h"
#include "ui/display/types/display_constants.h"
#include "ui/display/types/native_display_observer.h"
#include "ui/gfx/geometry/point.h"
namespace display {
class DisplayMode;
class DisplaySnapshot;
class NativeDisplayDelegate;
struct DisplayConfigurationParams;
struct DISPLAY_MANAGER_EXPORT DisplayConfigureRequest {
DisplayConfigureRequest(DisplaySnapshot* display,
const DisplayMode* mode,
const gfx::Point& origin,
bool enable_vrr);
DisplayConfigureRequest(DisplaySnapshot* display,
const DisplayMode* mode,
const gfx::Point& origin);
DisplayConfigureRequest(const DisplayConfigureRequest& other);
~DisplayConfigureRequest();
raw_ptr<DisplaySnapshot> display;
std::unique_ptr<const DisplayMode> mode;
gfx::Point origin;
bool enable_vrr;
};
using RequestAndStatusList = std::pair<const 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.
//
// Note that the |enable_vrr| property is not modified in the event of a
// downgrade, as it does not affect bandwidth and changing its value would not
// cause a failing request to succeed.
class DISPLAY_MANAGER_EXPORT ConfigureDisplaysTask
: public NativeDisplayObserver {
public:
// Note: the enum values below match those of the ConfigureDisplaysTaskStatus
// histogram enum and should never change, or else it will make historical
// data of the affected metrics difficult to process.
enum Status {
// At least one of the displays failed to apply any mode it supports.
ERROR = 0,
// The requested configuration was applied.
SUCCESS = 1,
// At least one of the displays failed to apply the requested
// configuration, but it managed to fall back to another mode.
PARTIAL_SUCCESS = 2,
kMaxValue = PARTIAL_SUCCESS
};
using ResponseCallback = base::OnceCallback<void(Status)>;
ConfigureDisplaysTask(
NativeDisplayDelegate* delegate,
const std::vector<DisplayConfigureRequest>& requests,
ResponseCallback callback,
ConfigurationType configuration_type = kConfigurationTypeFull);
ConfigureDisplaysTask(const ConfigureDisplaysTask&) = delete;
ConfigureDisplaysTask& operator=(const ConfigureDisplaysTask&) = delete;
~ConfigureDisplaysTask() override;
// Starts the configuration task.
void Run();
// display::NativeDisplayObserver:
void OnConfigurationChanged() override;
void OnDisplaySnapshotsInvalidated() override;
private:
struct RequestToOriginalMode {
RequestToOriginalMode(DisplayConfigureRequest* request,
const DisplayMode* original_mode);
RequestToOriginalMode(const RequestToOriginalMode& other);
~RequestToOriginalMode();
raw_ptr<DisplayConfigureRequest> request;
const std::unique_ptr<const DisplayMode> original_mode;
};
using PartitionedRequestsQueue =
std::queue<std::vector<RequestToOriginalMode>>;
// 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(
const std::vector<DisplayConfigurationParams>& request_results,
bool config_success);
// 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(
const std::vector<DisplayConfigurationParams>& request_results,
bool config_success);
// Finalizes the configuration after a modeset attempt was made (as opposed to
// test-modeset).
void OnConfigured(
const std::vector<DisplayConfigurationParams>& request_results,
bool config_success);
// 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 DowngradeDisplayRequestGroup();
raw_ptr<NativeDisplayDelegate> delegate_; // Not owned.
// Holds the next configuration request to attempt modeset.
std::vector<DisplayConfigureRequest> requests_;
// Whether this request should be seamless or not (i.e. should a full modeset
// be permitted or not).
const ConfigurationType configuration_type_;
// A queue of display requests grouped by their
// |requests_[index]->display->base_connector_id()|. These request groups are
// used to downgrade displays' modes stored in |requests_| when the original
// request fails to modeset and a the fallback logic is triggered.
PartitionedRequestsQueue pending_display_group_requests_;
// The last configuration parameter request list that passed modeset test
// during retry, which will be used for modeset once we are done testing.
std::vector<display::DisplayConfigurationParams>
last_successful_config_parameters_;
// 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
// task is done and the task status.
ResponseCallback callback_;
Status task_status_;
base::WeakPtrFactory<ConfigureDisplaysTask> weak_ptr_factory_{this};
};
} // namespace display
#endif // UI_DISPLAY_MANAGER_CONFIGURE_DISPLAYS_TASK_H_