Merge changes I0f0307dc,I3ae3d407 into main
* changes:
CursorInputMapper: move unit tests to InputMapperUnitTest
InputMapperUnitTest: extract device creation from SetUp
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 83b336c..b99f443 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -178,6 +178,7 @@
#define PROFILE_DATA_DIR_CUR "/data/misc/profiles/cur"
#define PROFILE_DATA_DIR_REF "/data/misc/profiles/ref"
#define XFRM_STAT_PROC_FILE "/proc/net/xfrm_stat"
+#define KERNEL_CONFIG "/proc/config.gz"
#define WLUTIL "/vendor/xbin/wlutil"
#define WMTRACE_DATA_DIR "/data/misc/wmtrace"
#define OTA_METADATA_DIR "/metadata/ota"
@@ -1951,6 +1952,8 @@
DumpFile("PSI memory", "/proc/pressure/memory");
DumpFile("PSI io", "/proc/pressure/io");
+ ds.AddZipEntry(ZIP_ROOT_DIR + KERNEL_CONFIG, KERNEL_CONFIG);
+
RunCommand("SDK EXTENSIONS", {SDK_EXT_INFO, "--dump"},
CommandOptions::WithTimeout(10).Always().DropRoot().Build());
diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp
index 041c280..dd2be94 100644
--- a/libs/binder/tests/Android.bp
+++ b/libs/binder/tests/Android.bp
@@ -836,6 +836,7 @@
],
// Adds bugs to hotlist "AIDL fuzzers bugs" on buganizer
hotlists: ["4637097"],
+ use_for_presubmit: true,
},
}
diff --git a/libs/renderengine/threaded/RenderEngineThreaded.cpp b/libs/renderengine/threaded/RenderEngineThreaded.cpp
index 367bee8..f58f543 100644
--- a/libs/renderengine/threaded/RenderEngineThreaded.cpp
+++ b/libs/renderengine/threaded/RenderEngineThreaded.cpp
@@ -125,8 +125,10 @@
}
void RenderEngineThreaded::waitUntilInitialized() const {
- std::unique_lock<std::mutex> lock(mInitializedMutex);
- mInitializedCondition.wait(lock, [=] { return mIsInitialized; });
+ if (!mIsInitialized) {
+ std::unique_lock<std::mutex> lock(mInitializedMutex);
+ mInitializedCondition.wait(lock, [this] { return mIsInitialized.load(); });
+ }
}
std::future<void> RenderEngineThreaded::primeCache(bool shouldPrimeUltraHDR) {
diff --git a/libs/renderengine/threaded/RenderEngineThreaded.h b/libs/renderengine/threaded/RenderEngineThreaded.h
index 74af2bd..3f1e67f 100644
--- a/libs/renderengine/threaded/RenderEngineThreaded.h
+++ b/libs/renderengine/threaded/RenderEngineThreaded.h
@@ -97,7 +97,7 @@
// Used to allow select thread safe methods to be accessed without requiring the
// method to be invoked on the RenderEngine thread
- bool mIsInitialized = false;
+ std::atomic_bool mIsInitialized = false;
mutable std::mutex mInitializedMutex;
mutable std::condition_variable mInitializedCondition;
diff --git a/libs/sensor/SensorManager.cpp b/libs/sensor/SensorManager.cpp
index d112a12..f8ee3fc 100644
--- a/libs/sensor/SensorManager.cpp
+++ b/libs/sensor/SensorManager.cpp
@@ -248,6 +248,22 @@
return static_cast<ssize_t>(mSensors.size());
}
+ssize_t SensorManager::getDefaultDeviceSensorList(Vector<Sensor> & list) {
+ Mutex::Autolock _l(mLock);
+ status_t err = assertStateLocked();
+ if (err < 0) {
+ return static_cast<ssize_t>(err);
+ }
+
+ if (mDeviceId == DEVICE_ID_DEFAULT) {
+ list = mSensors;
+ } else {
+ list = mSensorServer->getSensorList(mOpPackageName);
+ }
+
+ return static_cast<ssize_t>(list.size());
+}
+
ssize_t SensorManager::getDynamicSensorList(Vector<Sensor> & dynamicSensors) {
Mutex::Autolock _l(mLock);
status_t err = assertStateLocked();
diff --git a/libs/sensor/include/sensor/SensorManager.h b/libs/sensor/include/sensor/SensorManager.h
index e67fac7..49f050a 100644
--- a/libs/sensor/include/sensor/SensorManager.h
+++ b/libs/sensor/include/sensor/SensorManager.h
@@ -58,6 +58,7 @@
~SensorManager();
ssize_t getSensorList(Sensor const* const** list);
+ ssize_t getDefaultDeviceSensorList(Vector<Sensor> & list);
ssize_t getDynamicSensorList(Vector<Sensor>& list);
ssize_t getDynamicSensorList(Sensor const* const** list);
ssize_t getRuntimeSensorList(int deviceId, Vector<Sensor>& list);
diff --git a/opengl/tests/gldual/AndroidManifest.xml b/opengl/tests/gldual/AndroidManifest.xml
index a36f4f7..d6335b0 100644
--- a/opengl/tests/gldual/AndroidManifest.xml
+++ b/opengl/tests/gldual/AndroidManifest.xml
@@ -20,8 +20,9 @@
android:label="@string/gldual_activity">
<activity android:name="GLDualActivity"
android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
- android:launchMode="singleTask"
- android:configChanges="orientation|keyboardHidden">
+ android:launchMode="singleTask"
+ android:configChanges="orientation|keyboardHidden"
+ android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
diff --git a/services/inputflinger/InputFilter.cpp b/services/inputflinger/InputFilter.cpp
index 5d87d34..43ebc69 100644
--- a/services/inputflinger/InputFilter.cpp
+++ b/services/inputflinger/InputFilter.cpp
@@ -52,15 +52,15 @@
}
void InputFilter::notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) {
+ mDeviceInfos.clear();
+ mDeviceInfos.reserve(args.inputDeviceInfos.size());
+ for (auto info : args.inputDeviceInfos) {
+ AidlDeviceInfo& aidlInfo = mDeviceInfos.emplace_back();
+ aidlInfo.deviceId = info.getId();
+ aidlInfo.external = info.isExternal();
+ }
if (isFilterEnabled()) {
- std::vector<AidlDeviceInfo> deviceInfos;
- for (auto info : args.inputDeviceInfos) {
- AidlDeviceInfo aidlInfo;
- aidlInfo.deviceId = info.getId();
- aidlInfo.external = info.isExternal();
- deviceInfos.push_back(aidlInfo);
- }
- LOG_ALWAYS_FATAL_IF(!mInputFilterRust->notifyInputDevicesChanged(deviceInfos).isOk());
+ LOG_ALWAYS_FATAL_IF(!mInputFilterRust->notifyInputDevicesChanged(mDeviceInfos).isOk());
}
mNextListener.notify(args);
}
@@ -74,7 +74,7 @@
LOG_ALWAYS_FATAL_IF(!mInputFilterRust->notifyKey(notifyKeyArgsToKeyEvent(args)).isOk());
return;
}
- mNextListener.notifyKey(args);
+ mNextListener.notify(args);
}
void InputFilter::notifyMotion(const NotifyMotionArgs& args) {
@@ -112,7 +112,7 @@
if (mConfig.bounceKeysThresholdNs != threshold) {
mConfig.bounceKeysThresholdNs = threshold;
- LOG_ALWAYS_FATAL_IF(!mInputFilterRust->notifyConfigurationChanged(mConfig).isOk());
+ notifyConfigurationChangedLocked();
}
}
@@ -121,7 +121,14 @@
if (mConfig.stickyKeysEnabled != enabled) {
mConfig.stickyKeysEnabled = enabled;
- LOG_ALWAYS_FATAL_IF(!mInputFilterRust->notifyConfigurationChanged(mConfig).isOk());
+ notifyConfigurationChangedLocked();
+ }
+}
+
+void InputFilter::notifyConfigurationChangedLocked() {
+ LOG_ALWAYS_FATAL_IF(!mInputFilterRust->notifyConfigurationChanged(mConfig).isOk());
+ if (isFilterEnabled()) {
+ LOG_ALWAYS_FATAL_IF(!mInputFilterRust->notifyInputDevicesChanged(mDeviceInfos).isOk());
}
}
diff --git a/services/inputflinger/InputFilter.h b/services/inputflinger/InputFilter.h
index 9fa7a87..a38fbf6 100644
--- a/services/inputflinger/InputFilter.h
+++ b/services/inputflinger/InputFilter.h
@@ -45,6 +45,7 @@
aidl::com::android::server::inputflinger::IInputFilter::IInputFilterCallbacks;
using InputFilterConfiguration =
aidl::com::android::server::inputflinger::InputFilterConfiguration;
+ using AidlDeviceInfo = aidl::com::android::server::inputflinger::DeviceInfo;
explicit InputFilter(InputListenerInterface& listener, IInputFlingerRust&);
~InputFilter() override = default;
@@ -65,10 +66,14 @@
InputListenerInterface& mNextListener;
std::shared_ptr<InputFilterCallbacks> mCallbacks;
std::shared_ptr<IInputFilter> mInputFilterRust;
+ // Keep track of connected peripherals, so that if filters are enabled later, we can pass that
+ // info to the filters
+ std::vector<AidlDeviceInfo> mDeviceInfos;
mutable std::mutex mLock;
InputFilterConfiguration mConfig GUARDED_BY(mLock);
bool isFilterEnabled();
+ void notifyConfigurationChangedLocked() REQUIRES(mLock);
};
} // namespace android
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 8d0ff4b..ab815ea 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -1121,7 +1121,10 @@
}
}
if (dropReason == DropReason::NOT_DROPPED && mNextUnblockedEvent) {
- dropReason = DropReason::BLOCKED;
+ if (!isFromSource(motionEntry->source, AINPUT_SOURCE_CLASS_POINTER)) {
+ // Only drop events that are focus-dispatched.
+ dropReason = DropReason::BLOCKED;
+ }
}
done = dispatchMotionLocked(currentTime, motionEntry, &dropReason, nextWakeupTime);
break;
@@ -1392,8 +1395,9 @@
reason = "inbound event was dropped because of pending overdue app switch";
break;
case DropReason::BLOCKED:
- ALOGI("Dropped event because the current application is not responding and the user "
- "has started interacting with a different application.");
+ LOG(INFO) << "Dropping because the current application is not responding and the user "
+ "has started interacting with a different application: "
+ << entry.getDescription();
reason = "inbound event was dropped because the current application is not responding "
"and the user has started interacting with a different application";
break;
@@ -1414,6 +1418,9 @@
switch (entry.type) {
case EventEntry::Type::KEY: {
CancelationOptions options(CancelationOptions::Mode::CANCEL_NON_POINTER_EVENTS, reason);
+ const KeyEntry& keyEntry = static_cast<const KeyEntry&>(entry);
+ options.displayId = keyEntry.displayId;
+ options.deviceId = keyEntry.deviceId;
synthesizeCancelationEventsForAllConnectionsLocked(options);
break;
}
@@ -1421,10 +1428,14 @@
const MotionEntry& motionEntry = static_cast<const MotionEntry&>(entry);
if (motionEntry.source & AINPUT_SOURCE_CLASS_POINTER) {
CancelationOptions options(CancelationOptions::Mode::CANCEL_POINTER_EVENTS, reason);
+ options.displayId = motionEntry.displayId;
+ options.deviceId = motionEntry.deviceId;
synthesizeCancelationEventsForAllConnectionsLocked(options);
} else {
CancelationOptions options(CancelationOptions::Mode::CANCEL_NON_POINTER_EVENTS,
reason);
+ options.displayId = motionEntry.displayId;
+ options.deviceId = motionEntry.deviceId;
synthesizeCancelationEventsForAllConnectionsLocked(options);
}
break;
@@ -3389,27 +3400,40 @@
dispatchEntry->resolvedFlags = resolvedFlags;
if (resolvedAction != motionEntry.action) {
+ std::optional<std::vector<PointerProperties>> usingProperties;
+ std::optional<std::vector<PointerCoords>> usingCoords;
+ if (resolvedAction == AMOTION_EVENT_ACTION_HOVER_EXIT ||
+ resolvedAction == AMOTION_EVENT_ACTION_CANCEL) {
+ // This is a HOVER_EXIT or an ACTION_CANCEL event that was synthesized by
+ // the dispatcher, and therefore the coordinates of this event are currently
+ // incorrect. These events should use the coordinates of the last dispatched
+ // ACTION_MOVE or HOVER_MOVE. We need to query InputState to get this data.
+ const bool hovering = resolvedAction == AMOTION_EVENT_ACTION_HOVER_EXIT;
+ std::optional<std::pair<std::vector<PointerProperties>,
+ std::vector<PointerCoords>>>
+ pointerInfo =
+ connection->inputState.getPointersOfLastEvent(motionEntry,
+ hovering);
+ if (pointerInfo) {
+ usingProperties = pointerInfo->first;
+ usingCoords = pointerInfo->second;
+ }
+ }
// Generate a new MotionEntry with a new eventId using the resolved action and
// flags.
- resolvedMotion =
- std::make_shared<MotionEntry>(mIdGenerator.nextId(),
- motionEntry.injectionState,
- motionEntry.eventTime,
- motionEntry.deviceId, motionEntry.source,
- motionEntry.displayId,
- motionEntry.policyFlags, resolvedAction,
- motionEntry.actionButton, resolvedFlags,
- motionEntry.metaState,
- motionEntry.buttonState,
- motionEntry.classification,
- motionEntry.edgeFlags,
- motionEntry.xPrecision,
- motionEntry.yPrecision,
- motionEntry.xCursorPosition,
- motionEntry.yCursorPosition,
- motionEntry.downTime,
- motionEntry.pointerProperties,
- motionEntry.pointerCoords);
+ resolvedMotion = std::make_shared<
+ MotionEntry>(mIdGenerator.nextId(), motionEntry.injectionState,
+ motionEntry.eventTime, motionEntry.deviceId,
+ motionEntry.source, motionEntry.displayId,
+ motionEntry.policyFlags, resolvedAction,
+ motionEntry.actionButton, resolvedFlags,
+ motionEntry.metaState, motionEntry.buttonState,
+ motionEntry.classification, motionEntry.edgeFlags,
+ motionEntry.xPrecision, motionEntry.yPrecision,
+ motionEntry.xCursorPosition, motionEntry.yCursorPosition,
+ motionEntry.downTime,
+ usingProperties.value_or(motionEntry.pointerProperties),
+ usingCoords.value_or(motionEntry.pointerCoords));
if (ATRACE_ENABLED()) {
std::string message = StringPrintf("Transmute MotionEvent(id=0x%" PRIx32
") to MotionEvent(id=0x%" PRIx32 ").",
@@ -4436,7 +4460,8 @@
policyFlags |= POLICY_FLAG_TRUSTED;
android::base::Timer t;
- mPolicy.interceptMotionBeforeQueueing(args.displayId, args.eventTime, policyFlags);
+ mPolicy.interceptMotionBeforeQueueing(args.displayId, args.source, args.action, args.eventTime,
+ policyFlags);
if (t.duration() > SLOW_INTERCEPTION_THRESHOLD) {
ALOGW("Excessive delay in interceptMotionBeforeQueueing; took %s ms",
std::to_string(t.duration().count()).c_str());
@@ -4697,7 +4722,9 @@
if (!(policyFlags & POLICY_FLAG_FILTERED)) {
nsecs_t eventTime = motionEvent.getEventTime();
android::base::Timer t;
- mPolicy.interceptMotionBeforeQueueing(displayId, eventTime, /*byref*/ policyFlags);
+ mPolicy.interceptMotionBeforeQueueing(displayId, motionEvent.getSource(),
+ motionEvent.getAction(), eventTime,
+ /*byref*/ policyFlags);
if (t.duration() > SLOW_INTERCEPTION_THRESHOLD) {
ALOGW("Excessive delay in interceptMotionBeforeQueueing; took %s ms",
std::to_string(t.duration().count()).c_str());
diff --git a/services/inputflinger/dispatcher/InputState.cpp b/services/inputflinger/dispatcher/InputState.cpp
index a4ac4fb..1fec9b7 100644
--- a/services/inputflinger/dispatcher/InputState.cpp
+++ b/services/inputflinger/dispatcher/InputState.cpp
@@ -195,6 +195,16 @@
}
}
+std::optional<std::pair<std::vector<PointerProperties>, std::vector<PointerCoords>>>
+InputState::getPointersOfLastEvent(const MotionEntry& entry, bool hovering) const {
+ ssize_t index = findMotionMemento(entry, hovering);
+ if (index == -1) {
+ return std::nullopt;
+ }
+ return std::make_pair(mMotionMementos[index].pointerProperties,
+ mMotionMementos[index].pointerCoords);
+}
+
ssize_t InputState::findKeyMemento(const KeyEntry& entry) const {
for (size_t i = 0; i < mKeyMementos.size(); i++) {
const KeyMemento& memento = mKeyMementos[i];
@@ -311,16 +321,6 @@
return true;
}
- // Use the previous stream cancellation logic to generate all HOVER_EXIT events.
- // If this hover event was generated as a result of the pointer leaving the window,
- // the HOVER_EXIT event should have the same coordinates as the previous
- // HOVER_MOVE event in this stream. Ensure that all HOVER_EXITs have the same
- // coordinates as the previous event by cancelling the stream here. With this approach, the
- // HOVER_EXIT event is generated from the previous event.
- if (actionMasked == AMOTION_EVENT_ACTION_HOVER_EXIT && lastMemento.hovering) {
- return true;
- }
-
// If the stream changes its source, just cancel the current gesture to be safe. It's
// possible that the app isn't handling source changes properly
if (motionEntry.source != lastMemento.source) {
diff --git a/services/inputflinger/dispatcher/InputState.h b/services/inputflinger/dispatcher/InputState.h
index b0e4209..d49469d 100644
--- a/services/inputflinger/dispatcher/InputState.h
+++ b/services/inputflinger/dispatcher/InputState.h
@@ -48,6 +48,15 @@
// and should be skipped.
bool trackMotion(const MotionEntry& entry, int32_t flags);
+ /**
+ * Return the PointerProperties and the PointerCoords for the last event, if found. Return
+ * std::nullopt if not found. We should not return std::vector<PointerCoords> in isolation,
+ * because the pointers can technically be stored in the vector in any order, so the
+ * PointerProperties are needed to specify the order in which the pointer coords are stored.
+ */
+ std::optional<std::pair<std::vector<PointerProperties>, std::vector<PointerCoords>>>
+ getPointersOfLastEvent(const MotionEntry& entry, bool hovering) const;
+
// Create cancel events for the previous stream if the current motionEntry requires it.
std::unique_ptr<EventEntry> cancelConflictingInputStream(const MotionEntry& motionEntry);
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
index 1c23720..9e6209b 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
@@ -99,8 +99,8 @@
* This method is expected to set the POLICY_FLAG_PASS_TO_USER policy flag if the event
* should be dispatched to applications.
*/
- virtual void interceptMotionBeforeQueueing(int32_t displayId, nsecs_t when,
- uint32_t& policyFlags) = 0;
+ virtual void interceptMotionBeforeQueueing(int32_t displayId, uint32_t source, int32_t action,
+ nsecs_t when, uint32_t& policyFlags) = 0;
/* Allows the policy a chance to intercept a key before dispatching. */
virtual nsecs_t interceptKeyBeforeDispatching(const sp<IBinder>& token,
diff --git a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
index d331d55..255f02d 100644
--- a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
@@ -21,9 +21,11 @@
#include <iterator>
#include <limits>
#include <map>
+#include <mutex>
#include <optional>
#include <android-base/stringprintf.h>
+#include <android-base/thread_annotations.h>
#include <android/input.h>
#include <com_android_input_flags.h>
#include <ftl/enum.h>
@@ -156,13 +158,20 @@
return sAccumulator;
}
- void recordFinger(const TouchpadInputMapper::MetricsIdentifier& id) { mCounters[id].fingers++; }
+ void recordFinger(const TouchpadInputMapper::MetricsIdentifier& id) {
+ std::scoped_lock lock(mLock);
+ mCounters[id].fingers++;
+ }
- void recordPalm(const TouchpadInputMapper::MetricsIdentifier& id) { mCounters[id].palms++; }
+ void recordPalm(const TouchpadInputMapper::MetricsIdentifier& id) {
+ std::scoped_lock lock(mLock);
+ mCounters[id].palms++;
+ }
// Checks whether a Gesture struct is for the end of a gesture that we log metrics for, and
// records it if so.
void processGesture(const TouchpadInputMapper::MetricsIdentifier& id, const Gesture& gesture) {
+ std::scoped_lock lock(mLock);
switch (gesture.type) {
case kGestureTypeFling:
if (gesture.details.fling.fling_state == GESTURES_FLING_START) {
@@ -200,15 +209,20 @@
void* cookie) {
LOG_ALWAYS_FATAL_IF(atomTag != android::util::TOUCHPAD_USAGE);
MetricsAccumulator& accumulator = MetricsAccumulator::getInstance();
- accumulator.produceAtoms(outEventList);
- accumulator.resetCounters();
+ accumulator.produceAtomsAndReset(*outEventList);
return AStatsManager_PULL_SUCCESS;
}
- void produceAtoms(AStatsEventList* outEventList) const {
+ void produceAtomsAndReset(AStatsEventList& outEventList) {
+ std::scoped_lock lock(mLock);
+ produceAtomsLocked(outEventList);
+ resetCountersLocked();
+ }
+
+ void produceAtomsLocked(AStatsEventList& outEventList) const REQUIRES(mLock) {
for (auto& [id, counters] : mCounters) {
auto [busId, vendorId, productId, versionId] = id;
- addAStatsEvent(outEventList, android::util::TOUCHPAD_USAGE, vendorId, productId,
+ addAStatsEvent(&outEventList, android::util::TOUCHPAD_USAGE, vendorId, productId,
versionId, linuxBusToInputDeviceBusEnum(busId, /*isUsi=*/false),
counters.fingers, counters.palms, counters.twoFingerSwipeGestures,
counters.threeFingerSwipeGestures, counters.fourFingerSwipeGestures,
@@ -216,7 +230,7 @@
}
}
- void resetCounters() { mCounters.clear(); }
+ void resetCountersLocked() REQUIRES(mLock) { mCounters.clear(); }
// Stores the counters for a specific touchpad model. Fields have the same meanings as those of
// the TouchpadUsage atom; see that definition for detailed documentation.
@@ -232,7 +246,10 @@
// Metrics are aggregated by device model and version, so if two devices of the same model and
// version are connected at once, they will have the same counters.
- std::map<TouchpadInputMapper::MetricsIdentifier, Counters> mCounters;
+ std::map<TouchpadInputMapper::MetricsIdentifier, Counters> mCounters GUARDED_BY(mLock);
+
+ // Metrics are pulled by a binder thread, so we need to guard them with a mutex.
+ mutable std::mutex mLock;
};
} // namespace
diff --git a/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp b/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
index 8daacdb..01e983a 100644
--- a/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
+++ b/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
@@ -116,8 +116,6 @@
void GestureConverter::populateMotionRanges(InputDeviceInfo& info) const {
info.addMotionRange(AMOTION_EVENT_AXIS_PRESSURE, SOURCE, 0.0f, 1.0f, 0, 0, 0);
- // TODO(b/259547750): set this using the raw axis ranges from the touchpad when pointer capture
- // is enabled.
if (!mBoundsInLogicalDisplay.isEmpty()) {
info.addMotionRange(AMOTION_EVENT_AXIS_X, SOURCE, mBoundsInLogicalDisplay.left,
mBoundsInLogicalDisplay.right, 0, 0, 0);
diff --git a/services/inputflinger/tests/FakeInputDispatcherPolicy.h b/services/inputflinger/tests/FakeInputDispatcherPolicy.h
index e9d93af..fb2db06 100644
--- a/services/inputflinger/tests/FakeInputDispatcherPolicy.h
+++ b/services/inputflinger/tests/FakeInputDispatcherPolicy.h
@@ -63,7 +63,7 @@
void interceptKeyBeforeQueueing(const KeyEvent&, uint32_t&) override {}
- void interceptMotionBeforeQueueing(int32_t, nsecs_t, uint32_t&) override {}
+ void interceptMotionBeforeQueueing(int32_t, uint32_t, int32_t, nsecs_t, uint32_t&) override {}
nsecs_t interceptKeyBeforeDispatching(const sp<IBinder>&, const KeyEvent&, uint32_t) override {
return 0;
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index ef07540..c92736e 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -549,7 +549,7 @@
}
}
- void interceptMotionBeforeQueueing(int32_t, nsecs_t, uint32_t&) override {}
+ void interceptMotionBeforeQueueing(int32_t, uint32_t, int32_t, nsecs_t, uint32_t&) override {}
nsecs_t interceptKeyBeforeDispatching(const sp<IBinder>&, const KeyEvent&, uint32_t) override {
nsecs_t delay = std::chrono::nanoseconds(mInterceptKeyTimeout).count();
@@ -8809,6 +8809,104 @@
ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertNotifyAnrWasNotCalled());
}
+/**
+ * If we are pruning input queue, we should never drop pointer events. Otherwise, we risk having
+ * an inconsistent event stream inside the dispatcher. In this test, we make sure that the
+ * dispatcher doesn't prune pointer events incorrectly.
+ *
+ * This test reproduces a crash in InputDispatcher.
+ * To reproduce the crash, we need to simulate the conditions for "pruning input queue" to occur.
+ *
+ * Keep the currently focused application (mApplication), and have no focused window.
+ * We set up two additional windows:
+ * 1) The navigation bar window. This simulates the system "NavigationBar", which is used in the
+ * 3-button navigation mode. This window injects a BACK button when it's touched. 2) The application
+ * window. This window is not focusable, but is touchable.
+ *
+ * We first touch the navigation bar, which causes it to inject a key. Since there's no focused
+ * window, the dispatcher doesn't process this key, and all other events inside dispatcher are now
+ * blocked. The dispatcher is waiting for 'mApplication' to add a focused window.
+ *
+ * Now, we touch "Another window". This window is owned by a different application than
+ * 'mApplication'. This causes the dispatcher to stop waiting for 'mApplication' to add a focused
+ * window. Now, the "pruning input queue" behaviour should kick in, and the dispatcher should start
+ * dropping the events from its queue. Ensure that no crash occurs.
+ *
+ * In this test, we are setting long timeouts to prevent ANRs and events dropped due to being stale.
+ * This does not affect the test running time.
+ */
+TEST_F(InputDispatcherMultiWindowAnr, PruningInputQueueShouldNotDropPointerEvents) {
+ std::shared_ptr<FakeApplicationHandle> systemUiApplication =
+ std::make_shared<FakeApplicationHandle>();
+ systemUiApplication->setDispatchingTimeout(3000ms);
+ mFakePolicy->setStaleEventTimeout(3000ms);
+ sp<FakeWindowHandle> navigationBar =
+ sp<FakeWindowHandle>::make(systemUiApplication, mDispatcher, "NavigationBar",
+ ADISPLAY_ID_DEFAULT);
+ navigationBar->setFocusable(false);
+ navigationBar->setWatchOutsideTouch(true);
+ navigationBar->setFrame(Rect(0, 0, 100, 100));
+
+ mApplication->setDispatchingTimeout(3000ms);
+ // 'mApplication' is already focused, but we call it again here to make it explicit.
+ mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, mApplication);
+
+ std::shared_ptr<FakeApplicationHandle> anotherApplication =
+ std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> appWindow =
+ sp<FakeWindowHandle>::make(anotherApplication, mDispatcher, "Another window",
+ ADISPLAY_ID_DEFAULT);
+ appWindow->setFocusable(false);
+ appWindow->setFrame(Rect(100, 100, 200, 200));
+
+ mDispatcher->onWindowInfosChanged(
+ {{*navigationBar->getInfo(), *appWindow->getInfo()}, {}, 0, 0});
+ // 'mFocusedWindow' is no longer in the dispatcher window list, and therefore loses focus
+ mFocusedWindow->consumeFocusEvent(false);
+
+ // Touch down the navigation bar. It consumes the touch and injects a key into the dispatcher
+ // in response.
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50))
+ .build());
+ navigationBar->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
+
+ // Key will not be sent anywhere because we have no focused window. It will remain pending.
+ // Pretend we are injecting KEYCODE_BACK, but it doesn't actually matter what key it is.
+ InputEventInjectionResult result =
+ injectKey(*mDispatcher, AKEY_EVENT_ACTION_DOWN, /*repeatCount=*/0, ADISPLAY_ID_DEFAULT,
+ InputEventInjectionSync::NONE, /*injectionTimeout=*/100ms,
+ /*allowKeyRepeat=*/false);
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, result);
+
+ // Finish the gesture - lift up finger and inject ACTION_UP key event
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50))
+ .build());
+ result = injectKey(*mDispatcher, AKEY_EVENT_ACTION_UP, /*repeatCount=*/0, ADISPLAY_ID_DEFAULT,
+ InputEventInjectionSync::NONE, /*injectionTimeout=*/100ms,
+ /*allowKeyRepeat=*/false);
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, result);
+ // The key that was injected is blocking the dispatcher, so the navigation bar shouldn't be
+ // getting any events yet.
+ navigationBar->assertNoEvents();
+
+ // Now touch "Another window". This touch is going to a different application than the one we
+ // are waiting for (which is 'mApplication').
+ // This should cause the dispatcher to drop the pending focus-dispatched events (like the key
+ // trying to be injected) and to continue processing the rest of the events in the original
+ // order.
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(150).y(150))
+ .build());
+ navigationBar->consumeMotionEvent(WithMotionAction(ACTION_UP));
+ navigationBar->consumeMotionEvent(WithMotionAction(ACTION_OUTSIDE));
+ appWindow->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
+
+ appWindow->assertNoEvents();
+ navigationBar->assertNoEvents();
+}
+
// These tests ensure we cannot send touch events to a window that's positioned behind a window
// that has feature NO_INPUT_CHANNEL.
// Layout:
diff --git a/services/powermanager/WorkDuration.cpp b/services/powermanager/WorkDuration.cpp
index ef723c2..bd2b10a 100644
--- a/services/powermanager/WorkDuration.cpp
+++ b/services/powermanager/WorkDuration.cpp
@@ -25,8 +25,9 @@
WorkDuration::WorkDuration(int64_t startTimestampNanos, int64_t totalDurationNanos,
int64_t cpuDurationNanos, int64_t gpuDurationNanos)
- : workPeriodStartTimestampNanos(startTimestampNanos),
+ : timestampNanos(0),
actualTotalDurationNanos(totalDurationNanos),
+ workPeriodStartTimestampNanos(startTimestampNanos),
actualCpuDurationNanos(cpuDurationNanos),
actualGpuDurationNanos(gpuDurationNanos) {}
diff --git a/services/powermanager/include/android/WorkDuration.h b/services/powermanager/include/android/WorkDuration.h
index 99b5b8b..26a575f 100644
--- a/services/powermanager/include/android/WorkDuration.h
+++ b/services/powermanager/include/android/WorkDuration.h
@@ -61,11 +61,11 @@
return os;
}
- int64_t workPeriodStartTimestampNanos;
+ int64_t timestampNanos;
int64_t actualTotalDurationNanos;
+ int64_t workPeriodStartTimestampNanos;
int64_t actualCpuDurationNanos;
int64_t actualGpuDurationNanos;
- int64_t timestampNanos;
};
} // namespace android::os
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
index 1a235e9..18a96f4 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
@@ -24,6 +24,7 @@
#include <compositionengine/LayerFE.h>
#include <compositionengine/OutputColorSetting.h>
#include <math/mat4.h>
+#include <scheduler/interface/ICompositor.h>
#include <ui/FenceTime.h>
#include <ui/Transform.h>
@@ -89,17 +90,14 @@
// If set, causes the dirty regions to flash with the delay
std::optional<std::chrono::microseconds> devOptFlashDirtyRegionsDelay;
- // Optional.
- // The earliest time to send the present command to the HAL.
- std::optional<std::chrono::steady_clock::time_point> earliestPresentTime;
-
- // The expected time for the next present
- nsecs_t expectedPresentTime{0};
+ scheduler::FrameTargets frameTargets;
// The frameInterval for the next present
- Fps frameInterval{};
+ // TODO (b/315371484): Calculate per display and store on `FrameTarget`.
+ Fps frameInterval;
// If set, a frame has been scheduled for that time.
+ // TODO (b/255601557): Calculate per display.
std::optional<std::chrono::steady_clock::time_point> scheduledFrameTime;
std::vector<BorderRenderInfo> borderInfoList;
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index 09c7c99..1c2f6cb 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -28,8 +28,11 @@
#include <compositionengine/impl/OutputLayer.h>
#include <compositionengine/impl/OutputLayerCompositionState.h>
#include <compositionengine/impl/planner/Planner.h>
+#include <ftl/algorithm.h>
#include <ftl/future.h>
#include <gui/TraceUtils.h>
+#include <scheduler/FrameTargeter.h>
+#include <scheduler/Time.h>
#include <optional>
#include <thread>
@@ -429,7 +432,28 @@
ftl::Future<std::monostate> Output::present(
const compositionengine::CompositionRefreshArgs& refreshArgs) {
- ATRACE_FORMAT("%s for %s", __func__, mNamePlusId.c_str());
+ const auto stringifyExpectedPresentTime = [this, &refreshArgs]() -> std::string {
+ return ftl::Optional(getDisplayId())
+ .and_then(PhysicalDisplayId::tryCast)
+ .and_then([&refreshArgs](PhysicalDisplayId id) {
+ return refreshArgs.frameTargets.get(id);
+ })
+ .transform([](const auto& frameTargetPtr) {
+ return frameTargetPtr.get()->expectedPresentTime();
+ })
+ .transform([](TimePoint expectedPresentTime) {
+ return base::StringPrintf(" vsyncIn %.2fms",
+ ticks<std::milli, float>(expectedPresentTime -
+ TimePoint::now()));
+ })
+ .or_else([] {
+ // There is no vsync for this output.
+ return std::make_optional(std::string());
+ })
+ .value();
+ };
+ ATRACE_FORMAT("%s for %s%s", __func__, mNamePlusId.c_str(),
+ stringifyExpectedPresentTime().c_str());
ALOGV(__FUNCTION__);
updateColorProfile(refreshArgs);
@@ -853,8 +877,14 @@
return;
}
- editState().earliestPresentTime = refreshArgs.earliestPresentTime;
- editState().expectedPresentTime = refreshArgs.expectedPresentTime;
+ if (auto frameTargetPtrOpt = ftl::Optional(getDisplayId())
+ .and_then(PhysicalDisplayId::tryCast)
+ .and_then([&refreshArgs](PhysicalDisplayId id) {
+ return refreshArgs.frameTargets.get(id);
+ })) {
+ editState().earliestPresentTime = frameTargetPtrOpt->get()->earliestPresentTime();
+ editState().expectedPresentTime = frameTargetPtrOpt->get()->expectedPresentTime().ns();
+ }
editState().frameInterval = refreshArgs.frameInterval;
editState().powerCallback = refreshArgs.powerCallback;
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index 7b0aad7..799d62c 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -222,6 +222,7 @@
const hal::VsyncPeriodChangeConstraints& constraints,
hal::VsyncPeriodChangeTimeline& outTimeline) {
mPendingModeOpt = std::move(desiredMode);
+ mIsModeSetPending = true;
const auto& mode = *mPendingModeOpt->mode.modePtr;
@@ -234,22 +235,9 @@
return true;
}
-auto DisplayDevice::finalizeModeChange() -> ModeChange {
- if (!mPendingModeOpt) return NoModeChange{"No pending mode"};
-
- auto pendingMode = *std::exchange(mPendingModeOpt, std::nullopt);
- auto& pendingModePtr = pendingMode.mode.modePtr;
-
- if (!mRefreshRateSelector->displayModes().contains(pendingModePtr->getId())) {
- return NoModeChange{"Unknown pending mode"};
- }
-
- if (getActiveMode().modePtr->getResolution() != pendingModePtr->getResolution()) {
- return ResolutionChange{std::move(pendingMode)};
- }
-
- setActiveMode(pendingModePtr->getId(), pendingModePtr->getVsyncRate(), pendingMode.mode.fps);
- return RefreshRateChange{std::move(pendingMode)};
+void DisplayDevice::finalizeModeChange(DisplayModeId modeId, Fps vsyncRate, Fps renderFps) {
+ setActiveMode(modeId, vsyncRate, renderFps);
+ mIsModeSetPending = false;
}
nsecs_t DisplayDevice::getVsyncPeriodFromHWC() const {
@@ -587,14 +575,10 @@
return mDesiredModeOpt;
}
-auto DisplayDevice::takeDesiredMode() -> DisplayModeRequestOpt {
- DisplayModeRequestOpt desiredModeOpt;
- {
- std::scoped_lock lock(mDesiredModeLock);
- std::swap(mDesiredModeOpt, desiredModeOpt);
- mHasDesiredModeTrace = false;
- }
- return desiredModeOpt;
+void DisplayDevice::clearDesiredMode() {
+ std::scoped_lock lock(mDesiredModeLock);
+ mDesiredModeOpt.reset();
+ mHasDesiredModeTrace = false;
}
void DisplayDevice::adjustRefreshRate(Fps pacesetterDisplayRefreshRate) {
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index 4c09ffd..97b56a2 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -19,7 +19,6 @@
#include <memory>
#include <string>
#include <unordered_map>
-#include <variant>
#include <android-base/thread_annotations.h>
#include <android/native_window.h>
@@ -196,11 +195,12 @@
using DisplayModeRequestOpt = ftl::Optional<display::DisplayModeRequest>;
DisplayModeRequestOpt getDesiredMode() const EXCLUDES(mDesiredModeLock);
- DisplayModeRequestOpt takeDesiredMode() EXCLUDES(mDesiredModeLock);
+ void clearDesiredMode() EXCLUDES(mDesiredModeLock);
- bool isModeSetPending() const REQUIRES(kMainThreadContext) {
- return mPendingModeOpt.has_value();
+ DisplayModeRequestOpt getPendingMode() const REQUIRES(kMainThreadContext) {
+ return mPendingModeOpt;
}
+ bool isModeSetPending() const REQUIRES(kMainThreadContext) { return mIsModeSetPending; }
scheduler::FrameRateMode getActiveMode() const REQUIRES(kMainThreadContext) {
return mRefreshRateSelector->getActiveMode();
@@ -212,25 +212,8 @@
hal::VsyncPeriodChangeTimeline& outTimeline)
REQUIRES(kMainThreadContext);
- struct NoModeChange {
- const char* reason;
- };
-
- struct ResolutionChange {
- display::DisplayModeRequest activeMode;
- };
-
- struct RefreshRateChange {
- display::DisplayModeRequest activeMode;
- };
-
- using ModeChange = std::variant<NoModeChange, ResolutionChange, RefreshRateChange>;
-
- // Clears the pending DisplayModeRequest, and returns the ModeChange that occurred. If it was a
- // RefreshRateChange, the pending mode becomes the active mode. If it was a ResolutionChange,
- // the caller is responsible for resizing the framebuffer to match the active resolution by
- // recreating the DisplayDevice.
- ModeChange finalizeModeChange() REQUIRES(kMainThreadContext);
+ void finalizeModeChange(DisplayModeId, Fps vsyncRate, Fps renderFps)
+ REQUIRES(kMainThreadContext);
scheduler::RefreshRateSelector& refreshRateSelector() const { return *mRefreshRateSelector; }
@@ -267,8 +250,6 @@
void dump(utils::Dumper&) const;
private:
- friend class TestableSurfaceFlinger;
-
template <size_t N>
inline std::string concatId(const char (&str)[N]) const {
return std::string(ftl::Concat(str, ' ', getId().value).str());
@@ -319,15 +300,12 @@
// This parameter is only used for hdr/sdr ratio overlay
float mHdrSdrRatio = 1.0f;
- // A DisplayModeRequest flows through three states: desired, pending, and active. Requests
- // within a frame are merged into a single desired request. Unless cleared, the request is
- // relayed to HWC on the next frame, and becomes pending. The mode becomes active once HWC
- // signals the present fence to confirm the mode set.
mutable std::mutex mDesiredModeLock;
DisplayModeRequestOpt mDesiredModeOpt GUARDED_BY(mDesiredModeLock);
TracedOrdinal<bool> mHasDesiredModeTrace GUARDED_BY(mDesiredModeLock);
DisplayModeRequestOpt mPendingModeOpt GUARDED_BY(kMainThreadContext);
+ bool mIsModeSetPending GUARDED_BY(kMainThreadContext) = false;
};
struct DisplayDeviceState {
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index 3ffd8ea..cf1c3c1 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -77,6 +77,8 @@
using aidl::android::hardware::graphics::common::HdrConversionStrategy;
using aidl::android::hardware::graphics::composer3::Capability;
using aidl::android::hardware::graphics::composer3::DisplayCapability;
+using aidl::android::hardware::graphics::composer3::VrrConfig;
+using namespace std::string_literals;
namespace hal = android::hardware::graphics::composer::hal;
namespace android {
@@ -89,7 +91,8 @@
: mComposer(std::move(composer)),
mMaxVirtualDisplayDimension(static_cast<size_t>(sysprop::max_virtual_display_dimension(0))),
mUpdateDeviceProductInfoOnHotplugReconnect(
- sysprop::update_device_product_info_on_hotplug_reconnect(false)) {}
+ sysprop::update_device_product_info_on_hotplug_reconnect(false)),
+ mEnableVrrTimeout(base::GetBoolProperty("debug.sf.vrr_timeout_hint_enabled"s, false)) {}
HWComposer::HWComposer(const std::string& composerServiceName)
: HWComposer(Hwc2::Composer::create(composerServiceName)) {}
@@ -299,6 +302,10 @@
hwcMode.dpiY = config.dpi->y;
}
+ if (!mEnableVrrTimeout) {
+ hwcMode.vrrConfig->notifyExpectedPresentConfig = {};
+ }
+
modes.push_back(hwcMode);
}
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index 4ca528a..fb32ff4 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -491,6 +491,7 @@
private:
// For unit tests
friend TestableSurfaceFlinger;
+ friend HWComposerTest;
struct DisplayData {
std::unique_ptr<HWC2::Display> hwcDisplay;
@@ -542,6 +543,7 @@
const size_t mMaxVirtualDisplayDimension;
const bool mUpdateDeviceProductInfoOnHotplugReconnect;
+ bool mEnableVrrTimeout;
};
} // namespace impl
diff --git a/services/surfaceflinger/DisplayHardware/Hal.h b/services/surfaceflinger/DisplayHardware/Hal.h
index 31c2833..e3d9622 100644
--- a/services/surfaceflinger/DisplayHardware/Hal.h
+++ b/services/surfaceflinger/DisplayHardware/Hal.h
@@ -169,10 +169,8 @@
out << "}, ";
out << "notifyExpectedPresentConfig={";
if (vrrConfig->notifyExpectedPresentConfig) {
- out << "notifyExpectedPresentHeadsUpNs="
- << vrrConfig->notifyExpectedPresentConfig->notifyExpectedPresentHeadsUpNs
- << ", notifyExpectedPresentTimeoutNs="
- << vrrConfig->notifyExpectedPresentConfig->notifyExpectedPresentTimeoutNs;
+ out << "headsUpNs=" << vrrConfig->notifyExpectedPresentConfig->headsUpNs
+ << ", timeoutNs=" << vrrConfig->notifyExpectedPresentConfig->timeoutNs;
}
out << "}}";
return out.str();
diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp
index 6e862b4..c80c8fd 100644
--- a/services/surfaceflinger/Scheduler/EventThread.cpp
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -584,6 +584,9 @@
case DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG:
return true;
+ case DisplayEventReceiver::DISPLAY_EVENT_HDCP_LEVELS_CHANGE:
+ return true;
+
case DisplayEventReceiver::DISPLAY_EVENT_MODE_CHANGE: {
return connection->mEventRegistration.test(
gui::ISurfaceComposer::EventRegistration::modeChanged);
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index bfc47e6..27ca17f 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -51,8 +51,12 @@
#include "FrameRateOverrideMappings.h"
#include "FrontEnd/LayerHandle.h"
#include "OneShotTimer.h"
+#include "RefreshRateStats.h"
+#include "SurfaceFlingerFactory.h"
#include "SurfaceFlingerProperties.h"
+#include "TimeStats/TimeStats.h"
#include "VSyncTracker.h"
+#include "VsyncConfiguration.h"
#include "VsyncController.h"
#include "VsyncSchedule.h"
@@ -67,10 +71,14 @@
namespace android::scheduler {
Scheduler::Scheduler(ICompositor& compositor, ISchedulerCallback& callback, FeatureFlags features,
- sp<VsyncModulator> modulatorPtr, IVsyncTrackerCallback& vsyncTrackerCallback)
- : impl::MessageQueue(compositor),
+ surfaceflinger::Factory& factory, Fps activeRefreshRate, TimeStats& timeStats,
+ IVsyncTrackerCallback& vsyncTrackerCallback)
+ : android::impl::MessageQueue(compositor),
mFeatures(features),
- mVsyncModulator(std::move(modulatorPtr)),
+ mVsyncConfiguration(factory.createVsyncConfiguration(activeRefreshRate)),
+ mVsyncModulator(sp<VsyncModulator>::make(mVsyncConfiguration->getCurrentConfigs())),
+ mRefreshRateStats(std::make_unique<RefreshRateStats>(timeStats, activeRefreshRate,
+ hal::PowerMode::OFF)),
mSchedulerCallback(callback),
mVsyncTrackerCallback(vsyncTrackerCallback) {}
@@ -182,9 +190,9 @@
const FrameTargeter::BeginFrameArgs beginFrameArgs =
{.frameBeginTime = SchedulerClock::now(),
.vsyncId = vsyncId,
- // TODO(b/255601557): Calculate per display.
.expectedVsyncTime = expectedVsyncTime,
- .sfWorkDuration = mVsyncModulator->getVsyncConfig().sfWorkDuration};
+ .sfWorkDuration = mVsyncModulator->getVsyncConfig().sfWorkDuration,
+ .hwcMinWorkDuration = mVsyncConfiguration->getCurrentConfigs().hwcMinWorkDuration};
ftl::NonNull<const Display*> pacesetterPtr = pacesetterPtrLocked();
pacesetterPtr->targeterPtr->beginFrame(beginFrameArgs, *pacesetterPtr->schedulePtr);
@@ -193,11 +201,20 @@
FrameTargets targets;
targets.try_emplace(pacesetterPtr->displayId, &pacesetterPtr->targeterPtr->target());
+ // TODO (b/256196556): Followers should use the next VSYNC after the frontrunner, not the
+ // pacesetter.
+ // Update expectedVsyncTime, which may have been adjusted by beginFrame.
+ expectedVsyncTime = pacesetterPtr->targeterPtr->target().expectedPresentTime();
+
for (const auto& [id, display] : mDisplays) {
if (id == pacesetterPtr->displayId) continue;
+ auto followerBeginFrameArgs = beginFrameArgs;
+ followerBeginFrameArgs.expectedVsyncTime =
+ display.schedulePtr->vsyncDeadlineAfter(expectedVsyncTime);
+
FrameTargeter& targeter = *display.targeterPtr;
- targeter.beginFrame(beginFrameArgs, *display.schedulePtr);
+ targeter.beginFrame(followerBeginFrameArgs, *display.schedulePtr);
targets.try_emplace(id, &targeter.target());
}
@@ -307,9 +324,10 @@
frametimeline::TokenManager* tokenManager,
std::chrono::nanoseconds workDuration,
std::chrono::nanoseconds readyDuration) {
- auto eventThread = std::make_unique<impl::EventThread>(cycle == Cycle::Render ? "app" : "appSf",
- getVsyncSchedule(), tokenManager, *this,
- workDuration, readyDuration);
+ auto eventThread =
+ std::make_unique<android::impl::EventThread>(cycle == Cycle::Render ? "app" : "appSf",
+ getVsyncSchedule(), tokenManager, *this,
+ workDuration, readyDuration);
auto& handle = cycle == Cycle::Render ? mAppConnectionHandle : mSfConnectionHandle;
handle = createConnection(std::move(eventThread));
@@ -495,8 +513,23 @@
thread->setDuration(workDuration, readyDuration);
}
-void Scheduler::setVsyncConfigSet(const VsyncConfigSet& configs, Period vsyncPeriod) {
- setVsyncConfig(mVsyncModulator->setVsyncConfigSet(configs), vsyncPeriod);
+void Scheduler::updatePhaseConfiguration(Fps refreshRate) {
+ mRefreshRateStats->setRefreshRate(refreshRate);
+ mVsyncConfiguration->setRefreshRateFps(refreshRate);
+ setVsyncConfig(mVsyncModulator->setVsyncConfigSet(mVsyncConfiguration->getCurrentConfigs()),
+ refreshRate.getPeriod());
+}
+
+void Scheduler::resetPhaseConfiguration(Fps refreshRate) {
+ // Cancel the pending refresh rate change, if any, before updating the phase configuration.
+ mVsyncModulator->cancelRefreshRateChange();
+
+ mVsyncConfiguration->reset();
+ updatePhaseConfiguration(refreshRate);
+}
+
+void Scheduler::setActiveDisplayPowerModeForRefreshRateStats(hal::PowerMode powerMode) {
+ mRefreshRateStats->setPowerMode(powerMode);
}
void Scheduler::setVsyncConfig(const VsyncConfig& config, Period vsyncPeriod) {
@@ -560,7 +593,7 @@
// On main thread to serialize reads/writes of pending hardware VSYNC state.
static_cast<void>(
- schedule([=]() FTL_FAKE_GUARD(mDisplayLock) FTL_FAKE_GUARD(kMainThreadContext) {
+ schedule([=, this]() FTL_FAKE_GUARD(mDisplayLock) FTL_FAKE_GUARD(kMainThreadContext) {
ATRACE_NAME(ftl::Concat(whence, ' ', id.value, ' ', enabled).c_str());
if (const auto displayOpt = mDisplays.get(id)) {
@@ -869,6 +902,12 @@
mFrameRateOverrideMappings.dump(dumper);
dumper.eol();
+ mVsyncConfiguration->dump(dumper.out());
+ dumper.eol();
+
+ mRefreshRateStats->dump(dumper.out());
+ dumper.eol();
+
{
utils::Dumper::Section section(dumper, "Frame Targeting"sv);
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index a29d153..f62f1ba 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -88,23 +88,30 @@
namespace android {
class FenceTime;
+class TimeStats;
namespace frametimeline {
class TokenManager;
} // namespace frametimeline
+namespace surfaceflinger {
+class Factory;
+} // namespace surfaceflinger
+
namespace scheduler {
using GlobalSignals = RefreshRateSelector::GlobalSignals;
+class RefreshRateStats;
+class VsyncConfiguration;
class VsyncSchedule;
class Scheduler : public IEventThreadCallback, android::impl::MessageQueue {
using Impl = android::impl::MessageQueue;
public:
- Scheduler(ICompositor&, ISchedulerCallback&, FeatureFlags, sp<VsyncModulator>,
- IVsyncTrackerCallback&);
+ Scheduler(ICompositor&, ISchedulerCallback&, FeatureFlags, surfaceflinger::Factory&,
+ Fps activeRefreshRate, TimeStats&, IVsyncTrackerCallback&);
virtual ~Scheduler();
void startTimers();
@@ -201,7 +208,10 @@
}
}
- void setVsyncConfigSet(const VsyncConfigSet&, Period vsyncPeriod);
+ void updatePhaseConfiguration(Fps);
+ void resetPhaseConfiguration(Fps) REQUIRES(kMainThreadContext);
+
+ const VsyncConfiguration& getVsyncConfiguration() const { return *mVsyncConfiguration; }
// Sets the render rate for the scheduler to run at.
void setRenderRate(PhysicalDisplayId, Fps);
@@ -249,8 +259,10 @@
// Indicates that touch interaction is taking place.
void onTouchHint();
- void setDisplayPowerMode(PhysicalDisplayId, hal::PowerMode powerMode)
- REQUIRES(kMainThreadContext);
+ void setDisplayPowerMode(PhysicalDisplayId, hal::PowerMode) REQUIRES(kMainThreadContext);
+
+ // TODO(b/255635821): Track this per display.
+ void setActiveDisplayPowerModeForRefreshRateStats(hal::PowerMode) REQUIRES(kMainThreadContext);
ConstVsyncSchedulePtr getVsyncSchedule(std::optional<PhysicalDisplayId> = std::nullopt) const
EXCLUDES(mDisplayLock);
@@ -464,9 +476,14 @@
const FeatureFlags mFeatures;
+ // Stores phase offsets configured per refresh rate.
+ const std::unique_ptr<VsyncConfiguration> mVsyncConfiguration;
+
// Shifts the VSYNC phase during certain transactions and refresh rate changes.
const sp<VsyncModulator> mVsyncModulator;
+ const std::unique_ptr<RefreshRateStats> mRefreshRateStats;
+
// Used to choose refresh rate if content detection is enabled.
LayerHistory mLayerHistory;
@@ -497,9 +514,7 @@
: displayId(displayId),
selectorPtr(std::move(selectorPtr)),
schedulePtr(std::move(schedulePtr)),
- targeterPtr(std::make_unique<
- FrameTargeter>(displayId,
- features.test(Feature::kBackpressureGpuComposition))) {}
+ targeterPtr(std::make_unique<FrameTargeter>(displayId, features)) {}
const PhysicalDisplayId displayId;
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
index 33d3652..28e35de 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
@@ -405,7 +405,7 @@
: std::nullopt;
ALOGV("%s %s: DisplayMode %s notifyExpectedPresentTimeout %s", __func__, to_string(mId).c_str(),
to_string(*modePtr).c_str(),
- timeout ? std::to_string(timeout->notifyExpectedPresentTimeoutNs).c_str() : "N/A");
+ timeout ? std::to_string(timeout->timeoutNs).c_str() : "N/A");
std::lock_guard lock(mMutex);
mDisplayModePtr = modePtr;
diff --git a/services/surfaceflinger/Scheduler/include/scheduler/Features.h b/services/surfaceflinger/Scheduler/include/scheduler/Features.h
index 7c72ac6..52485fb 100644
--- a/services/surfaceflinger/Scheduler/include/scheduler/Features.h
+++ b/services/surfaceflinger/Scheduler/include/scheduler/Features.h
@@ -29,6 +29,7 @@
kTracePredictedVsync = 1 << 3,
kBackpressureGpuComposition = 1 << 4,
kSmallDirtyContentDetection = 1 << 5,
+ kExpectedPresentTime = 1 << 6,
};
using FeatureFlags = ftl::Flags<Feature>;
diff --git a/services/surfaceflinger/Scheduler/include/scheduler/FrameTargeter.h b/services/surfaceflinger/Scheduler/include/scheduler/FrameTargeter.h
index 70d4846..a5bb6c2 100644
--- a/services/surfaceflinger/Scheduler/include/scheduler/FrameTargeter.h
+++ b/services/surfaceflinger/Scheduler/include/scheduler/FrameTargeter.h
@@ -19,11 +19,13 @@
#include <array>
#include <atomic>
#include <memory>
+#include <optional>
#include <ui/DisplayId.h>
#include <ui/Fence.h>
#include <ui/FenceTime.h>
+#include <scheduler/Features.h>
#include <scheduler/Time.h>
#include <scheduler/VsyncId.h>
#include <scheduler/interface/CompositeResult.h>
@@ -49,14 +51,11 @@
TimePoint expectedPresentTime() const { return mExpectedPresentTime; }
+ std::optional<TimePoint> earliestPresentTime() const { return mEarliestPresentTime; }
+
// The time of the VSYNC that preceded this frame. See `presentFenceForPastVsync` for details.
TimePoint pastVsyncTime(Period minFramePeriod) const;
- // Equivalent to `pastVsyncTime` unless running N VSYNCs ahead.
- TimePoint previousFrameVsyncTime(Period minFramePeriod) const {
- return mExpectedPresentTime - minFramePeriod;
- }
-
// The present fence for the frame that had targeted the most recent VSYNC before this frame.
// If the target VSYNC for any given frame is more than `vsyncPeriod` in the future, then the
// VSYNC of at least one previous frame has not yet passed. In other words, this is NOT the
@@ -69,8 +68,6 @@
return mPresentFences.front().fenceTime;
}
- bool wouldPresentEarly(Period minFramePeriod) const;
-
bool isFramePending() const { return mFramePending; }
bool didMissFrame() const { return mFrameMissed; }
bool didMissHwcFrame() const { return mHwcFrameMissed && !mGpuFrameMissed; }
@@ -79,9 +76,17 @@
explicit FrameTarget(const std::string& displayLabel);
~FrameTarget() = default;
+ bool wouldPresentEarly(Period minFramePeriod) const;
+
+ // Equivalent to `pastVsyncTime` unless running N VSYNCs ahead.
+ TimePoint previousFrameVsyncTime(Period minFramePeriod) const {
+ return mExpectedPresentTime - minFramePeriod;
+ }
+
VsyncId mVsyncId;
TimePoint mFrameBeginTime;
TimePoint mExpectedPresentTime;
+ std::optional<TimePoint> mEarliestPresentTime;
TracedOrdinal<bool> mFramePending;
TracedOrdinal<bool> mFrameMissed;
@@ -95,6 +100,8 @@
std::array<FenceWithFenceTime, 2> mPresentFences;
private:
+ friend class FrameTargeterTestBase;
+
template <int N>
inline bool targetsVsyncsAhead(Period minFramePeriod) const {
static_assert(N > 1);
@@ -105,9 +112,10 @@
// Computes a display's per-frame metrics about past/upcoming targeting of present deadlines.
class FrameTargeter final : private FrameTarget {
public:
- FrameTargeter(PhysicalDisplayId displayId, bool backpressureGpuComposition)
+ FrameTargeter(PhysicalDisplayId displayId, FeatureFlags flags)
: FrameTarget(to_string(displayId)),
- mBackpressureGpuComposition(backpressureGpuComposition) {}
+ mBackpressureGpuComposition(flags.test(Feature::kBackpressureGpuComposition)),
+ mSupportsExpectedPresentTime(flags.test(Feature::kExpectedPresentTime)) {}
const FrameTarget& target() const { return *this; }
@@ -116,10 +124,14 @@
VsyncId vsyncId;
TimePoint expectedVsyncTime;
Duration sfWorkDuration;
+ Duration hwcMinWorkDuration;
};
void beginFrame(const BeginFrameArgs&, const IVsyncSource&);
+ std::optional<TimePoint> computeEarliestPresentTime(Period minFramePeriod,
+ Duration hwcMinWorkDuration);
+
// TODO(b/241285191): Merge with FrameTargeter::endFrame.
FenceTimePtr setPresentFence(sp<Fence>);
@@ -128,7 +140,7 @@
void dump(utils::Dumper&) const;
private:
- friend class FrameTargeterTest;
+ friend class FrameTargeterTestBase;
// For tests.
using IsFencePendingFuncPtr = bool (*)(const FenceTimePtr&, int graceTimeMs);
@@ -138,6 +150,7 @@
static bool isFencePending(const FenceTimePtr&, int graceTimeMs);
const bool mBackpressureGpuComposition;
+ const bool mSupportsExpectedPresentTime;
TimePoint mScheduledPresentTime;
CompositionCoverageFlags mCompositionCoverage;
diff --git a/services/surfaceflinger/Scheduler/src/FrameTargeter.cpp b/services/surfaceflinger/Scheduler/src/FrameTargeter.cpp
index e80372b..68c277d 100644
--- a/services/surfaceflinger/Scheduler/src/FrameTargeter.cpp
+++ b/services/surfaceflinger/Scheduler/src/FrameTargeter.cpp
@@ -82,6 +82,10 @@
}
}
+ if (!mSupportsExpectedPresentTime) {
+ mEarliestPresentTime = computeEarliestPresentTime(minFramePeriod, args.hwcMinWorkDuration);
+ }
+
ATRACE_FORMAT("%s %" PRId64 " vsyncIn %.2fms%s", __func__, ftl::to_underlying(args.vsyncId),
ticks<std::milli, float>(mExpectedPresentTime - TimePoint::now()),
mExpectedPresentTime == args.expectedVsyncTime ? "" : " (adjusted)");
@@ -121,6 +125,14 @@
if (mGpuFrameMissed) mGpuFrameMissedCount++;
}
+std::optional<TimePoint> FrameTargeter::computeEarliestPresentTime(Period minFramePeriod,
+ Duration hwcMinWorkDuration) {
+ if (wouldPresentEarly(minFramePeriod)) {
+ return previousFrameVsyncTime(minFramePeriod) - hwcMinWorkDuration;
+ }
+ return {};
+}
+
void FrameTargeter::endFrame(const CompositeResult& result) {
mCompositionCoverage = result.compositionCoverage;
}
diff --git a/services/surfaceflinger/Scheduler/tests/FrameTargeterTest.cpp b/services/surfaceflinger/Scheduler/tests/FrameTargeterTest.cpp
index c883385..a9abcaf 100644
--- a/services/surfaceflinger/Scheduler/tests/FrameTargeterTest.cpp
+++ b/services/surfaceflinger/Scheduler/tests/FrameTargeterTest.cpp
@@ -44,12 +44,18 @@
} // namespace
-class FrameTargeterTest : public testing::Test {
+class FrameTargeterTestBase : public testing::Test {
public:
+ FrameTargeterTestBase(FeatureFlags flags) : mTargeter(PhysicalDisplayId::fromPort(13), flags) {}
+
const auto& target() const { return mTargeter.target(); }
+ bool wouldPresentEarly(Period minFramePeriod) const {
+ return target().wouldPresentEarly(minFramePeriod);
+ }
+
struct Frame {
- Frame(FrameTargeterTest* testPtr, VsyncId vsyncId, TimePoint& frameBeginTime,
+ Frame(FrameTargeterTestBase* testPtr, VsyncId vsyncId, TimePoint& frameBeginTime,
Duration frameDuration, Fps refreshRate, Fps peakRefreshRate,
FrameTargeter::IsFencePendingFuncPtr isFencePendingFuncPtr = Frame::fenceSignaled,
const ftl::Optional<VsyncSource>& vsyncSourceOpt = std::nullopt)
@@ -61,7 +67,8 @@
.vsyncId = vsyncId,
.expectedVsyncTime =
frameBeginTime + frameDuration,
- .sfWorkDuration = 10ms};
+ .sfWorkDuration = 10ms,
+ .hwcMinWorkDuration = kHwcMinWorkDuration};
testPtr->mTargeter.beginFrame(args,
vsyncSourceOpt
@@ -93,7 +100,7 @@
static bool fencePending(const FenceTimePtr&, int) { return true; }
static bool fenceSignaled(const FenceTimePtr&, int) { return false; }
- FrameTargeterTest* const testPtr;
+ FrameTargeterTestBase* const testPtr;
TimePoint& frameBeginTime;
const Period period;
@@ -102,11 +109,24 @@
bool ended = false;
};
+ static constexpr Duration kHwcMinWorkDuration = std::chrono::nanoseconds(5ns);
+
private:
FenceToFenceTimeMap mFenceMap;
- static constexpr bool kBackpressureGpuComposition = true;
- FrameTargeter mTargeter{PhysicalDisplayId::fromPort(13), kBackpressureGpuComposition};
+ FrameTargeter mTargeter;
+};
+
+class FrameTargeterTest : public FrameTargeterTestBase {
+public:
+ FrameTargeterTest() : FrameTargeterTestBase(Feature::kBackpressureGpuComposition) {}
+};
+
+class FrameTargeterWithExpectedPresentSupportTest : public FrameTargeterTestBase {
+public:
+ FrameTargeterWithExpectedPresentSupportTest()
+ : FrameTargeterTestBase(FeatureFlags(Feature::kBackpressureGpuComposition) |
+ Feature::kExpectedPresentTime) {}
};
TEST_F(FrameTargeterTest, targetsFrames) {
@@ -208,7 +228,7 @@
TEST_F(FrameTargeterTest, doesNotDetectEarlyPresentIfNoFence) {
constexpr Period kPeriod = (60_Hz).getPeriod();
EXPECT_EQ(target().presentFenceForPastVsync(kPeriod), FenceTime::NO_FENCE);
- EXPECT_FALSE(target().wouldPresentEarly(kPeriod));
+ EXPECT_FALSE(wouldPresentEarly(kPeriod));
}
TEST_F(FrameTargeterTest, detectsEarlyPresent) {
@@ -220,7 +240,8 @@
// The target is not early while past present fences are pending.
for (int n = 3; n-- > 0;) {
const Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate);
- EXPECT_FALSE(target().wouldPresentEarly(kPeriod));
+ EXPECT_FALSE(wouldPresentEarly(kPeriod));
+ EXPECT_FALSE(target().earliestPresentTime());
}
// The target is early if the past present fence was signaled.
@@ -228,7 +249,41 @@
const auto fence = frame.end();
fence->signalForTest(frameBeginTime.ns());
- EXPECT_TRUE(target().wouldPresentEarly(kPeriod));
+ Frame finalFrame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate);
+
+ // `finalFrame` would present early, so it has an earliest present time.
+ EXPECT_TRUE(wouldPresentEarly(kPeriod));
+ ASSERT_NE(std::nullopt, target().earliestPresentTime());
+ EXPECT_EQ(*target().earliestPresentTime(),
+ target().expectedPresentTime() - kPeriod - kHwcMinWorkDuration);
+}
+
+// Same as `detectsEarlyPresent`, above, but verifies that we do not set an earliest present time
+// when there is expected present time support.
+TEST_F(FrameTargeterWithExpectedPresentSupportTest, detectsEarlyPresent) {
+ VsyncId vsyncId{333};
+ TimePoint frameBeginTime(3000ms);
+ constexpr Fps kRefreshRate = 60_Hz;
+ constexpr Period kPeriod = kRefreshRate.getPeriod();
+
+ // The target is not early while past present fences are pending.
+ for (int n = 3; n-- > 0;) {
+ const Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate);
+ EXPECT_FALSE(wouldPresentEarly(kPeriod));
+ EXPECT_FALSE(target().earliestPresentTime());
+ }
+
+ // The target is early if the past present fence was signaled.
+ Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate);
+ const auto fence = frame.end();
+ fence->signalForTest(frameBeginTime.ns());
+
+ Frame finalFrame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate);
+
+ // `finalFrame` would present early, but we have expected present time support, so it has no
+ // earliest present time.
+ EXPECT_TRUE(wouldPresentEarly(kPeriod));
+ ASSERT_EQ(std::nullopt, target().earliestPresentTime());
}
TEST_F(FrameTargeterTest, detectsEarlyPresentTwoVsyncsAhead) {
@@ -240,7 +295,8 @@
// The target is not early while past present fences are pending.
for (int n = 3; n-- > 0;) {
const Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate);
- EXPECT_FALSE(target().wouldPresentEarly(kPeriod));
+ EXPECT_FALSE(wouldPresentEarly(kPeriod));
+ EXPECT_FALSE(target().earliestPresentTime());
}
Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate);
@@ -248,12 +304,18 @@
fence->signalForTest(frameBeginTime.ns());
// The target is two VSYNCs ahead, so the past present fence is still pending.
- EXPECT_FALSE(target().wouldPresentEarly(kPeriod));
+ EXPECT_FALSE(wouldPresentEarly(kPeriod));
+ EXPECT_FALSE(target().earliestPresentTime());
{ const Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate); }
+ Frame finalFrame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate);
+
// The target is early if the past present fence was signaled.
- EXPECT_TRUE(target().wouldPresentEarly(kPeriod));
+ EXPECT_TRUE(wouldPresentEarly(kPeriod));
+ ASSERT_NE(std::nullopt, target().earliestPresentTime());
+ EXPECT_EQ(*target().earliestPresentTime(),
+ target().expectedPresentTime() - kPeriod - kHwcMinWorkDuration);
}
TEST_F(FrameTargeterTest, detectsEarlyPresentThreeVsyncsAhead) {
@@ -264,7 +326,10 @@
const Frame frame(this, VsyncId{555}, frameBeginTime, 16ms, kRefreshRate, kRefreshRate);
// The target is more than two VSYNCs ahead, but present fences are not tracked that far back.
- EXPECT_TRUE(target().wouldPresentEarly(kPeriod));
+ EXPECT_TRUE(wouldPresentEarly(kPeriod));
+ EXPECT_TRUE(target().earliestPresentTime());
+ EXPECT_EQ(*target().earliestPresentTime(),
+ target().expectedPresentTime() - kPeriod - kHwcMinWorkDuration);
}
TEST_F(FrameTargeterTest, detectsMissedFrames) {
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 138a9bb..c3bfb58 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -59,7 +59,6 @@
#include <ftl/concat.h>
#include <ftl/fake_guard.h>
#include <ftl/future.h>
-#include <ftl/match.h>
#include <ftl/unit.h>
#include <gui/AidlStatusUtil.h>
#include <gui/BufferQueue.h>
@@ -1109,7 +1108,7 @@
outMode.peakRefreshRate = peakFps.getValue();
outMode.vsyncRate = mode->getVsyncRate().getValue();
- const auto vsyncConfigSet = mVsyncConfiguration->getConfigsForRefreshRate(
+ const auto vsyncConfigSet = mScheduler->getVsyncConfiguration().getConfigsForRefreshRate(
Fps::fromValue(outMode.peakRefreshRate));
outMode.appVsyncOffset = vsyncConfigSet.late.appOffset;
outMode.sfVsyncOffset = vsyncConfigSet.late.sfOffset;
@@ -1236,21 +1235,20 @@
return NO_ERROR;
}
-void SurfaceFlinger::setDesiredMode(display::DisplayModeRequest&& desiredMode, bool force) {
- const auto mode = desiredMode.mode;
- const auto displayId = mode.modePtr->getPhysicalDisplayId();
-
+void SurfaceFlinger::setDesiredMode(display::DisplayModeRequest&& request, bool force) {
+ const auto displayId = request.mode.modePtr->getPhysicalDisplayId();
ATRACE_NAME(ftl::Concat(__func__, ' ', displayId.value).c_str());
const auto display = getDisplayDeviceLocked(displayId);
if (!display) {
- ALOGW("%s: Unknown display %s", __func__, to_string(displayId).c_str());
+ ALOGW("%s: display is no longer valid", __func__);
return;
}
- const bool emitEvent = desiredMode.emitEvent;
+ const auto mode = request.mode;
+ const bool emitEvent = request.emitEvent;
- switch (display->setDesiredMode(std::move(desiredMode), force)) {
+ switch (display->setDesiredMode(std::move(request), force)) {
case DisplayDevice::DesiredModeAction::InitiateDisplayModeSwitch:
// DisplayDevice::setDesiredMode updated the render rate, so inform Scheduler.
mScheduler->setRenderRate(displayId,
@@ -1269,7 +1267,7 @@
mScheduler->modulateVsync(displayId, &VsyncModulator::onRefreshRateChangeInitiated);
if (displayId == mActiveDisplayId) {
- updatePhaseConfiguration(mode.fps);
+ mScheduler->updatePhaseConfiguration(mode.fps);
}
mScheduler->setModeChangePending(true);
@@ -1278,8 +1276,7 @@
mScheduler->setRenderRate(displayId, mode.fps);
if (displayId == mActiveDisplayId) {
- updatePhaseConfiguration(mode.fps);
- mRefreshRateStats->setRefreshRate(mode.fps);
+ mScheduler->updatePhaseConfiguration(mode.fps);
}
if (emitEvent) {
@@ -1346,56 +1343,61 @@
const auto displayId = display.getPhysicalId();
ATRACE_NAME(ftl::Concat(__func__, ' ', displayId.value).c_str());
- ftl::match(
- display.finalizeModeChange(),
- [this, displayId](DisplayDevice::RefreshRateChange change) {
- ftl::FakeGuard guard(mStateLock);
+ const auto pendingModeOpt = display.getPendingMode();
+ if (!pendingModeOpt) {
+ // There is no pending mode change. This can happen if the active
+ // display changed and the mode change happened on a different display.
+ return;
+ }
- if (change.activeMode.emitEvent) {
- dispatchDisplayModeChangeEvent(displayId, change.activeMode.mode);
- }
+ const auto& activeMode = pendingModeOpt->mode;
- applyActiveMode(std::move(change.activeMode));
- },
- [&](DisplayDevice::ResolutionChange change) {
- auto& state = mCurrentState.displays.editValueFor(display.getDisplayToken());
- // Assign a new sequence ID to recreate the display and so its framebuffer.
- state.sequenceId = DisplayDeviceState{}.sequenceId;
- state.physical->activeMode = change.activeMode.mode.modePtr.get();
+ if (display.getActiveMode().modePtr->getResolution() != activeMode.modePtr->getResolution()) {
+ auto& state = mCurrentState.displays.editValueFor(display.getDisplayToken());
+ // We need to generate new sequenceId in order to recreate the display (and this
+ // way the framebuffer).
+ state.sequenceId = DisplayDeviceState{}.sequenceId;
+ state.physical->activeMode = activeMode.modePtr.get();
+ processDisplayChangesLocked();
- ftl::FakeGuard guard1(kMainThreadContext);
- ftl::FakeGuard guard2(mStateLock);
- processDisplayChangesLocked();
+ // processDisplayChangesLocked will update all necessary components so we're done here.
+ return;
+ }
- applyActiveMode(std::move(change.activeMode));
- },
- [](DisplayDevice::NoModeChange noChange) {
- // TODO(b/255635821): Remove this case, as it should no longer happen.
- ALOGE("A mode change was initiated but not finalized: %s", noChange.reason);
- });
+ display.finalizeModeChange(activeMode.modePtr->getId(), activeMode.modePtr->getVsyncRate(),
+ activeMode.fps);
+
+ if (displayId == mActiveDisplayId) {
+ mScheduler->updatePhaseConfiguration(activeMode.fps);
+ }
+
+ if (pendingModeOpt->emitEvent) {
+ dispatchDisplayModeChangeEvent(displayId, activeMode);
+ }
}
-void SurfaceFlinger::dropModeRequest(display::DisplayModeRequest&& request) {
- if (request.mode.modePtr->getPhysicalDisplayId() == mActiveDisplayId) {
+void SurfaceFlinger::dropModeRequest(const sp<DisplayDevice>& display) {
+ display->clearDesiredMode();
+ if (display->getPhysicalId() == mActiveDisplayId) {
// TODO(b/255635711): Check for pending mode changes on other displays.
mScheduler->setModeChangePending(false);
}
}
-void SurfaceFlinger::applyActiveMode(display::DisplayModeRequest&& activeMode) {
- auto activeModePtr = activeMode.mode.modePtr;
+void SurfaceFlinger::applyActiveMode(const sp<DisplayDevice>& display) {
+ const auto activeModeOpt = display->getDesiredMode();
+ auto activeModePtr = activeModeOpt->mode.modePtr;
const auto displayId = activeModePtr->getPhysicalDisplayId();
- const auto renderFps = activeMode.mode.fps;
+ const auto renderFps = activeModeOpt->mode.fps;
- dropModeRequest(std::move(activeMode));
+ dropModeRequest(display);
constexpr bool kAllowToEnable = true;
mScheduler->resyncToHardwareVsync(displayId, kAllowToEnable, std::move(activeModePtr).take());
mScheduler->setRenderRate(displayId, renderFps);
if (displayId == mActiveDisplayId) {
- mRefreshRateStats->setRefreshRate(renderFps);
- updatePhaseConfiguration(renderFps);
+ mScheduler->updatePhaseConfiguration(renderFps);
}
}
@@ -1408,50 +1410,50 @@
const auto display = getDisplayDeviceLocked(id);
if (!display) continue;
- auto desiredModeOpt = display->takeDesiredMode();
+ auto desiredModeOpt = display->getDesiredMode();
if (!desiredModeOpt) {
continue;
}
- auto desiredMode = std::move(*desiredModeOpt);
-
if (!shouldApplyRefreshRateSelectorPolicy(*display)) {
- dropModeRequest(std::move(desiredMode));
+ dropModeRequest(display);
continue;
}
- const auto desiredModeId = desiredMode.mode.modePtr->getId();
+ const auto desiredModeId = desiredModeOpt->mode.modePtr->getId();
const auto displayModePtrOpt = physical.snapshot().displayModes().get(desiredModeId);
if (!displayModePtrOpt) {
- ALOGW("%s: Unknown mode %d for display %s", __func__, desiredModeId.value(),
- to_string(id).c_str());
- dropModeRequest(std::move(desiredMode));
+ ALOGW("Desired display mode is no longer supported. Mode ID = %d",
+ desiredModeId.value());
+ dropModeRequest(display);
continue;
}
- if (display->getActiveMode() == desiredMode.mode) {
- dropModeRequest(std::move(desiredMode));
+ ALOGV("%s changing active mode to %d(%s) for display %s", __func__, desiredModeId.value(),
+ to_string(displayModePtrOpt->get()->getVsyncRate()).c_str(),
+ to_string(display->getId()).c_str());
+
+ if (display->getActiveMode() == desiredModeOpt->mode) {
+ applyActiveMode(display);
continue;
}
- // The desired mode is different from the active mode. However, the allowed modes might have
- // changed since setDesiredMode scheduled a mode transition.
- if (!display->refreshRateSelector().isModeAllowed(desiredMode.mode)) {
- dropModeRequest(std::move(desiredMode));
+ // Desired active mode was set, it is different than the mode currently in use, however
+ // allowed modes might have changed by the time we process the refresh.
+ // Make sure the desired mode is still allowed
+ if (!display->refreshRateSelector().isModeAllowed(desiredModeOpt->mode)) {
+ dropModeRequest(display);
continue;
}
- ALOGV("Mode setting display %s to %d (%s)", to_string(id).c_str(), desiredModeId.value(),
- to_string(displayModePtrOpt.value().get()->getVsyncRate()).c_str());
-
- // TODO(b/142753666): Use constraints.
+ // TODO(b/142753666) use constrains
hal::VsyncPeriodChangeConstraints constraints;
constraints.desiredTimeNanos = systemTime();
constraints.seamlessRequired = false;
hal::VsyncPeriodChangeTimeline outTimeline;
- if (!display->initiateModeChange(std::move(desiredMode), constraints, outTimeline)) {
+ if (!display->initiateModeChange(std::move(*desiredModeOpt), constraints, outTimeline)) {
continue;
}
@@ -1471,6 +1473,11 @@
if (displayToUpdateImmediately) {
const auto display = getDisplayDeviceLocked(*displayToUpdateImmediately);
finalizeDisplayModeChange(*display);
+
+ const auto desiredModeOpt = display->getDesiredMode();
+ if (desiredModeOpt && display->getActiveMode() == desiredModeOpt->mode) {
+ applyActiveMode(display);
+ }
}
}
@@ -2674,6 +2681,8 @@
if (const auto display = getCompositionDisplayLocked(id)) {
refreshArgs.outputs.push_back(display);
}
+
+ refreshArgs.frameTargets.try_emplace(id, &targeter->target());
}
std::vector<DisplayId> displayIds;
@@ -2742,23 +2751,10 @@
refreshArgs.devOptFlashDirtyRegionsDelay = std::chrono::milliseconds(mDebugFlashDelay);
}
- const Period minFramePeriod = mScheduler->getVsyncSchedule()->minFramePeriod();
-
- if (!getHwComposer().getComposer()->isSupported(
- Hwc2::Composer::OptionalFeature::ExpectedPresentTime) &&
- pacesetterTarget.wouldPresentEarly(minFramePeriod)) {
- const auto hwcMinWorkDuration = mVsyncConfiguration->getCurrentConfigs().hwcMinWorkDuration;
-
- // TODO(b/255601557): Calculate and pass per-display values for each FrameTarget.
- refreshArgs.earliestPresentTime =
- pacesetterTarget.previousFrameVsyncTime(minFramePeriod) - hwcMinWorkDuration;
- }
-
const TimePoint expectedPresentTime = pacesetterTarget.expectedPresentTime();
// TODO(b/255601557) Update frameInterval per display
refreshArgs.frameInterval = mScheduler->getNextFrameInterval(pacesetterId, expectedPresentTime);
refreshArgs.scheduledFrameTime = mScheduler->getScheduledFrameTime();
- refreshArgs.expectedPresentTime = expectedPresentTime.ns();
refreshArgs.hasTrustedPresentationListener = mNumTrustedPresentationListeners > 0;
{
auto& notifyExpectedPresentData = mNotifyExpectedPresentMap[pacesetterId];
@@ -3055,7 +3051,8 @@
const auto schedule = mScheduler->getVsyncSchedule();
const TimePoint vsyncDeadline = schedule->vsyncDeadlineAfter(presentTime);
const Period vsyncPeriod = schedule->period();
- const nsecs_t vsyncPhase = mVsyncConfiguration->getCurrentConfigs().late.sfOffset;
+ const nsecs_t vsyncPhase =
+ mScheduler->getVsyncConfiguration().getCurrentConfigs().late.sfOffset;
const CompositorTiming compositorTiming(vsyncDeadline.ns(), vsyncPeriod.ns(), vsyncPhase,
presentLatency.ns());
@@ -3732,7 +3729,7 @@
// TODO(b/175678251) Call a listener instead.
if (currentState.physical->hwcDisplayId == getHwComposer().getPrimaryHwcDisplayId()) {
- resetPhaseConfiguration(display->getActiveMode().fps);
+ mScheduler->resetPhaseConfiguration(display->getActiveMode().fps);
}
}
return;
@@ -3768,15 +3765,6 @@
}
}
-void SurfaceFlinger::resetPhaseConfiguration(Fps refreshRate) {
- // Cancel the pending refresh rate change, if any, before updating the phase configuration.
- mScheduler->vsyncModulator().cancelRefreshRateChange();
-
- mVsyncConfiguration->reset();
- updatePhaseConfiguration(refreshRate);
- mRefreshRateStats->setRefreshRate(refreshRate);
-}
-
void SurfaceFlinger::processDisplayChangesLocked() {
// here we take advantage of Vector's copy-on-write semantics to
// improve performance by skipping the transaction entirely when
@@ -4133,7 +4121,7 @@
const auto notifyExpectedPresentConfig =
modePtr->getVrrConfig()->notifyExpectedPresentConfig;
if (!notifyExpectedPresentConfig) return std::nullopt;
- return Period::fromNs(notifyExpectedPresentConfig->notifyExpectedPresentTimeoutNs);
+ return Period::fromNs(notifyExpectedPresentConfig->timeoutNs);
}();
notifyExpectedPresentIfRequired(modePtr->getPhysicalDisplayId(), vsyncPeriod,
@@ -4193,10 +4181,6 @@
const auto activeMode = display->refreshRateSelector().getActiveMode();
const Fps activeRefreshRate = activeMode.fps;
- mRefreshRateStats =
- std::make_unique<RefreshRateStats>(*mTimeStats, activeRefreshRate, hal::PowerMode::OFF);
-
- mVsyncConfiguration = getFactory().createVsyncConfiguration(activeRefreshRate);
FeatureFlags features;
@@ -4222,12 +4206,14 @@
if (mBackpressureGpuComposition) {
features |= Feature::kBackpressureGpuComposition;
}
-
- auto modulatorPtr = sp<VsyncModulator>::make(mVsyncConfiguration->getCurrentConfigs());
+ if (getHwComposer().getComposer()->isSupported(
+ Hwc2::Composer::OptionalFeature::ExpectedPresentTime)) {
+ features |= Feature::kExpectedPresentTime;
+ }
mScheduler = std::make_unique<Scheduler>(static_cast<ICompositor&>(*this),
static_cast<ISchedulerCallback&>(*this), features,
- std::move(modulatorPtr),
+ getFactory(), activeRefreshRate, *mTimeStats,
static_cast<IVsyncTrackerCallback&>(*this));
mScheduler->registerDisplay(display->getPhysicalId(), display->holdRefreshRateSelector());
if (FlagManager::getInstance().vrr_config()) {
@@ -4235,7 +4221,7 @@
}
mScheduler->startTimers();
- const auto configs = mVsyncConfiguration->getCurrentConfigs();
+ const auto configs = mScheduler->getVsyncConfiguration().getCurrentConfigs();
mAppConnectionHandle =
mScheduler->createEventThread(Scheduler::Cycle::Render,
@@ -4257,12 +4243,6 @@
mFpsReporter = sp<FpsReporter>::make(*mFrameTimeline, *this);
}
-void SurfaceFlinger::updatePhaseConfiguration(Fps refreshRate) {
- mVsyncConfiguration->setRefreshRateFps(refreshRate);
- mScheduler->setVsyncConfigSet(mVsyncConfiguration->getCurrentConfigs(),
- refreshRate.getPeriod());
-}
-
void SurfaceFlinger::doCommitTransactions() {
ATRACE_CALL();
@@ -6017,7 +5997,7 @@
if (displayId == mActiveDisplayId) {
mTimeStats->setPowerMode(mode);
- mRefreshRateStats->setPowerMode(mode);
+ mScheduler->setActiveDisplayPowerModeForRefreshRateStats(mode);
}
mScheduler->setDisplayPowerMode(displayId, mode);
@@ -6182,10 +6162,6 @@
dumper.dump("debugDisplayModeSetByBackdoor"sv, mDebugDisplayModeSetByBackdoor);
dumper.eol();
- mRefreshRateStats->dump(result);
- dumper.eol();
-
- mVsyncConfiguration->dump(result);
StringAppendF(&result,
" present offset: %9" PRId64 " ns\t VSYNC period: %9" PRId64
" ns\n\n",
@@ -7395,7 +7371,7 @@
if (!updateOverlay) return;
// Update the overlay on the main thread to avoid race conditions with
- // RefreshRateSelector::getActiveMode.
+ // RefreshRateSelector::getActiveMode
static_cast<void>(mScheduler->schedule([=, this] {
const auto display = FTL_FAKE_GUARD(mStateLock, getDefaultDisplayDeviceLocked());
if (!display) {
@@ -8625,7 +8601,8 @@
}
int SurfaceFlinger::getMaxAcquiredBufferCountForRefreshRate(Fps refreshRate) const {
- const auto vsyncConfig = mVsyncConfiguration->getConfigsForRefreshRate(refreshRate).late;
+ const auto vsyncConfig =
+ mScheduler->getVsyncConfiguration().getConfigsForRefreshRate(refreshRate).late;
const auto presentLatency = vsyncConfig.appWorkDuration + vsyncConfig.sfWorkDuration;
return calculateMaxAcquiredBufferCount(refreshRate, presentLatency);
}
@@ -8718,7 +8695,7 @@
mActiveDisplayId = activeDisplay.getPhysicalId();
activeDisplay.getCompositionDisplay()->setLayerCachingTexturePoolEnabled(true);
- resetPhaseConfiguration(activeDisplay.getActiveMode().fps);
+ mScheduler->resetPhaseConfiguration(activeDisplay.getActiveMode().fps);
// TODO(b/255635711): Check for pending mode changes on other displays.
mScheduler->setModeChangePending(false);
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 4440c7b..5846214 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -82,7 +82,6 @@
#include "MutexUtils.h"
#include "Scheduler/ISchedulerCallback.h"
#include "Scheduler/RefreshRateSelector.h"
-#include "Scheduler/RefreshRateStats.h"
#include "Scheduler/Scheduler.h"
#include "SurfaceFlingerFactory.h"
#include "ThreadContext.h"
@@ -727,9 +726,9 @@
void initiateDisplayModeChanges() REQUIRES(mStateLock, kMainThreadContext);
void finalizeDisplayModeChange(DisplayDevice&) REQUIRES(mStateLock, kMainThreadContext);
- // TODO(b/241285191): Move to Scheduler.
- void dropModeRequest(display::DisplayModeRequest&&) REQUIRES(mStateLock);
- void applyActiveMode(display::DisplayModeRequest&&) REQUIRES(mStateLock);
+ // TODO(b/241285191): Replace DisplayDevice with DisplayModeRequest, and move to Scheduler.
+ void dropModeRequest(const sp<DisplayDevice>&) REQUIRES(mStateLock);
+ void applyActiveMode(const sp<DisplayDevice>&) REQUIRES(mStateLock);
// Called on the main thread in response to setPowerMode()
void setPowerModeInternal(const sp<DisplayDevice>& display, hal::PowerMode mode)
@@ -785,9 +784,6 @@
void initScheduler(const sp<const DisplayDevice>&) REQUIRES(kMainThreadContext, mStateLock);
- void resetPhaseConfiguration(Fps) REQUIRES(mStateLock, kMainThreadContext);
- void updatePhaseConfiguration(Fps) REQUIRES(mStateLock);
-
/*
* Transactions
*/
@@ -1374,10 +1370,6 @@
scheduler::ConnectionHandle mAppConnectionHandle;
scheduler::ConnectionHandle mSfConnectionHandle;
- // Stores phase offsets configured per refresh rate.
- std::unique_ptr<scheduler::VsyncConfiguration> mVsyncConfiguration;
-
- std::unique_ptr<scheduler::RefreshRateStats> mRefreshRateStats;
scheduler::PresentLatencyTracker mPresentLatencyTracker GUARDED_BY(kMainThreadContext);
bool mLumaSampling = true;
diff --git a/services/surfaceflinger/Utils/Dumper.h b/services/surfaceflinger/Utils/Dumper.h
index ee94217..62d2ebb 100644
--- a/services/surfaceflinger/Utils/Dumper.h
+++ b/services/surfaceflinger/Utils/Dumper.h
@@ -35,6 +35,8 @@
void eol() { mOut += '\n'; }
+ std::string& out() { return mOut; }
+
void dump(std::string_view name, std::string_view value = {}) {
using namespace std::string_view_literals;
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
index 4fc39cc..fa79956 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
@@ -224,20 +224,13 @@
class TestableScheduler : public Scheduler, private ICompositor {
public:
- TestableScheduler(const std::shared_ptr<scheduler::RefreshRateSelector>& selectorPtr,
- sp<VsyncModulator> modulatorPtr, ISchedulerCallback& callback,
- IVsyncTrackerCallback& vsyncTrackerCallback)
- : TestableScheduler(std::make_unique<android::mock::VsyncController>(),
- std::make_shared<android::mock::VSyncTracker>(), selectorPtr,
- std::move(modulatorPtr), callback, vsyncTrackerCallback) {}
-
TestableScheduler(std::unique_ptr<VsyncController> controller,
VsyncSchedule::TrackerPtr tracker,
std::shared_ptr<RefreshRateSelector> selectorPtr,
- sp<VsyncModulator> modulatorPtr, ISchedulerCallback& callback,
- IVsyncTrackerCallback& vsyncTrackerCallback)
- : Scheduler(*this, callback, Feature::kContentDetection, std::move(modulatorPtr),
- vsyncTrackerCallback) {
+ surfaceflinger::Factory& factory, TimeStats& timeStats,
+ ISchedulerCallback& callback, IVsyncTrackerCallback& vsyncTrackerCallback)
+ : Scheduler(*this, callback, Feature::kContentDetection, factory,
+ selectorPtr->getActiveMode().fps, timeStats, vsyncTrackerCallback) {
const auto displayId = selectorPtr->getActiveMode().modePtr->getPhysicalDisplayId();
registerDisplayInternal(displayId, std::move(selectorPtr),
std::shared_ptr<VsyncSchedule>(
@@ -613,7 +606,14 @@
mFlinger->commitTransactions();
mFlinger->flushTransactionQueues(getFuzzedVsyncId(mFdp));
- scheduler::FrameTargeter frameTargeter(displayId, mFdp.ConsumeBool());
+ scheduler::FeatureFlags flags;
+ if (mFdp.ConsumeBool()) {
+ flags |= scheduler::Feature::kBackpressureGpuComposition;
+ }
+ if (mFdp.ConsumeBool()) {
+ flags |= scheduler::Feature::kExpectedPresentTime;
+ }
+ scheduler::FrameTargeter frameTargeter(displayId, flags);
mFlinger->onCompositionPresented(displayId, ftl::init::map(displayId, &frameTargeter),
mFdp.ConsumeIntegral<nsecs_t>());
}
@@ -671,19 +671,11 @@
}
mRefreshRateSelector = std::make_shared<scheduler::RefreshRateSelector>(modes, kModeId60);
- const auto fps = mRefreshRateSelector->getActiveMode().modePtr->getVsyncRate();
- mFlinger->mVsyncConfiguration = mFactory.createVsyncConfiguration(fps);
-
- mFlinger->mRefreshRateStats =
- std::make_unique<scheduler::RefreshRateStats>(*mFlinger->mTimeStats, fps,
- hal::PowerMode::OFF);
-
- auto modulatorPtr = sp<scheduler::VsyncModulator>::make(
- mFlinger->mVsyncConfiguration->getCurrentConfigs());
mScheduler = new scheduler::TestableScheduler(std::move(vsyncController),
std::move(vsyncTracker), mRefreshRateSelector,
- std::move(modulatorPtr), *(callback ?: this),
+ mFactory, *mFlinger->mTimeStats,
+ *(callback ?: this),
*(vsyncTrackerCallback ?: this));
mFlinger->mAppConnectionHandle = mScheduler->createConnection(std::move(appEventThread));
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp
index 649ad25..8a5500f 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp
@@ -24,6 +24,7 @@
#include "Scheduler/OneShotTimer.h"
#include "Scheduler/RefreshRateSelector.h"
+#include "Scheduler/RefreshRateStats.h"
#include "Scheduler/VSyncDispatchTimerQueue.h"
#include "Scheduler/VSyncPredictor.h"
#include "Scheduler/VSyncReactor.h"
@@ -425,7 +426,15 @@
}
void SchedulerFuzzer::fuzzFrameTargeter() {
- scheduler::FrameTargeter frameTargeter(kDisplayId, mFdp.ConsumeBool());
+ scheduler::FeatureFlags flags;
+ if (mFdp.ConsumeBool()) {
+ flags |= scheduler::Feature::kBackpressureGpuComposition;
+ }
+ if (mFdp.ConsumeBool()) {
+ flags |= scheduler::Feature::kExpectedPresentTime;
+ }
+
+ scheduler::FrameTargeter frameTargeter(kDisplayId, flags);
const struct VsyncSource final : scheduler::IVsyncSource {
explicit VsyncSource(FuzzedDataProvider& fuzzer) : fuzzer(fuzzer) {}
@@ -441,7 +450,8 @@
frameTargeter.beginFrame({.frameBeginTime = getFuzzedTimePoint(mFdp),
.vsyncId = getFuzzedVsyncId(mFdp),
.expectedVsyncTime = getFuzzedTimePoint(mFdp),
- .sfWorkDuration = getFuzzedDuration(mFdp)},
+ .sfWorkDuration = getFuzzedDuration(mFdp),
+ .hwcMinWorkDuration = getFuzzedDuration(mFdp)},
vsyncSource);
frameTargeter.setPresentFence(makeFakeFence());
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index c75f902..5809ea0 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -118,6 +118,7 @@
"RefreshRateSelectorTest.cpp",
"RefreshRateStatsTest.cpp",
"RegionSamplingTest.cpp",
+ "TestableScheduler.cpp",
"TimeStatsTest.cpp",
"FrameTracerTest.cpp",
"TransactionApplicationTest.cpp",
diff --git a/services/surfaceflinger/tests/unittests/DisplayDevice_InitiateModeChange.cpp b/services/surfaceflinger/tests/unittests/DisplayDevice_InitiateModeChange.cpp
index 220001b..c463a92 100644
--- a/services/surfaceflinger/tests/unittests/DisplayDevice_InitiateModeChange.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayDevice_InitiateModeChange.cpp
@@ -23,13 +23,10 @@
#include <gmock/gmock.h>
#include <gtest/gtest.h>
-#define EXPECT_DISPLAY_MODE_REQUEST(expected, request) \
- EXPECT_FRAME_RATE_MODE(expected.mode.modePtr, expected.mode.fps, request.mode); \
- EXPECT_EQ(expected.emitEvent, request.emitEvent)
-
-#define EXPECT_DISPLAY_MODE_REQUEST_OPT(expected, requestOpt) \
- ASSERT_TRUE(requestOpt); \
- EXPECT_DISPLAY_MODE_REQUEST(expected, (*requestOpt))
+#define EXPECT_DISPLAY_MODE_REQUEST(expected, requestOpt) \
+ ASSERT_TRUE(requestOpt); \
+ EXPECT_FRAME_RATE_MODE(expected.mode.modePtr, expected.mode.fps, requestOpt->mode); \
+ EXPECT_EQ(expected.emitEvent, requestOpt->emitEvent)
namespace android {
namespace {
@@ -40,7 +37,6 @@
class InitiateModeChangeTest : public DisplayTransactionTest {
public:
using Action = DisplayDevice::DesiredModeAction;
-
void SetUp() override {
injectFakeBufferQueueFactory();
injectFakeNativeWindowSurfaceFactory();
@@ -88,43 +84,36 @@
TEST_F(InitiateModeChangeTest, setDesiredMode) {
EXPECT_EQ(Action::InitiateDisplayModeSwitch,
mDisplay->setDesiredMode(DisplayModeRequest(kDesiredMode90)));
- EXPECT_DISPLAY_MODE_REQUEST_OPT(kDesiredMode90, mDisplay->getDesiredMode());
+ EXPECT_DISPLAY_MODE_REQUEST(kDesiredMode90, mDisplay->getDesiredMode());
EXPECT_EQ(Action::None, mDisplay->setDesiredMode(DisplayModeRequest(kDesiredMode120)));
- EXPECT_DISPLAY_MODE_REQUEST_OPT(kDesiredMode120, mDisplay->getDesiredMode());
+ EXPECT_DISPLAY_MODE_REQUEST(kDesiredMode120, mDisplay->getDesiredMode());
}
-TEST_F(InitiateModeChangeTest, takeDesiredMode) {
+TEST_F(InitiateModeChangeTest, clearDesiredMode) {
EXPECT_EQ(Action::InitiateDisplayModeSwitch,
mDisplay->setDesiredMode(DisplayModeRequest(kDesiredMode90)));
- EXPECT_EQ(kDesiredMode90, mDisplay->getDesiredMode());
+ EXPECT_TRUE(mDisplay->getDesiredMode());
- EXPECT_EQ(kDesiredMode90, mDisplay->takeDesiredMode());
+ mDisplay->clearDesiredMode();
EXPECT_FALSE(mDisplay->getDesiredMode());
}
-TEST_F(InitiateModeChangeTest, initiateModeChange) FTL_FAKE_GUARD(kMainThreadContext) {
+TEST_F(InitiateModeChangeTest, initiateModeChange) REQUIRES(kMainThreadContext) {
EXPECT_EQ(Action::InitiateDisplayModeSwitch,
mDisplay->setDesiredMode(DisplayModeRequest(kDesiredMode90)));
- EXPECT_DISPLAY_MODE_REQUEST_OPT(kDesiredMode90, mDisplay->getDesiredMode());
+ EXPECT_DISPLAY_MODE_REQUEST(kDesiredMode90, mDisplay->getDesiredMode());
const hal::VsyncPeriodChangeConstraints constraints{
.desiredTimeNanos = systemTime(),
.seamlessRequired = false,
};
hal::VsyncPeriodChangeTimeline timeline;
+ EXPECT_TRUE(mDisplay->initiateModeChange(*mDisplay->getDesiredMode(), constraints, timeline));
+ EXPECT_DISPLAY_MODE_REQUEST(kDesiredMode90, mDisplay->getPendingMode());
- auto desiredModeOpt = mDisplay->takeDesiredMode();
- ASSERT_TRUE(desiredModeOpt);
+ mDisplay->clearDesiredMode();
EXPECT_FALSE(mDisplay->getDesiredMode());
-
- EXPECT_TRUE(mDisplay->initiateModeChange(std::move(*desiredModeOpt), constraints, timeline));
-
- auto modeChange = mDisplay->finalizeModeChange();
-
- auto* changePtr = std::get_if<DisplayDevice::RefreshRateChange>(&modeChange);
- ASSERT_TRUE(changePtr);
- EXPECT_DISPLAY_MODE_REQUEST(kDesiredMode90, changePtr->activeMode);
}
TEST_F(InitiateModeChangeTest, initiateRenderRateSwitch) {
@@ -136,47 +125,27 @@
TEST_F(InitiateModeChangeTest, initiateDisplayModeSwitch) FTL_FAKE_GUARD(kMainThreadContext) {
EXPECT_EQ(Action::InitiateDisplayModeSwitch,
mDisplay->setDesiredMode(DisplayModeRequest(kDesiredMode90)));
- EXPECT_DISPLAY_MODE_REQUEST_OPT(kDesiredMode90, mDisplay->getDesiredMode());
+ EXPECT_DISPLAY_MODE_REQUEST(kDesiredMode90, mDisplay->getDesiredMode());
const hal::VsyncPeriodChangeConstraints constraints{
.desiredTimeNanos = systemTime(),
.seamlessRequired = false,
};
hal::VsyncPeriodChangeTimeline timeline;
+ EXPECT_TRUE(mDisplay->initiateModeChange(*mDisplay->getDesiredMode(), constraints, timeline));
+ EXPECT_DISPLAY_MODE_REQUEST(kDesiredMode90, mDisplay->getPendingMode());
- auto desiredModeOpt = mDisplay->takeDesiredMode();
- ASSERT_TRUE(desiredModeOpt);
- EXPECT_FALSE(mDisplay->getDesiredMode());
-
- EXPECT_TRUE(mDisplay->initiateModeChange(std::move(*desiredModeOpt), constraints, timeline));
-
- auto modeChange = mDisplay->finalizeModeChange();
- auto* changePtr = std::get_if<DisplayDevice::RefreshRateChange>(&modeChange);
- ASSERT_TRUE(changePtr);
- EXPECT_DISPLAY_MODE_REQUEST(kDesiredMode90, changePtr->activeMode);
-
- // The latest request should override the desired mode.
- EXPECT_EQ(Action::InitiateDisplayModeSwitch,
- mDisplay->setDesiredMode(DisplayModeRequest(kDesiredMode60)));
EXPECT_EQ(Action::None, mDisplay->setDesiredMode(DisplayModeRequest(kDesiredMode120)));
+ ASSERT_TRUE(mDisplay->getDesiredMode());
+ EXPECT_DISPLAY_MODE_REQUEST(kDesiredMode120, mDisplay->getDesiredMode());
- EXPECT_DISPLAY_MODE_REQUEST_OPT(kDesiredMode120, mDisplay->getDesiredMode());
+ EXPECT_DISPLAY_MODE_REQUEST(kDesiredMode90, mDisplay->getPendingMode());
- // No pending mode change.
- EXPECT_TRUE(
- std::holds_alternative<DisplayDevice::NoModeChange>(mDisplay->finalizeModeChange()));
+ EXPECT_TRUE(mDisplay->initiateModeChange(*mDisplay->getDesiredMode(), constraints, timeline));
+ EXPECT_DISPLAY_MODE_REQUEST(kDesiredMode120, mDisplay->getPendingMode());
- desiredModeOpt = mDisplay->takeDesiredMode();
- ASSERT_TRUE(desiredModeOpt);
+ mDisplay->clearDesiredMode();
EXPECT_FALSE(mDisplay->getDesiredMode());
-
- EXPECT_TRUE(mDisplay->initiateModeChange(std::move(*desiredModeOpt), constraints, timeline));
-
- modeChange = mDisplay->finalizeModeChange();
-
- changePtr = std::get_if<DisplayDevice::RefreshRateChange>(&modeChange);
- ASSERT_TRUE(changePtr);
- EXPECT_DISPLAY_MODE_REQUEST(kDesiredMode120, changePtr->activeMode);
}
} // namespace
diff --git a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
index 4e8a609..45db0c5 100644
--- a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
+++ b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
@@ -55,6 +55,9 @@
constexpr std::chrono::duration VSYNC_PERIOD(16ms);
+constexpr int HDCP_V1 = 2;
+constexpr int HDCP_V2 = 3;
+
} // namespace
class EventThreadTest : public testing::Test, public IEventThreadCallback {
@@ -833,6 +836,19 @@
expectVSyncCallbackScheduleReceived(true);
}
+TEST_F(EventThreadTest, postHcpLevelsChanged) {
+ setupEventThread();
+
+ mThread->onHdcpLevelsChanged(EXTERNAL_DISPLAY_ID, HDCP_V1, HDCP_V2);
+ auto args = mConnectionEventCallRecorder.waitForCall();
+ ASSERT_TRUE(args.has_value());
+ const auto& event = std::get<0>(args.value());
+ EXPECT_EQ(DisplayEventReceiver::DISPLAY_EVENT_HDCP_LEVELS_CHANGE, event.header.type);
+ EXPECT_EQ(EXTERNAL_DISPLAY_ID, event.header.displayId);
+ EXPECT_EQ(HDCP_V1, event.hdcpLevelsChange.connectedLevel);
+ EXPECT_EQ(HDCP_V2, event.hdcpLevelsChange.maxLevel);
+}
+
} // namespace
} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
index 6edecff..a5c0657 100644
--- a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
@@ -21,6 +21,7 @@
#undef LOG_TAG
#define LOG_TAG "LibSurfaceFlingerUnittests"
+#include <optional>
#include <vector>
// StrictMock<T> derives from T and is not marked final, so the destructor of T is expected to be
@@ -82,6 +83,8 @@
EXPECT_CALL(*mHal, setVsyncEnabled(hwcDisplayId, Hwc2::IComposerClient::Vsync::DISABLE));
EXPECT_CALL(*mHal, onHotplugConnect(hwcDisplayId));
}
+
+ void setVrrTimeoutHint(bool status) { mHwc.mEnableVrrTimeout = status; }
};
TEST_F(HWComposerTest, isHeadless) {
@@ -323,6 +326,7 @@
EXPECT_TRUE(mHwc.getModes(info->id, kMaxFrameIntervalNs).empty());
}
{
+ setVrrTimeoutHint(true);
constexpr int32_t kWidth = 480;
constexpr int32_t kHeight = 720;
constexpr int32_t kConfigGroup = 1;
@@ -330,10 +334,8 @@
const hal::VrrConfig vrrConfig =
hal::VrrConfig{.minFrameIntervalNs = static_cast<Fps>(120_Hz).getPeriodNsecs(),
.notifyExpectedPresentConfig = hal::VrrConfig::
- NotifyExpectedPresentConfig{.notifyExpectedPresentHeadsUpNs =
- ms2ns(30),
- .notifyExpectedPresentTimeoutNs =
- ms2ns(30)}};
+ NotifyExpectedPresentConfig{.headsUpNs = ms2ns(30),
+ .timeoutNs = ms2ns(30)}};
hal::DisplayConfiguration displayConfiguration{.configId = kConfigId,
.width = kWidth,
.height = kHeight,
@@ -363,9 +365,9 @@
displayConfiguration.dpi = {kDpi, kDpi};
EXPECT_CALL(*mHal, getDisplayConfigurations(kHwcDisplayId, _, _))
- .WillOnce(DoAll(SetArgPointee<2>(std::vector<hal::DisplayConfiguration>{
- displayConfiguration}),
- Return(HalError::NONE)));
+ .WillRepeatedly(DoAll(SetArgPointee<2>(std::vector<hal::DisplayConfiguration>{
+ displayConfiguration}),
+ Return(HalError::NONE)));
modes = mHwc.getModes(info->id, kMaxFrameIntervalNs);
EXPECT_EQ(modes.size(), size_t{1});
@@ -377,6 +379,10 @@
EXPECT_EQ(modes.front().vrrConfig, vrrConfig);
EXPECT_EQ(modes.front().dpiX, kDpi);
EXPECT_EQ(modes.front().dpiY, kDpi);
+
+ setVrrTimeoutHint(false);
+ modes = mHwc.getModes(info->id, kMaxFrameIntervalNs);
+ EXPECT_EQ(modes.front().vrrConfig->notifyExpectedPresentConfig, std::nullopt);
}
}
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp
index e9d2319..734fddb 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp
@@ -167,10 +167,10 @@
mock::SchedulerCallback mSchedulerCallback;
mock::VsyncTrackerCallback mVsyncTrackerCallback;
- TestableScheduler* mScheduler =
- new TestableScheduler(mSelector, mSchedulerCallback, mVsyncTrackerCallback);
-
TestableSurfaceFlinger mFlinger;
+
+ TestableScheduler* mScheduler =
+ new TestableScheduler(mSelector, mFlinger, mSchedulerCallback, mVsyncTrackerCallback);
};
namespace {
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
index 0ae3ca3..9456e37 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
@@ -148,10 +148,9 @@
mock::SchedulerCallback mSchedulerCallback;
mock::VsyncTrackerCallback mVsyncTrackerCallback;
- TestableScheduler* mScheduler =
- new TestableScheduler(mSelector, mSchedulerCallback, mVsyncTrackerCallback);
-
TestableSurfaceFlinger mFlinger;
+ TestableScheduler* mScheduler =
+ new TestableScheduler(mSelector, mFlinger, mSchedulerCallback, mVsyncTrackerCallback);
};
namespace {
diff --git a/services/surfaceflinger/tests/unittests/LayerInfoTest.cpp b/services/surfaceflinger/tests/unittests/LayerInfoTest.cpp
index 07a522a..22cfbd8 100644
--- a/services/surfaceflinger/tests/unittests/LayerInfoTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerInfoTest.cpp
@@ -65,9 +65,9 @@
DisplayModeId(0));
mock::SchedulerCallback mSchedulerCallback;
mock::VsyncTrackerCallback mVsyncTrackerCallback;
- TestableScheduler* mScheduler =
- new TestableScheduler(mSelector, mSchedulerCallback, mVsyncTrackerCallback);
TestableSurfaceFlinger mFlinger;
+ TestableScheduler* mScheduler =
+ new TestableScheduler(mSelector, mFlinger, mSchedulerCallback, mVsyncTrackerCallback);
};
namespace {
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index 7fdca71..6986689 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -96,15 +96,14 @@
mock::SchedulerCallback mSchedulerCallback;
mock::VsyncTrackerCallback mVsyncTrackerCallback;
+ TestableSurfaceFlinger mFlinger;
TestableScheduler* mScheduler =
- new TestableScheduler{mSelector, mSchedulerCallback, mVsyncTrackerCallback};
+ new TestableScheduler{mSelector, mFlinger, mSchedulerCallback, mVsyncTrackerCallback};
surfaceflinger::frontend::LayerHierarchyBuilder mLayerHierarchyBuilder;
ConnectionHandle mConnectionHandle;
MockEventThread* mEventThread;
sp<MockEventThreadConnection> mEventThreadConnection;
-
- TestableSurfaceFlinger mFlinger;
};
SchedulerTest::SchedulerTest() {
@@ -575,7 +574,8 @@
TestableScheduler scheduler{std::make_unique<android::mock::VsyncController>(),
vrrTracker,
vrrSelectorPtr,
- sp<VsyncModulator>::make(VsyncConfigSet{}),
+ mFlinger.getFactory(),
+ mFlinger.getTimeStats(),
mSchedulerCallback,
mVsyncTrackerCallback};
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
index 3291dc3..8b16a8a 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
@@ -179,7 +179,7 @@
Mock::VerifyAndClearExpectations(mComposer);
- EXPECT_FALSE(mDisplay->getDesiredMode());
+ EXPECT_TRUE(mDisplay->getDesiredMode());
EXPECT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId60);
// Verify that the next commit will complete the mode change and send
@@ -263,13 +263,11 @@
mFlinger.commit();
- // The 120 Hz mode should be pending.
- EXPECT_FALSE(mDisplay->getDesiredMode());
- EXPECT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId90);
+ ASSERT_TRUE(mDisplay->getDesiredMode());
+ EXPECT_EQ(mDisplay->getDesiredMode()->mode.modePtr->getId(), kModeId120);
mFlinger.commit();
- // The 120 Hz mode should be active.
EXPECT_FALSE(mDisplay->getDesiredMode());
EXPECT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId120);
}
@@ -326,7 +324,7 @@
EXPECT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId90_4K);
}
-MATCHER_P2(HasDesiredMode, flinger, modeId, "") {
+MATCHER_P2(ModeSwitchingTo, flinger, modeId, "") {
if (!arg->getDesiredMode()) {
*result_listener << "No desired mode";
return false;
@@ -345,33 +343,12 @@
return true;
}
-MATCHER_P(HasPendingMode, modeId, "") {
- const auto pendingOpt = TestableSurfaceFlinger::getPendingMode(arg);
-
- if (!pendingOpt) {
- *result_listener << "No pending mode";
- return false;
- }
-
- if (pendingOpt->mode.modePtr->getId() != modeId) {
- *result_listener << "Unexpected pending mode " << modeId;
- return false;
- }
-
- return true;
-}
-
-MATCHER_P(HasActiveMode, modeId, "") {
+MATCHER_P(ModeSettledTo, modeId, "") {
if (const auto desiredOpt = arg->getDesiredMode()) {
*result_listener << "Unsettled desired mode " << desiredOpt->mode.modePtr->getId();
return false;
}
- if (const auto pendingOpt = TestableSurfaceFlinger::getPendingMode(arg)) {
- *result_listener << "Unsettled pending mode " << pendingOpt->mode.modePtr->getId();
- return false;
- }
-
ftl::FakeGuard guard(kMainThreadContext);
if (arg->getActiveMode().modePtr->getId() != modeId) {
@@ -388,14 +365,14 @@
EXPECT_TRUE(innerDisplay->isPoweredOn());
EXPECT_FALSE(outerDisplay->isPoweredOn());
- EXPECT_THAT(innerDisplay, HasActiveMode(kModeId60));
- EXPECT_THAT(outerDisplay, HasActiveMode(kModeId120));
+ EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId60));
+ EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId120));
// Only the inner display is powered on.
mFlinger.onActiveDisplayChanged(nullptr, *innerDisplay);
- EXPECT_THAT(innerDisplay, HasActiveMode(kModeId60));
- EXPECT_THAT(outerDisplay, HasActiveMode(kModeId120));
+ EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId60));
+ EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId120));
EXPECT_EQ(NO_ERROR,
mFlinger.setDesiredDisplayModeSpecs(innerDisplay->getDisplayToken().promote(),
@@ -407,8 +384,8 @@
mock::createDisplayModeSpecs(kModeId60.value(),
false, 0.f, 120.f)));
- EXPECT_THAT(innerDisplay, HasDesiredMode(&mFlinger, kModeId90));
- EXPECT_THAT(outerDisplay, HasActiveMode(kModeId120));
+ EXPECT_THAT(innerDisplay, ModeSwitchingTo(&mFlinger, kModeId90));
+ EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId120));
const VsyncPeriodChangeTimeline timeline{.refreshRequired = true};
EXPECT_CALL(*mComposer,
@@ -418,13 +395,13 @@
mFlinger.commit();
- EXPECT_THAT(innerDisplay, HasPendingMode(kModeId90));
- EXPECT_THAT(outerDisplay, HasActiveMode(kModeId120));
+ EXPECT_THAT(innerDisplay, ModeSwitchingTo(&mFlinger, kModeId90));
+ EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId120));
mFlinger.commit();
- EXPECT_THAT(innerDisplay, HasActiveMode(kModeId90));
- EXPECT_THAT(outerDisplay, HasActiveMode(kModeId120));
+ EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId90));
+ EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId120));
innerDisplay->setPowerMode(hal::PowerMode::OFF);
outerDisplay->setPowerMode(hal::PowerMode::ON);
@@ -432,8 +409,8 @@
// Only the outer display is powered on.
mFlinger.onActiveDisplayChanged(innerDisplay.get(), *outerDisplay);
- EXPECT_THAT(innerDisplay, HasActiveMode(kModeId90));
- EXPECT_THAT(outerDisplay, HasDesiredMode(&mFlinger, kModeId60));
+ EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId90));
+ EXPECT_THAT(outerDisplay, ModeSwitchingTo(&mFlinger, kModeId60));
EXPECT_CALL(*mComposer,
setActiveConfigWithConstraints(kOuterDisplayHwcId,
@@ -442,13 +419,13 @@
mFlinger.commit();
- EXPECT_THAT(innerDisplay, HasActiveMode(kModeId90));
- EXPECT_THAT(outerDisplay, HasPendingMode(kModeId60));
+ EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId90));
+ EXPECT_THAT(outerDisplay, ModeSwitchingTo(&mFlinger, kModeId60));
mFlinger.commit();
- EXPECT_THAT(innerDisplay, HasActiveMode(kModeId90));
- EXPECT_THAT(outerDisplay, HasActiveMode(kModeId60));
+ EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId90));
+ EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId60));
}
TEST_F(DisplayModeSwitchingTest, innerAndOuterDisplay) {
@@ -457,16 +434,16 @@
EXPECT_TRUE(innerDisplay->isPoweredOn());
EXPECT_FALSE(outerDisplay->isPoweredOn());
- EXPECT_THAT(innerDisplay, HasActiveMode(kModeId60));
- EXPECT_THAT(outerDisplay, HasActiveMode(kModeId120));
+ EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId60));
+ EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId120));
outerDisplay->setPowerMode(hal::PowerMode::ON);
// Both displays are powered on.
mFlinger.onActiveDisplayChanged(nullptr, *innerDisplay);
- EXPECT_THAT(innerDisplay, HasActiveMode(kModeId60));
- EXPECT_THAT(outerDisplay, HasActiveMode(kModeId120));
+ EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId60));
+ EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId120));
EXPECT_EQ(NO_ERROR,
mFlinger.setDesiredDisplayModeSpecs(innerDisplay->getDisplayToken().promote(),
@@ -478,8 +455,8 @@
mock::createDisplayModeSpecs(kModeId60.value(),
false, 0.f, 120.f)));
- EXPECT_THAT(innerDisplay, HasDesiredMode(&mFlinger, kModeId90));
- EXPECT_THAT(outerDisplay, HasDesiredMode(&mFlinger, kModeId60));
+ EXPECT_THAT(innerDisplay, ModeSwitchingTo(&mFlinger, kModeId90));
+ EXPECT_THAT(outerDisplay, ModeSwitchingTo(&mFlinger, kModeId60));
const VsyncPeriodChangeTimeline timeline{.refreshRequired = true};
EXPECT_CALL(*mComposer,
@@ -494,25 +471,25 @@
mFlinger.commit();
- EXPECT_THAT(innerDisplay, HasPendingMode(kModeId90));
- EXPECT_THAT(outerDisplay, HasPendingMode(kModeId60));
+ EXPECT_THAT(innerDisplay, ModeSwitchingTo(&mFlinger, kModeId90));
+ EXPECT_THAT(outerDisplay, ModeSwitchingTo(&mFlinger, kModeId60));
mFlinger.commit();
- EXPECT_THAT(innerDisplay, HasActiveMode(kModeId90));
- EXPECT_THAT(outerDisplay, HasActiveMode(kModeId60));
+ EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId90));
+ EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId60));
}
TEST_F(DisplayModeSwitchingTest, powerOffDuringModeSet) {
EXPECT_TRUE(mDisplay->isPoweredOn());
- EXPECT_THAT(mDisplay, HasActiveMode(kModeId60));
+ EXPECT_THAT(mDisplay, ModeSettledTo(kModeId60));
EXPECT_EQ(NO_ERROR,
mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(),
mock::createDisplayModeSpecs(kModeId90.value(),
false, 0.f, 120.f)));
- EXPECT_THAT(mDisplay, HasDesiredMode(&mFlinger, kModeId90));
+ EXPECT_THAT(mDisplay, ModeSwitchingTo(&mFlinger, kModeId90));
// Power off the display before the mode has been set.
mDisplay->setPowerMode(hal::PowerMode::OFF);
@@ -527,11 +504,11 @@
// Powering off should not abort the mode set.
EXPECT_FALSE(mDisplay->isPoweredOn());
- EXPECT_THAT(mDisplay, HasPendingMode(kModeId90));
+ EXPECT_THAT(mDisplay, ModeSwitchingTo(&mFlinger, kModeId90));
mFlinger.commit();
- EXPECT_THAT(mDisplay, HasActiveMode(kModeId90));
+ EXPECT_THAT(mDisplay, ModeSettledTo(kModeId90));
}
TEST_F(DisplayModeSwitchingTest, powerOffDuringConcurrentModeSet) {
@@ -540,16 +517,16 @@
EXPECT_TRUE(innerDisplay->isPoweredOn());
EXPECT_FALSE(outerDisplay->isPoweredOn());
- EXPECT_THAT(innerDisplay, HasActiveMode(kModeId60));
- EXPECT_THAT(outerDisplay, HasActiveMode(kModeId120));
+ EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId60));
+ EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId120));
outerDisplay->setPowerMode(hal::PowerMode::ON);
// Both displays are powered on.
mFlinger.onActiveDisplayChanged(nullptr, *innerDisplay);
- EXPECT_THAT(innerDisplay, HasActiveMode(kModeId60));
- EXPECT_THAT(outerDisplay, HasActiveMode(kModeId120));
+ EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId60));
+ EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId120));
EXPECT_EQ(NO_ERROR,
mFlinger.setDesiredDisplayModeSpecs(innerDisplay->getDisplayToken().promote(),
@@ -561,8 +538,8 @@
mock::createDisplayModeSpecs(kModeId60.value(),
false, 0.f, 120.f)));
- EXPECT_THAT(innerDisplay, HasDesiredMode(&mFlinger, kModeId90));
- EXPECT_THAT(outerDisplay, HasDesiredMode(&mFlinger, kModeId60));
+ EXPECT_THAT(innerDisplay, ModeSwitchingTo(&mFlinger, kModeId90));
+ EXPECT_THAT(outerDisplay, ModeSwitchingTo(&mFlinger, kModeId60));
// Power off the outer display before the mode has been set.
outerDisplay->setPowerMode(hal::PowerMode::OFF);
@@ -576,13 +553,13 @@
mFlinger.commit();
// Powering off the inactive display should abort the mode set.
- EXPECT_THAT(innerDisplay, HasPendingMode(kModeId90));
- EXPECT_THAT(outerDisplay, HasActiveMode(kModeId120));
+ EXPECT_THAT(innerDisplay, ModeSwitchingTo(&mFlinger, kModeId90));
+ EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId120));
mFlinger.commit();
- EXPECT_THAT(innerDisplay, HasActiveMode(kModeId90));
- EXPECT_THAT(outerDisplay, HasActiveMode(kModeId120));
+ EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId90));
+ EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId120));
innerDisplay->setPowerMode(hal::PowerMode::OFF);
outerDisplay->setPowerMode(hal::PowerMode::ON);
@@ -598,13 +575,13 @@
mFlinger.commit();
// The mode set should resume once the display becomes active.
- EXPECT_THAT(innerDisplay, HasActiveMode(kModeId90));
- EXPECT_THAT(outerDisplay, HasPendingMode(kModeId60));
+ EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId90));
+ EXPECT_THAT(outerDisplay, ModeSwitchingTo(&mFlinger, kModeId60));
mFlinger.commit();
- EXPECT_THAT(innerDisplay, HasActiveMode(kModeId90));
- EXPECT_THAT(outerDisplay, HasActiveMode(kModeId60));
+ EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId90));
+ EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId60));
}
} // namespace
diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.cpp b/services/surfaceflinger/tests/unittests/TestableScheduler.cpp
new file mode 100644
index 0000000..e0b7366
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.cpp
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "TestableScheduler.h"
+#include "TestableSurfaceFlinger.h"
+
+namespace android::scheduler {
+
+TestableScheduler::TestableScheduler(RefreshRateSelectorPtr selectorPtr,
+ TestableSurfaceFlinger& testableSurfaceFlinger,
+ ISchedulerCallback& callback,
+ IVsyncTrackerCallback& vsyncTrackerCallback)
+ : TestableScheduler(std::make_unique<android::mock::VsyncController>(),
+ std::make_shared<android::mock::VSyncTracker>(), std::move(selectorPtr),
+ testableSurfaceFlinger.getFactory(),
+ testableSurfaceFlinger.getTimeStats(), callback, vsyncTrackerCallback) {}
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h
index 2a1b88e..6213713 100644
--- a/services/surfaceflinger/tests/unittests/TestableScheduler.h
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h
@@ -32,25 +32,27 @@
#include "mock/MockVSyncTracker.h"
#include "mock/MockVsyncController.h"
+namespace android {
+class TestableSurfaceFlinger;
+} // namespace android
+
namespace android::scheduler {
class TestableScheduler : public Scheduler, private ICompositor {
public:
- TestableScheduler(RefreshRateSelectorPtr selectorPtr, ISchedulerCallback& callback,
- IVsyncTrackerCallback& vsyncTrackerCallback)
- : TestableScheduler(std::make_unique<mock::VsyncController>(),
- std::make_shared<mock::VSyncTracker>(), std::move(selectorPtr),
- sp<VsyncModulator>::make(VsyncConfigSet{}), callback,
- vsyncTrackerCallback) {}
+ TestableScheduler(RefreshRateSelectorPtr selectorPtr,
+ TestableSurfaceFlinger& testableSurfaceFlinger, ISchedulerCallback& callback,
+ IVsyncTrackerCallback& vsyncTrackerCallback);
TestableScheduler(std::unique_ptr<VsyncController> controller,
std::shared_ptr<VSyncTracker> tracker, RefreshRateSelectorPtr selectorPtr,
- sp<VsyncModulator> modulatorPtr, ISchedulerCallback& schedulerCallback,
+ surfaceflinger::Factory& factory, TimeStats& timeStats,
+ ISchedulerCallback& schedulerCallback,
IVsyncTrackerCallback& vsyncTrackerCallback)
: Scheduler(*this, schedulerCallback,
(FeatureFlags)Feature::kContentDetection |
Feature::kSmallDirtyContentDetection,
- std::move(modulatorPtr), vsyncTrackerCallback) {
+ factory, selectorPtr->getActiveMode().fps, timeStats, vsyncTrackerCallback) {
const auto displayId = selectorPtr->getActiveMode().modePtr->getPhysicalDisplayId();
registerDisplay(displayId, std::move(selectorPtr), std::move(controller),
std::move(tracker));
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 16815be..f00eacc 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -216,6 +216,10 @@
using DisplayModesVariant = std::variant<DefaultDisplayMode, RefreshRateSelectorPtr>;
+ surfaceflinger::Factory& getFactory() { return mFactory; }
+
+ TimeStats& getTimeStats() { return *mFlinger->mTimeStats; }
+
void setupScheduler(
std::unique_ptr<scheduler::VsyncController> vsyncController,
std::shared_ptr<scheduler::VSyncTracker> vsyncTracker,
@@ -234,13 +238,6 @@
},
[](RefreshRateSelectorPtr selectorPtr) { return selectorPtr; });
- const auto fps = selectorPtr->getActiveMode().fps;
- mFlinger->mVsyncConfiguration = mFactory.createVsyncConfiguration(fps);
-
- mFlinger->mRefreshRateStats =
- std::make_unique<scheduler::RefreshRateStats>(*mFlinger->mTimeStats, fps,
- hal::PowerMode::OFF);
-
mTokenManager = std::make_unique<frametimeline::impl::TokenManager>();
using ISchedulerCallback = scheduler::ISchedulerCallback;
@@ -254,23 +251,21 @@
? static_cast<VsyncTrackerCallback&>(mNoOpVsyncTrackerCallback)
: static_cast<VsyncTrackerCallback&>(mVsyncTrackerCallback);
- auto modulatorPtr = sp<scheduler::VsyncModulator>::make(
- mFlinger->mVsyncConfiguration->getCurrentConfigs());
-
if (useNiceMock) {
mScheduler =
new testing::NiceMock<scheduler::TestableScheduler>(std::move(vsyncController),
std::move(vsyncTracker),
std::move(selectorPtr),
- std::move(modulatorPtr),
+ mFactory,
+ *mFlinger->mTimeStats,
schedulerCallback,
vsyncTrackerCallback);
} else {
mScheduler = new scheduler::TestableScheduler(std::move(vsyncController),
std::move(vsyncTracker),
- std::move(selectorPtr),
- std::move(modulatorPtr),
- schedulerCallback, vsyncTrackerCallback);
+ std::move(selectorPtr), mFactory,
+ *mFlinger->mTimeStats, schedulerCallback,
+ vsyncTrackerCallback);
}
mScheduler->initVsync(mScheduler->getVsyncSchedule()->getDispatch(), *mTokenManager, 0ms);
@@ -391,13 +386,14 @@
LOG_ALWAYS_FATAL_IF(!displayIdOpt);
const auto displayId = *displayIdOpt;
- constexpr bool kBackpressureGpuComposition = true;
- scheduler::FrameTargeter frameTargeter(displayId, kBackpressureGpuComposition);
+ scheduler::FrameTargeter frameTargeter(displayId,
+ scheduler::Feature::kBackpressureGpuComposition);
frameTargeter.beginFrame({.frameBeginTime = frameTime,
.vsyncId = vsyncId,
.expectedVsyncTime = expectedVsyncTime,
- .sfWorkDuration = 10ms},
+ .sfWorkDuration = 10ms,
+ .hwcMinWorkDuration = 10ms},
*mScheduler->getVsyncSchedule());
scheduler::FrameTargets targets;
@@ -484,10 +480,6 @@
return mFlinger->setDisplayBrightness(display, brightness);
}
- static const auto& getPendingMode(const sp<DisplayDevice>& display) {
- return display->mPendingModeOpt;
- }
-
// Allow reading display state without locking, as if called on the SF main thread.
auto setPowerModeInternal(const sp<DisplayDevice>& display,
hal::PowerMode mode) NO_THREAD_SAFETY_ANALYSIS {
diff --git a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
index 9afaabe..961ba57 100644
--- a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
@@ -662,7 +662,7 @@
const auto refreshRate = Fps::fromPeriodNsecs(mPeriod);
NotifyExpectedPresentConfig notifyExpectedPresentConfig;
- notifyExpectedPresentConfig.notifyExpectedPresentTimeoutNs = Period::fromNs(30).ns();
+ notifyExpectedPresentConfig.timeoutNs = Period::fromNs(30).ns();
hal::VrrConfig vrrConfig;
vrrConfig.notifyExpectedPresentConfig = notifyExpectedPresentConfig;
diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp
index 0df5e77..6b3c379 100644
--- a/vulkan/libvulkan/swapchain.cpp
+++ b/vulkan/libvulkan/swapchain.cpp
@@ -1762,6 +1762,8 @@
}
int query_value;
+ // TODO: Now that we are calling into GPDSC2 directly, this query may be redundant
+ // the call to std::max(min_buffer_count, num_images) may be redundant as well
err = window->query(window, NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
&query_value);
if (err != android::OK || query_value < 0) {
@@ -1778,12 +1780,33 @@
// with extra images (which they can't actually use!).
const uint32_t min_buffer_count = min_undequeued_buffers + 1;
- uint32_t num_images;
- if (create_info->presentMode == VK_PRESENT_MODE_MAILBOX_KHR) {
- num_images = std::max(3u, create_info->minImageCount);
- } else {
- num_images = create_info->minImageCount;
- }
+ // Call into GPDSC2 to get the minimum and maximum allowable buffer count for the surface of
+ // interest. This step is only necessary if the app requests a number of images
+ // (create_info->minImageCount) that is less or more than the surface capabilities.
+ // An app should be calling GPDSC2 and using those values to set create_info, but in the
+ // event that the app has hard-coded image counts an error can occur
+ VkSurfacePresentModeEXT present_mode = {
+ VK_STRUCTURE_TYPE_SURFACE_PRESENT_MODE_EXT,
+ nullptr,
+ create_info->presentMode
+ };
+ VkPhysicalDeviceSurfaceInfo2KHR surface_info2 = {
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SURFACE_INFO_2_KHR,
+ &present_mode,
+ create_info->surface
+ };
+ VkSurfaceCapabilities2KHR surface_capabilities2 = {
+ VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_2_KHR,
+ nullptr,
+ {},
+ };
+ result = GetPhysicalDeviceSurfaceCapabilities2KHR(GetData(device).driver_physical_device,
+ &surface_info2, &surface_capabilities2);
+
+ uint32_t num_images = create_info->minImageCount;
+ num_images = std::clamp(num_images,
+ surface_capabilities2.surfaceCapabilities.minImageCount,
+ surface_capabilities2.surfaceCapabilities.maxImageCount);
const uint32_t buffer_count = std::max(min_buffer_count, num_images);
err = native_window_set_buffer_count(window, buffer_count);