Support external display rendering in vr flinger
Add support for external displays to vr flinger, hidden behind a system
property until we're ready to use it. To turn it on for testing:
$ adb shell setprop persist.vr.use_external_display 1
With external display support turned on and an external display
connected, all output is redirected to the external display, and we
switch to the external display's vsync.
Bug: 75047963
Test: Most of the testing was done on a Polaris, which now supports
external display hotplugging.
- Tested external display connect/disconnect while the device was
donned.
- Tested external display connect/disconnect while doffed.
- Verified booting with the external display connected doesn't cause the
device to crash, although we don't seem to get a hotplug event for the
external display until the cable is disconnected and reconnected, so
we output to the primary display at first.
- Did some basic testing on a Walleye to confirm vr behavior there is
unchanged.
- Verified the external display is ignored when the sysprop is unset.
Change-Id: I3d3f40e276d354551fc1c577b9392709398ad96e
diff --git a/libs/vr/libvrflinger/Android.bp b/libs/vr/libvrflinger/Android.bp
index 3e4a42c..4dc669b 100644
--- a/libs/vr/libvrflinger/Android.bp
+++ b/libs/vr/libvrflinger/Android.bp
@@ -72,6 +72,7 @@
srcs: sourceFiles,
export_include_dirs: includeFiles,
+ clang: true,
cflags: [
"-DLOG_TAG=\"vr_flinger\"",
"-DTRACE=0",
@@ -83,6 +84,9 @@
"-Wno-error=sign-compare", // to fix later
"-Wno-unused-variable",
],
+ cppflags: [
+ "-std=c++1z"
+ ],
shared_libs: sharedLibraries,
whole_static_libs: staticLibraries,
header_libs: headerLibraries,
diff --git a/libs/vr/libvrflinger/display_service.cpp b/libs/vr/libvrflinger/display_service.cpp
index a18ff1a..87162c0 100644
--- a/libs/vr/libvrflinger/display_service.cpp
+++ b/libs/vr/libvrflinger/display_service.cpp
@@ -177,12 +177,12 @@
Status<display::Metrics> DisplayService::OnGetMetrics(
pdx::Message& /*message*/) {
- return {{static_cast<uint32_t>(GetDisplayMetrics().width),
- static_cast<uint32_t>(GetDisplayMetrics().height),
- static_cast<uint32_t>(GetDisplayMetrics().dpi.x),
- static_cast<uint32_t>(GetDisplayMetrics().dpi.y),
- static_cast<uint32_t>(
- hardware_composer_.native_display_metrics().vsync_period_ns),
+ const auto& params = hardware_composer_.GetPrimaryDisplayParams();
+ return {{static_cast<uint32_t>(params.width),
+ static_cast<uint32_t>(params.height),
+ static_cast<uint32_t>(params.dpi.x),
+ static_cast<uint32_t>(params.dpi.y),
+ static_cast<uint32_t>(params.vsync_period_ns),
0,
0,
0,
diff --git a/libs/vr/libvrflinger/display_service.h b/libs/vr/libvrflinger/display_service.h
index 316f15a..3090bd1 100644
--- a/libs/vr/libvrflinger/display_service.h
+++ b/libs/vr/libvrflinger/display_service.h
@@ -65,10 +65,6 @@
hardware_composer_.SetVSyncCallback(callback);
}
- HWCDisplayMetrics GetDisplayMetrics() {
- return hardware_composer_.display_metrics();
- }
-
void GrantDisplayOwnership() { hardware_composer_.Enable(); }
void SeizeDisplayOwnership() { hardware_composer_.Disable(); }
void OnBootFinished() { hardware_composer_.OnBootFinished(); }
diff --git a/libs/vr/libvrflinger/hardware_composer.cpp b/libs/vr/libvrflinger/hardware_composer.cpp
index 5b49645..44ce78c 100644
--- a/libs/vr/libvrflinger/hardware_composer.cpp
+++ b/libs/vr/libvrflinger/hardware_composer.cpp
@@ -50,9 +50,17 @@
const char kRightEyeOffsetProperty[] = "dvr.right_eye_offset_ns";
+const char kUseExternalDisplayProperty[] = "persist.vr.use_external_display";
+
// How long to wait after boot finishes before we turn the display off.
constexpr int kBootFinishedDisplayOffTimeoutSec = 10;
+constexpr int kDefaultDisplayWidth = 1920;
+constexpr int kDefaultDisplayHeight = 1080;
+constexpr int64_t kDefaultVsyncPeriodNs = 16666667;
+// Hardware composer reports dpi as dots per thousand inches (dpi * 1000).
+constexpr int kDefaultDpi = 400000;
+
// Get time offset from a vsync to when the pose for that vsync should be
// predicted out to. For example, if scanout gets halfway through the frame
// at the halfway point between vsyncs, then this could be half the period.
@@ -109,6 +117,11 @@
#define TRACE_FORMAT(format, ...) \
TraceArgs PASTE(__tracer, __LINE__) { format, ##__VA_ARGS__ }
+// Returns "primary" or "external". Useful for writing more readable logs.
+const char* GetDisplayName(bool is_primary) {
+ return is_primary ? "primary" : "external";
+}
+
} // anonymous namespace
HardwareComposer::HardwareComposer()
@@ -132,42 +145,7 @@
request_display_callback_ = request_display_callback;
- HWC::Error error = HWC::Error::None;
-
- Hwc2::Config config;
- error = composer->getActiveConfig(primary_display_id, &config);
-
- if (error != HWC::Error::None) {
- ALOGE("HardwareComposer: Failed to get current display config : %d",
- config);
- return false;
- }
-
- error = GetDisplayMetrics(composer, primary_display_id, config,
- &native_display_metrics_);
-
- if (error != HWC::Error::None) {
- ALOGE(
- "HardwareComposer: Failed to get display attributes for current "
- "configuration : %d",
- error.value);
- return false;
- }
-
- ALOGI(
- "HardwareComposer: primary display attributes: width=%d height=%d "
- "vsync_period_ns=%d DPI=%dx%d",
- native_display_metrics_.width, native_display_metrics_.height,
- native_display_metrics_.vsync_period_ns, native_display_metrics_.dpi.x,
- native_display_metrics_.dpi.y);
-
- // Set the display metrics but never use rotation to avoid the long latency of
- // rotation processing in hwc.
- display_transform_ = HWC_TRANSFORM_NONE;
- display_metrics_ = native_display_metrics_;
-
- // Setup the display metrics used by all Layer instances.
- Layer::SetDisplayMetrics(native_display_metrics_);
+ primary_display_ = GetDisplayParams(composer, primary_display_id, true);
post_thread_event_fd_.Reset(eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK));
LOG_ALWAYS_FATAL_IF(
@@ -236,43 +214,38 @@
}
}
-void HardwareComposer::OnPostThreadResumed() {
- // Phones create a new composer client on resume and destroy it on pause.
- // Standalones only create the composer client once and then use SetPowerMode
- // to control the screen on pause/resume.
- if (!is_standalone_device_ || !composer_) {
- composer_.reset(new Hwc2::impl::Composer("default"));
- composer_callback_ = new ComposerCallback;
- composer_->registerCallback(composer_callback_);
- LOG_ALWAYS_FATAL_IF(!composer_callback_->HasDisplayId(),
- "Registered composer callback but didn't get primary display");
- Layer::SetComposer(composer_.get());
- Layer::SetDisplayId(composer_callback_->GetDisplayId());
- } else {
- SetPowerMode(true);
- }
+void HardwareComposer::CreateComposer() {
+ if (composer_)
+ return;
+ composer_.reset(new Hwc2::impl::Composer("default"));
+ composer_callback_ = new ComposerCallback;
+ composer_->registerCallback(composer_callback_);
+ LOG_ALWAYS_FATAL_IF(!composer_callback_->GotFirstHotplug(),
+ "Registered composer callback but didn't get hotplug for primary"
+ " display");
+}
- EnableVsync(true);
+void HardwareComposer::OnPostThreadResumed() {
+ ALOGI("OnPostThreadResumed");
+ EnableDisplay(*target_display_, true);
// Trigger target-specific performance mode change.
property_set(kDvrPerformanceProperty, "performance");
}
void HardwareComposer::OnPostThreadPaused() {
+ ALOGI("OnPostThreadPaused");
retire_fence_fds_.clear();
layers_.clear();
- if (composer_) {
- EnableVsync(false);
- }
-
+ // Phones create a new composer client on resume and destroy it on pause.
+ // Standalones only create the composer client once and then use SetPowerMode
+ // to control the screen on pause/resume.
if (!is_standalone_device_) {
composer_callback_ = nullptr;
composer_.reset(nullptr);
- Layer::SetComposer(nullptr);
- Layer::SetDisplayId(0);
} else {
- SetPowerMode(false);
+ EnableDisplay(*target_display_, false);
}
// Trigger target-specific performance mode change.
@@ -305,28 +278,55 @@
composer_->validateDisplay(display, &num_types, &num_requests);
if (error == HWC2_ERROR_HAS_CHANGES) {
- // TODO(skiazyk): We might need to inspect the requested changes first, but
- // so far it seems like we shouldn't ever hit a bad state.
- // error = hwc2_funcs_.accept_display_changes_fn_(hardware_composer_device_,
- // display);
+ ALOGE("Hardware composer has requested composition changes, "
+ "which we don't support.");
+ // Accept the changes anyway and see if we can get something on the screen.
error = composer_->acceptDisplayChanges(display);
}
return error;
}
-HWC::Error HardwareComposer::EnableVsync(bool enabled) {
- return composer_->setVsyncEnabled(
- composer_callback_->GetDisplayId(),
+bool HardwareComposer::EnableVsync(const DisplayParams& display, bool enabled) {
+ HWC::Error error = composer_->setVsyncEnabled(display.id,
(Hwc2::IComposerClient::Vsync)(enabled ? HWC2_VSYNC_ENABLE
: HWC2_VSYNC_DISABLE));
+ if (error != HWC::Error::None) {
+ ALOGE("Error attempting to %s vsync on %s display: %s",
+ enabled ? "enable" : "disable", GetDisplayName(display.is_primary),
+ error.to_string().c_str());
+ }
+ return error == HWC::Error::None;
}
-HWC::Error HardwareComposer::SetPowerMode(bool active) {
+bool HardwareComposer::SetPowerMode(const DisplayParams& display, bool active) {
+ ALOGI("Turning %s display %s", GetDisplayName(display.is_primary),
+ active ? "on" : "off");
HWC::PowerMode power_mode = active ? HWC::PowerMode::On : HWC::PowerMode::Off;
- return composer_->setPowerMode(
- composer_callback_->GetDisplayId(),
+ HWC::Error error = composer_->setPowerMode(display.id,
power_mode.cast<Hwc2::IComposerClient::PowerMode>());
+ if (error != HWC::Error::None) {
+ ALOGE("Error attempting to turn %s display %s: %s",
+ GetDisplayName(display.is_primary), active ? "on" : "off",
+ error.to_string().c_str());
+ }
+ return error == HWC::Error::None;
+}
+
+bool HardwareComposer::EnableDisplay(const DisplayParams& display,
+ bool enabled) {
+ bool power_result;
+ bool vsync_result;
+ // When turning a display on, we set the power state then set vsync. When
+ // turning a display off we do it in the opposite order.
+ if (enabled) {
+ power_result = SetPowerMode(display, enabled);
+ vsync_result = EnableVsync(display, enabled);
+ } else {
+ vsync_result = EnableVsync(display, enabled);
+ power_result = SetPowerMode(display, enabled);
+ }
+ return power_result && vsync_result;
}
HWC::Error HardwareComposer::Present(hwc2_display_t display) {
@@ -345,78 +345,105 @@
return error;
}
-HWC::Error HardwareComposer::GetDisplayAttribute(Hwc2::Composer* composer,
- hwc2_display_t display,
- hwc2_config_t config,
- hwc2_attribute_t attribute,
- int32_t* out_value) const {
- return composer->getDisplayAttribute(
- display, config, (Hwc2::IComposerClient::Attribute)attribute, out_value);
-}
+DisplayParams HardwareComposer::GetDisplayParams(
+ Hwc2::Composer* composer, hwc2_display_t display, bool is_primary) {
+ DisplayParams params;
+ params.id = display;
+ params.is_primary = is_primary;
-HWC::Error HardwareComposer::GetDisplayMetrics(
- Hwc2::Composer* composer, hwc2_display_t display, hwc2_config_t config,
- HWCDisplayMetrics* out_metrics) const {
- HWC::Error error;
+ Hwc2::Config config;
+ HWC::Error error = composer->getActiveConfig(display, &config);
- error = GetDisplayAttribute(composer, display, config, HWC2_ATTRIBUTE_WIDTH,
- &out_metrics->width);
- if (error != HWC::Error::None) {
- ALOGE(
- "HardwareComposer::GetDisplayMetrics: Failed to get display width: %s",
- error.to_string().c_str());
- return error;
+ if (error == HWC::Error::None) {
+ auto get_attr = [&](hwc2_attribute_t attr, const char* attr_name)
+ -> std::optional<int32_t> {
+ int32_t val;
+ HWC::Error error = composer->getDisplayAttribute(
+ display, config, (Hwc2::IComposerClient::Attribute)attr, &val);
+ if (error != HWC::Error::None) {
+ ALOGE("Failed to get %s display attr %s: %s",
+ GetDisplayName(is_primary), attr_name,
+ error.to_string().c_str());
+ return std::nullopt;
+ }
+ return val;
+ };
+
+ auto width = get_attr(HWC2_ATTRIBUTE_WIDTH, "width");
+ auto height = get_attr(HWC2_ATTRIBUTE_HEIGHT, "height");
+
+ if (width && height) {
+ params.width = *width;
+ params.height = *height;
+ } else {
+ ALOGI("Failed to get width and/or height for %s display. Using default"
+ " size %dx%d.", GetDisplayName(is_primary), kDefaultDisplayWidth,
+ kDefaultDisplayHeight);
+ params.width = kDefaultDisplayWidth;
+ params.height = kDefaultDisplayHeight;
+ }
+
+ auto vsync_period = get_attr(HWC2_ATTRIBUTE_VSYNC_PERIOD, "vsync period");
+ if (vsync_period) {
+ params.vsync_period_ns = *vsync_period;
+ } else {
+ ALOGI("Failed to get vsync period for %s display. Using default vsync"
+ " period %.2fms", GetDisplayName(is_primary),
+ static_cast<float>(kDefaultVsyncPeriodNs) / 1000000);
+ params.vsync_period_ns = kDefaultVsyncPeriodNs;
+ }
+
+ auto dpi_x = get_attr(HWC2_ATTRIBUTE_DPI_X, "DPI X");
+ auto dpi_y = get_attr(HWC2_ATTRIBUTE_DPI_Y, "DPI Y");
+ if (dpi_x && dpi_y) {
+ params.dpi.x = *dpi_x;
+ params.dpi.y = *dpi_y;
+ } else {
+ ALOGI("Failed to get dpi_x and/or dpi_y for %s display. Using default"
+ " dpi %d.", GetDisplayName(is_primary), kDefaultDpi);
+ params.dpi.x = kDefaultDpi;
+ params.dpi.y = kDefaultDpi;
+ }
+ } else {
+ ALOGE("HardwareComposer: Failed to get current %s display config: %d."
+ " Using default display values.",
+ GetDisplayName(is_primary), error.value);
+ params.width = kDefaultDisplayWidth;
+ params.height = kDefaultDisplayHeight;
+ params.dpi.x = kDefaultDpi;
+ params.dpi.y = kDefaultDpi;
+ params.vsync_period_ns = kDefaultVsyncPeriodNs;
}
- error = GetDisplayAttribute(composer, display, config, HWC2_ATTRIBUTE_HEIGHT,
- &out_metrics->height);
- if (error != HWC::Error::None) {
- ALOGE(
- "HardwareComposer::GetDisplayMetrics: Failed to get display height: %s",
- error.to_string().c_str());
- return error;
- }
+ ALOGI(
+ "HardwareComposer: %s display attributes: width=%d height=%d "
+ "vsync_period_ns=%d DPI=%dx%d",
+ GetDisplayName(is_primary),
+ params.width,
+ params.height,
+ params.vsync_period_ns,
+ params.dpi.x,
+ params.dpi.y);
- error = GetDisplayAttribute(composer, display, config,
- HWC2_ATTRIBUTE_VSYNC_PERIOD,
- &out_metrics->vsync_period_ns);
- if (error != HWC::Error::None) {
- ALOGE(
- "HardwareComposer::GetDisplayMetrics: Failed to get display height: %s",
- error.to_string().c_str());
- return error;
- }
-
- error = GetDisplayAttribute(composer, display, config, HWC2_ATTRIBUTE_DPI_X,
- &out_metrics->dpi.x);
- if (error != HWC::Error::None) {
- ALOGE(
- "HardwareComposer::GetDisplayMetrics: Failed to get display DPI X: %s",
- error.to_string().c_str());
- return error;
- }
-
- error = GetDisplayAttribute(composer, display, config, HWC2_ATTRIBUTE_DPI_Y,
- &out_metrics->dpi.y);
- if (error != HWC::Error::None) {
- ALOGE(
- "HardwareComposer::GetDisplayMetrics: Failed to get display DPI Y: %s",
- error.to_string().c_str());
- return error;
- }
-
- return HWC::Error::None;
+ return params;
}
std::string HardwareComposer::Dump() {
std::unique_lock<std::mutex> lock(post_thread_mutex_);
std::ostringstream stream;
- stream << "Display metrics: " << display_metrics_.width << "x"
- << display_metrics_.height << " " << (display_metrics_.dpi.x / 1000.0)
- << "x" << (display_metrics_.dpi.y / 1000.0) << " dpi @ "
- << (1000000000.0 / display_metrics_.vsync_period_ns) << " Hz"
- << std::endl;
+ auto print_display_metrics = [&](const DisplayParams& params) {
+ stream << GetDisplayName(params.is_primary)
+ << " display metrics: " << params.width << "x"
+ << params.height << " " << (params.dpi.x / 1000.0)
+ << "x" << (params.dpi.y / 1000.0) << " dpi @ "
+ << (1000000000.0 / params.vsync_period_ns) << " Hz"
+ << std::endl;
+ };
+
+ print_display_metrics(primary_display_);
+ if (external_display_)
+ print_display_metrics(*external_display_);
stream << "Post thread resumed: " << post_thread_resumed_ << std::endl;
stream << "Active layers: " << layers_.size() << std::endl;
@@ -439,7 +466,7 @@
return stream.str();
}
-void HardwareComposer::PostLayers() {
+void HardwareComposer::PostLayers(hwc2_display_t display) {
ATRACE_NAME("HardwareComposer::PostLayers");
// Setup the hardware composer layers with current buffers.
@@ -487,14 +514,14 @@
}
#endif
- HWC::Error error = Validate(composer_callback_->GetDisplayId());
+ HWC::Error error = Validate(display);
if (error != HWC::Error::None) {
- ALOGE("HardwareComposer::PostLayers: Validate failed: %s",
- error.to_string().c_str());
+ ALOGE("HardwareComposer::PostLayers: Validate failed: %s display=%" PRIu64,
+ error.to_string().c_str(), display);
return;
}
- error = Present(composer_callback_->GetDisplayId());
+ error = Present(display);
if (error != HWC::Error::None) {
ALOGE("HardwareComposer::PostLayers: Present failed: %s",
error.to_string().c_str());
@@ -503,7 +530,7 @@
std::vector<Hwc2::Layer> out_layers;
std::vector<int> out_fences;
- error = composer_->getReleaseFences(composer_callback_->GetDisplayId(),
+ error = composer_->getReleaseFences(display,
&out_layers, &out_fences);
ALOGE_IF(error != HWC::Error::None,
"HardwareComposer::PostLayers: Failed to get release fences: %s",
@@ -527,7 +554,8 @@
const bool display_idle = surfaces.size() == 0;
{
std::unique_lock<std::mutex> lock(post_thread_mutex_);
- pending_surfaces_ = std::move(surfaces);
+ surfaces_ = std::move(surfaces);
+ surfaces_changed_ = true;
}
if (request_display_callback_ && !is_standalone_device_)
@@ -643,13 +671,11 @@
}
}
-// Waits for the next vsync and returns the timestamp of the vsync event. If
-// vsync already passed since the last call, returns the latest vsync timestamp
-// instead of blocking.
-Status<int64_t> HardwareComposer::WaitForVSync() {
- const int64_t predicted_vsync_time =
- last_vsync_timestamp_ +
- display_metrics_.vsync_period_ns * vsync_prediction_interval_;
+// Sleep until the next predicted vsync, returning the predicted vsync
+// timestamp.
+Status<int64_t> HardwareComposer::WaitForPredictedVSync() {
+ const int64_t predicted_vsync_time = last_vsync_timestamp_ +
+ (target_display_->vsync_period_ns * vsync_prediction_interval_);
const int error = SleepUntil(predicted_vsync_time);
if (error < 0) {
ALOGE("HardwareComposer::WaifForVSync:: Failed to sleep: %s",
@@ -695,20 +721,28 @@
"HardwareComposer: Failed to create vsync sleep timerfd: %s",
strerror(errno));
- const int64_t ns_per_frame = display_metrics_.vsync_period_ns;
- const int64_t photon_offset_ns = GetPosePredictionTimeOffset(ns_per_frame);
-
- // TODO(jbates) Query vblank time from device, when such an API is available.
- // This value (6.3%) was measured on A00 in low persistence mode.
- int64_t vblank_ns = ns_per_frame * 63 / 1000;
- int64_t right_eye_photon_offset_ns = (ns_per_frame - vblank_ns) / 2;
-
- // Check property for overriding right eye offset value.
- right_eye_photon_offset_ns =
- property_get_int64(kRightEyeOffsetProperty, right_eye_photon_offset_ns);
-
+ struct VsyncEyeOffsets { int64_t left_ns, right_ns; };
bool was_running = false;
+ auto get_vsync_eye_offsets = [this]() -> VsyncEyeOffsets {
+ VsyncEyeOffsets offsets;
+ offsets.left_ns =
+ GetPosePredictionTimeOffset(target_display_->vsync_period_ns);
+
+ // TODO(jbates) Query vblank time from device, when such an API is
+ // available. This value (6.3%) was measured on A00 in low persistence mode.
+ int64_t vblank_ns = target_display_->vsync_period_ns * 63 / 1000;
+ offsets.right_ns = (target_display_->vsync_period_ns - vblank_ns) / 2;
+
+ // Check property for overriding right eye offset value.
+ offsets.right_ns =
+ property_get_int64(kRightEyeOffsetProperty, offsets.right_ns);
+
+ return offsets;
+ };
+
+ VsyncEyeOffsets vsync_eye_offsets = get_vsync_eye_offsets();
+
if (is_standalone_device_) {
// First, wait until boot finishes.
std::unique_lock<std::mutex> lock(post_thread_mutex_);
@@ -727,8 +761,8 @@
"Vr flinger should own the display by now.");
post_thread_resumed_ = true;
post_thread_ready_.notify_all();
- OnPostThreadResumed();
- was_running = true;
+ if (!composer_)
+ CreateComposer();
}
while (1) {
@@ -741,10 +775,8 @@
std::unique_lock<std::mutex> lock(post_thread_mutex_);
ALOGI("HardwareComposer::PostThread: Entering quiescent state.");
- // Tear down resources if necessary.
- if (was_running)
- OnPostThreadPaused();
-
+ // Tear down resources.
+ OnPostThreadPaused();
was_running = false;
post_thread_resumed_ = false;
post_thread_ready_.notify_all();
@@ -761,10 +793,18 @@
ALOGI("HardwareComposer::PostThread: Exiting quiescent state.");
}
- if (!was_running) {
- // Setup resources.
+ if (!composer_)
+ CreateComposer();
+
+ bool target_display_changed = UpdateTargetDisplay();
+ bool just_resumed_running = !was_running;
+ was_running = true;
+
+ if (target_display_changed)
+ vsync_eye_offsets = get_vsync_eye_offsets();
+
+ if (just_resumed_running) {
OnPostThreadResumed();
- was_running = true;
// Try to setup the scheduler policy if it failed during startup. Only
// attempt to do this on transitions from inactive to active to avoid
@@ -773,12 +813,16 @@
thread_policy_setup =
SetThreadPolicy("graphics:high", "/system/performance");
}
+ }
+ if (target_display_changed || just_resumed_running) {
// Initialize the last vsync timestamp with the current time. The
// predictor below uses this time + the vsync interval in absolute time
// units for the initial delay. Once the driver starts reporting vsync the
// predictor will sync up with the real vsync.
last_vsync_timestamp_ = GetSystemClockNs();
+ vsync_prediction_interval_ = 1;
+ retire_fence_fds_.clear();
}
int64_t vsync_timestamp = 0;
@@ -788,7 +832,7 @@
vsync_count_ + 1, last_vsync_timestamp_,
vsync_prediction_interval_);
- auto status = WaitForVSync();
+ auto status = WaitForPredictedVSync();
ALOGE_IF(
!status,
"HardwareComposer::PostThread: Failed to wait for vsync event: %s",
@@ -809,16 +853,16 @@
if (vsync_prediction_interval_ == 1)
++vsync_count_;
- const bool layer_config_changed = UpdateLayerConfig();
+ UpdateLayerConfig();
// Publish the vsync event.
if (vsync_ring_) {
DvrVsync vsync;
vsync.vsync_count = vsync_count_;
vsync.vsync_timestamp_ns = vsync_timestamp;
- vsync.vsync_left_eye_offset_ns = photon_offset_ns;
- vsync.vsync_right_eye_offset_ns = right_eye_photon_offset_ns;
- vsync.vsync_period_ns = ns_per_frame;
+ vsync.vsync_left_eye_offset_ns = vsync_eye_offsets.left_ns;
+ vsync.vsync_right_eye_offset_ns = vsync_eye_offsets.right_ns;
+ vsync.vsync_period_ns = target_display_->vsync_period_ns;
vsync_ring_->Publish(vsync);
}
@@ -832,7 +876,8 @@
// Sleep until shortly before vsync.
ATRACE_NAME("sleep");
- const int64_t display_time_est_ns = vsync_timestamp + ns_per_frame;
+ const int64_t display_time_est_ns =
+ vsync_timestamp + target_display_->vsync_period_ns;
const int64_t now_ns = GetSystemClockNs();
const int64_t sleep_time_ns = display_time_est_ns - now_ns -
post_thread_config_.frame_post_offset_ns;
@@ -853,11 +898,7 @@
}
{
- auto status = composer_callback_->GetVsyncTime();
- if (!status) {
- ALOGE("HardwareComposer::PostThread: Failed to get VSYNC time: %s",
- status.GetErrorMessage().c_str());
- }
+ auto status = composer_callback_->GetVsyncTime(target_display_->id);
// If we failed to read vsync there might be a problem with the driver.
// Since there's nothing we can do just behave as though we didn't get an
@@ -884,20 +925,84 @@
}
}
- PostLayers();
+ PostLayers(target_display_->id);
}
}
+bool HardwareComposer::UpdateTargetDisplay() {
+ bool target_display_changed = false;
+ auto displays = composer_callback_->GetDisplays();
+ if (displays.external_display_was_hotplugged) {
+ bool was_using_external_display = !target_display_->is_primary;
+ if (was_using_external_display) {
+ // The external display was hotplugged, so make sure to ignore any bad
+ // display errors as we destroy the layers.
+ for (auto& layer: layers_)
+ layer.IgnoreBadDisplayErrorsOnDestroy(true);
+ }
+
+ if (displays.external_display) {
+ // External display was connected
+ external_display_ = GetDisplayParams(composer_.get(),
+ *displays.external_display, /*is_primary*/ false);
+
+ if (property_get_bool(kUseExternalDisplayProperty, false)) {
+ ALOGI("External display connected. Switching to external display.");
+ target_display_ = &(*external_display_);
+ target_display_changed = true;
+ } else {
+ ALOGI("External display connected, but sysprop %s is unset, so"
+ " using primary display.", kUseExternalDisplayProperty);
+ if (was_using_external_display) {
+ target_display_ = &primary_display_;
+ target_display_changed = true;
+ }
+ }
+ } else {
+ // External display was disconnected
+ external_display_ = std::nullopt;
+ if (was_using_external_display) {
+ ALOGI("External display disconnected. Switching to primary display.");
+ target_display_ = &primary_display_;
+ target_display_changed = true;
+ }
+ }
+ }
+
+ if (target_display_changed) {
+ // If we're switching to the external display, turn the primary display off.
+ if (!target_display_->is_primary) {
+ EnableDisplay(primary_display_, false);
+ }
+ // If we're switching to the primary display, and the external display is
+ // still connected, turn the external display off.
+ else if (target_display_->is_primary && external_display_) {
+ EnableDisplay(*external_display_, false);
+ }
+
+ // Turn the new target display on.
+ EnableDisplay(*target_display_, true);
+
+ // When we switch displays we need to recreate all the layers, so clear the
+ // current list, which will trigger layer recreation.
+ layers_.clear();
+ }
+
+ return target_display_changed;
+}
+
// Checks for changes in the surface stack and updates the layer config to
// accomodate the new stack.
-bool HardwareComposer::UpdateLayerConfig() {
+void HardwareComposer::UpdateLayerConfig() {
std::vector<std::shared_ptr<DirectDisplaySurface>> surfaces;
{
std::unique_lock<std::mutex> lock(post_thread_mutex_);
- if (pending_surfaces_.empty())
- return false;
- surfaces = std::move(pending_surfaces_);
+ if (!surfaces_changed_ && (!layers_.empty() || surfaces_.empty()))
+ return;
+
+ surfaces = surfaces_;
+ surfaces_changed_ = false;
}
ATRACE_NAME("UpdateLayerConfig_HwLayers");
@@ -934,8 +1039,8 @@
layers_.erase(search);
} else {
// Insert a layer for the new surface.
- layers.emplace_back(surface, blending, display_transform_,
- HWC::Composition::Device, layer_index);
+ layers.emplace_back(composer_.get(), *target_display_, surface, blending,
+ HWC::Composition::Device, layer_index);
}
ALOGI_IF(
@@ -956,7 +1061,6 @@
ALOGD_IF(TRACE, "HardwareComposer::UpdateLayerConfig: %zd active layers",
layers_.size());
- return true;
}
void HardwareComposer::SetVSyncCallback(VSyncCallback callback) {
@@ -965,15 +1069,27 @@
Return<void> HardwareComposer::ComposerCallback::onHotplug(
Hwc2::Display display, IComposerCallback::Connection conn) {
+ std::lock_guard<std::mutex> lock(mutex_);
+ ALOGI("onHotplug display=%" PRIu64 " conn=%d", display, conn);
+
+ bool is_primary = !got_first_hotplug_ || display == primary_display_.id;
+
// Our first onHotplug callback is always for the primary display.
- //
- // Ignore any other hotplug callbacks since the primary display is never
- // disconnected and we don't care about other displays.
- if (!has_display_id_) {
+ if (!got_first_hotplug_) {
LOG_ALWAYS_FATAL_IF(conn != IComposerCallback::Connection::CONNECTED,
"Initial onHotplug callback should be primary display connected");
- has_display_id_ = true;
- display_id_ = display;
+ got_first_hotplug_ = true;
+ } else if (is_primary) {
+ ALOGE("Ignoring unexpected onHotplug() call for primary display");
+ return Void();
+ }
+
+ if (conn == IComposerCallback::Connection::CONNECTED) {
+ if (!is_primary)
+ external_display_ = DisplayInfo();
+ DisplayInfo& display_info = is_primary ?
+ primary_display_ : *external_display_;
+ display_info.id = display;
std::array<char, 1024> buffer;
snprintf(buffer.data(), buffer.size(),
@@ -983,15 +1099,20 @@
"HardwareComposer::ComposerCallback::onHotplug: Driver supports "
"vsync_event node for display %" PRIu64,
display);
- driver_vsync_event_fd_ = std::move(handle);
+ display_info.driver_vsync_event_fd = std::move(handle);
} else {
ALOGI(
"HardwareComposer::ComposerCallback::onHotplug: Driver does not "
"support vsync_event node for display %" PRIu64,
display);
}
+ } else if (conn == IComposerCallback::Connection::DISCONNECTED) {
+ external_display_ = std::nullopt;
}
+ if (!is_primary)
+ external_display_was_hotplugged_ = true;
+
return Void();
}
@@ -1002,23 +1123,45 @@
Return<void> HardwareComposer::ComposerCallback::onVsync(Hwc2::Display display,
int64_t timestamp) {
- // Ignore any onVsync callbacks for the non-primary display.
- if (has_display_id_ && display == display_id_) {
+ DisplayInfo* display_info = GetDisplayInfo(display);
+ if (display_info) {
TRACE_FORMAT("vsync_callback|display=%" PRIu64 ";timestamp=%" PRId64 "|",
display, timestamp);
- callback_vsync_timestamp_ = timestamp;
+ display_info->callback_vsync_timestamp = timestamp;
}
+
return Void();
}
-Status<int64_t> HardwareComposer::ComposerCallback::GetVsyncTime() {
+HardwareComposer::ComposerCallback::Displays
+HardwareComposer::ComposerCallback::GetDisplays() {
+ std::lock_guard<std::mutex> lock(mutex_);
+ Displays displays;
+ displays.primary_display = primary_display_.id;
+ if (external_display_)
+ displays.external_display = external_display_->id;
+ if (external_display_was_hotplugged_) {
+ external_display_was_hotplugged_ = false;
+ displays.external_display_was_hotplugged = true;
+ }
+ return displays;
+}
+
+Status<int64_t> HardwareComposer::ComposerCallback::GetVsyncTime(
+ hwc2_display_t display) {
+ DisplayInfo* display_info = GetDisplayInfo(display);
+ if (!display_info) {
+ ALOGW("Attempt to get vsync time for unknown display %" PRIu64, display);
+ return ErrorStatus(EINVAL);
+ }
+
// See if the driver supports direct vsync events.
- LocalHandle& event_fd = driver_vsync_event_fd_;
+ LocalHandle& event_fd = display_info->driver_vsync_event_fd;
if (!event_fd) {
// Fall back to returning the last timestamp returned by the vsync
// callback.
- std::lock_guard<std::mutex> autolock(vsync_mutex_);
- return callback_vsync_timestamp_;
+ std::lock_guard<std::mutex> autolock(mutex_);
+ return display_info->callback_vsync_timestamp;
}
// When the driver supports the vsync_event sysfs node we can use it to
@@ -1066,19 +1209,32 @@
return {timestamp};
}
-Hwc2::Composer* Layer::composer_{nullptr};
-HWCDisplayMetrics Layer::display_metrics_{0, 0, {0, 0}, 0};
-hwc2_display_t Layer::display_id_{0};
+HardwareComposer::ComposerCallback::DisplayInfo*
+HardwareComposer::ComposerCallback::GetDisplayInfo(hwc2_display_t display) {
+ if (display == primary_display_.id) {
+ return &primary_display_;
+ } else if (external_display_ && display == external_display_->id) {
+ return &(*external_display_);
+ }
+ return nullptr;
+}
void Layer::Reset() {
if (hardware_composer_layer_) {
- composer_->destroyLayer(display_id_, hardware_composer_layer_);
+ HWC::Error error =
+ composer_->destroyLayer(display_params_.id, hardware_composer_layer_);
+ if (error != HWC::Error::None &&
+ (!ignore_bad_display_errors_on_destroy_ ||
+ error != HWC::Error::BadDisplay)) {
+ ALOGE("destroyLayer() failed for display %" PRIu64 ", layer %" PRIu64
+ ". error: %s", display_params_.id, hardware_composer_layer_,
+ error.to_string().c_str());
+ }
hardware_composer_layer_ = 0;
}
z_order_ = 0;
blending_ = HWC::BlendMode::None;
- transform_ = HWC::Transform::None;
composition_type_ = HWC::Composition::Invalid;
target_composition_type_ = composition_type_;
source_ = EmptyVariant{};
@@ -1086,25 +1242,29 @@
surface_rect_functions_applied_ = false;
pending_visibility_settings_ = true;
cached_buffer_map_.clear();
+ ignore_bad_display_errors_on_destroy_ = false;
}
-Layer::Layer(const std::shared_ptr<DirectDisplaySurface>& surface,
- HWC::BlendMode blending, HWC::Transform transform,
- HWC::Composition composition_type, size_t z_order)
- : z_order_{z_order},
+Layer::Layer(Hwc2::Composer* composer, const DisplayParams& display_params,
+ const std::shared_ptr<DirectDisplaySurface>& surface,
+ HWC::BlendMode blending, HWC::Composition composition_type,
+ size_t z_order)
+ : composer_(composer),
+ display_params_(display_params),
+ z_order_{z_order},
blending_{blending},
- transform_{transform},
target_composition_type_{composition_type},
source_{SourceSurface{surface}} {
CommonLayerSetup();
}
-Layer::Layer(const std::shared_ptr<IonBuffer>& buffer, HWC::BlendMode blending,
- HWC::Transform transform, HWC::Composition composition_type,
- size_t z_order)
- : z_order_{z_order},
+Layer::Layer(Hwc2::Composer* composer, const DisplayParams& display_params,
+ const std::shared_ptr<IonBuffer>& buffer, HWC::BlendMode blending,
+ HWC::Composition composition_type, size_t z_order)
+ : composer_(composer),
+ display_params_(display_params),
+ z_order_{z_order},
blending_{blending},
- transform_{transform},
target_composition_type_{composition_type},
source_{SourceBuffer{buffer}} {
CommonLayerSetup();
@@ -1118,10 +1278,11 @@
if (this != &other) {
Reset();
using std::swap;
+ swap(composer_, other.composer_);
+ swap(display_params_, other.display_params_);
swap(hardware_composer_layer_, other.hardware_composer_layer_);
swap(z_order_, other.z_order_);
swap(blending_, other.blending_);
- swap(transform_, other.transform_);
swap(composition_type_, other.composition_type_);
swap(target_composition_type_, other.target_composition_type_);
swap(source_, other.source_);
@@ -1130,6 +1291,8 @@
other.surface_rect_functions_applied_);
swap(pending_visibility_settings_, other.pending_visibility_settings_);
swap(cached_buffer_map_, other.cached_buffer_map_);
+ swap(ignore_bad_display_errors_on_destroy_,
+ other.ignore_bad_display_errors_on_destroy_);
}
return *this;
}
@@ -1169,14 +1332,14 @@
HWC::Error error;
error = composer_->setLayerBlendMode(
- display_id_, hardware_composer_layer_,
+ display_params_.id, hardware_composer_layer_,
blending_.cast<Hwc2::IComposerClient::BlendMode>());
ALOGE_IF(error != HWC::Error::None,
"Layer::UpdateLayerSettings: Error setting layer blend mode: %s",
error.to_string().c_str());
- error = composer_->setLayerZOrder(display_id_, hardware_composer_layer_,
- z_order_);
+ error = composer_->setLayerZOrder(display_params_.id,
+ hardware_composer_layer_, z_order_);
ALOGE_IF(error != HWC::Error::None,
"Layer::UpdateLayerSettings: Error setting z_ order: %s",
error.to_string().c_str());
@@ -1191,28 +1354,28 @@
// TODO(eieio): Use surface attributes or some other mechanism to control
// the layer display frame.
error = composer_->setLayerDisplayFrame(
- display_id_, hardware_composer_layer_,
- {0, 0, display_metrics_.width, display_metrics_.height});
+ display_params_.id, hardware_composer_layer_,
+ {0, 0, display_params_.width, display_params_.height});
ALOGE_IF(error != HWC::Error::None,
"Layer::UpdateLayerSettings: Error setting layer display frame: %s",
error.to_string().c_str());
error = composer_->setLayerVisibleRegion(
- display_id_, hardware_composer_layer_,
- {{0, 0, display_metrics_.width, display_metrics_.height}});
+ display_params_.id, hardware_composer_layer_,
+ {{0, 0, display_params_.width, display_params_.height}});
ALOGE_IF(error != HWC::Error::None,
"Layer::UpdateLayerSettings: Error setting layer visible region: %s",
error.to_string().c_str());
- error = composer_->setLayerPlaneAlpha(display_id_, hardware_composer_layer_,
- 1.0f);
+ error = composer_->setLayerPlaneAlpha(display_params_.id,
+ hardware_composer_layer_, 1.0f);
ALOGE_IF(error != HWC::Error::None,
"Layer::UpdateLayerSettings: Error setting layer plane alpha: %s",
error.to_string().c_str());
}
void Layer::CommonLayerSetup() {
- HWC::Error error = composer_->createLayer(display_id_,
+ HWC::Error error = composer_->createLayer(display_params_.id,
&hardware_composer_layer_);
ALOGE_IF(error != HWC::Error::None,
"Layer::CommonLayerSetup: Failed to create layer on primary "
@@ -1257,10 +1420,10 @@
if (composition_type_ == HWC::Composition::Invalid) {
composition_type_ = HWC::Composition::SolidColor;
composer_->setLayerCompositionType(
- display_id_, hardware_composer_layer_,
+ display_params_.id, hardware_composer_layer_,
composition_type_.cast<Hwc2::IComposerClient::Composition>());
Hwc2::IComposerClient::Color layer_color = {0, 0, 0, 0};
- composer_->setLayerColor(display_id_, hardware_composer_layer_,
+ composer_->setLayerColor(display_params_.id, hardware_composer_layer_,
layer_color);
} else {
// The composition type is already set. Nothing else to do until a
@@ -1270,7 +1433,7 @@
if (composition_type_ != target_composition_type_) {
composition_type_ = target_composition_type_;
composer_->setLayerCompositionType(
- display_id_, hardware_composer_layer_,
+ display_params_.id, hardware_composer_layer_,
composition_type_.cast<Hwc2::IComposerClient::Composition>());
}
@@ -1281,7 +1444,7 @@
HWC::Error error{HWC::Error::None};
error =
- composer_->setLayerBuffer(display_id_, hardware_composer_layer_,
+ composer_->setLayerBuffer(display_params_.id, hardware_composer_layer_,
slot, handle, acquire_fence_.Get());
ALOGE_IF(error != HWC::Error::None,
@@ -1291,7 +1454,7 @@
if (!surface_rect_functions_applied_) {
const float float_right = right;
const float float_bottom = bottom;
- error = composer_->setLayerSourceCrop(display_id_,
+ error = composer_->setLayerSourceCrop(display_params_.id,
hardware_composer_layer_,
{0, 0, float_right, float_bottom});
diff --git a/libs/vr/libvrflinger/hardware_composer.h b/libs/vr/libvrflinger/hardware_composer.h
index 4a503f8..1d8d463 100644
--- a/libs/vr/libvrflinger/hardware_composer.h
+++ b/libs/vr/libvrflinger/hardware_composer.h
@@ -13,6 +13,7 @@
#include <condition_variable>
#include <memory>
#include <mutex>
+#include <optional>
#include <thread>
#include <tuple>
#include <vector>
@@ -35,16 +36,19 @@
namespace android {
namespace dvr {
-// Basic display metrics for physical displays. Dimensions and densities are
-// relative to the physical display orientation, which may be different from the
-// logical display orientation exposed to applications.
-struct HWCDisplayMetrics {
+// Basic display metrics for physical displays.
+struct DisplayParams {
+ hwc2_display_t id;
+ bool is_primary;
+
int width;
int height;
+
struct {
int x;
int y;
} dpi;
+
int vsync_period_ns;
};
@@ -58,26 +62,29 @@
// automatically handles ACQUIRE/RELEASE phases for the surface's buffer train
// every frame.
//
+ // |composer| The composer instance.
+ // |display_params| Info about the display to use.
// |blending| receives HWC_BLENDING_* values.
- // |transform| receives HWC_TRANSFORM_* values.
// |composition_type| receives either HWC_FRAMEBUFFER for most layers or
// HWC_FRAMEBUFFER_TARGET (unless you know what you are doing).
// |index| is the index of this surface in the DirectDisplaySurface array.
- Layer(const std::shared_ptr<DirectDisplaySurface>& surface,
- HWC::BlendMode blending, HWC::Transform transform,
- HWC::Composition composition_type, size_t z_roder);
+ Layer(Hwc2::Composer* composer, const DisplayParams& display_params,
+ const std::shared_ptr<DirectDisplaySurface>& surface,
+ HWC::BlendMode blending, HWC::Composition composition_type,
+ size_t z_order);
// Sets up the layer to use a direct buffer as its content source. No special
// handling of the buffer is performed; responsibility for updating or
// changing the buffer each frame is on the caller.
//
+ // |composer| The composer instance.
+ // |display_params| Info about the display to use.
// |blending| receives HWC_BLENDING_* values.
- // |transform| receives HWC_TRANSFORM_* values.
// |composition_type| receives either HWC_FRAMEBUFFER for most layers or
// HWC_FRAMEBUFFER_TARGET (unless you know what you are doing).
- Layer(const std::shared_ptr<IonBuffer>& buffer, HWC::BlendMode blending,
- HWC::Transform transform, HWC::Composition composition_type,
- size_t z_order);
+ Layer(Hwc2::Composer* composer, const DisplayParams& display_params,
+ const std::shared_ptr<IonBuffer>& buffer, HWC::BlendMode blending,
+ HWC::Composition composition_type, size_t z_order);
Layer(Layer&&);
Layer& operator=(Layer&&);
@@ -144,17 +151,8 @@
}
bool operator<(int surface_id) const { return GetSurfaceId() < surface_id; }
- // Sets the composer instance used by all Layer instances.
- static void SetComposer(Hwc2::Composer* composer) { composer_ = composer; }
-
- // Sets the display metrics used by all Layer instances.
- static void SetDisplayMetrics(HWCDisplayMetrics display_metrics) {
- display_metrics_ = display_metrics;
- }
-
- // Sets the display id used by all Layer instances.
- static void SetDisplayId(hwc2_display_t display_id) {
- display_id_ = display_id;
+ void IgnoreBadDisplayErrorsOnDestroy(bool ignore) {
+ ignore_bad_display_errors_on_destroy_ = ignore;
}
private:
@@ -174,21 +172,11 @@
// associated and always returns false.
bool CheckAndUpdateCachedBuffer(std::size_t slot, int buffer_id);
- // Composer instance shared by all instances of Layer. This must be set
- // whenever a new instance of the Composer is created. This may be set to
- // nullptr as long as there are no instances of Layer that might need to use
- // it.
- static Hwc2::Composer* composer_;
+ // Composer instance.
+ Hwc2::Composer* composer_ = nullptr;
- // Display metrics shared by all instances of Layer. This must be set at least
- // once during VrFlinger initialization and is expected to remain constant
- // thereafter.
- static HWCDisplayMetrics display_metrics_;
-
- // Id of the primary display. Shared by all instances of Layer. This must be
- // set whenever the primary display id changes. This can be left unset as long
- // as there are no instances of Layer that might need to use it.
- static hwc2_display_t display_id_;
+ // Parameters of the display to use for this layer.
+ DisplayParams display_params_;
// The hardware composer layer and metrics to use during the prepare cycle.
hwc2_layer_t hardware_composer_layer_ = 0;
@@ -197,7 +185,6 @@
// Prepare phase.
size_t z_order_ = 0;
HWC::BlendMode blending_ = HWC::BlendMode::None;
- HWC::Transform transform_ = HWC::Transform::None;
HWC::Composition composition_type_ = HWC::Composition::Invalid;
HWC::Composition target_composition_type_ = HWC::Composition::Device;
@@ -293,6 +280,12 @@
// importing a buffer HWC already knows about.
std::map<std::size_t, int> cached_buffer_map_;
+ // When calling destroyLayer() on an external display that's been removed we
+ // typically get HWC2_ERROR_BAD_DISPLAY errors. If
+ // ignore_bad_display_errors_on_destroy_ is true, don't log the bad display
+ // errors, since they're expected.
+ bool ignore_bad_display_errors_on_destroy_ = false;
+
Layer(const Layer&) = delete;
void operator=(const Layer&) = delete;
};
@@ -330,22 +323,12 @@
// Called on a binder thread.
void OnBootFinished();
- // Get the HMD display metrics for the current display.
- display::Metrics GetHmdDisplayMetrics() const;
-
std::string Dump();
void SetVSyncCallback(VSyncCallback callback);
- // Metrics of the logical display, which is always landscape.
- int DisplayWidth() const { return display_metrics_.width; }
- int DisplayHeight() const { return display_metrics_.height; }
- HWCDisplayMetrics display_metrics() const { return display_metrics_; }
-
- // Metrics of the native display, which depends on the specific hardware
- // implementation of the display.
- HWCDisplayMetrics native_display_metrics() const {
- return native_display_metrics_;
+ const DisplayParams& GetPrimaryDisplayParams() const {
+ return primary_display_;
}
// Sets the display surfaces to compose the hardware layer stack.
@@ -356,16 +339,16 @@
void OnDeletedGlobalBuffer(DvrGlobalBufferKey key);
private:
- HWC::Error GetDisplayAttribute(Hwc2::Composer* composer,
- hwc2_display_t display, hwc2_config_t config,
- hwc2_attribute_t attributes,
- int32_t* out_value) const;
- HWC::Error GetDisplayMetrics(Hwc2::Composer* composer, hwc2_display_t display,
- hwc2_config_t config,
- HWCDisplayMetrics* out_metrics) const;
+ DisplayParams GetDisplayParams(Hwc2::Composer* composer,
+ hwc2_display_t display, bool is_primary);
- HWC::Error EnableVsync(bool enabled);
- HWC::Error SetPowerMode(bool active);
+ // Turn display vsync on/off. Returns true on success, false on failure.
+ bool EnableVsync(const DisplayParams& display, bool enabled);
+ // Turn display power on/off. Returns true on success, false on failure.
+ bool SetPowerMode(const DisplayParams& display, bool active);
+ // Convenience function to turn a display on/off. Turns both power and vsync
+ // on/off. Returns true on success, false on failure.
+ bool EnableDisplay(const DisplayParams& display, bool enabled);
class ComposerCallback : public Hwc2::IComposerCallback {
public:
@@ -376,22 +359,38 @@
hardware::Return<void> onVsync(Hwc2::Display display,
int64_t timestamp) override;
- bool HasDisplayId() { return has_display_id_; }
- hwc2_display_t GetDisplayId() { return display_id_; }
- pdx::Status<int64_t> GetVsyncTime();
+ bool GotFirstHotplug() { return got_first_hotplug_; }
+
+ struct Displays {
+ hwc2_display_t primary_display = 0;
+ std::optional<hwc2_display_t> external_display;
+ bool external_display_was_hotplugged = false;
+ };
+
+ Displays GetDisplays();
+ pdx::Status<int64_t> GetVsyncTime(hwc2_display_t display);
private:
- std::mutex vsync_mutex_;
- bool has_display_id_ = false;
- hwc2_display_t display_id_;
- pdx::LocalHandle driver_vsync_event_fd_;
- int64_t callback_vsync_timestamp_{0};
+ struct DisplayInfo {
+ hwc2_display_t id = 0;
+ pdx::LocalHandle driver_vsync_event_fd;
+ int64_t callback_vsync_timestamp{0};
+ };
+
+ DisplayInfo* GetDisplayInfo(hwc2_display_t display);
+
+ std::mutex mutex_;
+
+ bool got_first_hotplug_ = false;
+ DisplayInfo primary_display_;
+ std::optional<DisplayInfo> external_display_;
+ bool external_display_was_hotplugged_ = false;
};
HWC::Error Validate(hwc2_display_t display);
HWC::Error Present(hwc2_display_t display);
- void PostLayers();
+ void PostLayers(hwc2_display_t display);
void PostThread();
// The post thread has two controlling states:
@@ -419,16 +418,24 @@
int PostThreadPollInterruptible(const pdx::LocalHandle& event_fd,
int requested_events, int timeout_ms);
- // WaitForVSync and SleepUntil are blocking calls made on the post thread that
- // can be interrupted by a control thread. If interrupted, these calls return
- // kPostThreadInterrupted.
+ // WaitForPredictedVSync and SleepUntil are blocking calls made on the post
+ // thread that can be interrupted by a control thread. If interrupted, these
+ // calls return kPostThreadInterrupted.
int ReadWaitPPState();
- pdx::Status<int64_t> WaitForVSync();
+ pdx::Status<int64_t> WaitForPredictedVSync();
int SleepUntil(int64_t wakeup_timestamp);
+ // Initialize any newly connected displays, and set target_display_ to the
+ // display we should render to. Returns true if target_display_
+ // changed. Called only from the post thread.
+ bool UpdateTargetDisplay();
+
// Reconfigures the layer stack if the display surfaces changed since the last
// frame. Called only from the post thread.
- bool UpdateLayerConfig();
+ void UpdateLayerConfig();
+
+ // Called on the post thread to create the Composer instance.
+ void CreateComposer();
// Called on the post thread when the post thread is resumed.
void OnPostThreadResumed();
@@ -459,18 +466,19 @@
sp<ComposerCallback> composer_callback_;
RequestDisplayCallback request_display_callback_;
- // Display metrics of the physical display.
- HWCDisplayMetrics native_display_metrics_;
- // Display metrics of the logical display, adjusted so that orientation is
- // landscape.
- HWCDisplayMetrics display_metrics_;
- // Transform required to get from native to logical display orientation.
- HWC::Transform display_transform_ = HWC::Transform::None;
+ DisplayParams primary_display_;
+ std::optional<DisplayParams> external_display_;
+ DisplayParams* target_display_ = &primary_display_;
- // Pending surface list. Set by the display service when DirectSurfaces are
- // added, removed, or change visibility. Written by the message dispatch
- // thread and read by the post thread.
- std::vector<std::shared_ptr<DirectDisplaySurface>> pending_surfaces_;
+ // The list of surfaces we should draw. Set by the display service when
+ // DirectSurfaces are added, removed, or change visibility. Written by the
+ // message dispatch thread and read by the post thread.
+ std::vector<std::shared_ptr<DirectDisplaySurface>> surfaces_;
+ // Set to true by the dispatch thread whenever surfaces_ changes. Set to false
+ // by the post thread when the new list of surfaces is processed.
+ bool surfaces_changed_ = false;
+
+ std::vector<std::shared_ptr<DirectDisplaySurface>> current_surfaces_;
// Layer set for handling buffer flow into hardware composer layers. This
// vector must be sorted by surface_id in ascending order.