hwc2: Add VRR related Composer 2.4 APIs
Define SetActiveConfigWithConstraints and GetDisplayVsyncPeriod
APIs for VRR on Composer 2.4 .
Change-Id: Id64e71a9756e183b1d47a285fb6be0cae9ca046e
diff --git a/sdm/libs/hwc2/hwc_callbacks.cpp b/sdm/libs/hwc2/hwc_callbacks.cpp
index d1345ee..8085dcb 100644
--- a/sdm/libs/hwc2/hwc_callbacks.cpp
+++ b/sdm/libs/hwc2/hwc_callbacks.cpp
@@ -60,6 +60,27 @@
return HWC2::Error::None;
}
+HWC2::Error HWCCallbacks::Vsync_2_4(hwc2_display_t display, int64_t timestamp, uint32_t period) {
+ DTRACE_SCOPED();
+ if (!vsync_2_4_) {
+ return HWC2::Error::NoResources;
+ }
+
+ vsync_2_4_(vsync_2_4_data_, display, timestamp, period);
+ return HWC2::Error::None;
+}
+
+HWC2::Error HWCCallbacks::VsyncPeriodTimingChanged(
+ hwc2_display_t display, hwc_vsync_period_change_timeline_t *updated_timeline) {
+ DTRACE_SCOPED();
+ if (!vsync_period_timing_changed_) {
+ return HWC2::Error::NoResources;
+ }
+
+ vsync_period_timing_changed_(vsync_period_timing_changed_data_, display, updated_timeline);
+ return HWC2::Error::None;
+}
+
HWC2::Error HWCCallbacks::Register(HWC2::Callback descriptor, hwc2_callback_data_t callback_data,
hwc2_function_pointer_t pointer) {
switch (descriptor) {
@@ -75,6 +96,15 @@
vsync_data_ = callback_data;
vsync_ = reinterpret_cast<HWC2_PFN_VSYNC>(pointer);
break;
+ case HWC2::Callback::Vsync_2_4:
+ vsync_2_4_data_ = callback_data;
+ vsync_2_4_ = reinterpret_cast<HWC2_PFN_VSYNC_2_4>(pointer);
+ break;
+ case HWC2::Callback::VsyncPeriodTimingChanged:
+ vsync_period_timing_changed_data_ = callback_data;
+ vsync_period_timing_changed_ =
+ reinterpret_cast<HWC2_PFN_VSYNC_PERIOD_TIMING_CHANGED>(pointer);
+ break;
default:
return HWC2::Error::BadParameter;
}
diff --git a/sdm/libs/hwc2/hwc_callbacks.h b/sdm/libs/hwc2/hwc_callbacks.h
index 2e4bdba..baec40d 100644
--- a/sdm/libs/hwc2/hwc_callbacks.h
+++ b/sdm/libs/hwc2/hwc_callbacks.h
@@ -53,6 +53,9 @@
HWC2::Error Hotplug(hwc2_display_t display, HWC2::Connection state);
HWC2::Error Refresh(hwc2_display_t display);
HWC2::Error Vsync(hwc2_display_t display, int64_t timestamp);
+ HWC2::Error Vsync_2_4(hwc2_display_t display, int64_t timestamp, uint32_t period);
+ HWC2::Error VsyncPeriodTimingChanged(hwc2_display_t display,
+ hwc_vsync_period_change_timeline_t *updated_timeline);
HWC2::Error Register(HWC2::Callback, hwc2_callback_data_t callback_data,
hwc2_function_pointer_t pointer);
void UpdateVsyncSource(hwc2_display_t from) {
@@ -61,6 +64,7 @@
hwc2_display_t GetVsyncSource() { return vsync_source_; }
bool VsyncCallbackRegistered() { return (vsync_ != nullptr && vsync_data_ != nullptr); }
+ bool Vsync_2_4CallbackRegistered() { return (vsync_2_4_ != nullptr); }
bool NeedsRefresh(hwc2_display_t display) { return pending_refresh_.test(UINT32(display)); }
void ResetRefresh(hwc2_display_t display) { pending_refresh_.reset(UINT32(display)); }
@@ -68,10 +72,14 @@
hwc2_callback_data_t hotplug_data_ = nullptr;
hwc2_callback_data_t refresh_data_ = nullptr;
hwc2_callback_data_t vsync_data_ = nullptr;
+ hwc2_callback_data_t vsync_2_4_data_ = nullptr;
+ hwc2_callback_data_t vsync_period_timing_changed_data_ = nullptr;
HWC2_PFN_HOTPLUG hotplug_ = nullptr;
HWC2_PFN_REFRESH refresh_ = nullptr;
HWC2_PFN_VSYNC vsync_ = nullptr;
+ HWC2_PFN_VSYNC_2_4 vsync_2_4_ = nullptr;
+ HWC2_PFN_VSYNC_PERIOD_TIMING_CHANGED vsync_period_timing_changed_ = nullptr;
hwc2_display_t vsync_source_ = HWC_DISPLAY_PRIMARY; // hw vsync is active on this display
std::bitset<kNumDisplays> pending_refresh_; // Displays waiting to get refreshed
};
diff --git a/sdm/libs/hwc2/hwc_display.cpp b/sdm/libs/hwc2/hwc_display.cpp
index 238b1d0..467cc1b 100644
--- a/sdm/libs/hwc2/hwc_display.cpp
+++ b/sdm/libs/hwc2/hwc_display.cpp
@@ -52,6 +52,7 @@
namespace sdm {
uint32_t HWCDisplay::throttling_refresh_rate_ = 60;
+constexpr uint32_t kVsyncTimeDriftNs = 1000000;
bool NeedsToneMap(const LayerStack &layer_stack) {
for (Layer *layer : layer_stack.layers) {
@@ -62,6 +63,10 @@
return false;
}
+bool IsTimeAfterOrEqualVsyncTime(int64_t time, int64_t vsync_time) {
+ return ((vsync_time != INT64_MAX) && ((time - (vsync_time - kVsyncTimeDriftNs)) >= 0));
+}
+
HWCColorMode::HWCColorMode(DisplayInterface *display_intf) : display_intf_(display_intf) {}
HWC2::Error HWCColorMode::Init() {
@@ -856,7 +861,8 @@
ATRACE_INT("SetVsyncState ", enabled == HWC2::Vsync::Enable ? 1 : 0);
DisplayError error = kErrorNone;
- if (shutdown_pending_ || !callbacks_->VsyncCallbackRegistered()) {
+ if (shutdown_pending_ ||
+ (!callbacks_->VsyncCallbackRegistered() && !callbacks_->Vsync_2_4CallbackRegistered())) {
return HWC2::Error::None;
}
@@ -1222,7 +1228,17 @@
}
DisplayError HWCDisplay::VSync(const DisplayEventVSync &vsync) {
- callbacks_->Vsync(id_, vsync.timestamp);
+ if (callbacks_->Vsync_2_4CallbackRegistered()) {
+ hwc2_vsync_period_t vsync_period;
+ if (GetDisplayVsyncPeriod(&vsync_period) != HWC2::Error::None) {
+ vsync_period = 0;
+ }
+ ATRACE_INT("VsyncPeriod", INT32(vsync_period));
+ callbacks_->Vsync_2_4(id_, vsync.timestamp, vsync_period);
+ } else {
+ callbacks_->Vsync(id_, vsync.timestamp);
+ }
+
return kErrorNone;
}
@@ -2438,4 +2454,176 @@
return -1;
}
+HWC2::Error HWCDisplay::GetDisplayVsyncPeriod(hwc2_vsync_period_t *vsync_period) {
+ if (GetTransientVsyncPeriod(vsync_period)) {
+ return HWC2::Error::None;
+ }
+
+ return GetVsyncPeriodByActiveConfig(vsync_period);
+}
+
+HWC2::Error HWCDisplay::SetActiveConfigWithConstraints(hwc2_config_t config,
+ hwc_vsync_period_change_constraints_t *vsync_period_change_constraints,
+ hwc_vsync_period_change_timeline_t *out_timeline) {
+ if (variable_config_map_.find(config) == variable_config_map_.end()) {
+ DLOGE("Invalid config: %d", config);
+ return HWC2::Error::BadConfig;
+ }
+
+ if (vsync_period_change_constraints->seamlessRequired && !AllowSeamless(config)) {
+ DLOGE("Seamless switch to the config: %d, is not allowed!", config);
+ return HWC2::Error::SeamlessNotAllowed;
+ }
+
+ hwc2_vsync_period_t vsync_period;
+ if (GetDisplayVsyncPeriod(&vsync_period) != HWC2::Error::None) {
+ return HWC2::Error::BadConfig;
+ }
+
+ std::tie(out_timeline->refreshTimeNanos, out_timeline->newVsyncAppliedTimeNanos) =
+ RequestActiveConfigChange(config, vsync_period,
+ vsync_period_change_constraints->desiredTimeNanos);
+
+ out_timeline->refreshRequired = true;
+ return HWC2::Error::None;
+}
+
+void HWCDisplay::ProcessActiveConfigChange() {
+ if (!IsActiveConfigReadyToSubmit(systemTime(SYSTEM_TIME_MONOTONIC))) {
+ return;
+ }
+
+ DTRACE_SCOPED();
+ hwc2_vsync_period_t vsync_period;
+ if (GetVsyncPeriodByActiveConfig(&vsync_period) == HWC2::Error::None) {
+ SubmitActiveConfigChange(vsync_period);
+ }
+}
+
+HWC2::Error HWCDisplay::GetVsyncPeriodByActiveConfig(hwc2_vsync_period_t *vsync_period) {
+ hwc2_config_t active_config;
+
+ auto error = GetActiveConfig(&active_config);
+ if (error != HWC2::Error::None) {
+ DLOGE("Failed to get active config!");
+ return error;
+ }
+
+ int32_t active_vsync_period;
+ error = GetDisplayAttribute(active_config, HWC2::Attribute::VsyncPeriod, &active_vsync_period);
+ if (error != HWC2::Error::None) {
+ DLOGE("Failed to get VsyncPeriod of config: %d", active_config);
+ return error;
+ }
+
+ *vsync_period = static_cast<hwc2_vsync_period_t>(active_vsync_period);
+ return HWC2::Error::None;
+}
+
+bool HWCDisplay::GetTransientVsyncPeriod(hwc2_vsync_period_t *vsync_period) {
+ std::lock_guard<std::mutex> lock(transient_refresh_rate_lock_);
+ auto now = systemTime(SYSTEM_TIME_MONOTONIC);
+
+ while (!transient_refresh_rate_info_.empty()) {
+ if (IsActiveConfigApplied(now, transient_refresh_rate_info_.front().vsync_applied_time)) {
+ transient_refresh_rate_info_.pop_front();
+ } else {
+ *vsync_period = transient_refresh_rate_info_.front().transient_vsync_period;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+std::tuple<int64_t, int64_t> HWCDisplay::RequestActiveConfigChange(
+ hwc2_config_t config, hwc2_vsync_period_t current_vsync_period, int64_t desired_time) {
+ int64_t refresh_time, applied_time;
+ std::tie(refresh_time, applied_time) =
+ EstimateVsyncPeriodChangeTimeline(current_vsync_period, desired_time);
+
+ pending_refresh_rate_config_ = config;
+ pending_refresh_rate_refresh_time_ = refresh_time;
+ pending_refresh_rate_applied_time_ = applied_time;
+
+ return std::make_tuple(refresh_time, applied_time);
+}
+
+std::tuple<int64_t, int64_t> HWCDisplay::EstimateVsyncPeriodChangeTimeline(
+ hwc2_vsync_period_t current_vsync_period, int64_t desired_time) {
+ const auto now = systemTime(SYSTEM_TIME_MONOTONIC);
+ const auto delta = desired_time - now;
+ const auto refresh_rate_activate_period = current_vsync_period * vsyncs_to_apply_rate_change_;
+ nsecs_t refresh_time;
+
+ if (delta < 0) {
+ refresh_time = now + (delta % current_vsync_period);
+ } else if (delta < refresh_rate_activate_period) {
+ refresh_time = now + (delta % current_vsync_period) - current_vsync_period;
+ } else {
+ refresh_time = desired_time - refresh_rate_activate_period;
+ }
+
+ const auto applied_time = refresh_time + refresh_rate_activate_period;
+ return std::make_tuple(refresh_time, applied_time);
+}
+
+void HWCDisplay::SubmitActiveConfigChange(hwc2_vsync_period_t current_vsync_period) {
+ HWC2::Error error = SetActiveConfig(pending_refresh_rate_config_);
+ if (error != HWC2::Error::None) {
+ return;
+ }
+
+ std::lock_guard<std::mutex> lock(transient_refresh_rate_lock_);
+ hwc_vsync_period_change_timeline_t timeline;
+ std::tie(timeline.refreshTimeNanos, timeline.newVsyncAppliedTimeNanos) =
+ EstimateVsyncPeriodChangeTimeline(current_vsync_period, pending_refresh_rate_refresh_time_);
+
+ transient_refresh_rate_info_.push_back({current_vsync_period, timeline.newVsyncAppliedTimeNanos});
+ if (timeline.newVsyncAppliedTimeNanos != pending_refresh_rate_applied_time_) {
+ timeline.refreshRequired = false;
+ callbacks_->VsyncPeriodTimingChanged(id_, &timeline);
+ }
+
+ pending_refresh_rate_config_ = UINT_MAX;
+ pending_refresh_rate_refresh_time_ = INT64_MAX;
+ pending_refresh_rate_applied_time_ = INT64_MAX;
+}
+
+bool HWCDisplay::IsActiveConfigReadyToSubmit(int64_t time) {
+ return ((pending_refresh_rate_config_ != UINT_MAX) &&
+ IsTimeAfterOrEqualVsyncTime(time, pending_refresh_rate_refresh_time_));
+}
+
+bool HWCDisplay::IsActiveConfigApplied(int64_t time, int64_t vsync_applied_time) {
+ return IsTimeAfterOrEqualVsyncTime(time, vsync_applied_time);
+}
+
+bool HWCDisplay::IsSameGroup(hwc2_config_t config_id1, hwc2_config_t config_id2) {
+ const auto &variable_config1 = variable_config_map_.find(config_id1);
+ const auto &variable_config2 = variable_config_map_.find(config_id2);
+
+ if ((variable_config1 == variable_config_map_.end()) ||
+ (variable_config2 == variable_config_map_.end())) {
+ DLOGE("Invalid config: %u, %u", config_id1, config_id2);
+ return false;
+ }
+
+ const DisplayConfigGroupInfo &config_group1 = variable_config1->second;
+ const DisplayConfigGroupInfo &config_group2 = variable_config2->second;
+
+ return (config_group1 == config_group2);
+}
+
+bool HWCDisplay::AllowSeamless(hwc2_config_t config) {
+ hwc2_config_t active_config;
+ auto error = GetActiveConfig(&active_config);
+ if (error != HWC2::Error::None) {
+ DLOGE("Failed to get active config!");
+ return false;
+ }
+
+ return IsSameGroup(active_config, config);
+}
+
} // namespace sdm
diff --git a/sdm/libs/hwc2/hwc_display.h b/sdm/libs/hwc2/hwc_display.h
index ee060e9..96b3f65 100644
--- a/sdm/libs/hwc2/hwc_display.h
+++ b/sdm/libs/hwc2/hwc_display.h
@@ -65,6 +65,11 @@
kSecureMax,
};
+struct TransientRefreshRateInfo {
+ uint32_t transient_vsync_period;
+ int64_t vsync_applied_time;
+};
+
class HWCColorMode {
public:
explicit HWCColorMode(DisplayInterface *display_intf);
@@ -244,6 +249,7 @@
return HWC2::Error::Unsupported;
}
bool IsFirstCommitDone() { return !first_cycle_; }
+ virtual void ProcessActiveConfigChange();
// HWC2 APIs
virtual HWC2::Error AcceptDisplayChanges(void);
@@ -345,6 +351,10 @@
virtual void SetLayerStack(HWCLayerStack *stack);
virtual void PostPowerMode();
virtual void NotifyClientStatus(bool connected) { client_connected_ = connected; }
+ virtual HWC2::Error GetDisplayVsyncPeriod(hwc2_vsync_period_t *vsync_period);
+ virtual HWC2::Error SetActiveConfigWithConstraints(hwc2_config_t config,
+ hwc_vsync_period_change_constraints_t *vsync_period_change_constraints,
+ hwc_vsync_period_change_timeline_t *out_timeline);
protected:
static uint32_t throttling_refresh_rate_;
@@ -377,6 +387,19 @@
virtual void GetUnderScanConfig() { }
int32_t SetClientTargetDataSpace(int32_t dataspace);
int32_t GetDisplayConfigGroupId(const DisplayConfigGroupInfo &variable_config);
+ HWC2::Error GetVsyncPeriodByActiveConfig(hwc2_vsync_period_t *vsync_period);
+ bool GetTransientVsyncPeriod(hwc2_vsync_period_t *vsync_period);
+ std::tuple<int64_t, int64_t> RequestActiveConfigChange(hwc2_config_t config,
+ hwc2_vsync_period_t current_vsync_period,
+ int64_t desired_time);
+ std::tuple<int64_t, int64_t> EstimateVsyncPeriodChangeTimeline(
+ hwc2_vsync_period_t current_vsync_period, int64_t desired_time);
+ void SubmitActiveConfigChange(hwc2_vsync_period_t current_vsync_period);
+ bool IsActiveConfigReadyToSubmit(int64_t time);
+ bool IsActiveConfigApplied(int64_t time, int64_t vsync_applied_time);
+ bool IsSameGroup(hwc2_config_t config_id1, hwc2_config_t config_id2);
+ bool AllowSeamless(hwc2_config_t request_config);
+ void SetVsyncsApplyRateChange(uint32_t vsyncs) { vsyncs_to_apply_rate_change_ = vsyncs; }
enum {
INPUT_LAYER_DUMP,
@@ -437,6 +460,12 @@
bool fast_path_composition_ = false;
bool client_connected_ = true;
bool pending_config_ = false;
+ uint32_t vsyncs_to_apply_rate_change_ = 1;
+ hwc2_config_t pending_refresh_rate_config_ = UINT_MAX;
+ int64_t pending_refresh_rate_refresh_time_ = INT64_MAX;
+ int64_t pending_refresh_rate_applied_time_ = INT64_MAX;
+ std::deque<TransientRefreshRateInfo> transient_refresh_rate_info_;
+ std::mutex transient_refresh_rate_lock_;
private:
void DumpInputBuffers(void);
diff --git a/sdm/libs/hwc2/hwc_display_builtin.cpp b/sdm/libs/hwc2/hwc_display_builtin.cpp
index c85b772..aa65ba7 100644
--- a/sdm/libs/hwc2/hwc_display_builtin.cpp
+++ b/sdm/libs/hwc2/hwc_display_builtin.cpp
@@ -172,6 +172,13 @@
if (enable_optimize_refresh_) {
DLOGI("Drop redundant drawcycles %d", id_);
}
+
+ int vsyncs = 0;
+ HWCDebugHandler::Get()->GetProperty(DEFER_FPS_FRAME_COUNT, &vsyncs);
+ if (vsyncs > 0) {
+ SetVsyncsApplyRateChange(UINT32(vsyncs));
+ }
+
pmic_intf_ = new PMICInterface();
pmic_intf_->Init();
diff --git a/sdm/libs/hwc2/hwc_session.cpp b/sdm/libs/hwc2/hwc_session.cpp
index 9e2d8d6..108ee32 100644
--- a/sdm/libs/hwc2/hwc_session.cpp
+++ b/sdm/libs/hwc2/hwc_session.cpp
@@ -776,6 +776,7 @@
if (power_on_pending_[display]) {
status = HWC2::Error::None;
} else {
+ hwc_session->hwc_display_[display]->ProcessActiveConfigChange();
status = hwc_session->PresentDisplayInternal(display, out_retire_fence);
if (status == HWC2::Error::None) {
// Check if hwc's refresh trigger is getting exercised.
@@ -1313,6 +1314,11 @@
return AsFP<HWC2_PFN_SET_DISPLAY_BRIGHTNESS>(HWCSession::SetDisplayBrightness);
case HWC2::FunctionDescriptor::GetDisplayConnectionType:
return AsFP<HWC2_PFN_GET_DISPLAY_CONNECTION_TYPE>(HWCSession::GetDisplayConnectionType);
+ case HWC2::FunctionDescriptor::GetDisplayVsyncPeriod:
+ return AsFP<HWC2_PFN_GET_DISPLAY_VSYNC_PERIOD>(HWCSession::GetDisplayVsyncPeriod);
+ case HWC2::FunctionDescriptor::SetActiveConfigWithConstraints:
+ return AsFP<HWC2_PFN_SET_ACTIVE_CONFIG_WITH_CONSTRAINTS>
+ (HWCSession::SetActiveConfigWithConstraints);
default:
DLOGD("Unknown/Unimplemented function descriptor: %d (%s)", int_descriptor,
to_string(descriptor).c_str());
@@ -3332,6 +3338,26 @@
return HWC2_ERROR_NONE;
}
+int32_t HWCSession::GetDisplayVsyncPeriod(hwc2_device_t *device, hwc2_display_t display,
+ hwc2_vsync_period_t *out_vsync_period) {
+ if (!out_vsync_period || !device) {
+ return HWC2_ERROR_BAD_PARAMETER;
+ }
+
+ return CallDisplayFunction(device, display, &HWCDisplay::GetDisplayVsyncPeriod, out_vsync_period);
+}
+
+int32_t HWCSession::SetActiveConfigWithConstraints(hwc2_device_t *device, hwc2_display_t display,
+ hwc2_config_t config,
+ hwc_vsync_period_change_constraints_t *vsync_period_change_constraints,
+ hwc_vsync_period_change_timeline_t *out_timeline) {
+ if (!vsync_period_change_constraints || !out_timeline || !device) {
+ return HWC2_ERROR_BAD_PARAMETER;
+ }
+
+ return CallDisplayFunction(device, display, &HWCDisplay::SetActiveConfigWithConstraints, config,
+ vsync_period_change_constraints, out_timeline);
+}
int32_t HWCSession::GetDisplayBrightnessSupport(hwc2_device_t *device, hwc2_display_t display,
bool *outSupport) {
diff --git a/sdm/libs/hwc2/hwc_session.h b/sdm/libs/hwc2/hwc_session.h
index e0d9821..f056290 100644
--- a/sdm/libs/hwc2/hwc_session.h
+++ b/sdm/libs/hwc2/hwc_session.h
@@ -212,6 +212,12 @@
float brightness);
static int32_t GetDisplayConnectionType(hwc2_device_t *device, hwc2_display_t display,
uint32_t *outType);
+ static int32_t GetDisplayVsyncPeriod(hwc2_device_t *device, hwc2_display_t display,
+ hwc2_vsync_period_t *out_vsync_period);
+ static int32_t SetActiveConfigWithConstraints(hwc2_device_t *device, hwc2_display_t display,
+ hwc2_config_t config,
+ hwc_vsync_period_change_constraints_t *vsync_period_change_constraints,
+ hwc_vsync_period_change_timeline_t *out_timeline);
// HWCDisplayEventHandler
virtual void DisplayPowerReset();