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/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});