Merge "SF: Add autorefresh to drawingstate"
diff --git a/include/input/Input.h b/include/input/Input.h
index 54c7114..7cc595a 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -801,8 +801,11 @@
static std::string actionToString(int32_t action);
+ // MotionEvent will transform various axes in different ways, based on the source. For
+ // example, the x and y axes will not have any offsets/translations applied if it comes from a
+ // relative mouse device (since SOURCE_RELATIVE_MOUSE is a non-pointer source). These methods
+ // are used to apply these transformations for different axes.
static vec2 calculateTransformedXY(uint32_t source, const ui::Transform&, const vec2& xy);
-
static float calculateTransformedAxisValue(int32_t axis, uint32_t source, const ui::Transform&,
const PointerCoords&);
diff --git a/libs/gui/DisplayEventDispatcher.cpp b/libs/gui/DisplayEventDispatcher.cpp
index 6f1a7ae..c986b82 100644
--- a/libs/gui/DisplayEventDispatcher.cpp
+++ b/libs/gui/DisplayEventDispatcher.cpp
@@ -130,6 +130,19 @@
return 1; // keep the callback
}
+void DisplayEventDispatcher::populateFrameTimelines(const DisplayEventReceiver::Event& event,
+ VsyncEventData* outVsyncEventData) const {
+ for (size_t i = 0; i < DisplayEventReceiver::kFrameTimelinesLength; i++) {
+ DisplayEventReceiver::Event::VSync::FrameTimeline receiverTimeline =
+ event.vsync.frameTimelines[i];
+ outVsyncEventData->frameTimelines[i] = {.id = receiverTimeline.vsyncId,
+ .deadlineTimestamp =
+ receiverTimeline.deadlineTimestamp,
+ .expectedPresentTime =
+ receiverTimeline.expectedVSyncTimestamp};
+ }
+}
+
bool DisplayEventDispatcher::processPendingEvents(nsecs_t* outTimestamp,
PhysicalDisplayId* outDisplayId,
uint32_t* outCount,
@@ -154,6 +167,9 @@
outVsyncEventData->deadlineTimestamp = ev.vsync.deadlineTimestamp;
outVsyncEventData->frameInterval = ev.vsync.frameInterval;
outVsyncEventData->expectedPresentTime = ev.vsync.expectedVSyncTimestamp;
+ outVsyncEventData->preferredFrameTimelineIndex =
+ ev.vsync.preferredFrameTimelineIndex;
+ populateFrameTimelines(ev, outVsyncEventData);
break;
case DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG:
dispatchHotplug(ev.header.timestamp, ev.header.displayId, ev.hotplug.connected);
diff --git a/libs/gui/include/gui/DisplayEventDispatcher.h b/libs/gui/include/gui/DisplayEventDispatcher.h
index f3bd139..92c89b8 100644
--- a/libs/gui/include/gui/DisplayEventDispatcher.h
+++ b/libs/gui/include/gui/DisplayEventDispatcher.h
@@ -17,6 +17,7 @@
#include <gui/DisplayEventReceiver.h>
#include <utils/Log.h>
#include <utils/Looper.h>
+#include <array>
namespace android {
using FrameRateOverride = DisplayEventReceiver::Event::FrameRateOverride;
@@ -36,6 +37,26 @@
// The anticipated Vsync present time.
int64_t expectedPresentTime = 0;
+
+ struct FrameTimeline {
+ // The Vsync Id corresponsing to this vsync event. This will be used to
+ // populate ISurfaceComposer::setFrameTimelineVsync and
+ // SurfaceComposerClient::setFrameTimelineVsync
+ int64_t id = FrameTimelineInfo::INVALID_VSYNC_ID;
+
+ // The deadline in CLOCK_MONOTONIC that the app needs to complete its
+ // frame by (both on the CPU and the GPU)
+ int64_t deadlineTimestamp = std::numeric_limits<int64_t>::max();
+
+ // The anticipated Vsync present time.
+ int64_t expectedPresentTime = 0;
+ };
+
+ // Sorted possible frame timelines.
+ std::array<FrameTimeline, DisplayEventReceiver::kFrameTimelinesLength> frameTimelines;
+
+ // Index into the frameTimelines that represents the platform's preferred frame timeline.
+ size_t preferredFrameTimelineIndex = std::numeric_limits<size_t>::max();
};
class DisplayEventDispatcher : public LooperCallback {
@@ -77,5 +98,8 @@
bool processPendingEvents(nsecs_t* outTimestamp, PhysicalDisplayId* outDisplayId,
uint32_t* outCount, VsyncEventData* outVsyncEventData);
+
+ void populateFrameTimelines(const DisplayEventReceiver::Event& event,
+ VsyncEventData* outVsyncEventData) const;
};
} // namespace android
diff --git a/libs/gui/include/gui/DisplayEventReceiver.h b/libs/gui/include/gui/DisplayEventReceiver.h
index 0dffbde..ca36843 100644
--- a/libs/gui/include/gui/DisplayEventReceiver.h
+++ b/libs/gui/include/gui/DisplayEventReceiver.h
@@ -49,6 +49,9 @@
// ----------------------------------------------------------------------------
class DisplayEventReceiver {
public:
+ // Max amount of frame timelines is arbitrarily set to be reasonable.
+ static constexpr int64_t kFrameTimelinesLength = 7;
+
enum {
DISPLAY_EVENT_VSYNC = fourcc('v', 's', 'y', 'n'),
DISPLAY_EVENT_HOTPLUG = fourcc('p', 'l', 'u', 'g'),
@@ -77,6 +80,12 @@
nsecs_t deadlineTimestamp __attribute__((aligned(8)));
nsecs_t frameInterval __attribute__((aligned(8)));
int64_t vsyncId;
+ size_t preferredFrameTimelineIndex __attribute__((aligned(8)));
+ struct FrameTimeline {
+ nsecs_t expectedVSyncTimestamp __attribute__((aligned(8)));
+ nsecs_t deadlineTimestamp __attribute__((aligned(8)));
+ int64_t vsyncId;
+ } frameTimelines[kFrameTimelinesLength];
};
struct Hotplug {
diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp
index d018800..24a7720 100644
--- a/libs/input/Input.cpp
+++ b/libs/input/Input.cpp
@@ -66,11 +66,21 @@
return transformedXy - transformedOrigin;
}
-bool shouldDisregardTranslation(uint32_t source) {
+bool isFromSource(uint32_t source, uint32_t test) {
+ return (source & test) == test;
+}
+
+bool shouldDisregardTransformation(uint32_t source) {
+ // Do not apply any transformations to axes from joysticks or touchpads.
+ return isFromSource(source, AINPUT_SOURCE_CLASS_JOYSTICK) ||
+ isFromSource(source, AINPUT_SOURCE_CLASS_POSITION);
+}
+
+bool shouldDisregardOffset(uint32_t source) {
// Pointer events are the only type of events that refer to absolute coordinates on the display,
// so we should apply the entire window transform. For other types of events, we should make
// sure to not apply the window translation/offset.
- return (source & AINPUT_SOURCE_CLASS_POINTER) == 0;
+ return !isFromSource(source, AINPUT_SOURCE_CLASS_POINTER);
}
} // namespace
@@ -707,7 +717,7 @@
#endif
bool MotionEvent::isTouchEvent(uint32_t source, int32_t action) {
- if (source & AINPUT_SOURCE_CLASS_POINTER) {
+ if (isFromSource(source, AINPUT_SOURCE_CLASS_POINTER)) {
// Specifically excludes HOVER_MOVE and SCROLL.
switch (action & AMOTION_EVENT_ACTION_MASK) {
case AMOTION_EVENT_ACTION_DOWN:
@@ -764,17 +774,31 @@
return android::base::StringPrintf("%" PRId32, action);
}
+// Apply the given transformation to the point without checking whether the entire transform
+// should be disregarded altogether for the provided source.
+static inline vec2 calculateTransformedXYUnchecked(uint32_t source, const ui::Transform& transform,
+ const vec2& xy) {
+ return shouldDisregardOffset(source) ? transformWithoutTranslation(transform, xy)
+ : transform.transform(xy);
+}
+
vec2 MotionEvent::calculateTransformedXY(uint32_t source, const ui::Transform& transform,
const vec2& xy) {
- return shouldDisregardTranslation(source) ? transformWithoutTranslation(transform, xy)
- : transform.transform(xy);
+ if (shouldDisregardTransformation(source)) {
+ return xy;
+ }
+ return calculateTransformedXYUnchecked(source, transform, xy);
}
float MotionEvent::calculateTransformedAxisValue(int32_t axis, uint32_t source,
const ui::Transform& transform,
const PointerCoords& coords) {
+ if (shouldDisregardTransformation(source)) {
+ return coords.getAxisValue(axis);
+ }
+
if (axis == AMOTION_EVENT_AXIS_X || axis == AMOTION_EVENT_AXIS_Y) {
- const vec2 xy = calculateTransformedXY(source, transform, coords.getXYValue());
+ const vec2 xy = calculateTransformedXYUnchecked(source, transform, coords.getXYValue());
static_assert(AMOTION_EVENT_AXIS_X == 0 && AMOTION_EVENT_AXIS_Y == 1);
return xy[axis];
}
diff --git a/libs/input/tests/InputEvent_test.cpp b/libs/input/tests/InputEvent_test.cpp
index 1b594f1..a92016b 100644
--- a/libs/input/tests/InputEvent_test.cpp
+++ b/libs/input/tests/InputEvent_test.cpp
@@ -647,9 +647,8 @@
ASSERT_NEAR(originalRawY, event.getRawY(0), 0.001);
}
-MotionEvent createTouchDownEvent(float x, float y, float dx, float dy,
- const ui::Transform& transform,
- const ui::Transform& rawTransform) {
+MotionEvent createMotionEvent(int32_t source, uint32_t action, float x, float y, float dx, float dy,
+ const ui::Transform& transform, const ui::Transform& rawTransform) {
std::vector<PointerProperties> pointerProperties;
pointerProperties.push_back(PointerProperties{/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER});
std::vector<PointerCoords> pointerCoords;
@@ -660,8 +659,8 @@
pointerCoords.back().setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, dy);
nsecs_t eventTime = systemTime(SYSTEM_TIME_MONOTONIC);
MotionEvent event;
- event.initialize(InputEvent::nextId(), /* deviceId */ 1, AINPUT_SOURCE_TOUCHSCREEN,
- /* displayId */ 0, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN,
+ event.initialize(InputEvent::nextId(), /* deviceId */ 1, source,
+ /* displayId */ 0, INVALID_HMAC, action,
/* actionButton */ 0, /* flags */ 0, /* edgeFlags */ 0, AMETA_NONE,
/* buttonState */ 0, MotionClassification::NONE, transform,
/* xPrecision */ 0, /* yPrecision */ 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
@@ -670,6 +669,13 @@
return event;
}
+MotionEvent createTouchDownEvent(float x, float y, float dx, float dy,
+ const ui::Transform& transform,
+ const ui::Transform& rawTransform) {
+ return createMotionEvent(AINPUT_SOURCE_TOUCHSCREEN, AMOTION_EVENT_ACTION_DOWN, x, y, dx, dy,
+ transform, rawTransform);
+}
+
TEST_F(MotionEventTest, ApplyTransform) {
// Create a rotate-90 transform with an offset (like a window which isn't fullscreen).
ui::Transform identity;
@@ -708,16 +714,39 @@
changedEvent.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0), 0.001);
}
+TEST_F(MotionEventTest, JoystickAndTouchpadAreNotTransformed) {
+ constexpr static std::array kNonTransformedSources = {std::pair(AINPUT_SOURCE_TOUCHPAD,
+ AMOTION_EVENT_ACTION_DOWN),
+ std::pair(AINPUT_SOURCE_JOYSTICK,
+ AMOTION_EVENT_ACTION_MOVE)};
+ // Create a rotate-90 transform with an offset (like a window which isn't fullscreen).
+ ui::Transform transform(ui::Transform::ROT_90, 800, 400);
+ transform.set(transform.tx() + 20, transform.ty() + 40);
+
+ for (const auto& [source, action] : kNonTransformedSources) {
+ const MotionEvent event =
+ createMotionEvent(source, action, 60, 100, 0, 0, transform, transform);
+
+ // These events should not be transformed in any way.
+ ASSERT_EQ(60, event.getX(0));
+ ASSERT_EQ(100, event.getY(0));
+ ASSERT_EQ(event.getRawX(0), event.getX(0));
+ ASSERT_EQ(event.getRawY(0), event.getY(0));
+ }
+}
+
TEST_F(MotionEventTest, NonPointerSourcesAreNotTranslated) {
- constexpr static auto NON_POINTER_SOURCES = {AINPUT_SOURCE_TRACKBALL,
- AINPUT_SOURCE_MOUSE_RELATIVE,
- AINPUT_SOURCE_JOYSTICK};
- for (uint32_t source : NON_POINTER_SOURCES) {
- // Create a rotate-90 transform with an offset (like a window which isn't fullscreen).
- ui::Transform transform(ui::Transform::ROT_90, 800, 400);
- transform.set(transform.tx() + 20, transform.ty() + 40);
- MotionEvent event = createTouchDownEvent(60, 100, 42, 96, transform, transform);
- event.setSource(source);
+ constexpr static std::array kNonPointerSources = {std::pair(AINPUT_SOURCE_TRACKBALL,
+ AMOTION_EVENT_ACTION_DOWN),
+ std::pair(AINPUT_SOURCE_MOUSE_RELATIVE,
+ AMOTION_EVENT_ACTION_MOVE)};
+ // Create a rotate-90 transform with an offset (like a window which isn't fullscreen).
+ ui::Transform transform(ui::Transform::ROT_90, 800, 400);
+ transform.set(transform.tx() + 20, transform.ty() + 40);
+
+ for (const auto& [source, action] : kNonPointerSources) {
+ const MotionEvent event =
+ createMotionEvent(source, action, 60, 100, 42, 96, transform, transform);
// Since this event comes from a non-pointer source, it should include rotation but not
// translation/offset.
diff --git a/libs/nativedisplay/AChoreographer.cpp b/libs/nativedisplay/AChoreographer.cpp
index 79d9b93..fc9680b 100644
--- a/libs/nativedisplay/AChoreographer.cpp
+++ b/libs/nativedisplay/AChoreographer.cpp
@@ -100,17 +100,10 @@
* Implementation of AChoreographerFrameCallbackData.
*/
struct ChoreographerFrameCallbackDataImpl {
- struct FrameTimeline {
- int64_t vsyncId{0};
- int64_t expectedPresentTimeNanos{0};
- int64_t deadlineNanos{0};
- };
-
int64_t frameTimeNanos{0};
- size_t frameTimelinesLength;
-
- std::vector<FrameTimeline> frameTimelines;
+ std::array<VsyncEventData::FrameTimeline, DisplayEventReceiver::kFrameTimelinesLength>
+ frameTimelines;
size_t preferredFrameTimelineIndex;
@@ -456,14 +449,9 @@
}
ChoreographerFrameCallbackDataImpl Choreographer::createFrameCallbackData(nsecs_t timestamp) const {
- std::vector<ChoreographerFrameCallbackDataImpl::FrameTimeline> frameTimelines;
- frameTimelines.push_back({.vsyncId = mLastVsyncEventData.id,
- .expectedPresentTimeNanos = mLastVsyncEventData.expectedPresentTime,
- .deadlineNanos = mLastVsyncEventData.deadlineTimestamp});
return {.frameTimeNanos = timestamp,
- .frameTimelinesLength = 1,
- .preferredFrameTimelineIndex = 0,
- .frameTimelines = frameTimelines,
+ .preferredFrameTimelineIndex = mLastVsyncEventData.preferredFrameTimelineIndex,
+ .frameTimelines = mLastVsyncEventData.frameTimelines,
.choreographer = this};
}
@@ -646,7 +634,7 @@
AChoreographerFrameCallbackData_to_ChoreographerFrameCallbackDataImpl(data);
LOG_ALWAYS_FATAL_IF(!frameCallbackData->choreographer->inCallback(),
"Data is only valid in callback");
- return frameCallbackData->frameTimelinesLength;
+ return frameCallbackData->frameTimelines.size();
}
size_t AChoreographerFrameCallbackData_getPreferredFrameTimelineIndex(
const AChoreographerFrameCallbackData* data) {
@@ -662,8 +650,8 @@
AChoreographerFrameCallbackData_to_ChoreographerFrameCallbackDataImpl(data);
LOG_ALWAYS_FATAL_IF(!frameCallbackData->choreographer->inCallback(),
"Data is only valid in callback");
- LOG_ALWAYS_FATAL_IF(index >= frameCallbackData->frameTimelinesLength, "Index out of bounds");
- return frameCallbackData->frameTimelines[index].vsyncId;
+ LOG_ALWAYS_FATAL_IF(index >= frameCallbackData->frameTimelines.size(), "Index out of bounds");
+ return frameCallbackData->frameTimelines[index].id;
}
int64_t AChoreographerFrameCallbackData_getFrameTimelineExpectedPresentTime(
const AChoreographerFrameCallbackData* data, size_t index) {
@@ -671,8 +659,8 @@
AChoreographerFrameCallbackData_to_ChoreographerFrameCallbackDataImpl(data);
LOG_ALWAYS_FATAL_IF(!frameCallbackData->choreographer->inCallback(),
"Data is only valid in callback");
- LOG_ALWAYS_FATAL_IF(index >= frameCallbackData->frameTimelinesLength, "Index out of bounds");
- return frameCallbackData->frameTimelines[index].expectedPresentTimeNanos;
+ LOG_ALWAYS_FATAL_IF(index >= frameCallbackData->frameTimelines.size(), "Index out of bounds");
+ return frameCallbackData->frameTimelines[index].expectedPresentTime;
}
int64_t AChoreographerFrameCallbackData_getFrameTimelineDeadline(
const AChoreographerFrameCallbackData* data, size_t index) {
@@ -680,8 +668,8 @@
AChoreographerFrameCallbackData_to_ChoreographerFrameCallbackDataImpl(data);
LOG_ALWAYS_FATAL_IF(!frameCallbackData->choreographer->inCallback(),
"Data is only valid in callback");
- LOG_ALWAYS_FATAL_IF(index >= frameCallbackData->frameTimelinesLength, "Index out of bounds");
- return frameCallbackData->frameTimelines[index].deadlineNanos;
+ LOG_ALWAYS_FATAL_IF(index >= frameCallbackData->frameTimelines.size(), "Index out of bounds");
+ return frameCallbackData->frameTimelines[index].deadlineTimestamp;
}
AChoreographer* AChoreographer_create() {
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 176cf89..1b19311 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -342,18 +342,6 @@
std::unique_ptr<DispatchEntry> createDispatchEntry(const InputTarget& inputTarget,
std::shared_ptr<EventEntry> eventEntry,
int32_t inputTargetFlags) {
- if (eventEntry->type == EventEntry::Type::MOTION) {
- const MotionEntry& motionEntry = static_cast<const MotionEntry&>(*eventEntry);
- if ((motionEntry.source & AINPUT_SOURCE_CLASS_JOYSTICK) ||
- (motionEntry.source & AINPUT_SOURCE_CLASS_POSITION)) {
- const ui::Transform identityTransform;
- // Use identity transform for joystick and position-based (touchpad) events because they
- // don't depend on the window transform.
- return std::make_unique<DispatchEntry>(eventEntry, inputTargetFlags, identityTransform,
- identityTransform, 1.0f /*globalScaleFactor*/);
- }
- }
-
if (inputTarget.useDefaultPointerTransform()) {
const ui::Transform& transform = inputTarget.getDefaultPointerTransform();
return std::make_unique<DispatchEntry>(eventEntry, inputTargetFlags, transform,
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index ba0ce95..d8fd16c 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -3012,59 +3012,6 @@
EXPECT_EQ(motionArgs.buttonState, verifiedMotion.buttonState);
}
-TEST_F(InputDispatcherTest, NonPointerMotionEvent_JoystickAndTouchpadNotTransformed) {
- std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
- sp<FakeWindowHandle> window =
- new FakeWindowHandle(application, mDispatcher, "Test window", ADISPLAY_ID_DEFAULT);
- const std::string name = window->getName();
-
- // Window gets transformed by offset values.
- window->setWindowOffset(500.0f, 500.0f);
-
- mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
- window->setFocusable(true);
-
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
-
- // First, we set focused window so that focusedWindowHandle is not null.
- setFocusedWindow(window);
-
- // Second, we consume focus event if it is right or wrong according to onFocusChangedLocked.
- window->consumeFocusEvent(true);
-
- constexpr const std::array nonTransformedSources = {std::pair(AINPUT_SOURCE_TOUCHPAD,
- AMOTION_EVENT_ACTION_DOWN),
- std::pair(AINPUT_SOURCE_JOYSTICK,
- AMOTION_EVENT_ACTION_MOVE)};
- for (const auto& [source, action] : nonTransformedSources) {
- const NotifyMotionArgs motionArgs = generateMotionArgs(action, source, ADISPLAY_ID_DEFAULT);
- mDispatcher->notifyMotion(&motionArgs);
-
- MotionEvent* event = window->consumeMotion();
- ASSERT_NE(event, nullptr);
-
- const MotionEvent& motionEvent = *event;
- EXPECT_EQ(action, motionEvent.getAction());
- EXPECT_EQ(motionArgs.pointerCount, motionEvent.getPointerCount());
-
- float expectedX = motionArgs.pointerCoords[0].getX();
- float expectedY = motionArgs.pointerCoords[0].getY();
-
- // Ensure the axis values from the final motion event are not transformed.
- EXPECT_EQ(expectedX, motionEvent.getX(0))
- << "expected " << expectedX << " for x coord of " << name.c_str() << ", got "
- << motionEvent.getX(0);
- EXPECT_EQ(expectedY, motionEvent.getY(0))
- << "expected " << expectedY << " for y coord of " << name.c_str() << ", got "
- << motionEvent.getY(0);
- // Ensure the raw and transformed axis values for the motion event are the same.
- EXPECT_EQ(motionEvent.getRawX(0), motionEvent.getX(0))
- << "expected raw and transformed X-axis values to be equal";
- EXPECT_EQ(motionEvent.getRawY(0), motionEvent.getY(0))
- << "expected raw and transformed Y-axis values to be equal";
- }
-}
-
/**
* Ensure that separate calls to sign the same data are generating the same key.
* We avoid asserting against INVALID_HMAC. Since the key is random, there is a non-zero chance
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index 4f0bbd2..b6cbbb6 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -549,13 +549,19 @@
}
bool BufferStateLayer::setTransactionCompletedListeners(
- const std::vector<sp<CallbackHandle>>& handles) {
+ const std::vector<ListenerCallbacks>& listenerCallbacks, const sp<IBinder>& layerHandle) {
// If there is no handle, we will not send a callback so reset mReleasePreviousBuffer and return
- if (handles.empty()) {
+ if (listenerCallbacks.empty()) {
mReleasePreviousBuffer = false;
return false;
}
+ std::vector<sp<CallbackHandle>> handles;
+ handles.reserve(listenerCallbacks.size());
+ for (auto& [listener, callbackIds] : listenerCallbacks) {
+ handles.emplace_back(new CallbackHandle(listener, callbackIds, layerHandle));
+ }
+
const bool willPresent = willPresentCurrentTransaction();
for (const auto& handle : handles) {
@@ -571,9 +577,10 @@
// Store so latched time and release fence can be set
mDrawingState.callbackHandles.push_back(handle);
- } else { // If this layer will NOT need to be relatched and presented this frame
+ } else {
+ // If this layer will NOT need to be relatched and presented this frame
// Notify the transaction completed thread this handle is done
- mFlinger->getTransactionCallbackInvoker().registerUnpresentedCallbackHandle(handle);
+ mFlinger->getTransactionCallbackInvoker().addUnpresentedCallbackHandle(handle);
}
}
diff --git a/services/surfaceflinger/BufferStateLayer.h b/services/surfaceflinger/BufferStateLayer.h
index eea700c..ceed188 100644
--- a/services/surfaceflinger/BufferStateLayer.h
+++ b/services/surfaceflinger/BufferStateLayer.h
@@ -65,7 +65,8 @@
bool setSurfaceDamageRegion(const Region& surfaceDamage) override;
bool setApi(int32_t api) override;
bool setSidebandStream(const sp<NativeHandle>& sidebandStream) override;
- bool setTransactionCompletedListeners(const std::vector<sp<CallbackHandle>>& handles) override;
+ bool setTransactionCompletedListeners(const std::vector<ListenerCallbacks>& handles,
+ const sp<IBinder>& layerHandle) override;
bool addFrameEvent(const sp<Fence>& acquireFence, nsecs_t postedTime,
nsecs_t requestedPresentTime) override;
bool setPosition(float /*x*/, float /*y*/) override;
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 42aeca7..d85e843 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -1166,9 +1166,6 @@
}
bool Layer::setFrameRate(FrameRate frameRate) {
- if (!mFlinger->useFrameRateApi) {
- return false;
- }
if (mDrawingState.frameRate == frameRate) {
return false;
}
@@ -2643,6 +2640,17 @@
return true;
}
+bool Layer::setTransactionCompletedListeners(
+ const std::vector<ListenerCallbacks>& listenerCallbacks, const sp<IBinder>&) {
+ if (listenerCallbacks.empty()) {
+ return false;
+ }
+ for (auto& listener : listenerCallbacks) {
+ mFlinger->getTransactionCallbackInvoker().addEmptyCallback(listener);
+ }
+ return false;
+}
+
// ---------------------------------------------------------------------------
std::ostream& operator<<(std::ostream& stream, const Layer::FrameRate& rate) {
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 297ded0..b79903d 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -428,10 +428,8 @@
virtual bool setSurfaceDamageRegion(const Region& /*surfaceDamage*/) { return false; };
virtual bool setApi(int32_t /*api*/) { return false; };
virtual bool setSidebandStream(const sp<NativeHandle>& /*sidebandStream*/) { return false; };
- virtual bool setTransactionCompletedListeners(
- const std::vector<sp<CallbackHandle>>& /*handles*/) {
- return false;
- };
+ virtual bool setTransactionCompletedListeners(const std::vector<ListenerCallbacks>& /*handles*/,
+ const sp<IBinder>& /* layerHandle */);
virtual bool addFrameEvent(const sp<Fence>& /*acquireFence*/, nsecs_t /*postedTime*/,
nsecs_t /*requestedPresentTime*/) {
return false;
diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp
index 2bdcaf6..455289f 100644
--- a/services/surfaceflinger/Scheduler/EventThread.cpp
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -355,14 +355,7 @@
std::lock_guard<std::mutex> lock(mMutex);
LOG_FATAL_IF(!mVSyncState);
- const int64_t vsyncId = [&] {
- if (mTokenManager != nullptr) {
- return mTokenManager->generateTokenForPredictions(
- {timestamp, deadlineTimestamp, expectedVSyncTimestamp});
- }
- return FrameTimelineInfo::INVALID_VSYNC_ID;
- }();
-
+ const int64_t vsyncId = generateToken(timestamp, deadlineTimestamp, expectedVSyncTimestamp);
mPendingEvents.push_back(makeVSync(mVSyncState->displayId, timestamp, ++mVSyncState->count,
expectedVSyncTimestamp, deadlineTimestamp, vsyncId));
mCondition.notify_all();
@@ -567,12 +560,48 @@
}
}
+int64_t EventThread::generateToken(nsecs_t timestamp, nsecs_t deadlineTimestamp,
+ nsecs_t expectedVSyncTimestamp) const {
+ if (mTokenManager != nullptr) {
+ return mTokenManager->generateTokenForPredictions(
+ {timestamp, deadlineTimestamp, expectedVSyncTimestamp});
+ }
+ return FrameTimelineInfo::INVALID_VSYNC_ID;
+}
+
+void EventThread::generateFrameTimeline(DisplayEventReceiver::Event& event) const {
+ // Add 1 to ensure the preferredFrameTimelineIndex entry (when multiplier == 0) is included.
+ for (int multiplier = -DisplayEventReceiver::kFrameTimelinesLength + 1, currentIndex = 0;
+ currentIndex < DisplayEventReceiver::kFrameTimelinesLength; multiplier++) {
+ nsecs_t deadline = event.vsync.deadlineTimestamp + multiplier * event.vsync.frameInterval;
+ // Valid possible frame timelines must have future values.
+ if (deadline > event.header.timestamp) {
+ if (multiplier == 0) {
+ event.vsync.preferredFrameTimelineIndex = currentIndex;
+ event.vsync.frameTimelines[currentIndex] =
+ {.vsyncId = event.vsync.vsyncId,
+ .deadlineTimestamp = event.vsync.deadlineTimestamp,
+ .expectedVSyncTimestamp = event.vsync.expectedVSyncTimestamp};
+ } else {
+ nsecs_t expectedVSync =
+ event.vsync.expectedVSyncTimestamp + multiplier * event.vsync.frameInterval;
+ event.vsync.frameTimelines[currentIndex] =
+ {.vsyncId = generateToken(event.header.timestamp, deadline, expectedVSync),
+ .deadlineTimestamp = deadline,
+ .expectedVSyncTimestamp = expectedVSync};
+ }
+ currentIndex++;
+ }
+ }
+}
+
void EventThread::dispatchEvent(const DisplayEventReceiver::Event& event,
const DisplayEventConsumers& consumers) {
for (const auto& consumer : consumers) {
DisplayEventReceiver::Event copy = event;
if (event.header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC) {
copy.vsync.frameInterval = mGetVsyncPeriodFunction(consumer->mOwnerUid);
+ generateFrameTimeline(copy);
}
switch (consumer->postEvent(copy)) {
case NO_ERROR:
diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h
index 9265a25..de43570 100644
--- a/services/surfaceflinger/Scheduler/EventThread.h
+++ b/services/surfaceflinger/Scheduler/EventThread.h
@@ -204,6 +204,10 @@
void onVSyncEvent(nsecs_t timestamp, nsecs_t expectedVSyncTimestamp,
nsecs_t deadlineTimestamp) override;
+ int64_t generateToken(nsecs_t timestamp, nsecs_t deadlineTimestamp,
+ nsecs_t expectedVSyncTimestamp) const;
+ void generateFrameTimeline(DisplayEventReceiver::Event& event) const;
+
const std::unique_ptr<VSyncSource> mVSyncSource GUARDED_BY(mMutex);
frametimeline::TokenManager* const mTokenManager;
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index ac04ecd..c2dcd70 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -331,7 +331,6 @@
ui::PixelFormat SurfaceFlinger::defaultCompositionPixelFormat = ui::PixelFormat::RGBA_8888;
Dataspace SurfaceFlinger::wideColorGamutCompositionDataspace = Dataspace::V0_SRGB;
ui::PixelFormat SurfaceFlinger::wideColorGamutCompositionPixelFormat = ui::PixelFormat::RGBA_8888;
-bool SurfaceFlinger::useFrameRateApi;
bool SurfaceFlinger::enableSdrDimming;
LatchUnsignaledConfig SurfaceFlinger::enableLatchUnsignaledConfig;
@@ -486,8 +485,6 @@
android::hardware::details::setTrebleTestingOverride(true);
}
- useFrameRateApi = use_frame_rate_api(true);
-
mRefreshRateOverlaySpinner = property_get_bool("sf.debug.show_refresh_rate_overlay_spinner", 0);
// Debug property overrides ro. property
@@ -3806,11 +3803,10 @@
transactionFlags |= setDisplayStateLocked(display);
}
- // start and end registration for listeners w/ no surface so they can get their callback. Note
- // that listeners with SurfaceControls will start registration during setClientStateLocked
- // below.
+ // Add listeners w/ surfaces so they can get their callback. Note that listeners with
+ // SurfaceControls will start registration during setClientStateLocked below.
for (const auto& listener : listenerCallbacks) {
- mTransactionCallbackInvoker.addEmptyTransaction(listener);
+ mTransactionCallbackInvoker.addEmptyCallback(listener);
}
uint32_t clientStateFlags = 0;
@@ -3982,7 +3978,7 @@
}
if (layer == nullptr) {
for (auto& [listener, callbackIds] : s.listeners) {
- mTransactionCallbackInvoker.registerUnpresentedCallbackHandle(
+ mTransactionCallbackInvoker.addUnpresentedCallbackHandle(
new CallbackHandle(listener, callbackIds, s.surface));
}
return 0;
@@ -4253,12 +4249,6 @@
flags |= eTransactionNeeded | eTraversalNeeded;
}
}
- std::vector<sp<CallbackHandle>> callbackHandles;
- if ((what & layer_state_t::eHasListenerCallbacksChanged) && (!filteredListeners.empty())) {
- for (auto& [listener, callbackIds] : filteredListeners) {
- callbackHandles.emplace_back(new CallbackHandle(listener, callbackIds, s.surface));
- }
- }
if (what & layer_state_t::eBufferChanged &&
layer->setBuffer(s.bufferData, postTime, desiredPresentTime, isAutoTimestamp,
@@ -4268,7 +4258,11 @@
layer->setFrameTimelineVsyncForBufferlessTransaction(frameTimelineInfo, postTime);
}
- if (layer->setTransactionCompletedListeners(callbackHandles)) flags |= eTraversalNeeded;
+ if ((what & layer_state_t::eHasListenerCallbacksChanged) && (!filteredListeners.empty())) {
+ if (layer->setTransactionCompletedListeners(filteredListeners, s.surface)) {
+ flags |= eTraversalNeeded;
+ }
+ }
// Do not put anything that updates layer state or modifies flags after
// setTransactionCompletedListener
return flags;
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index c05f6cb..bf628dc 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -248,11 +248,6 @@
static ui::Dataspace wideColorGamutCompositionDataspace;
static ui::PixelFormat wideColorGamutCompositionPixelFormat;
- // Whether to use frame rate API when deciding about the refresh rate of the display. This
- // variable is caches in SF, so that we can check it with each layer creation, and a void the
- // overhead that is caused by reading from sysprop.
- static bool useFrameRateApi;
-
static constexpr SkipInitializationTag SkipInitialization;
// Whether or not SDR layers should be dimmed to the desired SDR white point instead of
diff --git a/services/surfaceflinger/SurfaceFlingerProperties.cpp b/services/surfaceflinger/SurfaceFlingerProperties.cpp
index a8117f7..16f6e31 100644
--- a/services/surfaceflinger/SurfaceFlingerProperties.cpp
+++ b/services/surfaceflinger/SurfaceFlingerProperties.cpp
@@ -304,14 +304,6 @@
return defaultValue;
}
-bool use_frame_rate_api(bool defaultValue) {
- auto temp = SurfaceFlingerProperties::use_frame_rate_api();
- if (temp.has_value()) {
- return *temp;
- }
- return defaultValue;
-}
-
bool enable_sdr_dimming(bool defaultValue) {
return SurfaceFlingerProperties::enable_sdr_dimming().value_or(defaultValue);
}
diff --git a/services/surfaceflinger/SurfaceFlingerProperties.h b/services/surfaceflinger/SurfaceFlingerProperties.h
index ed18260..8d0e426 100644
--- a/services/surfaceflinger/SurfaceFlingerProperties.h
+++ b/services/surfaceflinger/SurfaceFlingerProperties.h
@@ -88,8 +88,6 @@
bool support_kernel_idle_timer(bool defaultValue);
-bool use_frame_rate_api(bool defaultValue);
-
int32_t display_update_imminent_timeout_ms(int32_t defaultValue);
android::ui::DisplayPrimaries getDisplayNativePrimaries();
diff --git a/services/surfaceflinger/TransactionCallbackInvoker.cpp b/services/surfaceflinger/TransactionCallbackInvoker.cpp
index f3d46ea..418fbc5 100644
--- a/services/surfaceflinger/TransactionCallbackInvoker.cpp
+++ b/services/surfaceflinger/TransactionCallbackInvoker.cpp
@@ -74,10 +74,10 @@
}
}
-void TransactionCallbackInvoker::addEmptyTransaction(const ListenerCallbacks& listenerCallbacks) {
+void TransactionCallbackInvoker::addEmptyCallback(const ListenerCallbacks& listenerCallbacks) {
auto& [listener, callbackIds] = listenerCallbacks;
- auto& transactionStatsDeque = mCompletedTransactions[listener];
- transactionStatsDeque.emplace_back(callbackIds);
+ TransactionStats* transactionStats;
+ findOrCreateTransactionStats(listener, callbackIds, &transactionStats);
}
status_t TransactionCallbackInvoker::addOnCommitCallbackHandles(
@@ -116,7 +116,7 @@
return NO_ERROR;
}
-status_t TransactionCallbackInvoker::registerUnpresentedCallbackHandle(
+status_t TransactionCallbackInvoker::addUnpresentedCallbackHandle(
const sp<CallbackHandle>& handle) {
return addCallbackHandle(handle, std::vector<JankData>());
}
diff --git a/services/surfaceflinger/TransactionCallbackInvoker.h b/services/surfaceflinger/TransactionCallbackInvoker.h
index e203d41..6f67947 100644
--- a/services/surfaceflinger/TransactionCallbackInvoker.h
+++ b/services/surfaceflinger/TransactionCallbackInvoker.h
@@ -71,8 +71,10 @@
// Adds the Transaction CallbackHandle from a layer that does not need to be relatched and
// presented this frame.
- status_t registerUnpresentedCallbackHandle(const sp<CallbackHandle>& handle);
- void addEmptyTransaction(const ListenerCallbacks& listenerCallbacks);
+ status_t addUnpresentedCallbackHandle(const sp<CallbackHandle>& handle);
+ // Adds the callback handles for empty transactions or for non-buffer layer updates which do not
+ // include layer stats.
+ void addEmptyCallback(const ListenerCallbacks& listenerCallbacks);
void addPresentFence(const sp<Fence>& presentFence);
@@ -81,14 +83,12 @@
mCompletedTransactions.clear();
}
- status_t addCallbackHandle(const sp<CallbackHandle>& handle,
- const std::vector<JankData>& jankData);
-
-
private:
status_t findOrCreateTransactionStats(const sp<IBinder>& listener,
const std::vector<CallbackId>& callbackIds,
TransactionStats** outTransactionStats);
+ status_t addCallbackHandle(const sp<CallbackHandle>& handle,
+ const std::vector<JankData>& jankData);
std::unordered_map<sp<IBinder>, std::deque<TransactionStats>, IListenerHash>
mCompletedTransactions;
diff --git a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
index 78f8a2f..7702ea2 100644
--- a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
+++ b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
@@ -414,16 +414,6 @@
prop_name: "ro.surface_flinger.supports_background_blur"
}
-# Indicates whether Scheduler should use frame rate API when adjusting the
-# display refresh rate.
-prop {
- api_name: "use_frame_rate_api"
- type: Boolean
- scope: Public
- access: Readonly
- prop_name: "ro.surface_flinger.use_frame_rate_api"
-}
-
# Sets the timeout used to rate limit DISPLAY_UPDATE_IMMINENT Power HAL notifications.
# SurfaceFlinger wakeups will trigger this boost whenever they are separated by more than this
# duration (specified in milliseconds). A value of 0 disables the rate limit, and will result in
diff --git a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
index 9c567d6..bf1e7e2 100644
--- a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
+++ b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
@@ -152,10 +152,6 @@
prop_name: "ro.surface_flinger.use_context_priority"
}
prop {
- api_name: "use_frame_rate_api"
- prop_name: "ro.surface_flinger.use_frame_rate_api"
- }
- prop {
api_name: "use_smart_90_for_video"
prop_name: "ro.surface_flinger.use_smart_90_for_video"
deprecated: true
diff --git a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-latest.txt b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-latest.txt
index ba60a7d..640b9fb 100644
--- a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-latest.txt
+++ b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-latest.txt
@@ -136,10 +136,6 @@
prop_name: "ro.surface_flinger.use_context_priority"
}
prop {
- api_name: "use_frame_rate_api"
- prop_name: "ro.surface_flinger.use_frame_rate_api"
- }
- prop {
api_name: "use_smart_90_for_video"
prop_name: "ro.surface_flinger.use_smart_90_for_video"
deprecated: true
diff --git a/services/surfaceflinger/tests/LayerCallback_test.cpp b/services/surfaceflinger/tests/LayerCallback_test.cpp
index 91a5b52..7beba15 100644
--- a/services/surfaceflinger/tests/LayerCallback_test.cpp
+++ b/services/surfaceflinger/tests/LayerCallback_test.cpp
@@ -1067,7 +1067,7 @@
}
// b202394221
-TEST_F(LayerCallbackTest, DISABLED_NonBufferLayerStateChanges) {
+TEST_F(LayerCallbackTest, NonBufferLayerStateChanges) {
sp<SurfaceControl> layer;
ASSERT_NO_FATAL_FAILURE(layer = createColorLayer("ColorLayer", Color::RED));
@@ -1085,4 +1085,47 @@
EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true));
}
+class TimedCallbackHelper {
+public:
+ static void function(void* callbackContext, nsecs_t, const sp<Fence>&,
+ const std::vector<SurfaceControlStats>&) {
+ if (!callbackContext) {
+ ALOGE("failed to get callback context");
+ }
+ TimedCallbackHelper* helper = static_cast<TimedCallbackHelper*>(callbackContext);
+ std::lock_guard lock(helper->mMutex);
+ helper->mInvokedTime = systemTime();
+ helper->mCv.notify_all();
+ }
+
+ void waitForCallback() {
+ std::unique_lock lock(mMutex);
+ ASSERT_TRUE(mCv.wait_for(lock, std::chrono::seconds(3), [&] { return mInvokedTime != -1; }))
+ << "did not receive callback";
+ }
+ void* getContext() { return static_cast<void*>(this); }
+
+ std::mutex mMutex;
+ std::condition_variable mCv;
+ nsecs_t mInvokedTime = -1;
+};
+
+TEST_F(LayerCallbackTest, EmptyTransactionCallbackOrder) {
+ TimedCallbackHelper onCommitCallback;
+ TimedCallbackHelper onCompleteCallback;
+
+ // Add transaction callback before on commit callback
+ Transaction()
+ .addTransactionCompletedCallback(onCompleteCallback.function,
+ onCompleteCallback.getContext())
+ .addTransactionCommittedCallback(onCommitCallback.function,
+ onCommitCallback.getContext())
+ .apply();
+
+ EXPECT_NO_FATAL_FAILURE(onCompleteCallback.waitForCallback());
+ EXPECT_NO_FATAL_FAILURE(onCommitCallback.waitForCallback());
+ // verify we get the oncomplete at the same time or after the oncommit callback.
+ EXPECT_GE(onCompleteCallback.mInvokedTime, onCommitCallback.mInvokedTime);
+}
+
} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
index 28d0222..67a0d7e 100644
--- a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
+++ b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
@@ -28,6 +28,7 @@
#include "AsyncCallRecorder.h"
#include "DisplayHardware/DisplayMode.h"
+#include "FrameTimeline.h"
#include "Scheduler/EventThread.h"
using namespace std::chrono_literals;
@@ -96,6 +97,8 @@
ConnectionEventRecorder& connectionEventRecorder,
nsecs_t expectedTimestamp, unsigned expectedCount);
void expectVsyncEventReceivedByConnection(nsecs_t expectedTimestamp, unsigned expectedCount);
+ void expectVsyncEventFrameTimelinesCorrect(nsecs_t expectedTimestamp,
+ nsecs_t preferredDeadline);
void expectHotplugEventReceivedByConnection(PhysicalDisplayId expectedDisplayId,
bool expectedConnected);
void expectConfigChangedEventReceivedByConnection(PhysicalDisplayId expectedDisplayId,
@@ -120,6 +123,7 @@
std::unique_ptr<impl::EventThread> mThread;
sp<MockEventThreadConnection> mConnection;
sp<MockEventThreadConnection> mThrottledConnection;
+ std::unique_ptr<frametimeline::impl::TokenManager> mTokenManager;
static constexpr uid_t mConnectionUid = 443;
static constexpr uid_t mThrottledConnectionUid = 177;
@@ -173,8 +177,8 @@
return VSYNC_PERIOD.count();
};
- mThread = std::make_unique<impl::EventThread>(std::move(source),
- /*tokenManager=*/nullptr,
+ mTokenManager = std::make_unique<frametimeline::impl::TokenManager>();
+ mThread = std::make_unique<impl::EventThread>(std::move(source), mTokenManager.get(),
mInterceptVSyncCallRecorder.getInvocable(),
throttleVsync, getVsyncPeriod);
@@ -247,6 +251,45 @@
expectedCount);
}
+void EventThreadTest::expectVsyncEventFrameTimelinesCorrect(nsecs_t expectedTimestamp,
+ nsecs_t preferredDeadline) {
+ auto args = mConnectionEventCallRecorder.waitForCall();
+ ASSERT_TRUE(args.has_value()) << " did not receive an event for timestamp "
+ << expectedTimestamp;
+ const auto& event = std::get<0>(args.value());
+ for (int i = 0; i < DisplayEventReceiver::kFrameTimelinesLength; i++) {
+ auto prediction =
+ mTokenManager->getPredictionsForToken(event.vsync.frameTimelines[i].vsyncId);
+ EXPECT_TRUE(prediction.has_value());
+ EXPECT_EQ(prediction.value().endTime, event.vsync.frameTimelines[i].deadlineTimestamp)
+ << "Deadline timestamp does not match cached value";
+ EXPECT_EQ(prediction.value().presentTime,
+ event.vsync.frameTimelines[i].expectedVSyncTimestamp)
+ << "Expected vsync timestamp does not match cached value";
+
+ if (i > 0) {
+ EXPECT_GT(event.vsync.frameTimelines[i].deadlineTimestamp,
+ event.vsync.frameTimelines[i - 1].deadlineTimestamp)
+ << "Deadline timestamp out of order for frame timeline " << i;
+ EXPECT_GT(event.vsync.frameTimelines[i].expectedVSyncTimestamp,
+ event.vsync.frameTimelines[i - 1].expectedVSyncTimestamp)
+ << "Expected vsync timestamp out of order for frame timeline " << i;
+ }
+ if (event.vsync.frameTimelines[i].deadlineTimestamp == preferredDeadline) {
+ EXPECT_EQ(i, event.vsync.preferredFrameTimelineIndex)
+ << "Preferred frame timeline index should be " << i;
+ // For the platform-preferred frame timeline, the vsync ID is 0 because the first frame
+ // timeline is made before the rest.
+ EXPECT_EQ(0, event.vsync.frameTimelines[i].vsyncId)
+ << "Vsync ID incorrect for frame timeline " << i;
+ } else {
+ // Vsync ID 0 is used for the preferred frame timeline.
+ EXPECT_EQ(i + 1, event.vsync.frameTimelines[i].vsyncId)
+ << "Vsync ID incorrect for frame timeline " << i;
+ }
+ }
+}
+
void EventThreadTest::expectHotplugEventReceivedByConnection(PhysicalDisplayId expectedDisplayId,
bool expectedConnected) {
auto args = mConnectionEventCallRecorder.waitForCall();
@@ -344,6 +387,19 @@
expectVSyncSetEnabledCallReceived(false);
}
+TEST_F(EventThreadTest, requestNextVsyncEventFrameTimelinesCorrect) {
+ // Signal that we want the next vsync event to be posted to the connection
+ mThread->requestNextVsync(mConnection);
+
+ expectVSyncSetEnabledCallReceived(true);
+
+ // Use the received callback to signal a vsync event.
+ // The interceptor should receive the event, as well as the connection.
+ mCallback->onVSyncEvent(123, 456, 789);
+ expectInterceptCallReceived(123);
+ expectVsyncEventFrameTimelinesCorrect(123, 789);
+}
+
TEST_F(EventThreadTest, setVsyncRateZeroPostsNoVSyncEventsToThatConnection) {
// Create a first connection, register it, and request a vsync rate of zero.
ConnectionEventRecorder firstConnectionEventRecorder{0};
diff --git a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
index 3b40965..d021178 100644
--- a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
@@ -122,8 +122,6 @@
::testing::UnitTest::GetInstance()->current_test_info();
ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
- mFlinger.mutableUseFrameRateApi() = true;
-
setupScheduler();
mFlinger.setupComposer(std::make_unique<Hwc2::mock::Composer>());
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 9832372..8cca6af 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -446,7 +446,6 @@
auto& mutableHwcDisplayData() { return getHwComposer().mDisplayData; }
auto& mutableHwcPhysicalDisplayIdMap() { return getHwComposer().mPhysicalDisplayIdMap; }
auto& mutablePrimaryHwcDisplayId() { return getHwComposer().mPrimaryHwcDisplayId; }
- auto& mutableUseFrameRateApi() { return mFlinger->useFrameRateApi; }
auto& mutableActiveDisplayToken() { return mFlinger->mActiveDisplayToken; }
auto fromHandle(const sp<IBinder>& handle) {