blast: add desired present time
Add the option to set a desiredPresentTime for a transaction. This
lets the framework know approximately when the transaction should
be presented.
Test: Transaction_test
Bug: 80477568
Change-Id: Ic25617fb93f2c249b3b3c7a8f90f72ec358938f0
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index 7357ba9..c3a4737 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -66,7 +66,8 @@
virtual void setTransactionState(const Vector<ComposerState>& state,
const Vector<DisplayState>& displays, uint32_t flags,
const sp<IBinder>& applyToken,
- const InputWindowCommands& commands) {
+ const InputWindowCommands& commands,
+ int64_t desiredPresentTime) {
Parcel data, reply;
data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
@@ -83,6 +84,7 @@
data.writeUint32(flags);
data.writeStrongBinder(applyToken);
commands.write(data);
+ data.writeInt64(desiredPresentTime);
remote()->transact(BnSurfaceComposer::SET_TRANSACTION_STATE, data, &reply);
}
@@ -748,7 +750,10 @@
sp<IBinder> applyToken = data.readStrongBinder();
InputWindowCommands inputWindowCommands;
inputWindowCommands.read(data);
- setTransactionState(state, displays, stateFlags, applyToken, inputWindowCommands);
+
+ int64_t desiredPresentTime = data.readInt64();
+ setTransactionState(state, displays, stateFlags, applyToken, inputWindowCommands,
+ desiredPresentTime);
return NO_ERROR;
}
case BOOT_FINISHED: {
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 8b9e4d7..32a368e 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -159,11 +159,12 @@
// ---------------------------------------------------------------------------
-SurfaceComposerClient::Transaction::Transaction(const Transaction& other) :
- mForceSynchronous(other.mForceSynchronous),
- mTransactionNestCount(other.mTransactionNestCount),
- mAnimation(other.mAnimation),
- mEarlyWakeup(other.mEarlyWakeup) {
+SurfaceComposerClient::Transaction::Transaction(const Transaction& other)
+ : mForceSynchronous(other.mForceSynchronous),
+ mTransactionNestCount(other.mTransactionNestCount),
+ mAnimation(other.mAnimation),
+ mEarlyWakeup(other.mEarlyWakeup),
+ mDesiredPresentTime(other.mDesiredPresentTime) {
mDisplayStates = other.mDisplayStates;
mComposerStates = other.mComposerStates;
mInputWindowCommands = other.mInputWindowCommands;
@@ -218,7 +219,7 @@
s.state.parentHandleForChild = nullptr;
composerStates.add(s);
- sf->setTransactionState(composerStates, displayStates, 0, nullptr, {});
+ sf->setTransactionState(composerStates, displayStates, 0, nullptr, {}, -1);
}
status_t SurfaceComposerClient::Transaction::apply(bool synchronous) {
@@ -284,7 +285,8 @@
mEarlyWakeup = false;
sp<IBinder> applyToken = IInterface::asBinder(TransactionCompletedListener::getIInstance());
- sf->setTransactionState(composerStates, displayStates, flags, applyToken, mInputWindowCommands);
+ sf->setTransactionState(composerStates, displayStates, flags, applyToken, mInputWindowCommands,
+ mDesiredPresentTime);
mInputWindowCommands.clear();
mStatus = NO_ERROR;
return NO_ERROR;
@@ -742,6 +744,12 @@
return *this;
}
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setDesiredPresentTime(
+ nsecs_t desiredPresentTime) {
+ mDesiredPresentTime = desiredPresentTime;
+ return *this;
+}
+
SurfaceComposerClient::Transaction&
SurfaceComposerClient::Transaction::addTransactionCompletedCallback(
TransactionCompletedCallbackTakesContext callback, void* callbackContext) {
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index 9812e1c..25a4185 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -116,7 +116,8 @@
virtual void setTransactionState(const Vector<ComposerState>& state,
const Vector<DisplayState>& displays, uint32_t flags,
const sp<IBinder>& applyToken,
- const InputWindowCommands& inputWindowCommands) = 0;
+ const InputWindowCommands& inputWindowCommands,
+ int64_t desiredPresentTime) = 0;
/* signal that we're done booting.
* Requires ACCESS_SURFACE_FLINGER permission
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index f16f781..2f02328 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -237,6 +237,18 @@
bool mAnimation = false;
bool mEarlyWakeup = false;
+ // mDesiredPresentTime is the time in nanoseconds that the client would like the transaction
+ // to be presented. When it is not possible to present at exactly that time, it will be
+ // presented after the time has passed.
+ //
+ // Desired present times that are more than 1 second in the future may be ignored.
+ // When a desired present time has already passed, the transaction will be presented as soon
+ // as possible.
+ //
+ // Transactions from the same process are presented in the same order that they are applied.
+ // The desired present time does not affect this ordering.
+ int64_t mDesiredPresentTime = -1;
+
InputWindowCommands mInputWindowCommands;
int mStatus = NO_ERROR;
@@ -325,6 +337,7 @@
Transaction& setApi(const sp<SurfaceControl>& sc, int32_t api);
Transaction& setSidebandStream(const sp<SurfaceControl>& sc,
const sp<NativeHandle>& sidebandStream);
+ Transaction& setDesiredPresentTime(nsecs_t desiredPresentTime);
Transaction& addTransactionCompletedCallback(
TransactionCompletedCallbackTakesContext callback, void* callbackContext);
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 2d773f2..1d2950a 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -556,7 +556,9 @@
void setTransactionState(const Vector<ComposerState>& /*state*/,
const Vector<DisplayState>& /*displays*/, uint32_t /*flags*/,
const sp<IBinder>& /*applyToken*/,
- const InputWindowCommands& /*inputWindowCommands*/) override {}
+ const InputWindowCommands& /*inputWindowCommands*/,
+ int64_t /*desiredPresentTime*/) override {}
+
void bootFinished() override {}
bool authenticateSurfaceTexture(
const sp<IGraphicBufferProducer>& /*surface*/) const override {
diff --git a/services/surfaceflinger/Scheduler/DispSync.cpp b/services/surfaceflinger/Scheduler/DispSync.cpp
index b74b901..9b2a6fc 100644
--- a/services/surfaceflinger/Scheduler/DispSync.cpp
+++ b/services/surfaceflinger/Scheduler/DispSync.cpp
@@ -657,6 +657,9 @@
Mutex::Autolock lock(mMutex);
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
nsecs_t phase = mReferenceTime + mPhase;
+ if (mPeriod == 0) {
+ return 0;
+ }
return (((now - phase) / mPeriod) + periodOffset + 1) * mPeriod + phase;
}
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index dc02666..c0059d5 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -3498,8 +3498,8 @@
auto& [applyToken, transactionQueue] = *it;
while (!transactionQueue.empty()) {
- const auto& [states, displays, flags] = transactionQueue.front();
- if (composerStateContainsUnsignaledFences(states)) {
+ const auto& [states, displays, flags, desiredPresentTime] = transactionQueue.front();
+ if (!transactionIsReadyToBeApplied(desiredPresentTime, states)) {
break;
}
applyTransactionState(states, displays, flags, mInputWindowCommands);
@@ -3533,23 +3533,33 @@
return false;
}
-bool SurfaceFlinger::composerStateContainsUnsignaledFences(const Vector<ComposerState>& states) {
+bool SurfaceFlinger::transactionIsReadyToBeApplied(int64_t desiredPresentTime,
+ const Vector<ComposerState>& states) {
+ const nsecs_t expectedPresentTime = mPrimaryDispSync->expectedPresentTime();
+ // Do not present if the desiredPresentTime has not passed unless it is more than one second
+ // in the future. We ignore timestamps more than 1 second in the future for stability reasons.
+ if (desiredPresentTime >= 0 && desiredPresentTime >= expectedPresentTime &&
+ desiredPresentTime < expectedPresentTime + s2ns(1)) {
+ return false;
+ }
+
for (const ComposerState& state : states) {
const layer_state_t& s = state.state;
if (!(s.what & layer_state_t::eAcquireFenceChanged)) {
continue;
}
if (s.acquireFence && s.acquireFence->getStatus() == Fence::Status::Unsignaled) {
- return true;
+ return false;
}
}
- return false;
+ return true;
}
void SurfaceFlinger::setTransactionState(const Vector<ComposerState>& states,
const Vector<DisplayState>& displays, uint32_t flags,
const sp<IBinder>& applyToken,
- const InputWindowCommands& inputWindowCommands) {
+ const InputWindowCommands& inputWindowCommands,
+ int64_t desiredPresentTime) {
ATRACE_CALL();
Mutex::Autolock _l(mStateLock);
@@ -3559,8 +3569,8 @@
// If its TransactionQueue already has a pending TransactionState or if it is pending
if (mTransactionQueues.find(applyToken) != mTransactionQueues.end() ||
- composerStateContainsUnsignaledFences(states)) {
- mTransactionQueues[applyToken].emplace(states, displays, flags);
+ !transactionIsReadyToBeApplied(desiredPresentTime, states)) {
+ mTransactionQueues[applyToken].emplace(states, displays, flags, desiredPresentTime);
setTransactionFlags(eTransactionNeeded);
return;
}
@@ -4147,7 +4157,7 @@
d.width = 0;
d.height = 0;
displays.add(d);
- setTransactionState(state, displays, 0, nullptr, mInputWindowCommands);
+ setTransactionState(state, displays, 0, nullptr, mInputWindowCommands, -1);
const auto display = getDisplayDevice(displayToken);
if (!display) return;
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index c92874b..e5b3570 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -412,7 +412,8 @@
void setTransactionState(const Vector<ComposerState>& state,
const Vector<DisplayState>& displays, uint32_t flags,
const sp<IBinder>& applyToken,
- const InputWindowCommands& inputWindowCommands) override;
+ const InputWindowCommands& inputWindowCommands,
+ int64_t desiredPresentTime) override;
void bootFinished() override;
bool authenticateSurfaceTexture(
const sp<IGraphicBufferProducer>& bufferProducer) const override;
@@ -537,7 +538,8 @@
void latchAndReleaseBuffer(const sp<Layer>& layer);
void commitTransaction();
bool containsAnyInvalidClientState(const Vector<ComposerState>& states);
- bool composerStateContainsUnsignaledFences(const Vector<ComposerState>& states);
+ bool transactionIsReadyToBeApplied(int64_t desiredPresentTime,
+ const Vector<ComposerState>& states);
uint32_t setClientStateLocked(const ComposerState& composerState);
uint32_t setDisplayStateLocked(const DisplayState& s);
uint32_t addInputWindowCommands(const InputWindowCommands& inputWindowCommands);
@@ -982,12 +984,17 @@
};
struct TransactionState {
TransactionState(const Vector<ComposerState>& composerStates,
- const Vector<DisplayState>& displayStates, uint32_t transactionFlags)
- : states(composerStates), displays(displayStates), flags(transactionFlags) {}
+ const Vector<DisplayState>& displayStates, uint32_t transactionFlags,
+ int64_t desiredPresentTime)
+ : states(composerStates),
+ displays(displayStates),
+ flags(transactionFlags),
+ time(desiredPresentTime) {}
Vector<ComposerState> states;
Vector<DisplayState> displays;
uint32_t flags;
+ int64_t time;
};
std::unordered_map<sp<IBinder>, std::queue<TransactionState>, IBinderHash> mTransactionQueues;
diff --git a/services/surfaceflinger/tests/Transaction_test.cpp b/services/surfaceflinger/tests/Transaction_test.cpp
index 9339761..0ad2711 100644
--- a/services/surfaceflinger/tests/Transaction_test.cpp
+++ b/services/surfaceflinger/tests/Transaction_test.cpp
@@ -2568,11 +2568,23 @@
}
}
+ void addExpectedPresentTime(nsecs_t expectedPresentTime) {
+ mExpectedPresentTime = expectedPresentTime;
+ }
+
void verifyTransactionStats(const TransactionStats& transactionStats) const {
const auto& [latchTime, presentFence, surfaceStats] = transactionStats;
if (mTransactionResult == ExpectedResult::Transaction::PRESENTED) {
ASSERT_GE(latchTime, 0) << "bad latch time";
ASSERT_NE(presentFence, nullptr);
+ if (mExpectedPresentTime >= 0) {
+ ASSERT_EQ(presentFence->wait(3000), NO_ERROR);
+ ASSERT_GE(presentFence->getSignalTime(), mExpectedPresentTime - nsecs_t(5 * 1e6));
+ // if the panel is running at 30 hz, at the worst case, our expected time just
+ // misses vsync and we have to wait another 33.3ms
+ ASSERT_LE(presentFence->getSignalTime(),
+ mExpectedPresentTime + nsecs_t(66.666666 * 1e6));
+ }
} else {
ASSERT_EQ(presentFence, nullptr) << "transaction shouldn't have been presented";
ASSERT_EQ(latchTime, -1) << "unpresented transactions shouldn't be latched";
@@ -2623,6 +2635,7 @@
}
};
ExpectedResult::Transaction mTransactionResult = ExpectedResult::Transaction::NOT_PRESENTED;
+ nsecs_t mExpectedPresentTime = -1;
std::unordered_map<sp<IBinder>, ExpectedSurfaceResult, IBinderHash> mExpectedSurfaceResults;
};
@@ -3272,6 +3285,143 @@
EXPECT_NO_FATAL_FAILURE(waitForCallbacks(callback, expectedResults, true));
}
+TEST_F(LayerCallbackTest, DesiredPresentTime) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+
+ Transaction transaction;
+ CallbackHelper callback;
+ int err = fillTransaction(transaction, &callback, layer);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+
+ // Try to present 100ms in the future
+ nsecs_t time = systemTime() + (100 * 1e6);
+
+ transaction.setDesiredPresentTime(time);
+ transaction.apply();
+
+ ExpectedResult expected;
+ expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer);
+ expected.addExpectedPresentTime(time);
+ EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true));
+}
+
+TEST_F(LayerCallbackTest, DesiredPresentTime_Multiple) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+
+ Transaction transaction;
+ CallbackHelper callback1;
+ int err = fillTransaction(transaction, &callback1, layer);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+
+ // Try to present 100ms in the future
+ nsecs_t time = systemTime() + (100 * 1e6);
+
+ transaction.setDesiredPresentTime(time);
+ transaction.apply();
+
+ ExpectedResult expected1;
+ expected1.addSurface(ExpectedResult::Transaction::PRESENTED, layer);
+ expected1.addExpectedPresentTime(time);
+
+ CallbackHelper callback2;
+ err = fillTransaction(transaction, &callback2, layer);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+
+ // Try to present 33ms after the first frame
+ time += (33.3 * 1e6);
+
+ transaction.setDesiredPresentTime(time);
+ transaction.apply();
+
+ ExpectedResult expected2;
+ expected2.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
+ ExpectedResult::Buffer::ACQUIRED,
+ ExpectedResult::PreviousBuffer::RELEASED);
+ expected2.addExpectedPresentTime(time);
+
+ EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected1, true));
+ EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected2, true));
+}
+
+TEST_F(LayerCallbackTest, DesiredPresentTime_OutOfOrder) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+
+ Transaction transaction;
+ CallbackHelper callback1;
+ int err = fillTransaction(transaction, &callback1, layer);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+
+ // Try to present 100ms in the future
+ nsecs_t time = systemTime() + (100 * 1e6);
+
+ transaction.setDesiredPresentTime(time);
+ transaction.apply();
+
+ ExpectedResult expected1;
+ expected1.addSurface(ExpectedResult::Transaction::PRESENTED, layer);
+ expected1.addExpectedPresentTime(time);
+
+ CallbackHelper callback2;
+ err = fillTransaction(transaction, &callback2, layer);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+
+ // Try to present 33ms before the previous frame
+ time -= (33.3 * 1e6);
+
+ transaction.setDesiredPresentTime(time);
+ transaction.apply();
+
+ ExpectedResult expected2;
+ expected2.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
+ ExpectedResult::Buffer::ACQUIRED,
+ ExpectedResult::PreviousBuffer::RELEASED);
+
+ EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected1, true));
+ EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected2, true));
+}
+
+TEST_F(LayerCallbackTest, DesiredPresentTime_Past) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+
+ Transaction transaction;
+ CallbackHelper callback;
+ int err = fillTransaction(transaction, &callback, layer);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+
+ // Try to present 100ms in the past
+ nsecs_t time = systemTime() - (100 * 1e6);
+
+ transaction.setDesiredPresentTime(time);
+ transaction.apply();
+
+ ExpectedResult expected;
+ expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer);
+ expected.addExpectedPresentTime(systemTime());
+ EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true));
+}
+
class LayerUpdateTest : public LayerTransactionTest {
protected:
virtual void SetUp() {