blob: 8021b6e751ffa0ecf961a3e35a63bbca13244137 [file]
// Copyright 2017 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/ui/scenic/lib/display/display_manager.h"
#include <fidl/fuchsia.hardware.display.types/cpp/fidl.h>
#include <fidl/fuchsia.hardware.display/cpp/fidl.h>
#include <fidl/fuchsia.ui.composition.internal/cpp/fidl.h>
#include <lib/async/default.h>
#include <lib/fit/function.h>
#include <lib/syslog/cpp/macros.h>
#include "src/ui/scenic/lib/utils/logging.h"
namespace display {
namespace {
std::optional<size_t> PickFirstDisplayModeSatisfyingConstraints(
std::span<const WireDisplayMode> modes, const DisplayModeConstraints& constraints) {
for (size_t i = 0; i < modes.size(); ++i) {
if (constraints.ModeSatisfiesConstraints(modes[i])) {
return std::make_optional(i);
}
}
return std::nullopt;
}
} // namespace
bool DisplayModeConstraints::ModeSatisfiesConstraints(const WireDisplayMode& mode) const {
if (!width_px_range.Contains(static_cast<int>(mode.active_area.width))) {
return false;
}
if (!height_px_range.Contains(static_cast<int>(mode.active_area.height))) {
return false;
}
if (!refresh_rate_millihertz_range.Contains(static_cast<int>(mode.refresh_rate_millihertz))) {
return false;
}
return true;
}
DisplayManager::DisplayManager(fit::closure display_available_cb)
: DisplayManager(std::nullopt, std::nullopt, /*display_mode_constraints=*/{}, inspect::Node(),
std::move(display_available_cb)) {}
DisplayManager::DisplayManager(std::optional<WireDisplayId> i_can_haz_display_id,
std::optional<size_t> display_mode_index_override,
DisplayModeConstraints display_mode_constraints,
inspect::Node inspect_node, fit::closure display_available_cb,
CoordinatorProxy::CheckConfigHeuristics check_config_heuristics)
: check_config_heuristics_(check_config_heuristics),
i_can_haz_display_id_(i_can_haz_display_id),
display_mode_index_override_(display_mode_index_override),
display_mode_constraints_(std::move(display_mode_constraints)),
display_available_cb_(std::move(display_available_cb)),
inspect_node_(std::move(inspect_node)) {}
void DisplayManager::BindDefaultDisplayCoordinator(
async_dispatcher_t* dispatcher,
fidl::ClientEnd<fuchsia_hardware_display::Coordinator> coordinator,
fidl::ServerEnd<fuchsia_hardware_display::CoordinatorListener> coordinator_listener) {
FX_DCHECK(!coordinator_proxy_);
FX_DCHECK(coordinator.is_valid());
coordinator_proxy_ = std::make_shared<CoordinatorProxy>(
std::move(coordinator), dispatcher, check_config_heuristics_,
inspect_node_.CreateChild("Display Coordinator Proxy"));
display_coordinator_listener_ = std::make_shared<display::DisplayCoordinatorListener>(
std::move(coordinator_listener), fit::bind_member<&DisplayManager::OnDisplaysChanged>(this),
fit::bind_member<&DisplayManager::OnVsync>(this),
fit::bind_member<&DisplayManager::OnClientOwnershipChange>(this));
inspect_lazy_metrics_ = inspect_node_.CreateLazyValues("Vsync Metrics", [this, dispatcher]() {
inspect::Inspector inspector;
const zx::time_monotonic now(async_now(dispatcher));
const zx::duration duration = now - last_vsync_timestamp_;
inspector.GetRoot().RecordInt("time_since_last_vsync_ms", duration.to_msecs());
return fpromise::make_ok_promise(std::move(inspector));
});
}
void DisplayManager::OnDisplaysChanged(fidl::VectorView<WireDisplayInfo> added,
fidl::VectorView<WireDisplayId> removed) {
for (WireDisplayInfo& display_info : added) {
// Ignore display if |i_can_haz_display_id| is set and it doesn't match ID.
if (i_can_haz_display_id_.has_value() &&
display_info.id.value != i_can_haz_display_id_->value) {
FX_LOGS(INFO) << "Ignoring display with id=" << display_info.id.value
<< " ... waiting for display with id=" << i_can_haz_display_id_->value;
continue;
}
if (default_display_) {
FX_LOGS(INFO) << "Default display already exists with id="
<< default_display_->display_id().value()
<< " ... skipping newly added display id=" << display_info.id.value;
} else {
size_t mode_index = 0;
// Set display mode if requested.
if (display_mode_index_override_.has_value()) {
if (*display_mode_index_override_ < display_info.modes.size()) {
mode_index = *display_mode_index_override_;
} else {
FX_LOGS(ERROR) << "Requested display mode=" << *display_mode_index_override_
<< " doesn't exist for display with id=" << display_info.id.value;
}
} else {
std::optional<size_t> mode_index_satisfying_constraints =
PickFirstDisplayModeSatisfyingConstraints(display_info.modes,
display_mode_constraints_);
// TODO(https://fxbug.dev/42097581): handle this more robustly.
FX_CHECK(mode_index_satisfying_constraints.has_value())
<< "Failed to find a display mode satisfying all display constraints for "
"display with id="
<< display_info.id.value;
mode_index = *mode_index_satisfying_constraints;
}
if (mode_index != 0) {
// TODO(https://fxbug.dev/402804098): `flatland::DisplayCompositor` now handles this, so
// this is redundant, right? Verify and delete.
[[maybe_unused]] fidl::OneWayStatus set_display_mode_result =
coordinator_proxy_->raw()->SetDisplayMode(display_info.id,
display_info.modes[mode_index]);
}
const WireDisplayMode& mode = display_info.modes[mode_index];
std::vector<fuchsia_images2::PixelFormat> pixel_formats(display_info.pixel_format.begin(),
display_info.pixel_format.end());
default_display_ = std::make_unique<Display>(
display_info.id, mode, display_info.horizontal_size_mm, display_info.vertical_size_mm,
display_info.max_layer_count, std::move(pixel_formats));
OnClientOwnershipChange(owns_display_coordinator_);
if (display_available_cb_) {
display_available_cb_();
display_available_cb_ = nullptr;
}
if (display_added_cb_) {
display_added_cb_(*default_display_);
}
}
}
for (const WireDisplayId& id : removed) {
if (default_display_ && default_display_->display_id() == display::DisplayId(id)) {
// TODO(https://fxbug.dev/42097581): handle this more robustly.
FX_CHECK(false) << "Display disconnected";
return;
}
}
}
void DisplayManager::SetDisplayAddedCallback(
fit::function<void(display::Display&)> display_added_cb) {
display_added_cb_ = std::move(display_added_cb);
if (default_display_) {
display_added_cb_(*default_display_);
}
}
void DisplayManager::OnClientOwnershipChange(bool has_ownership) {
owns_display_coordinator_ = has_ownership;
if (default_display_) {
if (has_ownership) {
default_display_->ownership_event().signal(
fuchsia_ui_composition_internal::kSignalDisplayNotOwned,
fuchsia_ui_composition_internal::kSignalDisplayOwned);
} else {
default_display_->ownership_event().signal(
fuchsia_ui_composition_internal::kSignalDisplayOwned,
fuchsia_ui_composition_internal::kSignalDisplayNotOwned);
}
}
}
void DisplayManager::OnVsync(WireDisplayId display_id, zx::time_monotonic timestamp,
WireConfigStamp displayed_config_stamp, WireVsyncAckCookie cookie) {
if (cookie.value != fuchsia_hardware_display_types::kInvalidDispId) {
FLATLAND_VERBOSE_LOG << "DisplayManager::OnVsync(): acknowledging vsync display_id="
<< display_id.value << " timestamp=" << timestamp.get()
<< " displayed_config_stamp=" << displayed_config_stamp.value
<< " cookie=" << cookie.value;
[[maybe_unused]] fidl::OneWayStatus acknowledge_vsync_result =
coordinator_proxy_->raw()->AcknowledgeVsync(cookie.value);
} else {
FLATLAND_VERBOSE_LOG << "DisplayManager::OnVsync(): received vsync display_id="
<< display_id.value << " timestamp=" << timestamp.get()
<< " displayed_config_stamp=" << displayed_config_stamp.value
<< " with no cookie.";
}
if (!default_display_) {
FLATLAND_VERBOSE_LOG << "DisplayManager::OnVsync(): ignoring vsync, no default display";
return;
}
if (default_display_->display_id() != display::DisplayId(display_id)) {
FLATLAND_VERBOSE_LOG << "DisplayManager::OnVsync(): ignoring vsync, display_id="
<< display_id.value << " doesn't match default_display->display_id="
<< default_display_->display_id().value();
return;
}
default_display_->OnVsync(timestamp, displayed_config_stamp);
last_vsync_timestamp_ = timestamp;
}
} // namespace display