drm_hwcomposer: Support DisplayCommand.expectedPresentTime
DisplayCommand.expectedPresentTime indicates that the HWC should present
the composition as close to the desired time as possible. It may be that
the desired time is some number of vsync periods in the future.
A drm atomic commit will always present at the next vsync, and there is
no mechanism for userspace to indicate a desired present time.
Lacking a kernel uAPI to support this, an alternative is to sleep in
userspace until the next expected vsync aligns with the expected present
time.
Change-Id: Iad703eed9b0e459194f895339dd604990875f157
Signed-off-by: Drew Davenport <ddavenport@google.com>
diff --git a/hwc2_device/HwcDisplay.cpp b/hwc2_device/HwcDisplay.cpp
index 82f826b..abc15fd 100644
--- a/hwc2_device/HwcDisplay.cpp
+++ b/hwc2_device/HwcDisplay.cpp
@@ -336,8 +336,8 @@
}
auto HwcDisplay::PresentStagedComposition(
- SharedFd &out_present_fence, std::vector<ReleaseFence> &out_release_fences)
- -> bool {
+ std::optional<int64_t> desired_present_time, SharedFd &out_present_fence,
+ std::vector<ReleaseFence> &out_release_fences) -> bool {
if (IsInHeadlessMode()) {
return true;
}
@@ -345,6 +345,17 @@
++total_stats_.total_frames_;
+ uint32_t vperiod_ns = 0;
+ GetDisplayVsyncPeriod(&vperiod_ns);
+
+ if (desired_present_time && vperiod_ns != 0) {
+ // DRM atomic uAPI does not support specifying that a commit should be
+ // applied to some future vsync. Until such uAPI is available, sleep in
+ // userspace until the next expected vsync time is consistent with the
+ // desired present time.
+ WaitForPresentTime(desired_present_time.value(), vperiod_ns);
+ }
+
AtomicCommitArgs a_args{};
ret = CreateComposition(a_args);
@@ -705,6 +716,39 @@
return args;
}
+void HwcDisplay::WaitForPresentTime(int64_t present_time,
+ uint32_t vsync_period_ns) {
+ const int64_t current_time = ResourceManager::GetTimeMonotonicNs();
+ int64_t next_vsync_time = vsync_worker_->GetNextVsyncTimestamp(current_time);
+
+ int64_t vsync_after_present_time = vsync_worker_->GetNextVsyncTimestamp(
+ present_time);
+ int64_t vsync_before_present_time = vsync_after_present_time -
+ vsync_period_ns;
+
+ // Check if |present_time| is closer to the expected vsync before or after.
+ int64_t desired_vsync = (vsync_after_present_time - present_time) <
+ (present_time - vsync_before_present_time)
+ ? vsync_after_present_time
+ : vsync_before_present_time;
+
+ // Don't sleep if desired_vsync is before or nearly equal to vsync_period of
+ // the next expected vsync.
+ const int64_t quarter_vsync_period = vsync_period_ns / 4;
+ if ((desired_vsync - next_vsync_time) < quarter_vsync_period) {
+ return;
+ }
+
+ // Sleep until 75% vsync_period before the desired_vsync.
+ int64_t sleep_until = desired_vsync - (quarter_vsync_period * 3);
+ struct timespec sleep_until_ts{};
+ constexpr int64_t kOneSecondNs = 1LL * 1000 * 1000 * 1000;
+ sleep_until_ts.tv_sec = int(sleep_until / kOneSecondNs);
+ sleep_until_ts.tv_nsec = int(sleep_until -
+ (sleep_until_ts.tv_sec * kOneSecondNs));
+ clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &sleep_until_ts, nullptr);
+}
+
// NOLINTNEXTLINE(readability-function-cognitive-complexity)
HWC2::Error HwcDisplay::CreateComposition(AtomicCommitArgs &a_args) {
if (IsInHeadlessMode()) {
diff --git a/hwc2_device/HwcDisplay.h b/hwc2_device/HwcDisplay.h
index 069a6ee..d0b4a47 100644
--- a/hwc2_device/HwcDisplay.h
+++ b/hwc2_device/HwcDisplay.h
@@ -97,7 +97,7 @@
auto GetConfig(hwc2_config_t config_id) const -> const HwcDisplayConfig *;
auto GetDisplayBoundsMm() -> std::pair<int32_t, int32_t>;
-
+
// To be called after SetDisplayProperties. Returns an empty vector if the
// requested layers have been validated, otherwise the vector describes
// the requested composition type changes.
@@ -107,11 +107,14 @@
// Mark previously validated properties as ready to present.
auto AcceptValidatedComposition() -> void;
+ using ReleaseFence = std::pair<ILayerId, SharedFd>;
// Present previously staged properties, and return fences to indicate when
// the new content has been presented, and when the previous buffers have
- // been released.
- using ReleaseFence = std::pair<ILayerId, SharedFd>;
- auto PresentStagedComposition(SharedFd &out_present_fence,
+ // been released. If |desired_present_time| is set, ensure that the
+ // composition is presented at the closest vsync to that requested time.
+ // Otherwise, present immediately.
+ auto PresentStagedComposition(std::optional<int64_t> desired_present_time,
+ SharedFd &out_present_fence,
std::vector<ReleaseFence> &out_release_fences)
-> bool;
@@ -254,6 +257,10 @@
const HwcDisplayConfig *config,
const std::optional<LayerData> &modeset_layer);
+ // Sleep the current thread until |present_time| is closest to the next
+ // expected vsync time.
+ void WaitForPresentTime(int64_t present_time, uint32_t vsync_period_ns);
+
HwcDisplayConfigs configs_;
DrmHwc *const hwc_;
diff --git a/hwc2_device/hwc2_device.cpp b/hwc2_device/hwc2_device.cpp
index c1c5157..7c9d806 100644
--- a/hwc2_device/hwc2_device.cpp
+++ b/hwc2_device/hwc2_device.cpp
@@ -501,7 +501,7 @@
hwc2display->release_fences.clear();
- if (!idisplay->PresentStagedComposition(out_fence,
+ if (!idisplay->PresentStagedComposition(std::nullopt, out_fence,
hwc2display->release_fences)) {
ALOGE("Failed to present display");
return static_cast<int32_t>(HWC2::Error::BadDisplay);
diff --git a/hwc3/ComposerClient.cpp b/hwc3/ComposerClient.cpp
index 4dafb48..368d5eb 100644
--- a/hwc3/ComposerClient.cpp
+++ b/hwc3/ComposerClient.cpp
@@ -152,6 +152,14 @@
return AidlToSampleRange(dataspace->dataspace);
}
+std::optional<int64_t> AidlToPresentTimeNs(
+ const std::optional<ClockMonotonicTimestamp>& expected_present_time) {
+ if (!expected_present_time || expected_present_time->timestampNanos == 0) {
+ return std::nullopt;
+ }
+ return expected_present_time->timestampNanos;
+}
+
bool IsSupportedCompositionType(
const std::optional<ParcelableComposition> composition) {
if (!composition) {
@@ -725,6 +733,8 @@
cmd_result_writer_->AddChanges(changes);
auto hwc3_display = DrmHwcThree::GetHwc3Display(*display);
hwc3_display->must_validate = false;
+ hwc3_display->desired_present_time = AidlToPresentTimeNs(
+ command.expectedPresentTime);
// TODO: DisplayRequests are not implemented.
}
@@ -749,9 +759,12 @@
cmd_result_writer_->AddError(hwc3::Error::kNotValidated);
return;
}
+
::android::SharedFd present_fence;
std::vector<HwcDisplay::ReleaseFence> release_fences;
- bool ret = display->PresentStagedComposition(present_fence, release_fences);
+ bool ret = display->PresentStagedComposition(hwc3_display
+ ->desired_present_time,
+ present_fence, release_fences);
if (!ret) {
cmd_result_writer_->AddError(hwc3::Error::kBadDisplay);
diff --git a/hwc3/DrmHwcThree.h b/hwc3/DrmHwcThree.h
index 10470ea..89bcf46 100644
--- a/hwc3/DrmHwcThree.h
+++ b/hwc3/DrmHwcThree.h
@@ -26,6 +26,9 @@
class Hwc3Display : public ::android::FrontendDisplayBase {
public:
bool must_validate = false;
+ // Desired present time for a composition that has been validated but not
+ // yet presented. nullopt means it should be presented at the next vsync.
+ std::optional<int64_t> desired_present_time = std::nullopt;
int64_t next_layer_id = 1;
};