Variable refresh rate for virtual display
Allow virtual display to request a refresh rate, so it can be different
from VSYNC frequencies. SurfaceFlinger drops frames for the
corresponding virtual display based on the refresh rate.
Bug: 241286579
Test: atest libgui_test libsurfaceflinger_unittest SurfaceFlinger_test
Change-Id: I4fba0e553618bb4c7333514b16206ae4277acf72
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index cf9828b..9092f5f 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -1182,12 +1182,14 @@
}
// ---------------------------------------------------------------------------
-sp<IBinder> SurfaceComposerClient::createDisplay(const String8& displayName, bool secure) {
+sp<IBinder> SurfaceComposerClient::createDisplay(const String8& displayName, bool secure,
+ float requestedRefereshRate) {
sp<IBinder> display = nullptr;
binder::Status status =
ComposerServiceAIDL::getComposerService()->createDisplay(std::string(
displayName.string()),
- secure, &display);
+ secure, requestedRefereshRate,
+ &display);
return status.isOk() ? display : nullptr;
}
diff --git a/libs/gui/aidl/android/gui/ISurfaceComposer.aidl b/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
index c08a7c6..597749a 100644
--- a/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
+++ b/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
@@ -79,9 +79,21 @@
/**
* Create a virtual display
+ *
+ * displayName
+ * The name of the virtual display
+ * secure
+ * Whether this virtual display is secure
+ * requestedRefreshRate
+ * The refresh rate, frames per second, to request on the virtual display.
+ * This is just a request, the actual rate may be adjusted to align well
+ * with physical displays running concurrently. If 0 is specified, the
+ * virtual display is refreshed at the physical display refresh rate.
+ *
* requires ACCESS_SURFACE_FLINGER permission.
*/
- @nullable IBinder createDisplay(@utf8InCpp String displayName, boolean secure);
+ @nullable IBinder createDisplay(@utf8InCpp String displayName, boolean secure,
+ float requestedRefreshRate);
/**
* Destroy a virtual display
diff --git a/libs/gui/fuzzer/libgui_fuzzer_utils.h b/libs/gui/fuzzer/libgui_fuzzer_utils.h
index 685bd92..14a0e39 100644
--- a/libs/gui/fuzzer/libgui_fuzzer_utils.h
+++ b/libs/gui/fuzzer/libgui_fuzzer_utils.h
@@ -67,7 +67,7 @@
sp<gui::IDisplayEventConnection>*),
(override));
MOCK_METHOD(binder::Status, createConnection, (sp<gui::ISurfaceComposerClient>*), (override));
- MOCK_METHOD(binder::Status, createDisplay, (const std::string&, bool, sp<IBinder>*),
+ MOCK_METHOD(binder::Status, createDisplay, (const std::string&, bool, float, sp<IBinder>*),
(override));
MOCK_METHOD(binder::Status, destroyDisplay, (const sp<IBinder>&), (override));
MOCK_METHOD(binder::Status, getPhysicalDisplayIds, (std::vector<int64_t>*), (override));
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index c5f59c8..45f4dbe 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -355,7 +355,8 @@
sp<SurfaceControl> mirrorDisplay(DisplayId displayId);
//! Create a virtual display
- static sp<IBinder> createDisplay(const String8& displayName, bool secure);
+ static sp<IBinder> createDisplay(const String8& displayName, bool secure,
+ float requestedRefereshRate = 0);
//! Destroy a virtual display
static void destroyDisplay(const sp<IBinder>& display);
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 32d60cd..9b2bf7f 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -736,6 +736,7 @@
}
binder::Status createDisplay(const std::string& /*displayName*/, bool /*secure*/,
+ float /*requestedRefreshRate*/,
sp<IBinder>* /*outDisplay*/) override {
return binder::Status::ok();
}
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index c61f7d8..ba8b6cf 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -71,6 +71,7 @@
mRenderFrameRateFPSTrace("RenderRateFPS -" + to_string(getId())),
mPhysicalOrientation(args.physicalOrientation),
mIsPrimary(args.isPrimary),
+ mRequestedRefreshRate(args.requestedRefreshRate),
mRefreshRateSelector(std::move(args.refreshRateSelector)) {
mCompositionDisplay->editState().isSecure = args.isSecure;
mCompositionDisplay->createRenderSurface(
@@ -513,6 +514,23 @@
mDesiredActiveModeChanged = false;
}
+void DisplayDevice::adjustRefreshRate(Fps leaderDisplayRefreshRate) {
+ using fps_approx_ops::operator==;
+ if (mRequestedRefreshRate == 0_Hz) {
+ return;
+ }
+
+ using fps_approx_ops::operator>;
+ if (mRequestedRefreshRate > leaderDisplayRefreshRate) {
+ mAdjustedRefreshRate = leaderDisplayRefreshRate;
+ return;
+ }
+
+ unsigned divisor = static_cast<unsigned>(
+ std::round(leaderDisplayRefreshRate.getValue() / mRequestedRefreshRate.getValue()));
+ mAdjustedRefreshRate = leaderDisplayRefreshRate / divisor;
+}
+
std::atomic<int32_t> DisplayDeviceState::sNextSequenceId(1);
} // namespace android
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index 370bd66..59f7a30 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -245,6 +245,12 @@
nsecs_t getVsyncPeriodFromHWC() const;
+ Fps getAdjustedRefreshRate() const { return mAdjustedRefreshRate; }
+
+ // Round the requested refresh rate to match a divisor of the leader
+ // display's refresh rate. Only supported for virtual displays.
+ void adjustRefreshRate(Fps leaderDisplayRefreshRate);
+
// release HWC resources (if any) for removable displays
void disconnect();
@@ -279,6 +285,15 @@
uint32_t mFlags = 0;
+ // Requested refresh rate in fps, supported only for virtual displays.
+ // when this value is non zero, SurfaceFlinger will try to drop frames
+ // for virtual displays to match this requested refresh rate.
+ const Fps mRequestedRefreshRate;
+
+ // Adjusted refresh rate, rounded to match a divisor of the leader
+ // display's refresh rate. Only supported for virtual displays.
+ Fps mAdjustedRefreshRate = 0_Hz;
+
std::vector<ui::Hdr> mOverrideHdrTypes;
std::shared_ptr<scheduler::RefreshRateSelector> mRefreshRateSelector;
@@ -316,6 +331,8 @@
uint32_t height = 0;
std::string displayName;
bool isSecure = false;
+ // Refer to DisplayDevice::mRequestedRefreshRate, for virtual display only
+ Fps requestedRefreshRate;
private:
static std::atomic<int32_t> sNextSequenceId;
@@ -345,6 +362,8 @@
std::optional<hardware::graphics::composer::hal::PowerMode> initialPowerMode;
bool isPrimary{false};
DisplayModeId activeModeId;
+ // Refer to DisplayDevice::mRequestedRefreshRate, for virtual display only
+ Fps requestedRefreshRate;
};
} // namespace android
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 1fc1519..fa95685 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -166,6 +166,10 @@
return mVsyncSchedule->getTracker().isVSyncInPhase(expectedVsyncTimestamp.ns(), *frameRate);
}
+bool Scheduler::isVsyncInPhase(TimePoint timePoint, const Fps frameRate) const {
+ return mVsyncSchedule->getTracker().isVSyncInPhase(timePoint.ns(), frameRate);
+}
+
impl::EventThread::ThrottleVsyncCallback Scheduler::makeThrottleVsyncCallback() const {
return [this](nsecs_t expectedVsyncTimestamp, uid_t uid) {
return !isVsyncValid(TimePoint::fromNs(expectedVsyncTimestamp), uid);
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index ef7d0cf..e822448 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -231,6 +231,9 @@
// for a given uid
bool isVsyncValid(TimePoint expectedVsyncTimestamp, uid_t uid) const;
+ // Checks if a vsync timestamp is in phase for a frame rate
+ bool isVsyncInPhase(TimePoint timePoint, const Fps frameRate) const;
+
void dump(utils::Dumper&) const;
void dump(ConnectionHandle, std::string&) const;
void dumpVsync(std::string&) const;
@@ -262,6 +265,10 @@
return leaderSelectorPtr()->getActiveMode().fps.getPeriod();
}
+ Fps getLeaderRefreshRate() const EXCLUDES(mDisplayLock) {
+ return leaderSelectorPtr()->getActiveMode().fps;
+ }
+
// Returns the framerate of the layer with the given sequence ID
float getLayerFramerate(nsecs_t now, int32_t id) const {
return mLayerHistory.getLayerFramerate(now, id);
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 7d0dc93..fe4ae02 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -512,7 +512,8 @@
mScheduler->run();
}
-sp<IBinder> SurfaceFlinger::createDisplay(const String8& displayName, bool secure) {
+sp<IBinder> SurfaceFlinger::createDisplay(const String8& displayName, bool secure,
+ float requestedRefreshRate) {
// onTransact already checks for some permissions, but adding an additional check here.
// This is to ensure that only system and graphics can request to create a secure
// display. Secure displays can show secure content so we add an additional restriction on it.
@@ -543,6 +544,7 @@
DisplayDeviceState state;
state.isSecure = secure;
state.displayName = displayName;
+ state.requestedRefreshRate = Fps::fromValue(requestedRefreshRate);
mCurrentState.displays.add(token, state);
return token;
}
@@ -2343,7 +2345,15 @@
refreshArgs.outputs.reserve(displays.size());
std::vector<DisplayId> displayIds;
for (const auto& [_, display] : displays) {
- refreshArgs.outputs.push_back(display->getCompositionDisplay());
+ bool dropFrame = false;
+ if (display->isVirtual()) {
+ Fps refreshRate = display->getAdjustedRefreshRate();
+ using fps_approx_ops::operator>;
+ dropFrame = (refreshRate > 0_Hz) && !mScheduler->isVsyncInPhase(frameTime, refreshRate);
+ }
+ if (!dropFrame) {
+ refreshArgs.outputs.push_back(display->getCompositionDisplay());
+ }
displayIds.push_back(display->getId());
}
mPowerAdvisor->setDisplays(displayIds);
@@ -3082,6 +3092,8 @@
creationArgs.initialPowerMode =
state.isVirtual() ? std::make_optional(hal::PowerMode::ON) : std::nullopt;
+ creationArgs.requestedRefreshRate = state.requestedRefreshRate;
+
sp<DisplayDevice> display = getFactory().createDisplayDevice(creationArgs);
nativeWindowSurface->preallocateBuffers();
@@ -3198,6 +3210,10 @@
dispatchDisplayHotplugEvent(displayId, true);
}
+ if (display->isVirtual()) {
+ display->adjustRefreshRate(mScheduler->getLeaderRefreshRate());
+ }
+
mDisplays.try_emplace(displayToken, std::move(display));
}
@@ -7448,13 +7464,14 @@
}
binder::Status SurfaceComposerAIDL::createDisplay(const std::string& displayName, bool secure,
+ float requestedRefreshRate,
sp<IBinder>* outDisplay) {
status_t status = checkAccessPermission();
if (status != OK) {
return binderStatusFromStatusT(status);
}
String8 displayName8 = String8::format("%s", displayName.c_str());
- *outDisplay = mFlinger->createDisplay(displayName8, secure);
+ *outDisplay = mFlinger->createDisplay(displayName8, secure, requestedRefreshRate);
return binder::Status::ok();
}
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 207dfe2..170af48 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -483,7 +483,8 @@
EXCLUDES(mStateLock);
// Implements ISurfaceComposer
- sp<IBinder> createDisplay(const String8& displayName, bool secure);
+ sp<IBinder> createDisplay(const String8& displayName, bool secure,
+ float requestedRefreshRate = 0);
void destroyDisplay(const sp<IBinder>& displayToken);
std::vector<PhysicalDisplayId> getPhysicalDisplayIds() const EXCLUDES(mStateLock) {
Mutex::Autolock lock(mStateLock);
@@ -1403,7 +1404,7 @@
sp<gui::IDisplayEventConnection>* outConnection) override;
binder::Status createConnection(sp<gui::ISurfaceComposerClient>* outClient) override;
binder::Status createDisplay(const std::string& displayName, bool secure,
- sp<IBinder>* outDisplay) override;
+ float requestedRefreshRate, sp<IBinder>* outDisplay) override;
binder::Status destroyDisplay(const sp<IBinder>& display) override;
binder::Status getPhysicalDisplayIds(std::vector<int64_t>* outDisplayIds) override;
binder::Status getPhysicalDisplayToken(int64_t displayId, sp<IBinder>* outDisplay) override;