drm_hwcomposer: Implement SetActiveConfigWithConstraints
Enough to get 100% passes in Composer 2.4 VTS.
Some SOCs require a VTS fix to pass [1]
[1]: https://android-review.googlesource.com/c/platform/hardware/interfaces/+/1954544
Signed-off-by: Roman Stratiienko <roman.o.stratiienko@globallogic.com>
diff --git a/hwc2_device/DrmHwcTwo.cpp b/hwc2_device/DrmHwcTwo.cpp
index 37901ee..f1a5490 100644
--- a/hwc2_device/DrmHwcTwo.cpp
+++ b/hwc2_device/DrmHwcTwo.cpp
@@ -174,6 +174,11 @@
vsync_2_4_callback_ = std::make_pair(HWC2_PFN_VSYNC_2_4(function), data);
break;
}
+ case HWC2::Callback::VsyncPeriodTimingChanged: {
+ period_timing_changed_callback_ = std::
+ make_pair(HWC2_PFN_VSYNC_PERIOD_TIMING_CHANGED(function), data);
+ break;
+ }
#endif
default:
break;
@@ -220,4 +225,21 @@
}
}
+void DrmHwcTwo::SendVsyncPeriodTimingChangedEventToClient(
+ [[maybe_unused]] hwc2_display_t displayid,
+ [[maybe_unused]] int64_t timestamp) const {
+#if PLATFORM_SDK_VERSION > 29
+ hwc_vsync_period_change_timeline_t timeline = {
+ .newVsyncAppliedTimeNanos = timestamp,
+ .refreshRequired = false,
+ .refreshTimeNanos = 0,
+ };
+ if (period_timing_changed_callback_.first != nullptr &&
+ period_timing_changed_callback_.second != nullptr) {
+ period_timing_changed_callback_
+ .first(period_timing_changed_callback_.second, displayid, &timeline);
+ }
+#endif
+}
+
} // namespace android
diff --git a/hwc2_device/DrmHwcTwo.h b/hwc2_device/DrmHwcTwo.h
index 5379cba..0e72251 100644
--- a/hwc2_device/DrmHwcTwo.h
+++ b/hwc2_device/DrmHwcTwo.h
@@ -33,6 +33,8 @@
std::pair<HWC2_PFN_VSYNC, hwc2_callback_data_t> vsync_callback_{};
#if PLATFORM_SDK_VERSION > 29
std::pair<HWC2_PFN_VSYNC_2_4, hwc2_callback_data_t> vsync_2_4_callback_{};
+ std::pair<HWC2_PFN_VSYNC_PERIOD_TIMING_CHANGED, hwc2_callback_data_t>
+ period_timing_changed_callback_{};
#endif
std::pair<HWC2_PFN_REFRESH, hwc2_callback_data_t> refresh_callback_{};
@@ -66,6 +68,8 @@
void SendVsyncEventToClient(hwc2_display_t displayid, int64_t timestamp,
uint32_t vsync_period) const;
+ void SendVsyncPeriodTimingChangedEventToClient(hwc2_display_t displayid,
+ int64_t timestamp) const;
private:
void SendHotplugEventToClient(hwc2_display_t displayid, bool connected);
diff --git a/hwc2_device/HwcDisplay.cpp b/hwc2_device/HwcDisplay.cpp
index 656aa94..d7b753a 100644
--- a/hwc2_device/HwcDisplay.cpp
+++ b/hwc2_device/HwcDisplay.cpp
@@ -141,7 +141,10 @@
if (vsync_flattening_en_) {
ProcessFlatenningVsyncInternal();
}
- if (!vsync_event_en_ && !vsync_flattening_en_) {
+ if (vsync_tracking_en_) {
+ last_vsync_ts_ = timestamp;
+ }
+ if (!vsync_event_en_ && !vsync_flattening_en_ && !vsync_tracking_en_) {
vsync_worker_.VSyncControl(false);
}
});
@@ -215,10 +218,10 @@
}
HWC2::Error HwcDisplay::GetActiveConfig(hwc2_config_t *config) const {
- if (configs_.hwc_configs.count(configs_.active_config_id) == 0)
+ if (configs_.hwc_configs.count(staged_mode_config_id_) == 0)
return HWC2::Error::BadConfig;
- *config = configs_.active_config_id;
+ *config = staged_mode_config_id_;
return HWC2::Error::None;
}
@@ -321,7 +324,7 @@
case HWC2::Attribute::ConfigGroup:
/* Dispite ConfigGroup is a part of HWC2.4 API, framework
* able to request it even if service @2.1 is used */
- *value = hwc_config.group_id;
+ *value = int(hwc_config.group_id);
break;
#endif
default:
@@ -436,6 +439,26 @@
return HWC2::Error::None;
}
+ int PrevModeVsyncPeriodNs = static_cast<int>(
+ 1E9 / GetPipe().connector->Get()->GetActiveMode().v_refresh());
+
+ auto mode_update_commited_ = false;
+ if (staged_mode_ &&
+ staged_mode_change_time_ <= ResourceManager::GetTimeMonotonicNs()) {
+ client_layer_.SetLayerDisplayFrame(
+ (hwc_rect_t){.left = 0,
+ .top = 0,
+ .right = static_cast<int>(staged_mode_->h_display()),
+ .bottom = static_cast<int>(staged_mode_->v_display())});
+
+ configs_.active_config_id = staged_mode_config_id_;
+
+ a_args.display_mode = *staged_mode_;
+ if (!a_args.test_only) {
+ mode_update_commited_ = true;
+ }
+ }
+
// order the layers by z-order
bool use_client_layer = false;
uint32_t client_z_order = UINT32_MAX;
@@ -498,9 +521,6 @@
}
a_args.composition = composition;
- if (staged_mode) {
- a_args.display_mode = *staged_mode;
- }
ret = GetPipe().compositor->ExecuteAtomicCommit(a_args);
if (ret) {
@@ -509,8 +529,13 @@
return HWC2::Error::BadParameter;
}
- if (!a_args.test_only) {
- staged_mode.reset();
+ if (mode_update_commited_) {
+ staged_mode_.reset();
+ vsync_tracking_en_ = false;
+ if (last_vsync_ts_ != 0) {
+ hwc2_->SendVsyncPeriodTimingChangedEventToClient(
+ handle_, last_vsync_ts_ + PrevModeVsyncPeriodNs);
+ }
}
return HWC2::Error::None;
@@ -548,30 +573,24 @@
return HWC2::Error::None;
}
-HWC2::Error HwcDisplay::SetActiveConfig(hwc2_config_t config) {
- int conf = static_cast<int>(config);
-
- if (configs_.hwc_configs.count(conf) == 0) {
- ALOGE("Could not find active mode for %d", conf);
+HWC2::Error HwcDisplay::SetActiveConfigInternal(uint32_t config,
+ int64_t change_time) {
+ if (configs_.hwc_configs.count(config) == 0) {
+ ALOGE("Could not find active mode for %u", config);
return HWC2::Error::BadConfig;
}
- auto &mode = configs_.hwc_configs[conf].mode;
-
- staged_mode = mode;
-
- configs_.active_config_id = conf;
-
- // Setup the client layer's dimensions
- hwc_rect_t display_frame = {.left = 0,
- .top = 0,
- .right = static_cast<int>(mode.h_display()),
- .bottom = static_cast<int>(mode.v_display())};
- client_layer_.SetLayerDisplayFrame(display_frame);
+ staged_mode_ = configs_.hwc_configs[config].mode;
+ staged_mode_change_time_ = change_time;
+ staged_mode_config_id_ = config;
return HWC2::Error::None;
}
+HWC2::Error HwcDisplay::SetActiveConfig(hwc2_config_t config) {
+ return SetActiveConfigInternal(config, ResourceManager::GetTimeMonotonicNs());
+}
+
/* Find API details at:
* https://cs.android.com/android/platform/superproject/+/android-11.0.0_r3:hardware/libhardware/include/hardware/hwcomposer2.h;l=1861
*/
@@ -736,14 +755,37 @@
}
HWC2::Error HwcDisplay::SetActiveConfigWithConstraints(
- hwc2_config_t /*config*/,
+ hwc2_config_t config,
hwc_vsync_period_change_constraints_t *vsyncPeriodChangeConstraints,
hwc_vsync_period_change_timeline_t *outTimeline) {
if (vsyncPeriodChangeConstraints == nullptr || outTimeline == nullptr) {
return HWC2::Error::BadParameter;
}
- return HWC2::Error::BadConfig;
+ uint32_t current_vsync_period{};
+ GetDisplayVsyncPeriod(¤t_vsync_period);
+
+ if (vsyncPeriodChangeConstraints->seamlessRequired) {
+ return HWC2::Error::SeamlessNotAllowed;
+ }
+
+ outTimeline->refreshTimeNanos = vsyncPeriodChangeConstraints
+ ->desiredTimeNanos -
+ current_vsync_period;
+ auto ret = SetActiveConfigInternal(config, outTimeline->refreshTimeNanos);
+ if (ret != HWC2::Error::None) {
+ return ret;
+ }
+
+ outTimeline->refreshRequired = true;
+ outTimeline->newVsyncAppliedTimeNanos = vsyncPeriodChangeConstraints
+ ->desiredTimeNanos;
+
+ last_vsync_ts_ = 0;
+ vsync_tracking_en_ = true;
+ vsync_worker_.VSyncControl(true);
+
+ return HWC2::Error::None;
}
HWC2::Error HwcDisplay::SetAutoLowLatencyMode(bool /*on*/) {
diff --git a/hwc2_device/HwcDisplay.h b/hwc2_device/HwcDisplay.h
index 5a5c98e..e7ce7ef 100644
--- a/hwc2_device/HwcDisplay.h
+++ b/hwc2_device/HwcDisplay.h
@@ -195,7 +195,9 @@
DrmHwcTwo *const hwc2_;
- std::optional<DrmMode> staged_mode;
+ std::optional<DrmMode> staged_mode_;
+ int64_t staged_mode_change_time_{};
+ uint32_t staged_mode_config_id_{};
DrmDisplayPipeline *const pipeline_;
@@ -204,6 +206,8 @@
VSyncWorker vsync_worker_;
bool vsync_event_en_{};
bool vsync_flattening_en_{};
+ bool vsync_tracking_en_{};
+ int64_t last_vsync_ts_{};
const hwc2_display_t handle_;
HWC2::DisplayType type_;
@@ -223,6 +227,8 @@
std::string DumpDelta(HwcDisplay::Stats delta);
HWC2::Error Init();
+
+ HWC2::Error SetActiveConfigInternal(uint32_t config, int64_t change_time);
};
} // namespace android
diff --git a/hwc2_device/HwcDisplayConfigs.cpp b/hwc2_device/HwcDisplayConfigs.cpp
index c28ec9f..6a3ed5a 100644
--- a/hwc2_device/HwcDisplayConfigs.cpp
+++ b/hwc2_device/HwcDisplayConfigs.cpp
@@ -32,7 +32,7 @@
namespace android {
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
-int HwcDisplayConfigs::last_config_id = 1;
+uint32_t HwcDisplayConfigs::last_config_id = 1;
void HwcDisplayConfigs::FillHeadless() {
hwc_configs.clear();
@@ -77,15 +77,15 @@
mm_height = connector.GetMmHeight();
preferred_config_id = 0;
- int preferred_config_group_id = 0;
+ uint32_t preferred_config_group_id = 0;
- int first_config_id = last_config_id;
- int last_group_id = 1;
+ uint32_t first_config_id = last_config_id;
+ uint32_t last_group_id = 1;
/* Group modes */
for (const auto &mode : connector.GetModes()) {
/* Find group for the new mode or create new group */
- int group_found = 0;
+ uint32_t group_found = 0;
for (auto &hwc_config : hwc_configs) {
if (mode.h_display() == hwc_config.second.mode.h_display() &&
mode.v_display() == hwc_config.second.mode.v_display()) {
@@ -128,7 +128,7 @@
preferred_config_group_id = 1;
}
- for (int group = 1; group < last_group_id; group++) {
+ for (uint32_t group = 1; group < last_group_id; group++) {
bool has_interlaced = false;
bool has_progressive = false;
for (auto &hwc_config : hwc_configs) {
@@ -179,8 +179,8 @@
* otherwise android.graphics.cts.SetFrameRateTest CTS will fail
*/
constexpr float kMinFpsDelta = 1.0; // FPS
- for (int m1 = first_config_id; m1 < last_config_id; m1++) {
- for (int m2 = first_config_id; m2 < last_config_id; m2++) {
+ for (uint32_t m1 = first_config_id; m1 < last_config_id; m1++) {
+ for (uint32_t m2 = first_config_id; m2 < last_config_id; m2++) {
if (m1 != m2 && hwc_configs[m1].group_id == hwc_configs[m2].group_id &&
!hwc_configs[m1].disabled && !hwc_configs[m2].disabled &&
fabsf(hwc_configs[m1].mode.v_refresh() -
diff --git a/hwc2_device/HwcDisplayConfigs.h b/hwc2_device/HwcDisplayConfigs.h
index 75852a6..7c173d6 100644
--- a/hwc2_device/HwcDisplayConfigs.h
+++ b/hwc2_device/HwcDisplayConfigs.h
@@ -28,8 +28,8 @@
class DrmConnector;
struct HwcDisplayConfig {
- int id{};
- int group_id{};
+ uint32_t id{};
+ uint32_t group_id{};
DrmMode mode;
bool disabled{};
@@ -42,13 +42,13 @@
HWC2::Error Update(DrmConnector &conn);
void FillHeadless();
- std::map<int /*config_id*/, struct HwcDisplayConfig> hwc_configs;
+ std::map<uint32_t /*config_id*/, struct HwcDisplayConfig> hwc_configs;
- int active_config_id = 0;
- int preferred_config_id = 0;
+ uint32_t active_config_id = 0;
+ uint32_t preferred_config_id = 0;
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
- static int last_config_id;
+ static uint32_t last_config_id;
uint32_t mm_width = 0;
uint32_t mm_height = 0;